diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /other | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'other')
-rw-r--r-- | other/Makefile | 75 | ||||
-rw-r--r-- | other/pamarith.c | 503 | ||||
-rw-r--r-- | other/pambayer.c | 297 | ||||
-rw-r--r-- | other/pamchannel.c | 199 | ||||
-rw-r--r-- | other/pamdepth.c | 175 | ||||
-rw-r--r-- | other/pamendian.c | 70 | ||||
-rw-r--r-- | other/pamlookup.c | 299 | ||||
-rw-r--r-- | other/pampick.c | 250 | ||||
-rw-r--r-- | other/pamsplit.c | 187 | ||||
-rw-r--r-- | other/pamstack.c | 240 | ||||
-rw-r--r-- | other/pamsummcol.c | 257 | ||||
-rw-r--r-- | other/pamx/COPYRIGHT | 72 | ||||
-rw-r--r-- | other/pamx/Makefile | 43 | ||||
-rw-r--r-- | other/pamx/Makefile2 | 51 | ||||
-rw-r--r-- | other/pamx/fill.c | 82 | ||||
-rw-r--r-- | other/pamx/fill.h | 16 | ||||
-rw-r--r-- | other/pamx/image.c | 331 | ||||
-rw-r--r-- | other/pamx/image.h | 90 | ||||
-rw-r--r-- | other/pamx/pamx.c | 364 | ||||
-rw-r--r-- | other/pamx/send.c | 872 | ||||
-rw-r--r-- | other/pamx/send.h | 38 | ||||
-rw-r--r-- | other/pamx/valtomem.h | 65 | ||||
-rw-r--r-- | other/pamx/window.c | 1209 | ||||
-rw-r--r-- | other/pamx/window.h | 38 | ||||
-rw-r--r-- | other/pamx/ximageinfo.h | 25 | ||||
-rw-r--r-- | other/pnmcolormap.c | 973 | ||||
-rw-r--r-- | other/ppmdcfont.c | 200 | ||||
-rw-r--r-- | other/ppmddumpfont.c | 89 | ||||
-rw-r--r-- | other/ppmdmkfont.c | 705 | ||||
-rw-r--r-- | other/ppmsvgalib.c | 283 | ||||
-rwxr-xr-x | other/ppmtomap | 5 |
31 files changed, 8103 insertions, 0 deletions
diff --git a/other/Makefile b/other/Makefile new file mode 100644 index 00000000..87f92f96 --- /dev/null +++ b/other/Makefile @@ -0,0 +1,75 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = other +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +SUBDIRS = pamx + +ifneq ($(LINUXSVGALIB),NONE) + ifneq ($(LINUXSVGAHDR_DIR),) + INCLUDES += -I$(LINUXSVGAHDR_DIR) + endif +endif + +# We tend to separate out the build targets so that we don't have +# any more dependencies for a given target than it really needs. +# That way, if there is a problem with a dependency, we can still +# successfully build all the stuff that doesn't depend upon it. +# This package is so big, it's useful even when some parts won't +# build. + +PORTBINARIES = pamarith pambayer pamchannel pamdepth \ + pamendian pamlookup pampick pamsplit \ + pamstack pamsummcol pnmcolormap \ + ppmdcfont ppmddumpfont ppmdmkfont + +BINARIES = $(PORTBINARIES) + +ifneq ($(LINUXSVGALIB),NONE) + BINARIES += ppmsvgalib +endif + +SCRIPTS = ppmtomap + +OBJECTS = $(BINARIES:%=%.o) + +# We don't include programs that have special library dependencies in the +# merge scheme, because we don't want those dependencies to prevent us +# from building all the other programs. + +MERGEBINARIES = $(BINARIES) +MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) + +.PHONY: all +all: $(BINARIES) $(SUBDIRS:%=%/all) + +include $(SRCDIR)/Makefile.common + +ppmsvgalib: %: %.o $(NETPBMLIB) $(LIBOPT) + $(LD) $(LDFLAGS) -o $@ $< \ + $(shell $(LIBOPT) $(NETPBMLIB) $(LINUXSVGALIB)) \ + $(MATHLIB) $(LDLIBS) \ + $(LADD) + +install.bin: install.bin.local +.PHONY: install.bin.local +install.bin.local: $(PKGDIR)/bin +# Remember that $(SYMLINK) might just be a copy command. +# In July 2002, pamarith replaced pnmarith + cd $(PKGDIR)/bin ; \ + rm -f pnmarith ; \ + $(SYMLINK) pamarith$(EXE) pnmarith +# In December 2005, pamsplit replaced pnmsplit + cd $(PKGDIR)/bin ; \ + rm -f pnmsplit ; \ + $(SYMLINK) pamsplit$(EXE) pnmsplit +# In February 2006, pamdepth replaced pnmdepth + cd $(PKGDIR)/bin ; \ + rm -f pnmdepth ; \ + $(SYMLINK) pamdepth$(EXE) pnmdepth + +FORCE: diff --git a/other/pamarith.c b/other/pamarith.c new file mode 100644 index 00000000..c1e7f1ed --- /dev/null +++ b/other/pamarith.c @@ -0,0 +1,503 @@ +#include <assert.h> +#include <string.h> + +#include "shhopt.h" +#include "pam.h" + +enum function {FN_ADD, FN_SUBTRACT, FN_MULTIPLY, FN_DIVIDE, FN_DIFFERENCE, + FN_MINIMUM, FN_MAXIMUM, FN_MEAN, FN_COMPARE, + FN_AND, FN_OR, FN_NAND, FN_NOR, FN_XOR, + FN_SHIFTLEFT, FN_SHIFTRIGHT + }; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input1Filespec; + const char *input2Filespec; + enum function function; +}; + + + +static void +parseCommandLine(int argc, char ** const argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int addSpec, subtractSpec, multiplySpec, divideSpec, + differenceSpec, + minimumSpec, maximumSpec, meanSpec, compareSpec, + andSpec, orSpec, nandSpec, norSpec, xorSpec, + shiftleftSpec, shiftrightSpec; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "add", OPT_FLAG, NULL, &addSpec, 0); + OPTENT3(0, "subtract", OPT_FLAG, NULL, &subtractSpec, 0); + OPTENT3(0, "multiply", OPT_FLAG, NULL, &multiplySpec, 0); + OPTENT3(0, "divide", OPT_FLAG, NULL, ÷Spec, 0); + OPTENT3(0, "difference", OPT_FLAG, NULL, &differenceSpec, 0); + OPTENT3(0, "minimum", OPT_FLAG, NULL, &minimumSpec, 0); + OPTENT3(0, "maximum", OPT_FLAG, NULL, &maximumSpec, 0); + OPTENT3(0, "mean", OPT_FLAG, NULL, &meanSpec, 0); + OPTENT3(0, "compare", OPT_FLAG, NULL, &compareSpec, 0); + OPTENT3(0, "and", OPT_FLAG, NULL, &andSpec, 0); + OPTENT3(0, "or", OPT_FLAG, NULL, &orSpec, 0); + OPTENT3(0, "nand", OPT_FLAG, NULL, &nandSpec, 0); + OPTENT3(0, "nor", OPT_FLAG, NULL, &norSpec, 0); + OPTENT3(0, "xor", OPT_FLAG, NULL, &xorSpec, 0); + OPTENT3(0, "shiftleft", OPT_FLAG, NULL, &shiftleftSpec, 0); + OPTENT3(0, "shiftright", OPT_FLAG, NULL, &shiftrightSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (addSpec + subtractSpec + multiplySpec + divideSpec + differenceSpec + + minimumSpec + maximumSpec + meanSpec + compareSpec + + andSpec + orSpec + nandSpec + norSpec + xorSpec + + shiftleftSpec + shiftrightSpec > 1) + pm_error("You may specify only one function"); + + if (argc-1 != 2) + pm_error("You must specify two arguments: the files which are " + "the operands of the " + "dyadic function. You specified %d", argc-1); + else { + cmdlineP->input1Filespec = argv[1]; + cmdlineP->input2Filespec = argv[2]; + } + + if (addSpec) + cmdlineP->function = FN_ADD; + else if (subtractSpec) + cmdlineP->function = FN_SUBTRACT; + else if (multiplySpec) + cmdlineP->function = FN_MULTIPLY; + else if (divideSpec) + cmdlineP->function = FN_DIVIDE; + else if (differenceSpec) + cmdlineP->function = FN_DIFFERENCE; + else if (minimumSpec) + cmdlineP->function = FN_MINIMUM; + else if (maximumSpec) + cmdlineP->function = FN_MAXIMUM; + else if (meanSpec) + cmdlineP->function = FN_MEAN; + else if (compareSpec) + cmdlineP->function = FN_COMPARE; + else if (andSpec) + cmdlineP->function = FN_AND; + else if (orSpec) + cmdlineP->function = FN_OR; + else if (nandSpec) + cmdlineP->function = FN_NAND; + else if (norSpec) + cmdlineP->function = FN_NOR; + else if (xorSpec) + cmdlineP->function = FN_XOR; + else if (shiftleftSpec) + cmdlineP->function = FN_SHIFTLEFT; + else if (shiftrightSpec) + cmdlineP->function = FN_SHIFTRIGHT; + else + pm_error("You must specify a function (e.g. '-add')"); +} + + + +enum category { + CATEGORY_FRACTIONAL_ARITH, + /* Arithmetic in which each sample represents a the fraction + sample/maxval. + */ + CATEGORY_BITSTRING, + /* And, Or, etc. Maxval isn't a scale factor at all; it's a mask. */ + CATEGORY_SHIFT + /* Left argument is a bit string, but right argument is a whole + number (left maxval is a mask; right maxval is meaningless). + */ +}; + + + +static enum category +functionCategory(enum function const function) { + + enum category retval; + + switch (function) { + case FN_ADD: + case FN_SUBTRACT: + case FN_DIFFERENCE: + case FN_MINIMUM: + case FN_MAXIMUM: + case FN_MEAN: + case FN_COMPARE: + case FN_MULTIPLY: + case FN_DIVIDE: + retval = CATEGORY_FRACTIONAL_ARITH; + break; + case FN_AND: + case FN_OR: + case FN_NAND: + case FN_NOR: + case FN_XOR: + retval = CATEGORY_BITSTRING; + break; + case FN_SHIFTLEFT: + case FN_SHIFTRIGHT: + retval = CATEGORY_SHIFT; + break; + } + return retval; +} + + + +static void +computeOutputType(struct pam * const outpamP, + struct pam const inpam1, + struct pam const inpam2, + enum function const function) { + + outpamP->size = sizeof(struct pam); + outpamP->len = PAM_STRUCT_SIZE(tuple_type); + outpamP->file = stdout; + outpamP->format = MAX(inpam1.format, inpam2.format); + outpamP->plainformat = FALSE; + outpamP->height = inpam1.height; + outpamP->width = inpam1.width; + outpamP->depth = MAX(inpam1.depth, inpam2.depth); + + switch (functionCategory(function)) { + case CATEGORY_FRACTIONAL_ARITH: + if (function == FN_COMPARE) + outpamP->maxval = 2; + else + outpamP->maxval = MAX(inpam1.maxval, inpam2.maxval); + break; + case CATEGORY_BITSTRING: + if (inpam2.maxval != inpam1.maxval) + pm_error("For a bit string operation, the maxvals of the " + "left and right image must be the same. You have " + "left=%u and right=%u", + (unsigned)inpam1.maxval, (unsigned)inpam2.maxval); + + if (pm_bitstomaxval(pm_maxvaltobits(inpam1.maxval)) != inpam1.maxval) + pm_error("For a bit string operation, the maxvals of the inputs " + "must be a full binary count, i.e. a power of two " + "minus one such as 0xff. You have 0x%x", + (unsigned)inpam1.maxval); + + outpamP->maxval = inpam1.maxval; + break; + case CATEGORY_SHIFT: + if (pm_bitstomaxval(pm_maxvaltobits(inpam1.maxval)) != inpam1.maxval) + pm_error("For a bit shift operation, the maxval of the left " + "input image " + "must be a full binary count, i.e. a power of two " + "minus one such as 0xff. You have 0x%x", + (unsigned)inpam1.maxval); + outpamP->maxval = inpam1.maxval; + } + outpamP->bytes_per_sample = (pm_maxvaltobits(outpamP->maxval)+7)/8; + strcpy(outpamP->tuple_type, inpam1.tuple_type); +} + + + +static samplen +applyNormalizedFunction(enum function const function, + samplen const leftArg, + samplen const rightArg) { + + samplen result; + + switch (function) { + case FN_ADD: + result = MIN(1., leftArg + rightArg); + break; + case FN_SUBTRACT: + result = MAX(0., leftArg - rightArg); + break; + case FN_MULTIPLY: + result = leftArg * rightArg; + break; + case FN_DIVIDE: + result = (rightArg > leftArg) ? + leftArg / rightArg : 1.; + break; + case FN_DIFFERENCE: + result = leftArg > rightArg ? + leftArg - rightArg : rightArg - leftArg; + break; + case FN_MINIMUM: + result = MIN(leftArg, rightArg); + break; + case FN_MAXIMUM: + result = MAX(leftArg, rightArg); + break; + case FN_MEAN: + result = (leftArg + rightArg) / 2.0; + break; + case FN_COMPARE: + result = + leftArg > rightArg ? 1. : leftArg < rightArg ? 0. : .5; + break; + default: + pm_error("Internal error. applyNormalizedFunction() called " + "for a function it doesn't know how to do: %u", function); + } + + return result; +} + + + +static void +doNormalizedArith(struct pam * const inpam1P, + struct pam * const inpam2P, + struct pam * const outpamP, + enum function const function) { + + tuplen * tuplerown1; + tuplen * tuplerown2; + tuplen * tuplerownOut; + unsigned int row; + + tuplerown1 = pnm_allocpamrown(inpam1P); + tuplerown2 = pnm_allocpamrown(inpam2P); + tuplerownOut = pnm_allocpamrown(outpamP); + + for (row = 0; row < outpamP->height; ++row) { + unsigned int col; + pnm_readpamrown(inpam1P, tuplerown1); + pnm_readpamrown(inpam2P, tuplerown2); + + for (col = 0; col < outpamP->width; ++col) { + unsigned int outplane; + + for (outplane = 0; outplane < outpamP->depth; ++outplane) { + unsigned int const plane1 = MIN(outplane, inpam1P->depth-1); + unsigned int const plane2 = MIN(outplane, inpam2P->depth-1); + + tuplerownOut[col][outplane] = + applyNormalizedFunction(function, + tuplerown1[col][plane1], + tuplerown2[col][plane2]); + assert(tuplerownOut[col][outplane] >= 0.); + assert(tuplerownOut[col][outplane] <= 1.); + + } + } + pnm_writepamrown(outpamP, tuplerownOut); + } + + pnm_freepamrown(tuplerown1); + pnm_freepamrown(tuplerown2); + pnm_freepamrown(tuplerownOut); +} + + + +static sample +applyUnNormalizedFunction(enum function const function, + sample const leftArg, + sample const rightArg, + sample const maxval) { +/*---------------------------------------------------------------------------- + Apply dyadic function 'function' to the arguments 'leftArg' and + 'rightArg', assuming both are based on the same maxval 'maxval'. + Return a value which is also a fraction of 'maxval'. + + Exception: for the shift operations, 'rightArg' is not based on any + maxval. It is an absolute bit count. +-----------------------------------------------------------------------------*/ + sample result; + + switch (function) { + case FN_ADD: + result = MIN(maxval, leftArg + rightArg); + break; + case FN_SUBTRACT: + result = MAX(0, (int)leftArg - (int)rightArg); + break; + case FN_DIFFERENCE: + result = leftArg > rightArg ? leftArg - rightArg : rightArg - leftArg; + break; + case FN_MINIMUM: + result = MIN(leftArg, rightArg); + break; + case FN_MAXIMUM: + result = MAX(leftArg, rightArg); + break; + case FN_MEAN: + result = (leftArg + rightArg + 1) / 2; + break; + case FN_COMPARE: + result = leftArg > rightArg ? 2 : leftArg < rightArg ? 0 : 1; + break; + case FN_MULTIPLY: + result = (leftArg * rightArg + maxval/2) / maxval; + break; + case FN_DIVIDE: + result = (rightArg > leftArg) ? + (leftArg * maxval + rightArg/2) / rightArg : maxval; + break; + + case FN_AND: + result = leftArg & rightArg; + break; + case FN_OR: + result = leftArg | rightArg; + break; + case FN_NAND: + result = ~(leftArg & rightArg) & maxval; + break; + case FN_NOR: + result = ~(leftArg | rightArg) & maxval; + break; + case FN_XOR: + result = leftArg ^ rightArg; + break; + case FN_SHIFTLEFT: + result = (leftArg << rightArg) & maxval; + break; + case FN_SHIFTRIGHT: + result = leftArg >> rightArg; + break; + default: + pm_error("Internal error. applyUnNormalizedFunction() called " + "for a function it doesn't know how to do: %u", function); + } + + return result; +} + + + +static void +doUnNormalizedArith(struct pam * const inpam1P, + struct pam * const inpam2P, + struct pam * const outpamP, + enum function const function) { +/*---------------------------------------------------------------------------- + Take advantage of the fact that both inputs and the output use the same + maxval to do the computation without time-consuming normalization of + sample values. +-----------------------------------------------------------------------------*/ + sample const maxval = outpamP->maxval; + + tuple * tuplerow1; + tuple * tuplerow2; + tuple * tuplerowOut; + unsigned int row; + + /* Input conditions: */ + assert(inpam1P->maxval == maxval); + assert(inpam2P->maxval == maxval); + assert(outpamP->maxval == maxval); + + tuplerow1 = pnm_allocpamrow(inpam1P); + tuplerow2 = pnm_allocpamrow(inpam2P); + tuplerowOut = pnm_allocpamrow(outpamP); + + for (row = 0; row < outpamP->height; ++row) { + unsigned int col; + pnm_readpamrow(inpam1P, tuplerow1); + pnm_readpamrow(inpam2P, tuplerow2); + + for (col = 0; col < outpamP->width; ++col) { + unsigned int outplane; + + for (outplane = 0; outplane < outpamP->depth; ++outplane) { + unsigned int const plane1 = MIN(outplane, inpam1P->depth-1); + unsigned int const plane2 = MIN(outplane, inpam2P->depth-1); + + tuplerowOut[col][outplane] = + applyUnNormalizedFunction(function, + tuplerow1[col][plane1], + tuplerow2[col][plane2], + maxval); + + assert(tuplerowOut[col][outplane] >= 0); + assert(tuplerowOut[col][outplane] <= outpamP->maxval); + } + } + pnm_writepamrow(outpamP, tuplerowOut); + } + + pnm_freepamrow(tuplerow1); + pnm_freepamrow(tuplerow2); + pnm_freepamrow(tuplerowOut); +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + struct pam inpam1; + struct pam inpam2; + struct pam outpam; + FILE * if1P; + FILE * if2P; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + if1P = pm_openr(cmdline.input1Filespec); + if2P = pm_openr(cmdline.input2Filespec); + + pnm_readpaminit(if1P, &inpam1, PAM_STRUCT_SIZE(tuple_type)); + pnm_readpaminit(if2P, &inpam2, PAM_STRUCT_SIZE(tuple_type)); + + if (inpam1.width != inpam2.width || inpam1.height != inpam2.height) + pm_error("The two images must be the same width and height. " + "The first is %ux%ux%u, but the second is %ux%ux%u", + inpam1.width, inpam1.height, inpam1.depth, + inpam2.width, inpam2.height, inpam2.depth); + + if (inpam1.depth != 1 && inpam2.depth != 1 && inpam1.depth != inpam2.depth) + pm_error("The two images must have the same depth or one of them " + "must have depth 1. The first has depth %u and the second " + "has depth %u", inpam1.depth, inpam2.depth); + + computeOutputType(&outpam, inpam1, inpam2, cmdline.function); + + pnm_writepaminit(&outpam); + + switch (functionCategory(cmdline.function)) { + case CATEGORY_FRACTIONAL_ARITH: + if (inpam1.maxval == inpam2.maxval) + doUnNormalizedArith(&inpam1, &inpam2, &outpam, cmdline.function); + else + doNormalizedArith(&inpam1, &inpam2, &outpam, cmdline.function); + break; + case CATEGORY_BITSTRING: + case CATEGORY_SHIFT: + doUnNormalizedArith(&inpam1, &inpam2, &outpam, cmdline.function); + break; + } + + pm_close(if1P); + pm_close(if2P); + + return 0; +} diff --git a/other/pambayer.c b/other/pambayer.c new file mode 100644 index 00000000..aa4beef1 --- /dev/null +++ b/other/pambayer.c @@ -0,0 +1,297 @@ +/* + + Bayer matrix conversion tool + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + Copyright Alexandre Becoulet <diaxen AT free DOT fr> + + Completely rewritten for Netpbm by Bryan Henderson August 2005. +*/ + +#include <unistd.h> +#include <stdio.h> + +#include "pam.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "nstring.h" + + +enum bayerType { + BAYER1, + BAYER2, + BAYER3, + BAYER4 +}; + +struct cmdlineInfo { + const char * inputFilespec; + enum bayerType bayerType; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + unsigned int typeSpec; + unsigned int type; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "type", OPT_UINT, &type, + &typeSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else if (argc-1 > 1) + pm_error("There is at most one argument -- the input file. " + "You specified %u", argc-1); + + if (!typeSpec) + pm_error("You must specify the -type option"); + else { + switch (type) { + case 1: cmdlineP->bayerType = BAYER1; break; + case 2: cmdlineP->bayerType = BAYER2; break; + case 3: cmdlineP->bayerType = BAYER3; break; + case 4: cmdlineP->bayerType = BAYER4; break; + } + } +} + + + +static void +calc_4(const struct pam * const pamP, + tuple ** const intuples, + tuple ** const outtuples, + unsigned int const plane, + unsigned int const xoffset, + unsigned int const yoffset) { +/*---------------------------------------------------------------------------- + X . X + . . . + X . X +-----------------------------------------------------------------------------*/ + unsigned int y; + + for (y = yoffset; y < pamP->height; y += 2) { + unsigned int x; + for (x = xoffset; x + 2 < pamP->width; x += 2) { + outtuples[y][x][plane] = intuples[y][x][0]; + outtuples[y][x + 1][plane] = + (intuples[y][x][0] + intuples[y][x + 2][0]) / 2; + } + } + for (y = yoffset; y + 2 < pamP->height; y += 2) { + unsigned int x; + for (x = xoffset; x < pamP->width; ++x) + outtuples[y + 1][x][plane] = + (outtuples[y][x][plane] + outtuples[y + 2][x][plane]) / 2; + } +} + + + +static void +calc_5(const struct pam * const pamP, + tuple ** const intuples, + tuple ** const outtuples, + unsigned int const plane, + unsigned int const xoffset, + unsigned int const yoffset) { +/*---------------------------------------------------------------------------- + . X . + X . X + . X . +-----------------------------------------------------------------------------*/ + unsigned int y; + unsigned int j; + + j = 0; /* initial value */ + + for (y = yoffset; y + 2 < pamP->height; ++y) { + unsigned int x; + for (x = xoffset + j; x + 2 < pamP->width; x += 2) { + outtuples[y][x + 1][plane] = intuples[y][x + 1][0]; + outtuples[y + 1][x + 1][plane] = + (intuples[y][x + 1][0] + intuples[y + 1][x][0] + + intuples[y + 2][x + 1][0] + intuples[y + 1][x + 2][0]) / 4; + } + j = 1 - j; + } +} + + + +struct compAction { + unsigned int xoffset; + unsigned int yoffset; + void (*calc)(const struct pam * const pamP, + tuple ** const intuples, + tuple ** const outtuples, + unsigned int const plane, + unsigned int const xoffset, + unsigned int const yoffset); +}; + + + +static struct compAction const comp_1[3] = { +/*---------------------------------------------------------------------------- + G B G B + R G R G + G B G B + R G R G +-----------------------------------------------------------------------------*/ + + { 0, 1, calc_4 }, + { 0, 1, calc_5 }, + { 1, 0, calc_4 } +}; + +static struct compAction const comp_2[3] = { +/*---------------------------------------------------------------------------- + R G R G + G B G B + R G R G + G B G B +-----------------------------------------------------------------------------*/ + { 0, 0, calc_4 }, + { 0, 0, calc_5 }, + { 1, 1, calc_4 } +}; + +static struct compAction const comp_3[3] = { +/*---------------------------------------------------------------------------- + B G B G + G R G R + B G B G + G R G R +-----------------------------------------------------------------------------*/ + { 1, 1, calc_4 }, + { 0, 0, calc_5 }, + { 0, 0, calc_4 } +}; + +static struct compAction const comp_4[3] = { +/*---------------------------------------------------------------------------- + G R G R + B G B G + G R G R + B G B G +-----------------------------------------------------------------------------*/ + { 1, 0, calc_4 }, + { 0, 1, calc_5 }, + { 0, 1, calc_4 } +}; + + + +static void +makeOutputPam(const struct pam * const inpamP, + struct pam * const outpamP) { + + outpamP->size = sizeof(*outpamP); + outpamP->len = PAM_STRUCT_SIZE(tuple_type); + outpamP->file = stdout; + outpamP->format = PAM_FORMAT; + outpamP->plainformat = 0; + outpamP->width = inpamP->width; + outpamP->height = inpamP->height; + outpamP->depth = 3; + outpamP->maxval = inpamP->maxval; + outpamP->bytes_per_sample = inpamP->bytes_per_sample; + STRSCPY(outpamP->tuple_type, "RGB"); +} + + + +static const struct compAction * +actionTableForType(enum bayerType const bayerType) { + + const struct compAction * retval; + + switch (bayerType) { + case BAYER1: retval = comp_1; break; + case BAYER2: retval = comp_2; break; + case BAYER3: retval = comp_3; break; + case BAYER4: retval = comp_4; break; + } + return retval; +} + + + +int +main(int argc, char **argv) { + + struct cmdlineInfo cmdline; + FILE * ifP; + struct pam inpam; + struct pam outpam; + tuple ** intuples; + tuple ** outtuples; + const struct compAction * compActionTable; + unsigned int plane; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + intuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + compActionTable = actionTableForType(cmdline.bayerType); + + makeOutputPam(&inpam, &outpam); + + outtuples = pnm_allocpamarray(&outpam); + + for (plane = 0; plane < 3; ++plane) { + struct compAction const compAction = compActionTable[plane]; + + compAction.calc(&inpam, intuples, outtuples, plane, + compAction.xoffset, compAction.yoffset); + } + pnm_writepam(&outpam, outtuples); + + pnm_freepamarray(outtuples, &outpam); + pnm_freepamarray(intuples, &inpam); + + return 0; +} diff --git a/other/pamchannel.c b/other/pamchannel.c new file mode 100644 index 00000000..ac7bae65 --- /dev/null +++ b/other/pamchannel.c @@ -0,0 +1,199 @@ +/*---------------------------------------------------------------------------- + pamchannel +------------------------------------------------------------------------------ + Part of the Netpbm package. + + Extract specified channels (planes) from a PAM image + + + By Bryan Henderson, San Jose CA 2000.08.05 + + Contributed to the public domain by its author 2000.08.05. +-----------------------------------------------------------------------------*/ + +#include <string.h> + +#include "pam.h" +#include "shhopt.h" +#include "mallocvar.h" + +#define MAX_CHANNELS 16 + /* The most channels we allow user to specify */ + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* Filename of input files */ + const char * tupletype; /* Tuple type for output PAM */ + int n_channel; + /* The number of channels to extract. At least 1, at most 16. */ + unsigned int channel_to_extract[MAX_CHANNELS]; + /* The channel numbers to extract, in order. */ +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + extern struct pam pam; /* Just so we can look at field sizes */ + + unsigned int option_def_index; + unsigned int infileSpec, tupletypeSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "infile", OPT_STRING, &cmdlineP->inputFileName, + &infileSpec, 0); + OPTENT3(0, "tupletype", OPT_STRING, &cmdlineP->tupletype, + &tupletypeSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!infileSpec) + cmdlineP->inputFileName = "-"; + + if (!tupletypeSpec) + cmdlineP->tupletype = ""; + else + if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type)) + pm_error("Tuple type name specified is too long. Maximum of " + "%u characters allowed.", sizeof(pam.tuple_type)); + + cmdlineP->n_channel = 0; /* initial value */ + { + int argn; + for (argn = 1; argn < argc; argn++) { + int n; + char *endptr; + + if (cmdlineP->n_channel >= MAX_CHANNELS) + pm_error("You may not specify more than %d channels.", + MAX_CHANNELS); + + n = strtol(argv[argn], &endptr, 10); + if (n < 0) + pm_error("Channel numbers cannot be negative. " + "You specified %d", n); + if (endptr == NULL) + pm_error("non-numeric channel number argument: '%s'", + argv[argn]); + + cmdlineP->channel_to_extract[cmdlineP->n_channel++] = n; + } + } + if (cmdlineP->n_channel < 1) + pm_error("You must specify at least one channel to extract."); +} + + + +static void +validateChannels(int const n_channel, + unsigned int const channels[], + int const depth) { + + int i; + + for (i = 0; i < n_channel; i++) + if (channels[i] > depth-1) + pm_error("You specified channel number %d. The highest numbered\n" + "channel in the input image is %d.", + channels[i], depth-1); +} + + + +static void +doOneImage(FILE * const ifP, + FILE * const ofP, + unsigned int const nChannel, + unsigned int const channelToExtract[], + const char * const tupletype) { + + struct pam inpam; /* Input PAM image */ + struct pam outpam; /* Output PAM image */ + + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + validateChannels(nChannel, channelToExtract, inpam.depth); + + outpam = inpam; /* Initial value */ + outpam.file = ofP; + outpam.depth = nChannel; + outpam.format = PAM_FORMAT; + strcpy(outpam.tuple_type, tupletype); + + pnm_writepaminit(&outpam); + + { + tuple * inrow; + tuple * outrow; + + inrow = pnm_allocpamrow(&inpam); + outrow = pnm_allocpamrow(&outpam); + { + unsigned int row; + + for (row = 0; row < inpam.height; ++row) { + unsigned int col; + + pnm_readpamrow(&inpam, inrow); + + for (col = 0; col < inpam.width; ++col) { + unsigned int plane; + for (plane = 0; plane < nChannel; ++plane) + outrow[col][plane] = + inrow[col][channelToExtract[plane]]; + } + pnm_writepamrow(&outpam, outrow); + } + } + pnm_freepamrow(outrow); + pnm_freepamrow(inrow); + } +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + bool eof; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + eof = FALSE; + while (!eof) { + doOneImage(ifP, stdout, cmdline.n_channel, cmdline.channel_to_extract, + cmdline.tupletype); + + pnm_nextimage(ifP, &eof); + } + + pm_close(ifP); + + return 0; +} + diff --git a/other/pamdepth.c b/other/pamdepth.c new file mode 100644 index 00000000..0c4490ed --- /dev/null +++ b/other/pamdepth.c @@ -0,0 +1,175 @@ +/*============================================================================= + pamdepth +=============================================================================== + Change the maxval in a Netpbm image. + + This replaces Pnmdepth. + + By Bryan Henderson January 2006. + + Contributed to the public domain by its author. +=============================================================================*/ +#include <assert.h> + +#include "shhopt.h" +#include "mallocvar.h" +#include "pam.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; + unsigned int newMaxval; + unsigned int verbose; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec strings we return are stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "verbose", OPT_STRING, NULL, + &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + pm_error("You must specify at least one argument -- the new maxval"); + else { + int const intval = atoi(argv[1]); + + if (intval < 1) + pm_error("New maxval must be at least 1. You specified %d", + intval); + else if (intval > PNM_OVERALLMAXVAL) + pm_error("newmaxval (%d) is too large.\n" + "The maximum allowed by the PNM formats is %d.", + intval, PNM_OVERALLMAXVAL); + else + cmdlineP->newMaxval = intval; + + if (argc-1 < 2) + cmdlineP->inputFileName = "-"; + else + cmdlineP->inputFileName = argv[2]; + } +} + + + +static void +createSampleMap(sample const oldMaxval, + sample const newMaxval, + sample** const sampleMapP) { + + unsigned int i; + + sample * sampleMap; + + MALLOCARRAY_NOFAIL(sampleMap, oldMaxval+1); + + for (i = 0; i <= oldMaxval; ++i) + sampleMap[i] = (i * newMaxval + oldMaxval / 2) / oldMaxval; + + *sampleMapP = sampleMap; +} + + + +static void +transformRaster(struct pam * const inpamP, + struct pam * const outpamP) { + + tuple * tuplerow; + unsigned int row; + sample * sampleMap; /* malloc'ed */ + + createSampleMap(inpamP->maxval, outpamP->maxval, &sampleMap); + + assert(inpamP->height == outpamP->height); + assert(inpamP->width == outpamP->width); + assert(inpamP->depth == outpamP->depth); + + tuplerow = pnm_allocpamrow(inpamP); + + for (row = 0; row < inpamP->height; ++row) { + unsigned int col; + pnm_readpamrow(inpamP, tuplerow); + + for (col = 0; col < inpamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < inpamP->depth; ++plane) + tuplerow[col][plane] = sampleMap[tuplerow[col][plane]]; + } + pnm_writepamrow(outpamP, tuplerow); + } + + pnm_freepamrow(tuplerow); + + free(sampleMap); +} + + + +int +main(int argc, + char * argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + struct pam inpam; + struct pam outpam; + bool eof; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + eof = FALSE; + while (!eof) { + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + outpam = inpam; /* initial value */ + + outpam.file = stdout; + outpam.maxval = cmdline.newMaxval; + + if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE) { + pm_message( "promoting from PBM to PGM" ); + outpam.format = PGM_TYPE; + } else + outpam.format = inpam.format; + + pnm_writepaminit(&outpam); + + transformRaster(&inpam, &outpam); + + pnm_nextimage(ifP, &eof); + } + pm_close(ifP); + pm_close(stdout); + + return 0; +} diff --git a/other/pamendian.c b/other/pamendian.c new file mode 100644 index 00000000..16e1fe56 --- /dev/null +++ b/other/pamendian.c @@ -0,0 +1,70 @@ +/****************************************************************************** + pnmendian +******************************************************************************* + + Reverse the endianness of multi-byte samples in a Netpbm stream. + I.e. convert between the true format and the little endian variation of + it. +******************************************************************************/ + +#include "pam.h" + + +static sample +reverseSample(sample const insample, unsigned int const bytesPerSample) { +/*---------------------------------------------------------------------------- + Return a sample whose value is the least significant + 'bytes_per_sample' bytes, in reverse order. +-----------------------------------------------------------------------------*/ + unsigned int bytePos; + sample shiftedInsample; + sample outsample; + shiftedInsample = insample; /* initial value */ + outsample = 0; /* initial value */ + for (bytePos = 0; bytePos < bytesPerSample; ++bytePos) { + outsample = outsample * 256 + (shiftedInsample & 0xff); + shiftedInsample >>= 8; + } + return outsample; +} + + + +int main(int argc, char *argv[]) { + + struct pam inpam, outpam; + tuple * intuplerow; + tuple * outtuplerow; + unsigned int row; + + pnm_init(&argc, argv); + + pnm_readpaminit(stdin, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + outpam = inpam; + outpam.file = stdout; + + pnm_writepaminit(&outpam); + + intuplerow = pnm_allocpamrow(&inpam); + outtuplerow = pnm_allocpamrow(&outpam); + + for (row = 0; row < inpam.height; row++) { + unsigned int col; + pnm_readpamrow(&inpam, intuplerow); + for (col = 0; col < inpam.width; col++) { + unsigned int plane; + for (plane = 0; plane < inpam.depth; plane++) + outtuplerow[col][plane] = + reverseSample(intuplerow[col][plane], + inpam.bytes_per_sample); + } + pnm_writepamrow(&outpam, outtuplerow); + } + + pnm_freepamrow(outtuplerow); + pnm_freepamrow(intuplerow); + + exit(0); +} + diff --git a/other/pamlookup.c b/other/pamlookup.c new file mode 100644 index 00000000..2651d596 --- /dev/null +++ b/other/pamlookup.c @@ -0,0 +1,299 @@ +/*============================================================================ + pamlookup +============================================================================== + + Look up integers or ordered pairs from an index image in a lookup table and + produce a corresponding image containing the results of the lookups. + + The index image and lookup table are PAM images. The output image is + a PAM image with the width and height of the index image and tuples of + the kind in the lookup table. + + By Bryan Henderson, San Jose CA 2002.11.10 + +============================================================================*/ + +#include "pam.h" +#include "shhopt.h" +#include "pm_system.h" +#include "nstring.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *indexFilespec; + char *lookupFilespec; + char *missingcolor; /* -missingcolor value. null if not specified */ + unsigned int fit; /* -fit option */ +}; + + + +static void +parseCommandLine(int argc, char ** const argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int lookupfileSpec, missingcolorSpec; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "lookupfile", OPT_STRING, &cmdlineP->lookupFilespec, + &lookupfileSpec, 0); + OPTENT3(0, "missingcolor", OPT_STRING, + &cmdlineP->missingcolor, &missingcolorSpec, 0); + OPTENT3(0, "fit", OPT_FLAG, + NULL, &cmdlineP->fit, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!lookupfileSpec) + pm_error("You must specify the -lookupfile option"); + + if (!missingcolorSpec) + cmdlineP->missingcolor = NULL; + + if (argc-1 < 1) + cmdlineP->indexFilespec = "-"; + else + cmdlineP->indexFilespec = argv[1]; +} + + + +static void +fitLookup(tuple ** const inputLookup, + struct pam const inputLookuppam, + tuple *** const fitLookupP, + struct pam * const fitLookuppamP, + unsigned int const cols, + unsigned int const rows) { +/*---------------------------------------------------------------------------- + Scale the lookup table image so that it has dimensions 'cols' x 'rows'. +-----------------------------------------------------------------------------*/ + const char * pamscaleCommand; + struct pamtuples inPamtuples; + struct pamtuples outPamtuples; + + *fitLookuppamP = inputLookuppam; + fitLookuppamP->width = cols; + fitLookuppamP->height = rows; + + asprintfN(&pamscaleCommand, "pamscale -width=%u -height=%u", cols, rows); + + inPamtuples.pamP = (struct pam *) &inputLookuppam; + inPamtuples.tuplesP = (tuple ***) &inputLookup; + outPamtuples.pamP = fitLookuppamP; + outPamtuples.tuplesP = fitLookupP; + + pm_system(&pm_feed_from_pamtuples, &inPamtuples, + &pm_accept_to_pamtuples, &outPamtuples, + pamscaleCommand); + + strfree(pamscaleCommand); +} + + + +static void +getLookup(const char * const lookupFilespec, + unsigned int const indexDegree, + unsigned int const indexMaxval, + tuple *** const lookupP, + struct pam * const lookuppamP, + bool const fit) { + + FILE* lookupfileP; + + struct pam inputLookuppam; + tuple** inputLookup; + + lookupfileP = pm_openr(lookupFilespec); + inputLookup = pnm_readpam(lookupfileP, + &inputLookuppam, PAM_STRUCT_SIZE(tuple_type)); + + pm_close(lookupfileP); + + if (fit) { + fitLookup(inputLookup, inputLookuppam, lookupP, lookuppamP, + indexMaxval + 1, + indexDegree > 1 ? indexMaxval + 1 : 1); + pnm_freepamarray(inputLookup, &inputLookuppam); + } else { + *lookupP = inputLookup; + *lookuppamP = inputLookuppam; + } + + if (indexDegree == 1 && lookuppamP->height != 1) + pm_error("Your index image has integer indices, " + "so the lookup table image must be one row. " + "Yours is %d rows.", + lookuppamP->height); + + if (lookuppamP->width - 1 > indexMaxval) + pm_message("Warning: your lookup table image is wider than " + "the maxval of " + "your index message, so the right end of the lookup " + "table image will have no effect on the output."); + if (indexDegree == 2 && lookuppamP->height - 1 > indexMaxval) + pm_message("Warning: your lookup table image is taller than " + "the maxval of " + "your index message, so the bottom end of the lookup " + "table image has no effect on the output."); +} + + + +static void +computeDefaultTuple(struct cmdlineInfo const cmdline, + tuple ** const lookup, + struct pam * const lookuppamP, + tuple * const defaultTupleP) { + + tuple retval; + + retval = pnm_allocpamtuple(lookuppamP); + + /* Note: "missing color" really makes sense only for a color + lookup, whereas this program allows an arbitrary PAM image as a + lookup table. We should probably check here for a lookup file + that has a visual image tuple type, but we don't out of + laziness. The program probably ought to have a generic + "missing tuple type" option too. + */ + if (cmdline.missingcolor) { + pixel const color = + ppm_parsecolor(cmdline.missingcolor, lookuppamP->maxval); + + if (lookuppamP->depth >= 3) { + retval[PAM_RED_PLANE] = PPM_GETR(color); + retval[PAM_GRN_PLANE] = PPM_GETG(color); + retval[PAM_BLU_PLANE] = PPM_GETB(color); + } else { + if (PPM_GETR(color) != PPM_GETG(color) || + PPM_GETR(color) != PPM_GETB(color)) + pm_error("You specified as a missing color something which " + "is not monochrome, but your lookup table file, " + "and thus your " + "output file, can contain only monochrome values"); + else + retval[0] = PPM_GETR(color); + } + } else + pnm_assigntuple(lookuppamP, retval, lookup[0][0]); + + *defaultTupleP = retval; +} + + + +static void +doLookup(struct pam const indexpam, + struct pam const outpamarg, + tuple const defaultTuple, + tuple ** const lookup, + struct pam const lookuppam) { + + struct pam outpam; + unsigned int row; + + tuple* tuplerowIndex; + tuple* tuplerowOut; + + outpam = outpamarg; + + tuplerowIndex = pnm_allocpamrow(&indexpam); + tuplerowOut = pnm_allocpamrow(&outpam); + + pnm_writepaminit(&outpam); + + for (row = 0; row < outpam.height; ++row) { + unsigned int col; + pnm_readpamrow(&indexpam, tuplerowIndex); + + for (col = 0; col < outpam.width; ++col) { + unsigned int indexRow, indexCol; + tuple v; + + if (indexpam.depth < 2) { + indexRow = 0; + indexCol = tuplerowIndex[col][0]; + } else { + indexRow = tuplerowIndex[col][0]; + indexCol = tuplerowIndex[col][1]; + } + + if (indexRow >= lookuppam.height || indexCol >= lookuppam.width) + v = defaultTuple; + else + v = lookup[indexRow][indexCol]; + + pnm_assigntuple(&outpam, tuplerowOut[col], v); + } + pnm_writepamrow(&outpam, tuplerowOut); + } + pnm_freepamrow(tuplerowIndex); + pnm_freepamrow(tuplerowOut); +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + struct pam indexpam; + struct pam outpam; + FILE* ifP; + struct pam lookuppam; + tuple** lookup; + + tuple defaultTuple; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.indexFilespec); + + pnm_readpaminit(ifP, &indexpam, PAM_STRUCT_SIZE(tuple_type)); + + if (indexpam.depth != 1 && indexpam.depth != 2) + pm_error("The input (index) file must have depth 1 or 2. " + "Yours has depth %d", + indexpam.depth); + + getLookup(cmdline.lookupFilespec, indexpam.depth, indexpam.maxval, + &lookup, &lookuppam, cmdline.fit); + + computeDefaultTuple(cmdline, lookup, &lookuppam, &defaultTuple); + + outpam = lookuppam; + outpam.height = indexpam.height; + outpam.width = indexpam.width; + outpam.file = stdout; + + doLookup(indexpam, outpam, defaultTuple, lookup, lookuppam); + + pm_close(ifP); + + pnm_freepamtuple(defaultTuple); + pnm_freepamarray(lookup, &lookuppam); + + exit(0); +} + diff --git a/other/pampick.c b/other/pampick.c new file mode 100644 index 00000000..7c2d4385 --- /dev/null +++ b/other/pampick.c @@ -0,0 +1,250 @@ +/****************************************************************************** + pampick +******************************************************************************* + Select specified images from a Netpbm image stream and create a new + Netpbm image stream containing them. + + By Bryan Henderson, San Jose CA; October 2005 + + Contributed to the public domain by its author. +******************************************************************************/ + +#include <string.h> +#include <stdio.h> + +#include "pam.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" + + +struct uintSet { + unsigned int allocSize; + unsigned int count; + unsigned int * list; +}; + + + +static void +initUintSet(struct uintSet * const uintSetP, + unsigned int const allocSize) { + + uintSetP->allocSize = allocSize; + uintSetP->count = 0; + + MALLOCARRAY(uintSetP->list, allocSize); + if (uintSetP->list == NULL) + pm_error("Could not allocate memory for an array of %u numbers.", + allocSize); +} + + + +static void +termUintSet(struct uintSet * const uintSetP) { + + free(uintSetP->list); +} + + + +static bool +isMemberOfUintSet(const struct uintSet * const uintSetP, + unsigned int const searchValue) { + + bool retval; + unsigned int i; + + retval = FALSE; /* initial assumption */ + + for (i = 0; i < uintSetP->count; ++i) { + if (uintSetP->list[i] == searchValue) + retval = TRUE; + } + return retval; +} + + + +static void +addToUintSet(struct uintSet * const uintSetP, + unsigned int const newValue) { + + if (uintSetP->count >= uintSetP->allocSize) + pm_error("Overflow of number list"); + else { + if (!isMemberOfUintSet(uintSetP, newValue)) + uintSetP->list[uintSetP->count++] = newValue; + } +} + + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + struct uintSet imageSeqList; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the pointers we place into *cmdlineP are sometimes to storage + in the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + + /* We have no options. We use the parser just to gripe if user + specifies an option. But when we add an option in the future, + it will go right here with an OPTENT3() macro invocation. + */ + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + initUintSet(&cmdlineP->imageSeqList, argc-1); + + { + unsigned int i; + + for (i = 0; i < argc-1; ++i) { + if (strlen(argv[i+1]) == 0) + pm_error("An image sequence argument is a null string!"); + else { + char * endPtr; + int const strtolResult = strtol(argv[i+1], &endPtr, 10); + + if (*endPtr != '\0') + pm_error("Garbage in sequence number argument '%s': '%s'", + argv[i+1], endPtr); + + if (strtolResult < 0) + pm_error("Image sequence number cannot be negative. " + "You specified %d", strtolResult); + + addToUintSet(&cmdlineP->imageSeqList, strtolResult); + } + } + } + free(option_def); +} + + + +static void +destroyCmdline(struct cmdlineInfo * const cmdlineP) { + + termUintSet(&cmdlineP->imageSeqList); +} + + + +static void +extractOneImage(FILE * const infileP, + FILE * const outfileP) { +/*---------------------------------------------------------------------------- + Copy a complete image from input stream *infileP to output stream + *outfileP. + + But if outfileP == NULL, just read the image and discard it. +-----------------------------------------------------------------------------*/ + struct pam inpam; + struct pam outpam; + enum pm_check_code checkRetval; + + unsigned int row; + tuple * tuplerow; + + pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval); + + outpam = inpam; + outpam.file = outfileP; + + if (outfileP) + pnm_writepaminit(&outpam); + + tuplerow = pnm_allocpamrow(&inpam); + for (row = 0; row < inpam.height; ++row) { + pnm_readpamrow(&inpam, tuplerow); + if (outfileP) + pnm_writepamrow(&outpam, tuplerow); + } + pnm_freepamrow(tuplerow); +} + + + +static void +failIfUnpickedImages(const struct uintSet * const uintSetP, + unsigned int const imageCount) { +/*---------------------------------------------------------------------------- + Abort the program (pm_error()) if there are any image sequence numbers + in the set *uintSetP that are greater than or equal to 'imageCount'. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < uintSetP->count; ++i) { + if (uintSetP->list[i] >= imageCount) + pm_error("You asked for image sequence number %u " + "(relative to 0). " + "The number of images in the input stream is only %u.", + uintSetP->list[i], imageCount); + } +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + + bool eof; /* No more images in input */ + unsigned int imageSeq; + /* Sequence of current image in input file. First = 0 */ + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + eof = FALSE; + for (imageSeq = 0; !eof; ++imageSeq) { + if (isMemberOfUintSet(&cmdline.imageSeqList, imageSeq)) { + pm_message("Extracting Image #%u", imageSeq); + + extractOneImage(stdin, stdout); + } else + extractOneImage(stdin, NULL); + + pnm_nextimage(stdin, &eof); + } + + failIfUnpickedImages(&cmdline.imageSeqList, imageSeq); + + destroyCmdline(&cmdline); + + pm_close(stdin); + pm_close(stdout); + + return 0; +} diff --git a/other/pamsplit.c b/other/pamsplit.c new file mode 100644 index 00000000..b9ff6247 --- /dev/null +++ b/other/pamsplit.c @@ -0,0 +1,187 @@ +/****************************************************************************** + pamsplit +******************************************************************************* + Split a Netpbm format input file into multiple Netpbm format output files + with one image per output file. + + By Bryan Henderson, Olympia WA; June 2000 + + Contributed to the public domain by its author. +******************************************************************************/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> +#include <stdio.h> +#include "pam.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; + const char * outputFilePattern; + unsigned int debug; + unsigned int padname; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the pointers we place into *cmdlineP are sometimes to storage + in the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int padnameSpec; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "debug", OPT_FLAG, NULL, &cmdlineP->debug, 0); + OPTENT3(0, "padname", OPT_UINT, &cmdlineP->padname, &padnameSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!padnameSpec) + cmdlineP->padname = 0; + + if (argc - 1 < 1) + cmdlineP->inputFileName = "-"; + else + cmdlineP->inputFileName = argv[1]; + if (argc -1 < 2) + cmdlineP->outputFilePattern = "image%d"; + else + cmdlineP->outputFilePattern = argv[2]; + + if (!strstr(cmdlineP->outputFilePattern, "%d")) + pm_error("output file spec pattern parameter must include the " + "string '%%d',\n" + "to stand for the image sequence number.\n" + "You specified '%s'.", cmdlineP->outputFilePattern); +} + + + +static void +extractOneImage(FILE * const infileP, + FILE * const outfileP) { + + struct pam inpam; + struct pam outpam; + enum pm_check_code checkRetval; + + unsigned int row; + tuple * tuplerow; + + pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval); + + outpam = inpam; + outpam.file = outfileP; + + pnm_writepaminit(&outpam); + + tuplerow = pnm_allocpamrow(&inpam); + for (row = 0; row < inpam.height; ++row) { + pnm_readpamrow(&inpam, tuplerow); + pnm_writepamrow(&outpam, tuplerow); + } + pnm_freepamrow(tuplerow); +} + + + +static void +computeOutputName(char const outputFilePattern[], + unsigned int const padCount, + unsigned int const imageSeq, + const char ** const outputNameP) { +/*---------------------------------------------------------------------------- + Compute the name of an output file given the pattern + outputFilePattern[] and the image sequence number 'imageSeq'. + outputFilePattern[] contains at least one instance of the string + "%d" and we substitute the ASCII decimal representation of + imageSeq for the firstone of them to generate the output file + name. We add leading zeroes as necessary to bring the number up to + at least 'padCount' characters. +-----------------------------------------------------------------------------*/ + char * beforeSub; + const char * afterSub; + const char * filenameFormat; + /* A format string for asprintfN for the file name */ + + beforeSub = strdup(outputFilePattern); + *(strstr(beforeSub, "%d")) = '\0'; + + afterSub = strstr(outputFilePattern, "%d") + 2; + + /* Make filenameFormat something like "%s%04u%s" */ + asprintfN(&filenameFormat, "%%s%%0%ud%%s", padCount); + + asprintfN(outputNameP, filenameFormat, beforeSub, imageSeq, afterSub); + + strfree(filenameFormat); + + free(beforeSub); +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + + FILE * ifP; + bool eof; /* No more images in input */ + unsigned int imageSeq; + /* Sequence of current image in input file. First = 0 */ + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + eof = FALSE; + for (imageSeq = 0; !eof; ++imageSeq) { + FILE * ofP; + const char * outputFileName; /* malloc'ed */ + + computeOutputName(cmdline.outputFilePattern, cmdline.padname, + imageSeq, + &outputFileName); + pm_message("WRITING %s", outputFileName); + + ofP = pm_openw(outputFileName); + extractOneImage(ifP, ofP); + + pm_close(ofP); + strfree(outputFileName); + + pnm_nextimage(ifP, &eof); + } + pm_close(ifP); + + return 0; +} diff --git a/other/pamstack.c b/other/pamstack.c new file mode 100644 index 00000000..4f8d9945 --- /dev/null +++ b/other/pamstack.c @@ -0,0 +1,240 @@ +/*---------------------------------------------------------------------------- + pamstack +------------------------------------------------------------------------------ + Part of the Netpbm package. + + Combine the channels (stack the planes) of multiple PAM images to create + a single PAM image. + + + By Bryan Henderson, San Jose CA 2000.08.05 + + Contributed to the public domain by its author 2002.05.05. +-----------------------------------------------------------------------------*/ + +#include <string.h> + +#include "mallocvar.h" +#include "shhopt.h" +#include "pam.h" + +#define MAX_INPUTS 16 + /* The most input PAMs we allow user to specify */ + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *tupletype; /* Tuple type for output PAM */ + unsigned int nInput; + /* The number of input PAMs. At least 1, at most 16. */ + const char * inputFileName[MAX_INPUTS]; + /* The PAM files to combine, in order. */ +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec strings we return are stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + extern struct pam pam; /* Just so we can look at field sizes */ + + unsigned int option_def_index; + unsigned int tupletypeSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "tupletype", OPT_STRING, &cmdlineP->tupletype, + &tupletypeSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!tupletypeSpec) + cmdlineP->tupletype = ""; + else + if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type)) + pm_error("Tuple type name specified is too long. Maximum of " + "%u characters allowed.", sizeof(pam.tuple_type)); + + cmdlineP->nInput = 0; /* initial value */ + { + int argn; + for (argn = 1; argn < argc; argn++) { + if (cmdlineP->nInput >= MAX_INPUTS) + pm_error("You may not specify more than %d input images.", + MAX_INPUTS); + cmdlineP->inputFileName[cmdlineP->nInput++] = argv[argn]; + } + } + if (cmdlineP->nInput < 1) + pm_error("You must specify at least one input PAM image."); +} + + + +static void +openAllStreams(unsigned int const nInput, + const char ** const inputFileName, + FILE ** const ifP) { + + unsigned int inputSeq; + + for (inputSeq = 0; inputSeq < nInput; ++inputSeq) + ifP[inputSeq] = pm_openr(inputFileName[inputSeq]); +} + + + +static void +outputRaster(const struct pam inpam[], + unsigned int const nInput, + struct pam outpam) { + + tuple *inrow; + tuple *outrow; + + outrow = pnm_allocpamrow(&outpam); + inrow = pnm_allocpamrow(&outpam); + + { + int row; + + for (row = 0; row < outpam.height; row++) { + unsigned int inputSeq; + int outplane; + outplane = 0; /* initial value */ + for (inputSeq = 0; inputSeq < nInput; ++inputSeq) { + struct pam thisInpam = inpam[inputSeq]; + int col; + + pnm_readpamrow(&thisInpam, inrow); + + for (col = 0; col < outpam.width; col ++) { + int inplane; + for (inplane = 0; inplane < thisInpam.depth; ++inplane) + outrow[col][outplane+inplane] = inrow[col][inplane]; + } + outplane += thisInpam.depth; + } + pnm_writepamrow(&outpam, outrow); + } + } + pnm_freepamrow(outrow); + pnm_freepamrow(inrow); +} + + + +static void +processOneImageInAllStreams(unsigned int const nInput, + FILE * const ifP[], + FILE * const ofP, + const char * const tupletype) { + + struct pam inpam[MAX_INPUTS]; /* Input PAM images */ + struct pam outpam; /* Output PAM image */ + + unsigned int inputSeq; + /* The horizontal sequence -- i.e. the sequence of the + input stream, not the sequence of an image within a + stream. + */ + + unsigned int outputDepth; + outputDepth = 0; /* initial value */ + + for (inputSeq = 0; inputSeq < nInput; ++inputSeq) { + + pnm_readpaminit(ifP[inputSeq], &inpam[inputSeq], + PAM_STRUCT_SIZE(tuple_type)); + + if (inputSeq > 0) { + /* All images, including this one, must be compatible with the + first image. + */ + if (inpam[inputSeq].width != inpam[0].width) + pm_error("Image no. %u does not have the same width as " + "Image 0.", inputSeq); + if (inpam[inputSeq].height != inpam[0].height) + pm_error("Image no. %u does not have the same height as " + "Image 0.", inputSeq); + if (inpam[inputSeq].maxval != inpam[0].maxval) + pm_error("Image no. %u does not have the same maxval as " + "Image 0.", inputSeq); + } + outputDepth += inpam[inputSeq].depth; + } + + outpam = inpam[0]; /* Initial value */ + outpam.depth = outputDepth; + outpam.file = ofP; + outpam.format = PAM_FORMAT; + strcpy(outpam.tuple_type, tupletype); + + pm_message("Writing %u channel PAM image", outpam.depth); + + pnm_writepaminit(&outpam); + + outputRaster(inpam, nInput, outpam); +} + + + +static void +nextImageAllStreams(unsigned int const nInput, + FILE * const ifP[], + bool * const eofP) { +/*---------------------------------------------------------------------------- + Advance all the streams ifP[] to the next image. + + Return *eofP == TRUE iff at least one stream has no next image. +-----------------------------------------------------------------------------*/ + unsigned int inputSeq; + + for (inputSeq = 0; inputSeq < nInput; ++inputSeq) { + bool eof; + pnm_nextimage(ifP[inputSeq], &eof); + if (eof) + *eofP = eof; + } +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP[MAX_INPUTS]; + bool eof; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + openAllStreams(cmdline.nInput, cmdline.inputFileName, ifP); + + eof = FALSE; + while (!eof) { + processOneImageInAllStreams(cmdline.nInput, ifP, stdout, + cmdline.tupletype); + + nextImageAllStreams(cmdline.nInput, ifP, &eof); + } + + return 0; +} diff --git a/other/pamsummcol.c b/other/pamsummcol.c new file mode 100644 index 00000000..c2c3e46b --- /dev/null +++ b/other/pamsummcol.c @@ -0,0 +1,257 @@ +/****************************************************************************** + pamsummcol +******************************************************************************* + Summarize the columns of a PAM image with various functions. + + By Bryan Henderson, San Jose CA 2004.02.07. + + Contributed to the public domain + + +******************************************************************************/ + +#include "pam.h" +#include "shhopt.h" +#include "mallocvar.h" + +enum function {FN_ADD, FN_MEAN, FN_MIN, FN_MAX}; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespec of input file */ + enum function function; + unsigned int verbose; +}; + + +static void +parseCommandLine(int argc, char ** const argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int sumSpec, meanSpec, minSpec, maxSpec; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "sum", OPT_FLAG, NULL, &sumSpec, 0); + OPTENT3(0, "mean", OPT_FLAG, NULL, &meanSpec, 0); + OPTENT3(0, "min", OPT_FLAG, NULL, &minSpec, 0); + OPTENT3(0, "max", OPT_FLAG, NULL, &maxSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (sumSpec + minSpec + maxSpec > 1) + pm_error("You may specify at most one of -sum, -min, and -max"); + + if (sumSpec) { + cmdlineP->function = FN_ADD; + } else if (meanSpec) { + cmdlineP->function = FN_MEAN; + } else if (minSpec) { + cmdlineP->function = FN_MIN; + } else if (maxSpec) { + cmdlineP->function = FN_MAX; + } else + pm_error("You must specify one of -sum, -min, or -max"); + + if (argc-1 > 1) + pm_error("Too many arguments (%d). File spec is the only argument.", + argc-1); + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else + cmdlineP->inputFilespec = argv[1]; + +} + + +struct accum { + union { + unsigned int sum; + unsigned int min; + unsigned int max; + } u; +}; + + + +static void +createAccumulator(enum function const function, + unsigned int const cols, + unsigned int const planes, + struct accum *** const accumulatorP) { + + struct accum ** accumulator; + unsigned int col; + + MALLOCARRAY_NOFAIL(accumulator, cols); + + for (col = 0; col < cols; ++col) { + unsigned int plane; + + MALLOCARRAY_NOFAIL(accumulator[col], planes); + + for (plane = 0; plane < planes; ++plane) { + switch(function) { + case FN_ADD: accumulator[col][plane].u.sum = 0; break; + case FN_MEAN: accumulator[col][plane].u.sum = 0; break; + case FN_MIN: accumulator[col][plane].u.min = UINT_MAX; break; + case FN_MAX: accumulator[col][plane].u.max = 0; break; + } + } + } + *accumulatorP = accumulator; +} + + + +static void +destroyAccumulator(struct accum ** accumulator, + unsigned int const cols) { + + unsigned int col; + for (col = 0; col < cols; ++col) + free(accumulator[col]); + + free(accumulator); +} + + + +static void +aggregate(struct pam * const inpamP, + tuple * const tupleRow, + enum function const function, + struct accum ** const accumulator) { + + unsigned int col; + + for (col = 0; col < inpamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < inpamP->depth; ++plane) { + switch(function) { + case FN_ADD: + case FN_MEAN: + if (accumulator[col][plane].u.sum > + UINT_MAX - tupleRow[col][plane]) + pm_error("Numerical overflow in Column %u", col); + accumulator[col][plane].u.sum += tupleRow[col][plane]; + break; + case FN_MIN: + if (tupleRow[col][plane] < accumulator[col][plane].u.min) + accumulator[col][plane].u.min = tupleRow[col][plane]; + break; + case FN_MAX: + if (tupleRow[col][plane] > accumulator[col][plane].u.min) + accumulator[col][plane].u.min = tupleRow[col][plane]; + break; + } + } + } +} + + + +static void +makeSummaryRow(struct accum ** const accumulator, + unsigned int const count, + struct pam * const pamP, + enum function const function, + tuple * const tupleRow) { + + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) { + switch(function) { + case FN_ADD: + tupleRow[col][plane] = + MIN(accumulator[col][plane].u.sum, pamP->maxval); + break; + case FN_MEAN: + tupleRow[col][plane] = + ROUNDU((double)accumulator[col][plane].u.sum / count); + break; + case FN_MIN: + tupleRow[col][plane] = + accumulator[col][plane].u.min; + break; + case FN_MAX: + tupleRow[col][plane] = + accumulator[col][plane].u.max; + break; + } + } + } +} + + + +int +main(int argc, char *argv[]) { + + FILE* ifP; + tuple* inputRow; /* Row from input image */ + tuple* outputRow; /* Output row */ + int row; + struct cmdlineInfo cmdline; + struct pam inpam; /* Input PAM image */ + struct pam outpam; /* Output PAM image */ + struct accum ** accumulator; /* malloc'ed two-dimensional array */ + + pnm_init( &argc, argv ); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + createAccumulator(cmdline.function, inpam.width, inpam.depth, + &accumulator); + + inputRow = pnm_allocpamrow(&inpam); + + outpam = inpam; /* Initial value -- most fields should be same */ + outpam.file = stdout; + outpam.height = 1; + + pnm_writepaminit(&outpam); + + outputRow = pnm_allocpamrow(&outpam); + + for (row = 0; row < inpam.height; row++) { + pnm_readpamrow(&inpam, inputRow); + + aggregate(&inpam, inputRow, cmdline.function, accumulator); + } + makeSummaryRow(accumulator, inpam.height, &outpam, cmdline.function, + outputRow); + pnm_writepamrow(&outpam, outputRow); + + pnm_freepamrow(outputRow); + pnm_freepamrow(inputRow); + destroyAccumulator(accumulator, inpam.width); + pm_close(inpam.file); + pm_close(outpam.file); + + return 0; +} diff --git a/other/pamx/COPYRIGHT b/other/pamx/COPYRIGHT new file mode 100644 index 00000000..3ad3b675 --- /dev/null +++ b/other/pamx/COPYRIGHT @@ -0,0 +1,72 @@ +Many of the source files for Pamx contain code written by or derived +from code written by Jim Frost for his program Xloadimage. The files +in which Frost has copyright contain Frost's name at the top. Frost +licenses his copyright to the public as follows: + + Copyright 1989, 1993 Jim Frost + + Permission to use, copy, modify, distribute, and sell this software + and its documentation for any purpose is hereby granted without fee, + provided that the above copyright notice appear in all copies and + that both that copyright notice and this permission notice appear + in supporting documentation. The author makes no representations + about the suitability of this software for any purpose. It is + provided "as is" without express or implied warranty. + + THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR + CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + USE OR PERFORMANCE OF THIS SOFTWARE. + + +The rest of the code was primarily written by Bryan Henderson, +starting in 2006. Bryan and other authors have contributed their work +to the public domain. + + +Here is a more or less complete list of people who contributed to +the Xloadimage from which Pamx was derived. The list is from the README +file in the Xloadimage source code: + +Special thanks to the crew at the Boston University Graphics Lab for +their assistance and sample images, and to bzs@std.com for his simple +dithering algorithm (or what's left of it). Real special thanks to +Kirk L. Johnson (tuna@athena.mit.edu) for a very nice GIF loader and +dithering routine, to Mark Snitily (zok!mark@apple.com) for 386/ix +compatibility work, to Andreas Stolcke (stolcke@icsib12.berkeley.edu) +for miscellaneous bug fixes, to Anthony A. Datri (datri@convex.com) +for a number of things, to Mark Moraes (moraes@cs.toronto.edu) for +the slideshow colormap fix, to Gregg Townsend (gmt@cs.arizona.edu) for +a suggested dithering routine and other fixes, to Brian Frost +(B1F5814@RIGEL.TAMU.EDU) for changes for VMS, to Chip Horstman for G3 +FAX support, to Deron Dann Johnson (dj@eng.sun.com) for fixing the +RetainTemporary bug, to Tom Tatlow (tatlow@dash.enet.dec.com) for +image rotation code, to Mark A. Horstman (mhorstm@sarek.sbc.com) for +tilde expansion in .xloadimagerc files and virtual-root support in +root.c, to Tim Roper (timr@labtam.labtam.oz.au), Graeme Gill +(graeme@labtam.oz.au) for gamma correction and Utah RLE image support, +Mark Majhor (uunet!sequent!markm) for FBM and MacPaint support, Ian +MacPhedran (macphed@dvinci.usask.ca) for PGM and PPM support, Per +Fogelstrom (pf@diab.se) for a fix to send.c, Hans J. Albertsson +(hans@Sweden.Sun.COM) for cleaning up GIF aborting, Graham Hudspith +(gwh@inmos.com) for a geometry patch, Glenn P. Davis +(davis@unidata.ucar.edu) for McIDAS areafile support, Keith S. Pickens +(maxwell.nde.swri.edu!ksp) for fixing the RLE loader to work with the +updated zio package, Mike Douglas (douglas@wilbur.coyote.trw.com) for +normalization, Rod Johnson (johnson@wrl.epi.com) for speedup +suggestions, Hal Peterson (hrp@cray.com) for his Imakefile fix, Matt +Caprile (Matthew.Caprile@ec.bull.fr) for slideshow delay code, Bob +Deroy (rwd@bucrsb.bu.edu) for mondo 24-bit Sun Rasterfile images that +broke everything, Christos S. Zoulas (christo@ee.cornell.edu) for a +first-cut 24-bit implementation, Gerald James Barnes +(gjb@oasis.icl.stc.co.uk) for a first-cut forced-visual +implementation, Michael Campanella (campanella@cvg.enet.dec.com) for +more VMS changes, Kee Hinckley (nazgul@alfalfa.com) for robustness +changes to the g3 and MacPaint loaders and the ZIO package, Tim +Northrup (tim@brspyr1.brs.com) for PC Paintbrush and GEM image +formats, Richard Weidner (richard@elroy.jpl.nasa.gov) for lots of +24-bit testing, Eckhard Rueggeberg (erueg@cfgauss.uni-math.gwdg.de) +for a better PCX loader, and any others whose names I've missed. diff --git a/other/pamx/Makefile b/other/pamx/Makefile new file mode 100644 index 00000000..a86a3331 --- /dev/null +++ b/other/pamx/Makefile @@ -0,0 +1,43 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = other/pamx +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +ifneq ($(X11LIB),NONE) + ifneq ($(X11HDR_DIR),) + INCLUDES += -I$(X11HDR_DIR) + endif +endif + +ifneq ($(X11LIB),NONE) + BINARIES += pamx +endif + +PAMX_OBJECTS = \ + pamx.o \ + image.o \ + send.o \ + window.o \ + +MERGE_OBJECTS = \ + pamx.o2 \ + image.o \ + send.o \ + window.o \ + +OBJECTS = $(PAMX_OBJECTS) + +MERGEBINARIES = $(BINARIES) + +all: $(BINARIES) + +include $(SRCDIR)/Makefile.common + +pamx: $(PAMX_OBJECTS) $(NETPBMLIB) $(LIBOPT) + $(LD) $(LDFLAGS) -o $@ $(PAMX_OBJECTS) \ + $(shell $(LIBOPT) $(NETPBMLIB) $(X11LIB)) \ + $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD) diff --git a/other/pamx/Makefile2 b/other/pamx/Makefile2 new file mode 100644 index 00000000..f69e103f --- /dev/null +++ b/other/pamx/Makefile2 @@ -0,0 +1,51 @@ +# C compiler to use, including special flags. +CC=gcc + +WARNINGS = -Wall -Wmissing-declarations -Wundef -Wimplicit -Wwrite-strings \ + -Winline \ + -Wstrict-prototypes -Wmissing-prototypes \ + -Werror + +CFLAGS = $(WARNINGS) -fno-common -g +INCLUDES = -I /home/bryanh/netpbm/other/importinc + +# X11 include and library information. +X11_LIB_DIR=-L/subsysx/X11R6/lib +X11_LIB_NAME=-lX11 +NETPBMLIB = /home/bryanh/netpbm/lib/libnetpbm.so + +LIBS=$(X11_LIB_DIR) $(X11_LIB_NAME) -lm + +default: pamx + +# files for the image library +IMAGE_SRCS= image.c +IMAGE_OBJS= ${IMAGE_SRCS:.c=.o} + +# files for the image processing library +PROCESS_HDRS= +# no image processing. +PROCESS_SRCS= fill.c +PROCESS_OBJS= ${PROCESS_SRCS:.c=.o} + +X_SRCS= send.c window.c pamx.c +X_OBJS= ${X_SRCS:.c=.o} + +OBJS= $(IMAGE_OBJS) $(PROCESS_OBJS) $(X_OBJS) $(NETPBMLIB) + +.c.o: $*.c + $(CC) -c $(CFLAGS) $(INCLUDES) $*.c $(CADD) + +pamx: $(OBJS) $(OPTIONAL_LIBS) + $(CC) -o $@ $(OBJS) $(OPTIONAL_LIBS) $(LIBS) + +clean:: + rm -f *.o pamx + +dep: + $(CC) -MM -MG $(INCLUDES) *.c >Makefile.depend + +include Makefile.depend + +Makefile.depend: + >$@ diff --git a/other/pamx/fill.c b/other/pamx/fill.c new file mode 100644 index 00000000..13a2b21e --- /dev/null +++ b/other/pamx/fill.c @@ -0,0 +1,82 @@ +/* + fill an image area with a particular pixel value + + By Jim Frost 1989.10.02, Bryan Henderson 2006.03.25. + + See COPYRIGHT file for copyright information. +*/ + +#include "pm.h" +#include "image.h" +#include "valtomem.h" +#include "fill.h" + +void +fill(Image * const imageP, + unsigned int const fx, + unsigned int const fy, + unsigned int const fw, + unsigned int const fh, + Pixel const pixval) { + + assertGoodImage(imageP); + switch(imageP->type) { + case IBITMAP: { + unsigned int const linelen = (imageP->width +7)/ 8; + unsigned int const start = (fx +7) / 8; + unsigned char const startmask = 0x80 >> (fx % 8); + + unsigned int y; + unsigned char * lineptr; + + for (y = fy, lineptr = imageP->data + linelen * fy; + y < fy + fh; + ++y, lineptr += linelen) { + + unsigned int x; + unsigned char mask; + unsigned char * pixptr; + + mask = startmask; + pixptr = lineptr + start; + + for (x = fx; x < fw; ++x) { + if (pixval) + *pixptr |= mask; + else + *pixptr &= ~mask; + if (!(mask >>= 1)) { + mask = 0x80; + ++pixptr; + } + } + } + } break; + + case IRGB: + case ITRUE: { + unsigned int const linelen= imageP->width * imageP->pixlen; + unsigned int const start = imageP->pixlen * fx; + + unsigned int y; + unsigned char * lineptr; + + for (y = fy, lineptr = imageP->data + (linelen * fy); + y < fy + fh; + ++y, lineptr += linelen) { + + unsigned int x; + unsigned char * pixptr; + + pixptr = lineptr + start; + for (x = fx, pixptr = lineptr + start; + x < fw; + ++x, pixptr += imageP->pixlen) { + valToMem(pixval, pixptr, imageP->pixlen); + } + } + } break; + default: + pm_error("INTERNAL ERROR: Impossible image type %u", imageP->type); + } +} diff --git a/other/pamx/fill.h b/other/pamx/fill.h new file mode 100644 index 00000000..1f316d0b --- /dev/null +++ b/other/pamx/fill.h @@ -0,0 +1,16 @@ +#ifndef FILL_H_INCLUDED +#define FILL_H_INCLUDED + +#include "ximageinfo.h" + +struct Image; + +void +fill(struct Image * const imageP, + unsigned int const fx, + unsigned int const fy, + unsigned int const fw, + unsigned int const fh, + Pixel const pixval); + +#endif diff --git a/other/pamx/image.c b/other/pamx/image.c new file mode 100644 index 00000000..3aaa8478 --- /dev/null +++ b/other/pamx/image.c @@ -0,0 +1,331 @@ +/* + Functions to allocate and deallocate structures and structure data + + By Jim Frost 1989.09.29, Bryan Henderson 2006.03.25. + + See COPYRIGHT file for copyright information. +*/ + +#include <assert.h> + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" +#include "pam.h" +#include "image.h" + + + +/* this table is useful for quick conversions between depth and ncolors */ + +static unsigned long const DepthToColorsTable[] = { + /* 0 */ 1, + /* 1 */ 2, + /* 2 */ 4, + /* 3 */ 8, + /* 4 */ 16, + /* 5 */ 32, + /* 6 */ 64, + /* 7 */ 128, + /* 8 */ 256, + /* 9 */ 512, + /* 10 */ 1024, + /* 11 */ 2048, + /* 12 */ 4096, + /* 13 */ 8192, + /* 14 */ 16384, + /* 15 */ 32768, + /* 16 */ 65536, + /* 17 */ 131072, + /* 18 */ 262144, + /* 19 */ 524288, + /* 20 */ 1048576, + /* 21 */ 2097152, + /* 22 */ 4194304, + /* 23 */ 8388608, + /* 24 */ 16777216, + /* 25 */ 33554432, + /* 26 */ 67108864, + /* 27 */ 134217728, + /* 28 */ 268435456, + /* 29 */ 536870912, + /* 30 */ 1073741824, + /* 31 */ 2147483648u, + /* 32 */ 2147483648u /* bigger than unsigned int; this is good enough */ +}; + + + +unsigned int +depthToColors(unsigned int const depth) { + return DepthToColorsTable[MIN(depth,32)]; +} + + + +unsigned long +colorsToDepth(unsigned long const ncolors) { + + unsigned long a; + + for (a = 0; (a < 32) && (DepthToColorsTable[a] < ncolors); ++a) + /* EMPTY */ + ; + return a ; +} + + + +void +assertGoodImage(Image * const imageP) { + + assert(imageP != NULL); + + switch (imageP->type) { + case IBITMAP: + case IRGB: + case ITRUE: + break; + default: + assert(FALSE); /* can't be */ + } +} + + + +static void +newRGBMapData(RGBMap * const rgbP, + unsigned int const size) { + + rgbP->used = 0; + rgbP->size = size; + rgbP->compressed = FALSE; + MALLOCARRAY(rgbP->red, size); + MALLOCARRAY(rgbP->grn, size); + MALLOCARRAY(rgbP->blu, size); + + if (rgbP->red == NULL || rgbP->grn == NULL || rgbP->blu == NULL) + pm_error("Out of memory allocating %u pixels", size); +} + + + +static void +freeRGBMapData(RGBMap * const rgbP) { + + free(rgbP->red); + free(rgbP->grn); + free(rgbP->blu); +} + + + +Image * +newBitImage(unsigned int const width, + unsigned int const height) { + + unsigned int const linelen = (width + 7) / 8; + + Image * imageP; + + MALLOCVAR_NOFAIL(imageP); + + imageP->type = IBITMAP; + newRGBMapData(&imageP->rgb, 2); + imageP->rgb.red[0] = imageP->rgb.grn[0] = imageP->rgb.blu[0] = 65535; + imageP->rgb.red[1] = imageP->rgb.grn[1] = imageP->rgb.blu[1] = 0; + imageP->rgb.used = 2; + imageP->width = width; + imageP->height = height; + imageP->depth = 1; + + if (UINT_MAX / linelen < height) + pm_error("Image dimensions too big to compute: %u x %u", + linelen, height); + MALLOCARRAY(imageP->data, linelen * height); + + if (imageP->data == NULL) + pm_error("Out of memory allocating array of %u x %u", linelen, height); + + return imageP; +} + + + +Image * +newRGBImage(unsigned int const width, + unsigned int const height, + unsigned int const depth) { + + unsigned int const pixlen = pixlen > 0 ? (depth + 7) / 8 : 1; + /* Special case for "zero" depth image, which is sometimes + interpreted as "one color" + */ + unsigned int const numcolors = depthToColors(depth); + + Image * imageP; + + MALLOCVAR_NOFAIL(imageP); + imageP->type = IRGB; + newRGBMapData(&imageP->rgb, numcolors); + imageP->width = width; + imageP->height = height; + imageP->depth = depth; + imageP->pixlen = pixlen; + + if (UINT_MAX / width / height < pixlen) + pm_error("Image dimensions %u x %u x %u are too big to compute.", + width, height, pixlen); + MALLOCARRAY(imageP->data, width * height * pixlen); + if (imageP->data == NULL) + pm_error("Unable to allocate %u x %u x %u raster array", + width, height, pixlen); + + return imageP; +} + + + +Image * +newTrueImage(unsigned int const width, + unsigned int const height) { + + unsigned int const pixlen = 3; + + Image * imageP; + + MALLOCVAR_NOFAIL(imageP); + imageP->type = ITRUE; + imageP->rgb.used = 0; + imageP->rgb.size = 0; + imageP->width = width; + imageP->height = height; + imageP->depth = 24; + imageP->pixlen = 3; + + if (UINT_MAX / width / height < pixlen) + pm_error("Image dimensions %u x %u x %u are too big to compute.", + width, height, pixlen); + MALLOCARRAY(imageP->data, width * height * pixlen); + if (imageP->data == NULL) + pm_error("Unable to allocate %u x %u x %u raster array", + width, height, pixlen); + + return imageP; +} + + + +static void +freeImageData(Image * const imageP) { + + if (!TRUEP(imageP)) + freeRGBMapData(&imageP->rgb); + free(imageP->data); +} + + + +void +freeImage(Image * const imageP) { + + assertGoodImage(imageP); + + freeImageData(imageP); + + imageP->type = IBAD; + + free(imageP); +} + + + + +static void +fillRow1(struct pam * const pamP, + tuple * const tuplerow, + unsigned char ** const pP) { + + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + *(*pP)++ = + pnm_scalesample(tuplerow[col][0], pamP->maxval, 255); + } +} + + + +static void +fillRow3(struct pam * const pamP, + tuple * const tuplerow, + unsigned char ** const pP) { + + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + *(*pP)++ = + pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255); + } +} + + + +Image * +pbmLoad(const char * const fullname, + const char * const name, + bool const verbose) { + + FILE * ifP; + struct pam pam; + Image * imageP; + unsigned int row; + const char * filename; + tuple * tuplerow; + unsigned char * p; + enum {DEPTH_1, DEPTH_3} depth; + + if (STREQ(fullname, "stdin")) + filename = "-"; + else + filename = fullname; + + ifP = pm_openr(filename); + + pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); + + if (strncmp(pam.tuple_type, "RGB", 3) == 0) { + depth = DEPTH_3; + if (pam.depth < 3) + pm_error("Invalid depth %u for RGB tuple type.", pam.depth); + } else + depth = DEPTH_1; + + imageP = newTrueImage(pam.width, pam.height); + + p = &imageP->data[0]; /* initial value */ + + tuplerow = pnm_allocpamrow(&pam); + + for (row = 0; row < pam.height; ++row) { + pnm_readpamrow(&pam, tuplerow); + + switch (depth) { + case DEPTH_3: + fillRow3(&pam, tuplerow, &p); + break; + case DEPTH_1: + fillRow1(&pam, tuplerow, &p); + break; + } + } + pnm_freepamrow(tuplerow); + + pm_close(ifP); + + return imageP; +} diff --git a/other/pamx/image.h b/other/pamx/image.h new file mode 100644 index 00000000..9c9689ac --- /dev/null +++ b/other/pamx/image.h @@ -0,0 +1,90 @@ +#ifndef IMAGE_H_INCLUDED +#define IMAGE_H_INCLUDED +#include <strings.h> + +#include "pm_c_util.h" + +#include "ximageinfo.h" + +typedef struct rgbmap { + unsigned int size; /* size of RGB map */ + unsigned int used; /* number of colors used in RGB map */ + bool compressed; /* image uses colormap fully */ + Intensity * red; /* color values in X style */ + Intensity * grn; + Intensity * blu; +} RGBMap; + +typedef struct Image { + unsigned int type; /* type of image */ + RGBMap rgb; /* RGB map of image if IRGB type */ + unsigned int width; /* width of image in pixels */ + unsigned int height; /* height of image in pixels */ + unsigned int depth; /* depth of image in bits if IRGB type */ + unsigned int pixlen; /* length of pixel if IRGB type */ + unsigned char * data; /* data rounded to full byte for each row */ +} Image; + +#define IBAD 0 /* invalid image (used when freeing) */ +#define IBITMAP 1 /* image is a bitmap */ +#define IRGB 2 /* image is RGB */ +#define ITRUE 3 /* image is true color */ + +#define BITMAPP(IMAGE) ((IMAGE)->type == IBITMAP) +#define RGBP(IMAGE) ((IMAGE)->type == IRGB) +#define TRUEP(IMAGE) ((IMAGE)->type == ITRUE) + +#define TRUE_RED(PIXVAL) (((PIXVAL) & 0xff0000) >> 16) +#define TRUE_GRN(PIXVAL) (((PIXVAL) & 0xff00) >> 8) +#define TRUE_BLU(PIXVAL) ((PIXVAL) & 0xff) +#define RGB_TO_TRUE(R,G,B) \ + (((unsigned int)((R) & 0xff00) << 8) | ((unsigned int)(G) & 0xff00) | \ + ((unsigned int)(B) >> 8)) + +unsigned long +colorsToDepth(unsigned long const ncolors); + +void +assertGoodImage(Image * const imageP); + +Image * +newBitImage(unsigned int const width, + unsigned int const height); + +Image * +newRGBImage(unsigned int const width, + unsigned int const height, + unsigned int const depth); + +Image * +newTrueImage(unsigned int const width, + unsigned int const height); + +void +freeImage(Image * const imageP); + +unsigned int +depthToColors(unsigned int const depth); + +extern unsigned short RedIntensity[]; +extern unsigned short GreenIntensity[]; +extern unsigned short BlueIntensity[]; + +static __inline__ unsigned int +colorIntensity(unsigned int const red, + unsigned int const grn, + unsigned int const blu) { +/*---------------------------------------------------------------------------- + Return the (approximate) intensity of a color. +-----------------------------------------------------------------------------*/ + return (RedIntensity[red / 256] + + GreenIntensity[grn / 256] + + BlueIntensity[blu / 256]); +} + +Image * +pbmLoad(const char * const fullname, + const char * const name, + bool const verbose); + +#endif diff --git a/other/pamx/pamx.c b/other/pamx/pamx.c new file mode 100644 index 00000000..8a64fec4 --- /dev/null +++ b/other/pamx/pamx.c @@ -0,0 +1,364 @@ +/* By Bryan Henderson 2006.03.25 + + Copyright information is in the file COPYRIGHT +*/ + +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include "pam.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "nstring.h" +#include "filename.h" + +#include "ximageinfo.h" +#include "image.h" +#include "fill.h" +#include "window.h" + + +struct geometry { + bool specified; + const char * string; + unsigned int width; + unsigned int height; +}; + +struct cmdlineInfo { + const char * inputFileName; + const char * displayName; /* NULL if none */ + const char * display; + const char * title; + unsigned int fullscreen; + unsigned int verbose; + struct geometry geometry; + const char * foreground; + const char * background; + const char * border; + unsigned int install; + unsigned int private; + unsigned int pixmap; + unsigned int fit; + unsigned int visualSpec; + unsigned int visual; +}; + + + +static void +parseCommandLine(int argc, + char ** argv, + struct cmdlineInfo * const cmdlineP) { +/* -------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +--------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int displaySpec, titleSpec, foregroundSpec, backgroundSpec, + borderSpec, geometrySpec; + + const char * geometryOpt; + const char * visualOpt; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "fullscreen", OPT_FLAG, + NULL, &cmdlineP->fullscreen, 0); + OPTENT3(0, "install", OPT_FLAG, + NULL, &cmdlineP->install, 0); + OPTENT3(0, "private", OPT_FLAG, + NULL, &cmdlineP->private, 0); + OPTENT3(0, "fit", OPT_FLAG, + NULL, &cmdlineP->fit, 0); + OPTENT3(0, "pixmap", OPT_FLAG, + NULL, &cmdlineP->pixmap, 0); + OPTENT3(0, "verbose", OPT_FLAG, + NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "display", OPT_STRING, + &cmdlineP->display, &displaySpec, 0); + OPTENT3(0, "title", OPT_STRING, + &cmdlineP->title, &titleSpec, 0); + OPTENT3(0, "foreground", OPT_STRING, + &cmdlineP->foreground, &foregroundSpec, 0); + OPTENT3(0, "background", OPT_STRING, + &cmdlineP->background, &backgroundSpec, 0); + OPTENT3(0, "border", OPT_STRING, + &cmdlineP->border, &borderSpec, 0); + OPTENT3(0, "geometry", OPT_STRING, + &geometryOpt, &geometrySpec, 0); + OPTENT3(0, "visual", OPT_STRING, + &visualOpt, &cmdlineP->visualSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (geometrySpec) { + int rc; + + cmdlineP->geometry.specified = true; + cmdlineP->geometry.string = geometryOpt; + + rc = sscanf(geometryOpt, "%ux%u", + &cmdlineP->geometry.width, &cmdlineP->geometry.height); + + if (rc != 2) + pm_error("Geometry value '%s' does not have the form WxH, " + "where W and H are natural numbers", geometryOpt); + + if (cmdlineP->geometry.width == 0) + pm_error("Width in -geometry option is zero"); + if (cmdlineP->geometry.height == 0) + pm_error("Height in -geometry option is zero"); + } else + cmdlineP->geometry.specified = false; + + if (!displaySpec) + cmdlineP->display = NULL; + + if (!titleSpec) + cmdlineP->title = NULL; + + if (!foregroundSpec) + cmdlineP->foreground = NULL; + + if (!backgroundSpec) + cmdlineP->background = NULL; + + if (!borderSpec) + cmdlineP->border = NULL; + + if (cmdlineP->visualSpec) + cmdlineP->visual = visualClassFromName(visualOpt); + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} + + + +static int +errorHandler(Display * const disp, + XErrorEvent * const error) { +/*---------------------------------------------------------------------------- + This is an X error handler +-----------------------------------------------------------------------------*/ + char errortext[BUFSIZ]; + + XGetErrorText(disp, error->error_code, errortext, sizeof(errortext)); + pm_error("X Error: %s on resource 0x%x", + errortext, (unsigned)error->resourceid); + return 0; +} + + + +static void +fillRow1(struct pam * const pamP, + tuple * const tuplerow, + unsigned char ** const pP) { + + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + *(*pP)++ = + pnm_scalesample(tuplerow[col][0], pamP->maxval, 255); + } +} + + + +static void +fillRow3(struct pam * const pamP, + tuple * const tuplerow, + unsigned char ** const pP) { + + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + *(*pP)++ = + pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255); + } +} + + + +static void +loadPamImage(FILE * const ifP, + Image ** const imagePP) { + + struct pam pam; + Image * imageP; + unsigned int row; + tuple * tuplerow; + unsigned char * p; + enum {DEPTH_1, DEPTH_3} depth; + + pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); + + if (strncmp(pam.tuple_type, "RGB", 3) == 0) { + depth = DEPTH_3; + if (pam.depth < 3) + pm_error("Invalid depth %u for RGB tuple type.", pam.depth); + } else + depth = DEPTH_1; + + imageP = newTrueImage(pam.width, pam.height); + + p = &imageP->data[0]; /* initial value */ + + tuplerow = pnm_allocpamrow(&pam); + + for (row = 0; row < pam.height; ++row) { + pnm_readpamrow(&pam, tuplerow); + + switch (depth) { + case DEPTH_3: + fillRow3(&pam, tuplerow, &p); + break; + case DEPTH_1: + fillRow1(&pam, tuplerow, &p); + break; + } + } + pnm_freepamrow(tuplerow); + + *imagePP = imageP; +} + + +#define BACKGROUND_IDX 0 +#define FOREGROUND_IDX 1 + + +static void +processImage(Image * const imageP, + struct cmdlineInfo const cmdline, + Display * const dispP, + int const scrn) { +/*---------------------------------------------------------------------------- + Modify image *imageP according to various command line options. +-----------------------------------------------------------------------------*/ + if (imageP->depth <= 1) { + if (cmdline.background) { + XColor color; + XParseColor(dispP, DefaultColormap(dispP, scrn), + cmdline.background, &color); + imageP->rgb.red[BACKGROUND_IDX] = color.red; + imageP->rgb.grn[BACKGROUND_IDX] = color.green; + imageP->rgb.blu[BACKGROUND_IDX] = color.blue; + } + if (cmdline.foreground) { + XColor color; + XParseColor(dispP, DefaultColormap(dispP, scrn), + cmdline.foreground, &color); + imageP->rgb.red[FOREGROUND_IDX] = color.red; + imageP->rgb.grn[FOREGROUND_IDX] = color.green; + imageP->rgb.blu[FOREGROUND_IDX] = color.blue; + } + } +} + + + +static void +determineTitle(struct cmdlineInfo const cmdline, + const char ** const titleP) { + + const char * title; + + if (cmdline.title) + title = strdup(cmdline.title); + else { + if (STREQ(cmdline.inputFileName, "-")) + title = NULL; + else { + title = pm_basename(cmdline.inputFileName); + } + } + *titleP = title; +} + + + +int +main(int argc, + char ** argv) { + + struct cmdlineInfo cmdline; + FILE * ifP; + Image * imageP; + Display * dispP; /* display we're sending to */ + int scrn; /* screen we're sending to */ + int retval; + const char * geometryString; + const char * title; + viewer * viewerP; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + dispP = XOpenDisplay(cmdline.display); + if (!dispP) + pm_error("Cannot open display '%s'", XDisplayName(cmdline.display)); + + scrn = DefaultScreen(dispP); + XSetErrorHandler(errorHandler); + + geometryString = + cmdline.geometry.specified ? cmdline.geometry.string : NULL; + + createViewer(&viewerP, dispP, scrn, geometryString, cmdline.fullscreen); + + loadPamImage(ifP, &imageP); + + processImage(imageP, cmdline, dispP, scrn); + + determineTitle(cmdline, &title); + + displayInViewer(viewerP, imageP, + cmdline.install, cmdline.private, cmdline.fit, + cmdline.pixmap, cmdline.visualSpec, cmdline.visual, + title, cmdline.verbose, + &retval); + + freeImage(imageP); + + destroyViewer(viewerP); + + if (title) + strfree(title); + + XCloseDisplay(dispP); + + return retval; +} diff --git a/other/pamx/send.c b/other/pamx/send.c new file mode 100644 index 00000000..4b1268a5 --- /dev/null +++ b/other/pamx/send.c @@ -0,0 +1,872 @@ +/* + + Send an Image to an X pixmap + + + By Jim Frost 1989.10.02, Bryan Henderson 2006.03.25. + + Copyright 1989, 1990, 1991 Jim Frost. + See COPYRIGHT file for copyright information. +*/ + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include "pm_c_util.h" +#include "pm.h" +#include "mallocvar.h" +#include "ximageinfo.h" +#include "valtomem.h" +#include "image.h" +#include "send.h" + +#define TRUE_TO_15BIT(PIXEL) \ + ((((PIXEL) & 0xf80000) >> 9) | \ + (((PIXEL) & 0x00f800) >> 6) | \ + (((PIXEL) & 0x0000f8) >> 3)) + +#define RED_INTENSITY(P) (((P) & 0x7c00) >> 10) +#define GREEN_INTENSITY(P) (((P) & 0x03e0) >> 5) +#define BLUE_INTENSITY(P) ((P) & 0x001f) +#define PM_SCALE(a, b, c) (long)((a) * (c))/(b) + + +static bool GotError; + +static int +pixmapErrorTrap(Display * const disp, + XErrorEvent * const pErrorEvent) { + +#define MAXERRORLEN 100 + char buf[MAXERRORLEN+1]; + GotError = 1; + XGetErrorText(disp, pErrorEvent->error_code, buf, MAXERRORLEN); + pm_message("serial #%ld (request code %d) Got Error '%s'", + pErrorEvent->serial, + pErrorEvent->request_code, + buf); + return 0; +} + + + +Pixmap +ximageToPixmap(Display * const disp, + Window const parent, + XImageInfo * const ximageinfoP) { + + XErrorHandler old_handler; + Pixmap pixmap; + + GotError = FALSE; + old_handler = XSetErrorHandler(pixmapErrorTrap); + XSync(disp, False); + pixmap= XCreatePixmap(disp, parent, + ximageinfoP->ximageP->width, + ximageinfoP->ximageP->height, + ximageinfoP->depth); + XSetErrorHandler(old_handler); + if (GotError) + return None; + ximageinfoP->drawable = pixmap; + sendXImage(ximageinfoP, 0, 0, 0, 0, + ximageinfoP->ximageP->width, ximageinfoP->ximageP->height); + + return pixmap; +} + + + +/* find the best pixmap depth supported by the server for a particular + * visual and return that depth. + * + * this is complicated by R3's lack of XListPixmapFormats so we fake it + * by looking at the structure ourselves. + */ + +static unsigned int +bitsPerPixelAtDepth(Display * const disp, + int const scrn, + unsigned int const depth) { + +#if XlibSpecificationRelease < 4 /* the way things were */ + unsigned int a; + + for (a= 0; a < disp->nformats; a++) + if (disp->pixmap_format[a].depth == depth) + return(disp->pixmap_format[a].bits_per_pixel); + +#else /* the way things should be */ + XPixmapFormatValues *xf; + unsigned int nxf, a; + + xf = XListPixmapFormats(disp, (int *)&nxf); + for (a = 0; a < nxf; a++) + if (xf[a].depth == depth) + return(xf[a].bits_per_pixel); +#endif + + /* this should never happen; if it does, we're in trouble + */ + + fprintf(stderr, "bitsPerPixelAtDepth: Can't find pixmap depth info!\n"); + exit(1); +} + + + +static Image * +itrueToRGB(Image * const imageP, + unsigned int const ddepth) { + + int y, x, num_pixels, colors; + unsigned long pixel_counts[32786]; + unsigned long pixel_array[32786]; + Pixel pixval; + unsigned char * pixel; + unsigned char * dpixel; + Image * newImageP; + + newImageP = newRGBImage(imageP->width, imageP->height, ddepth); + + colors = 1 << ddepth; + + bzero(pixel_counts, 32768 * sizeof(unsigned long)); + + pixel= imageP->data; + for (y= 0; y < imageP->height; y++) { + unsigned int x; + for (x= 0; x < imageP->width; x++) { + unsigned int const z = TRUE_TO_15BIT(memToVal(pixel, 3)); + pixel_counts[z]++; + pixel += 3; + } + } + num_pixels = 0; + for (x = 0; x < 32768; ++x) { + if (pixel_counts[x] > 0) { + unsigned long const red = RED_INTENSITY(x); + unsigned long const grn = GREEN_INTENSITY(x); + unsigned long const blu = BLUE_INTENSITY(x); + pixel_counts[x] = num_pixels; + *(newImageP->rgb.red + num_pixels) = red<<11; + *(newImageP->rgb.grn + num_pixels) = grn<<11; + *(newImageP->rgb.blu + num_pixels) = blu<<11; + pixel_array[num_pixels++] = (short)x; + if (num_pixels > colors) + break; + } + } + + pixel = imageP->data; + dpixel = newImageP->data; + + for (y = 0; y < imageP->height; ++y) { + unsigned int x; + for (x = 0; x < imageP->width; ++x) { + unsigned int const z = TRUE_TO_15BIT(memToVal(pixel, 3)); + pixval = pixel_counts[z]; + valToMem(pixval, dpixel, newImageP->pixlen); + pixel += 3; + dpixel += newImageP->pixlen; + } + } + newImageP->rgb.used = num_pixels; + newImageP->rgb.compressed = 1; + + return newImageP; +} + + + +static void +makeUsableVisual(Image * const origImageP, + Visual * const visualP, + unsigned int const ddepth, + Image ** const newImagePP) { + + /* process image based on type of visual to which we're sending */ + + switch (origImageP->type) { + case ITRUE: + switch (visualP->class) { + case TrueColor: + case DirectColor: + /* goody goody */ + *newImagePP = origImageP; + break; + case PseudoColor: + *newImagePP = itrueToRGB(origImageP, ddepth); + if (*newImagePP == NULL) + pm_error("Unable to convert for Pseudocolor."); + break; + default: + pm_error("INTERNAL ERROR: impossible visual class %u", + visualP->class); + } + break; + + case IRGB: + switch(visualP->class) { + case TrueColor: + case DirectColor: + /* no problem, we handle this just fine */ + *newImagePP = origImageP; + break; + default: + pm_error("INTERNAL ERROR: impossible visual class %u", + visualP->class); + } + + case IBITMAP: + /* no processing ever needs to be done for bitmaps */ + *newImagePP = origImageP; + break; + } +} + + + +static void +makeColorMap1(Display * const disp, + int const scrn, + Visual * const visualP, + Colormap * const cmapP, + Pixel ** const redvalueP, + Pixel ** const grnvalueP, + Pixel ** const bluvalueP) { + + Pixel * redvalue; + Pixel * grnvalue; + Pixel * bluvalue; + Pixel pixval; + unsigned int redcolors, grncolors, blucolors; + unsigned int redstep, grnstep, blustep; + unsigned int redbottom, grnbottom, blubottom; + unsigned int redtop, grntop, blutop; + unsigned int a; + + MALLOCARRAY_NOFAIL(redvalue, 256); + MALLOCARRAY_NOFAIL(grnvalue, 256); + MALLOCARRAY_NOFAIL(bluvalue, 256); + + if (visualP == DefaultVisual(disp, scrn)) + *cmapP = DefaultColormap(disp, scrn); + else + *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), + visualP, AllocNone); + + retry_direct: /* tag we hit if a DirectColor allocation fails on + * default colormap */ + + /* calculate number of distinct colors in each band */ + + redcolors = grncolors = blucolors = 1; + for (pixval = 1; pixval; pixval <<= 1) { + if (pixval & visualP->red_mask) + redcolors <<= 1; + if (pixval & visualP->green_mask) + grncolors <<= 1; + if (pixval & visualP->blue_mask) + blucolors <<= 1; + } + + /* sanity check */ + + if ((redcolors > visualP->map_entries) || + (grncolors > visualP->map_entries) || + (blucolors > visualP->map_entries)) { + pm_message("Warning: inconsistency in color information " + "(this may be ugly)"); + } + + redstep= 256 / redcolors; + grnstep= 256 / grncolors; + blustep= 256 / blucolors; + redbottom = grnbottom = blubottom= 0; + for (a = 0; a < visualP->map_entries; ++a) { + XColor xcolor; + Status rc; + if (redbottom < 256) + redtop = redbottom + redstep; + if (grnbottom < 256) + grntop = grnbottom + grnstep; + if (blubottom < 256) + blutop = blubottom + blustep; + + xcolor.flags = DoRed | DoGreen | DoBlue; + xcolor.red = (redtop - 1) << 8; + xcolor.green = (grntop - 1) << 8; + xcolor.blue = (blutop - 1) << 8; + rc = XAllocColor(disp, *cmapP, &xcolor); + if (rc == 0) { + /* Allocation failed. If it's for a DirectColor default + visual then we should create a private colormap + and try again. + */ + + if ((visualP->class == DirectColor) && + (visualP == DefaultVisual(disp, scrn))) { + *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), + visualP, AllocNone); + goto retry_direct; + } + + /* something completely unexpected happened */ + + pm_error("INTERNAL ERROR: XAllocColor failed on a " + "TrueColor/Directcolor visual"); + } + + /* fill in pixel values for each band at this intensity */ + + while ((redbottom < 256) && (redbottom < redtop)) + redvalue[redbottom++] = xcolor.pixel & visualP->red_mask; + while ((grnbottom < 256) && (grnbottom < grntop)) + grnvalue[grnbottom++] = xcolor.pixel & visualP->green_mask; + while ((blubottom < 256) && (blubottom < blutop)) + bluvalue[blubottom++] = xcolor.pixel & visualP->blue_mask; + } + *redvalueP = redvalue; + *grnvalueP = grnvalue; + *bluvalueP = bluvalue; +} + + + +static void +allocColorCells(Display * const disp, + Colormap const cmap, + Pixel * const colorIndex, + unsigned int const colorCount, + unsigned int * const cellCountP) { + + bool outOfCells; + unsigned int cellCount; + + outOfCells = false; /* initial value */ + cellCount = 0; /* initial value */ + while (cellCount < colorCount && !outOfCells) { + Status rc; + rc = XAllocColorCells(disp, cmap, FALSE, NULL, 0, + &colorIndex[cellCount++], 1); + if (rc == 0) + outOfCells = true; + } + *cellCountP = cellCount; +} + + + + +static void +makeColorMap2(Display * const disp, + int const scrn, + Visual * const visualP, + RGBMap const rgb, + bool const userWantsPrivateCmap, + bool const userWantsFit, + bool const verbose, + Colormap * const cmapP, + Pixel ** const colorIndexP) { + + bool privateCmap; + bool fit; + bool newmap; + Pixel * colorIndex; + + MALLOCARRAY_NOFAIL(colorIndex, rgb.used); + + /* 'privateCmap' is invalid if not a dynamic visual */ + + switch (visualP->class) { + case StaticColor: + case StaticGray: + privateCmap = TRUE; + default: + privateCmap = userWantsPrivateCmap; + } + + /* get the colormap to use. */ + + if (privateCmap) { /* user asked us to use a private cmap */ + newmap = TRUE; + fit = FALSE; + } else if ((visualP == DefaultVisual(disp, scrn)) || + (visualP->class == StaticGray) || + (visualP->class == StaticColor) || + (visualP->class == TrueColor) || + (visualP->class == DirectColor)) { + + unsigned int a; + + fit = userWantsFit; + + /* if we're using the default visual, try to alloc colors + shareable. otherwise we're using a static visual and + should treat it accordingly. + */ + + if (visualP == DefaultVisual(disp, scrn)) + *cmapP = DefaultColormap(disp, scrn); + else + *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), + visualP, AllocNone); + newmap = FALSE; + + /* allocate colors shareable (if we can) */ + + for (a = 0; a < rgb.used; ++a) { + Status rc; + XColor xcolor; + + xcolor.flags = DoRed | DoGreen | DoBlue; + xcolor.red = rgb.red[a]; + xcolor.green = rgb.grn[a]; + xcolor.blue = rgb.blu[a]; + rc = XAllocColor(disp, *cmapP, &xcolor); + if (rc == 0) { + if ((visualP->class == StaticColor) || + (visualP->class == StaticGray) || + (visualP->class == TrueColor) || + (visualP->class == DirectColor)) { + pm_error("XAllocColor failed on a static visual"); + } else { + /* We can't allocate the colors shareable so + free all the colors we had allocated and + create a private colormap (or fit into the + default cmap if `fit' is true). + */ + XFreeColors(disp, *cmapP, colorIndex, a, 0); + newmap = TRUE; + break; + } + } + colorIndex[a] = xcolor.pixel; + } + } else { + newmap = TRUE; + fit = FALSE; + } + + if (newmap) { + /* Either create a new colormap or fit the image into the + one we have. To create a new one, we create a private + cmap and allocate the colors writable. Fitting the + colors is harder; we have to: + + 1. grab the server so no one can goof with the colormap. + 2. count the available colors using XAllocColorCells. + 3. free the colors we just allocated. + 4. reduce the depth of the image to fit. + 5. allocate the colors again shareable. + 6. ungrab the server and continue on our way. + + Someone should shoot the people who designed X color allocation. + */ + + unsigned int a; + + if (fit) { + if (verbose) + pm_message("Fitting image into default colormap"); + XGrabServer(disp); + } else { + if (verbose) + pm_message("Using private colormap"); + + /* create new colormap */ + + *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), + visualP, AllocNone); + } + + allocColorCells(disp, *cmapP, colorIndex, rgb.used, &a); + + if (fit) { + if (a > 0) + XFreeColors(disp, *cmapP, colorIndex, a, 0); + if (a <= 2) + pm_error("Cannot fit into default colormap"); + } + + if (a == 0) + pm_error("Color allocation failed!"); + + if (fit) { + unsigned int a; + for (a = 0; a < rgb.used; ++a) { + XColor xcolor; + xcolor.flags = DoRed | DoGreen | DoBlue; + xcolor.red = rgb.red[a]; + xcolor.green = rgb.grn[a]; + xcolor.blue = rgb.blu[a]; + + if (!XAllocColor(disp, *cmapP, &xcolor)) + pm_error("XAllocColor failed while fitting colormap!"); + colorIndex[a] = xcolor.pixel; + } + XUngrabServer(disp); + } else { + unsigned int b; + for (b = 0; b < a; ++b) { + XColor xcolor; + xcolor.flags = DoRed | DoGreen | DoBlue; + xcolor.pixel = colorIndex[b]; + xcolor.red = rgb.red[b]; + xcolor.green = rgb.grn[b]; + xcolor.blue = rgb.blu[b]; + XStoreColor(disp, *cmapP, &xcolor); + } + } + } + *colorIndexP = colorIndex; +} + + + +static void +doColorAllocation(XImageInfo * const ximageinfoP, + Display * const disp, + int const scrn, + Visual * const visualP, + Image * const imageP, + bool const userWantsPrivateCmap, + bool const userWantsFit, + bool const verbose, + Pixel ** const colorIndexP, + Pixel ** const redvalP, + Pixel ** const grnvalP, + Pixel ** const bluvalP) { + + if ((visualP->class == TrueColor || visualP->class == DirectColor) && + !BITMAPP(imageP)) { + makeColorMap1(disp, scrn, visualP, &ximageinfoP->cmap, + redvalP, grnvalP, bluvalP); + *colorIndexP = NULL; + } else { + makeColorMap2(disp, scrn, visualP, imageP->rgb, + userWantsPrivateCmap, userWantsFit, verbose, + &ximageinfoP->cmap, colorIndexP); + + *redvalP = *grnvalP = *bluvalP = NULL; + } +} + + + + +static void +makeXImage(XImageInfo * const ximageinfoP, + Display * const disp, + int const scrn, + Visual * const visualP, + unsigned int const ddepth, + Image * const imageP, + Pixel const colorIndex[], + Pixel const redvalue[], + Pixel const grnvalue[], + Pixel const bluvalue[], + bool const verbose) { +/*---------------------------------------------------------------------------- + Create an XImage and related colormap based on the image type we + have. +-----------------------------------------------------------------------------*/ + if (verbose) + pm_message("Building XImage..."); + + switch (imageP->type) { + case IBITMAP: { + unsigned int const byteCount = + (imageP->width + 7) / 8 * imageP->height; + unsigned char * data; + + /* we copy the data to be more consistent */ + + MALLOCARRAY(data, byteCount); + if (data == NULL) + pm_error("Can't allocate space for %u byte image", byteCount); + bcopy(imageP->data, data, byteCount); + + ximageinfoP->ximageP = + XCreateImage(disp, visualP, 1, XYBitmap, + 0, (char*)data, imageP->width, imageP->height, 8, 0); + ximageinfoP->depth = ddepth; + ximageinfoP->foreground = colorIndex[1]; + ximageinfoP->background = colorIndex[0]; + ximageinfoP->ximageP->bitmap_bit_order = MSBFirst; + ximageinfoP->ximageP->byte_order = MSBFirst; + } break; + + case IRGB: + case ITRUE: { + /* Modify image data to match visual and colormap */ + + unsigned int const dbits = bitsPerPixelAtDepth(disp, scrn, ddepth); + unsigned int const dpixlen = (dbits + 7) / 8; + + ximageinfoP->depth = ddepth; + + switch (visualP->class) { + case DirectColor: + case TrueColor: { + unsigned char * data; + unsigned char * destptr; + unsigned char * srcptr; + + ximageinfoP->ximageP = + XCreateImage(disp, visualP, ddepth, ZPixmap, 0, + NULL, imageP->width, imageP->height, 8, 0); + MALLOCARRAY(data, imageP->width * imageP->height * dpixlen); + if (data == NULL) + pm_error("Unable to allocate space for %u x %u x %u image", + imageP->width, imageP->height, dpixlen); + ximageinfoP->ximageP->data = (char*)data; + destptr = data; + srcptr = imageP->data; + switch (imageP->type) { + case ITRUE: { + unsigned int y; + for (y= 0; y < imageP->height; ++y) { + unsigned int x; + for (x= 0; x < imageP->width; ++x) { + Pixel const pixval = memToVal(srcptr, imageP->pixlen); + Pixel const newpixval = + redvalue[TRUE_RED(pixval)] | + grnvalue[TRUE_GRN(pixval)] | + bluvalue[TRUE_BLU(pixval)]; + valToMem(newpixval, destptr, dpixlen); + srcptr += imageP->pixlen; + destptr += dpixlen; + } + } + } break; + case IRGB: { + unsigned int y; + for (y= 0; y < imageP->height; ++y) { + unsigned int x; + for (x = 0; x < imageP->width; ++x) { + Pixel const pixval = memToVal(srcptr, imageP->pixlen); + Pixel const newpixval = + redvalue[imageP->rgb.red[pixval] >> 8] | + grnvalue[imageP->rgb.grn[pixval] >> 8] | + bluvalue[imageP->rgb.blu[pixval] >> 8]; + valToMem(newpixval, destptr, dpixlen); + srcptr += imageP->pixlen; + destptr += dpixlen; + } + } + } break; + default: /* something's broken */ + pm_error("INTERNAL ERROR: Unexpected image type %u for " + "DirectColor/TrueColor visual!", imageP->type); + } + ximageinfoP->ximageP->byte_order = MSBFirst; + /* Trust me, I know what I'm talking about */ + } break; + + default: { + + /* only IRGB images make it this far. */ + + /* If our XImage doesn't have modulus 8 bits per pixel, + it's unclear how to pack bits so we instead use an + XYPixmap image. This is slower. + */ + + if (dbits % 8) { + unsigned int const linelen = (imageP->width + 7) / 8; + unsigned int const size = + imageP->width * imageP->height * dpixlen; + unsigned char * data; + unsigned char * destptr; + unsigned char * srcptr; + Pixel pixval; + unsigned int a; + + ximageinfoP->ximageP = + XCreateImage(disp, visualP, ddepth, XYPixmap, 0, + NULL, imageP->width, imageP->height, 8, 0); + + MALLOCARRAY(data, size); + if (data == NULL) + pm_error("Unable to allocate space for %u x %x x %u " + "image", imageP->width, imageP->height, dpixlen); + ximageinfoP->ximageP->data = (char*)data; + bzero(data, size); + ximageinfoP->ximageP->bitmap_bit_order = MSBFirst; + ximageinfoP->ximageP->byte_order = MSBFirst; + for (a= 0; a < dbits; ++a) { + Pixel const pixmask = 1 << a; + unsigned char * const destdata = + data + ((ddepth - a - 1) * imageP->height * linelen); + + unsigned int y; + + srcptr = imageP->data; + for (y= 0; y < imageP->height; ++y) { + unsigned int x; + unsigned char mask; + destptr = destdata + (y * linelen); + *destptr = 0; + mask = 0x80; + for (x= 0; x < imageP->width; ++x) { + pixval = memToVal(srcptr, imageP->pixlen); + srcptr += imageP->pixlen; + if (colorIndex[pixval] & pixmask) + *destptr |= mask; + mask >>= 1; + if (mask == 0) { + mask = 0x80; + ++destptr; + } + } + } + } + } else { + unsigned int const dpixlen = + (ximageinfoP->ximageP->bits_per_pixel + 7) / 8; + + unsigned char * data; + unsigned char * srcptr; + unsigned char * destptr; + unsigned int y; + + ximageinfoP->ximageP = + XCreateImage(disp, visualP, ddepth, ZPixmap, 0, + NULL, imageP->width, imageP->height, 8, 0); + + MALLOCARRAY(data, imageP->width * imageP->height * dpixlen); + if (data == NULL) + pm_error("Failed to allocate space for %u x %u x %u image", + imageP->width, imageP->height, dpixlen); + ximageinfoP->ximageP->data = (char*)data; + ximageinfoP->ximageP->byte_order= MSBFirst; + /* Trust me, i know what I'm talking about */ + srcptr = imageP->data; + destptr = data; + for (y= 0; y < imageP->height; ++y) { + unsigned int x; + for (x= 0; x < imageP->width; x++) { + valToMem(colorIndex[memToVal(srcptr, imageP->pixlen)], + destptr, dpixlen); + srcptr += imageP->pixlen; + destptr += dpixlen; + } + } + } + } break; + } + } break; + } + if (verbose) + pm_message("done"); +} + + + +XImageInfo * +imageToXImage(Display * const disp, + int const scrn, + Visual * const visualP, /* visual to use */ + unsigned int const ddepth, /* depth of the visual to use */ + Image * const origImageP, + bool const userWantsPrivateCmap, + bool const userWantsFit, + bool const verbose) { + + XImageInfo * ximageinfoP; + Image * imageP; + Pixel * colorIndex; + Pixel * redvalue; + Pixel * grnvalue; + Pixel * bluvalue; + + assertGoodImage(origImageP); + + MALLOCVAR_NOFAIL(ximageinfoP); + ximageinfoP->disp = disp; + ximageinfoP->scrn = scrn; + ximageinfoP->depth = 0; + ximageinfoP->drawable = None; + ximageinfoP->foreground = ximageinfoP->background = 0; + ximageinfoP->gc = NULL; + ximageinfoP->ximageP = NULL; + + makeUsableVisual(origImageP, visualP, ddepth, &imageP); + + assertGoodImage(imageP); + + doColorAllocation(ximageinfoP, disp, scrn, visualP, imageP, + userWantsPrivateCmap, userWantsFit, verbose, + &colorIndex, &redvalue, &grnvalue, &bluvalue); + + makeXImage(ximageinfoP, disp, scrn, visualP, ddepth, imageP, + colorIndex, redvalue, grnvalue, bluvalue, verbose); + + if (colorIndex) + free(colorIndex); + if (redvalue) { + free(redvalue); + free(grnvalue); + free(bluvalue); + } + if (imageP != origImageP) + freeImage(imageP); + + return ximageinfoP; +} + + + +void +sendXImage(XImageInfo * const ximageinfoP, + int const src_x, + int const src_y, + int const dst_x, + int const dst_y, + unsigned int const w, + unsigned int const h) { +/*---------------------------------------------------------------------------- + Given an XImage and a drawable, move a rectangle from the Ximage + to the drawable. +-----------------------------------------------------------------------------*/ + XGCValues gcv; + + /* build and cache the GC */ + + if (!ximageinfoP->gc) { + gcv.function = GXcopy; + if (ximageinfoP->ximageP->depth == 1) { + gcv.foreground = ximageinfoP->foreground; + gcv.background= ximageinfoP->background; + ximageinfoP->gc = + XCreateGC(ximageinfoP->disp, ximageinfoP->drawable, + GCFunction | GCForeground | GCBackground, + &gcv); + }else + ximageinfoP->gc = + XCreateGC(ximageinfoP->disp, ximageinfoP->drawable, + GCFunction, &gcv); + } + + XPutImage(ximageinfoP->disp, ximageinfoP->drawable, ximageinfoP->gc, + ximageinfoP->ximageP, src_x, src_y, dst_x, dst_y, w, h); +} + + + +void +freeXImage(Image * const imageP, + XImageInfo * const ximageinfoP) { +/*---------------------------------------------------------------------------- + free up anything cached in the local Ximage structure. +-----------------------------------------------------------------------------*/ + if (ximageinfoP->gc) + XFreeGC(ximageinfoP->disp, ximageinfoP->gc); + free(ximageinfoP->ximageP->data); + ximageinfoP->ximageP->data = NULL; + XDestroyImage(ximageinfoP->ximageP); + + free(ximageinfoP); +} diff --git a/other/pamx/send.h b/other/pamx/send.h new file mode 100644 index 00000000..203b99c7 --- /dev/null +++ b/other/pamx/send.h @@ -0,0 +1,38 @@ +#ifndef SEND_H_INCLUDED +#define SEND_H_INCLUDED + +#include <X11/Xlib.h> + +#include "pm_c_util.h" + +struct Image; + +void +sendXImage(XImageInfo * const ximageinfoP, + int const src_x, + int const src_y, + int const dst_x, + int const dst_y, + unsigned int const w, + unsigned int const h); + +void +freeXImage(struct Image * const imageP, + XImageInfo * const ximageinfoP); + +XImageInfo * +imageToXImage(Display * const disp, + int const scrn, + Visual * const visualP, /* visual to use */ + unsigned int const ddepth, /* depth of the visual to use */ + struct Image * const origImageP, + bool const userWantsPrivateCmap, + bool const userWantsFit, + bool const verbose); + +Pixmap +ximageToPixmap(Display * const disp, + Window const parent, + XImageInfo * const ximageinfo); + +#endif diff --git a/other/pamx/valtomem.h b/other/pamx/valtomem.h new file mode 100644 index 00000000..e27ce0df --- /dev/null +++ b/other/pamx/valtomem.h @@ -0,0 +1,65 @@ +/* + By Jim Frost 1989.10.02. + + Copyright 1989 Jim Frost. + See COPYRIGHT file for copyright information. +*/ +#ifndef VALTOMEM_H_INCLUDED +#define VALTOMEM_H_INCLUDED + +/* inline these functions for speed. these only work for {len : 1,2,3,4}. + */ + +#define memToVal(PTR,LEN) \ + ((LEN) == 1 ? ((unsigned long)(*((unsigned char *)PTR))) : \ + ((LEN) == 3 ? ((unsigned long) \ + (*(unsigned char *)(PTR) << 16) | \ + (*((unsigned char *)(PTR) + 1) << 8) | \ + (*((unsigned char *)(PTR) + 2))) : \ + ((LEN) == 2 ? ((unsigned long) \ + (*(unsigned char *)(PTR) << 8) | \ + (*((unsigned char *)(PTR) + 1))) : \ + ((unsigned long)((*(unsigned char *)(PTR) << 24) | \ + (*((unsigned char *)(PTR) + 1) << 16) | \ + (*((unsigned char *)(PTR) + 2) << 8) | \ + (*((unsigned char *)(PTR) + 3))))))) + +#define memToValLSB(PTR,LEN) \ + ((LEN) == 1 ? ((unsigned long)(*(unsigned char *)(PTR))) : \ + ((LEN) == 3 ? ((unsigned long) \ + (*(unsigned char *)(PTR)) | \ + (*((unsigned char *)(PTR) + 1) << 8) | \ + (*((unsigned char *)(PTR) + 2) << 16)) : \ + ((LEN) == 2 ? ((unsigned long) \ + (*(unsigned char *)(PTR)) | (*((unsigned char *)(PTR) + 1) << 8)) : \ + ((unsigned long)((*(unsigned char *)(PTR)) | \ + (*((unsigned char *)(PTR) + 1) << 8) | \ + (*((unsigned char *)(PTR) + 2) << 16) | \ + (*((unsigned char *)(PTR) + 3) << 24)))))) + +#define valToMem(VAL,PTR,LEN) \ + ((LEN) == 1 ? (*(unsigned char *)(PTR) = ((unsigned int)(VAL) & 0xff)) : \ + ((LEN) == 3 ? (((*(unsigned char *)(PTR)) = ((unsigned int)(VAL) & 0xff0000) >> 16), \ + ((*((unsigned char *)(PTR) + 1)) = ((unsigned int)(VAL) & 0xff00) >> 8), \ + ((*((unsigned char *)(PTR) + 2)) = ((unsigned int)(VAL) & 0xff))) : \ + ((LEN) == 2 ? (((*(unsigned char *)(PTR)) = ((unsigned int)(VAL) & 0xff00) >> 8), \ + ((*((unsigned char *)(PTR) + 1)) = ((unsigned int)(VAL) & 0xff))) : \ + (((*(unsigned char *)(PTR)) = ((unsigned int)(VAL) & 0xff000000) >> 24), \ + ((*((unsigned char *)(PTR) + 1)) = ((unsigned int)(VAL) & 0xff0000) >> 16), \ + ((*((unsigned char *)(PTR) + 2)) = ((unsigned int)(VAL) & 0xff00) >> 8), \ + ((*((unsigned char *)(PTR) + 3)) = ((unsigned int)(VAL) & 0xff)))))) + +#define valToMemLSB(VAL,PTR,LEN) \ + ((LEN) == 1 ? (*(unsigned char *)(PTR) = ((unsigned int)(VAL) & 0xff)) : \ + ((LEN) == 3 ? (((*(unsigned char *)(PTR) + 2) = ((unsigned int)(VAL) & 0xff0000) >> 16), \ + ((*((unsigned char *)(PTR) + 1)) = ((unsigned int)(VAL) & 0xff00) >> 8), \ + ((*(unsigned char *)(PTR)) = ((unsigned int)(VAL) & 0xff))) : \ + ((LEN) == 2 ? (((*((unsigned char *)(PTR) + 1) = ((unsigned int)(VAL) & 0xff00) >> 8), \ + ((*(unsigned char *)(PTR)) = ((unsigned int)(VAL) & 0xff)))) : \ + (((*((unsigned char *)(PTR) + 3)) = ((unsigned int)(VAL) & 0xff000000) >> 24), \ + ((*((unsigned char *)(PTR) + 2)) = ((unsigned int)(VAL) & 0xff0000) >> 16), \ + ((*((unsigned char *)(PTR) + 1)) = ((unsigned int)(VAL) & 0xff00) >> 8), \ + ((*(unsigned char *)(PTR)) = ((unsigned int)(VAL) & 0xff)))))) + + +#endif diff --git a/other/pamx/window.c b/other/pamx/window.c new file mode 100644 index 00000000..1a6e510b --- /dev/null +++ b/other/pamx/window.c @@ -0,0 +1,1209 @@ +/* + Functions to allocate and deallocate structures and structure data + + By Jim Frost 1989.10.03, Bryan Henderson 2006.03.25. + + See COPYRIGHT file for copyright information. +*/ + +#include <assert.h> +#include <ctype.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <X11/cursorfont.h> +#include <X11/Xatom.h> +#include <X11/Xdefs.h> /* Needed by Xutil.h */ +#include <X11/X.h> /* Needed by Xutil.h */ +#include <X11/Xlib.h> /* Needed by Xutil.h */ +#include <X11/Xutil.h> + +#include "mallocvar.h" +#include "nstring.h" +#include "pm.h" +#include "ximageinfo.h" +#include "send.h" +#include "image.h" +#include "window.h" + +/* A viewer object is something in which you can display an image. You + can display multiple images in the same viewer, sequentially. + + The viewer has a permanent window, called the viewport. When you display + an image, it has a second window, called the image window, which is a + child of the viewport. +*/ + +struct viewer { + Display * dispP; + int scrn; + Window imageWin; + Window viewportWin; + Colormap imageColormap; + Cursor cursor; + unsigned int xpos, ypos; + unsigned int width, height; + bool fullscreen; + bool userChoseGeometry; + Atom deleteAtom; + bool blank; + /* I'm just guessing, but it seems that a "paint" operation is + necessary the first time a viewport is used, but not when + displaying a new image in an old viewport. I assume that's + because the new viewport is blank, and that's what this + value means. + */ + Pixmap pixmap; +}; + + + +static void +setXloadimageClassHint(Display * const dispP, + Window const window) { + + XClassHint classhint; + + classhint.res_class = (char*)"Xloadimage"; + classhint.res_name = (char*)"xloadimage"; + + XSetClassHint(dispP, window, &classhint); +} + + + +static void +setDeleteWindow(viewer * const viewerP) { + + Atom const protoAtom = XInternAtom(viewerP->dispP, "WM_PROTOCOLS", False); + + viewerP->deleteAtom = XInternAtom(viewerP->dispP, + "WM_DELETE_WINDOW", False); + + if ((protoAtom != None) && (viewerP->deleteAtom != None)) + XChangeProperty(viewerP->dispP, viewerP->viewportWin, + protoAtom, XA_ATOM, 32, PropModeReplace, + (unsigned char *)&viewerP->deleteAtom, 1); +} + + + +static void +getInitialViewerGeometry(const char * const geometryString, + bool const fullscreen, + Display * const dispP, + int const scrn, + unsigned int * const xposP, + unsigned int * const yposP, + unsigned int * const widthP, + unsigned int * const heightP, + bool * const userChoseP) { + + unsigned int const defaultWidth = 10; + unsigned int const defaultHeight = 10; + + if (fullscreen) { + *widthP = DisplayWidth(dispP, scrn); + *heightP = DisplayHeight(dispP, scrn); + *xposP = 0; + *yposP = 0; + *userChoseP = TRUE; + } else if (geometryString) { + const char * defGeom; + asprintfN(&defGeom, "%ux%u+0+0", defaultWidth, defaultHeight); + XGeometry(dispP, scrn, geometryString, defGeom, 0, 1, 1, 0, 0, + (int *)xposP, (int *)yposP, + (int *)widthP, (int *)heightP); + strfree(defGeom); + *userChoseP = TRUE; + } else { + *widthP = defaultWidth; + *heightP = defaultHeight; + *xposP = 0; + *yposP = 0; + *userChoseP = FALSE; + } +} + + + +void +createViewer(viewer ** const viewerPP, + Display * const dispP, + int const scrn, + const char * const geometryString, + bool const fullscreen) { + + viewer * viewerP; + + XSetWindowAttributes swa_view; + + MALLOCVAR_NOFAIL(viewerP); + + viewerP->dispP = dispP; + viewerP->scrn = scrn; + viewerP->fullscreen = fullscreen; + viewerP->imageWin = 0; + viewerP->viewportWin = 0; + + getInitialViewerGeometry(geometryString, fullscreen, dispP, scrn, + &viewerP->xpos, &viewerP->ypos, + &viewerP->width, &viewerP->height, + &viewerP->userChoseGeometry); + + viewerP->cursor = XCreateFontCursor(dispP, XC_watch); + + swa_view.background_pixel = WhitePixel(dispP, scrn); + swa_view.backing_store = NotUseful; + swa_view.cursor = viewerP->cursor; + swa_view.event_mask = + ButtonPressMask | Button1MotionMask | KeyPressMask | + StructureNotifyMask | EnterWindowMask | LeaveWindowMask; + swa_view.save_under = FALSE; + + viewerP->viewportWin = + XCreateWindow(dispP, RootWindow(dispP, scrn), + viewerP->xpos, viewerP->ypos, + viewerP->width, viewerP->height, 0, + DefaultDepth(dispP, scrn), InputOutput, + DefaultVisual(dispP, scrn), + CWBackingStore | CWBackPixel | CWCursor | + CWEventMask | CWSaveUnder, + &swa_view); + + setXloadimageClassHint(viewerP->dispP, viewerP->viewportWin); + + setDeleteWindow(viewerP); + + viewerP->blank = TRUE; + + *viewerPP = viewerP; +} + + + +static void +determineRepaintStrategy(viewer * const viewerP, + bool const userWantsPixmap, + bool const verbose, + XImageInfo * const ximageinfoP, + Pixmap * const pixmapP) { + + /* Decide how we're going to handle repaints. We have three modes: + use backing-store, use background pixmap, and use exposures. + If the server supports backing-store, we enable it and use it. + This really helps servers which are memory constrained. If the + server does not have backing-store, we try to send the image to + a pixmap and use that as backing-store. If that fails, we use + exposures to blit the image (which is ugly but it works). + + 'use_pixmap' forces background pixmap mode, which may improve + performance. + */ + + ximageinfoP->drawable = viewerP->imageWin; + if ((DoesBackingStore(ScreenOfDisplay(viewerP->dispP, viewerP->scrn)) == + NotUseful) || + userWantsPixmap) { + *pixmapP = ximageToPixmap(viewerP->dispP, viewerP->imageWin, + ximageinfoP); + if (*pixmapP == None && verbose) + pm_message("Cannot create image in server; " + "repaints will be ugly!"); + } else + *pixmapP = None; +} + + + +static void +setImageWindowAttr(Display * const dispP, + int const scrn, + Window const imageWindow, + Colormap const cmap, + Pixmap const pixmap) { + + /* build window attributes for the image window */ + + XSetWindowAttributes swa_img; + unsigned int wa_mask_img; + + swa_img.bit_gravity = NorthWestGravity; + swa_img.save_under = FALSE; + swa_img.colormap = cmap; + swa_img.border_pixel = 0; + + wa_mask_img = 0; /* initial value */ + + if (pixmap == None) { + /* No pixmap. Must paint over the wire. Ask for BackingStore + to cut down on the painting. But, ask for Exposures so we can + paint both viewables and backingstore. + */ + + swa_img.background_pixel = WhitePixel(dispP,scrn); + wa_mask_img |= CWBackPixel; + swa_img.event_mask = ExposureMask; + wa_mask_img |= CWEventMask; + swa_img.backing_store = WhenMapped; + wa_mask_img |= CWBackingStore; + } else { + /* We have a pixmap so tile the window. to move the image we only + have to move the window and the server should do the rest. + */ + + swa_img.background_pixmap = pixmap; + wa_mask_img |= CWBackPixmap; + swa_img.event_mask = 0; /* no exposures please */ + wa_mask_img |= CWEventMask; + swa_img.backing_store = NotUseful; + wa_mask_img |= CWBackingStore; + } + XChangeWindowAttributes(dispP, imageWindow, wa_mask_img, &swa_img); +} + + + +static void +createImageWindow(viewer * const viewerP, + XImageInfo * const ximageInfoP, + Image * const imageP, + Visual * const visualP, + bool const userWantsPixmap, + bool const verbose) { + + XSetWindowAttributes swa_img; + + swa_img.bit_gravity = NorthWestGravity; + swa_img.save_under = FALSE; + swa_img.colormap = ximageInfoP->cmap; + swa_img.border_pixel = 0; + viewerP->imageWin = XCreateWindow(viewerP->dispP, viewerP->viewportWin, + viewerP->xpos, viewerP->ypos, + imageP->width, imageP->height, 0, + ximageInfoP->depth, InputOutput, visualP, + CWBitGravity | CWColormap | CWSaveUnder | + CWBorderPixel, &swa_img); + viewerP->imageColormap = ximageInfoP->cmap; + setXloadimageClassHint(viewerP->dispP, viewerP->imageWin); + + determineRepaintStrategy(viewerP, userWantsPixmap, verbose, ximageInfoP, + &viewerP->pixmap); + + setImageWindowAttr(viewerP->dispP, viewerP->scrn, + viewerP->imageWin, ximageInfoP->cmap, + viewerP->pixmap); +} + + + +static void +destroyImageWindow(viewer * const viewerP) { + + if (viewerP->imageWin) { + if (viewerP->imageColormap && + (viewerP->imageColormap != DefaultColormap(viewerP->dispP, viewerP->scrn))) + XFreeColormap(viewerP->dispP, viewerP->imageColormap); + XDestroyWindow(viewerP->dispP, viewerP->imageWin); + } +} + + + +static void +changeCursor(viewer * const viewerP, + unsigned int const imageWidth, + unsigned int const imageHeight) { + + Cursor cursor; + XSetWindowAttributes swa; + + if ((viewerP->width >= imageWidth) && (viewerP->height >= imageHeight)) + cursor = XCreateFontCursor(viewerP->dispP, XC_icon); + else if ((viewerP->width < imageWidth) && (viewerP->height >= imageHeight)) + cursor = XCreateFontCursor(viewerP->dispP, XC_sb_h_double_arrow); + else if ((viewerP->width >= imageWidth) && (viewerP->height < imageHeight)) + cursor = XCreateFontCursor(viewerP->dispP, XC_sb_v_double_arrow); + else + cursor = XCreateFontCursor(viewerP->dispP, XC_fleur); + + swa.cursor = cursor; + XChangeWindowAttributes(viewerP->dispP, viewerP->viewportWin, + CWCursor, &swa); + + XFreeCursor(viewerP->dispP, viewerP->cursor); + + viewerP->cursor = cursor; +} + + + +static void +placeImage(viewer * const viewerP, + int const width, + int const height, + int * const rxP, /* input and output */ + int * const ryP) { /* input and output */ + + int pixx, pixy; + + pixx = *rxP; + pixy = *ryP; + + if (viewerP->width > width) + pixx = (viewerP->width - width) / 2; + else { + if ((pixx < 0) && (pixx + width < viewerP->width)) + pixx = viewerP->width - width; + if (pixx > 0) + pixx = 0; + } + if (viewerP->height > height) + pixy = (viewerP->height - height) / 2; + else { + if ((pixy < 0) && (pixy + height < viewerP->height)) + pixy = viewerP->height - viewerP->height; + if (pixy > 0) + pixy = 0; + } + XMoveWindow(viewerP->dispP, viewerP->imageWin, pixx, pixy); + + *rxP = pixx; + *ryP = pixy; +} + + + +static void +blitImage(XImageInfo * const ximageinfoP, + unsigned int const width, + unsigned int const height, + int const xArg, + int const yArg, + int const wArg, + int const hArg) { + + int w, h; + int x, y; + + w = MIN(wArg, width); + h = MIN(hArg, height); + + x = xArg; + y = yArg; + + if (x < 0) { + XClearArea(ximageinfoP->disp, ximageinfoP->drawable, + x, y, -x, h, False); + w -= (0 - x); + x = 0; + } + if (y < 0) { + XClearArea(ximageinfoP->disp, ximageinfoP->drawable, + x, y, w, -y, False); + h -= (0 - y); + y = 0; + } + if (x + w > width) { + XClearArea(ximageinfoP->disp, ximageinfoP->drawable, + x + width, y, x + w - width, h, False); + w -= x + w - width; + } + if (y + h > height) { + XClearArea(ximageinfoP->disp, ximageinfoP->drawable, + x, y + height, w, y + h - height, False); + h -= y + h - height; + } + sendXImage(ximageinfoP, x, y, x, y, w, h); +} + + + +void +destroyViewer(viewer * const viewerP) { +/*---------------------------------------------------------------------------- + Clean up static window. +-----------------------------------------------------------------------------*/ + if (viewerP->pixmap != None) + XFreePixmap(viewerP->dispP, viewerP->pixmap); + + if (viewerP->imageWin) + XDestroyWindow(viewerP->dispP, viewerP->imageWin); + viewerP->imageWin = 0; + + if (viewerP->viewportWin) + XDestroyWindow(viewerP->dispP, viewerP->viewportWin); + + viewerP->viewportWin = 0; + + XFreeCursor(viewerP->dispP, viewerP->cursor); + + free(viewerP); +} + + + +static void +setViewportColormap(viewer * const viewerP, + Visual * const visualP) { +/*---------------------------------------------------------------------------- + Set the colormap and WM_COLORMAP_WINDOWS properly for the viewport. +-----------------------------------------------------------------------------*/ + static Atom cmap_atom = None; + XSetWindowAttributes swa; + Window cmap_windows[2]; + + if (cmap_atom == None) + cmap_atom = XInternAtom(viewerP->dispP, "WM_COLORMAP_WINDOWS", False); + + /* If the visual we're using is the same as the default visual (used by + the viewport window) then we can set the viewport window to use the + image's colormap. This keeps most window managers happy. + */ + + if (visualP == DefaultVisual(viewerP->dispP, viewerP->scrn)) { + swa.colormap = viewerP->imageColormap; + XChangeWindowAttributes(viewerP->dispP, viewerP->viewportWin, + CWColormap, &swa); + XDeleteProperty(viewerP->dispP, viewerP->viewportWin, cmap_atom); + } else { + /* Smart window managers can handle it when we use a different colormap + in our subwindow so long as we set the WM_COLORMAP_WINDOWS property + ala ICCCM. + */ + cmap_windows[0] = viewerP->imageWin; + cmap_windows[1] = viewerP->viewportWin; + XChangeProperty(viewerP->dispP, viewerP->viewportWin, + cmap_atom, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)cmap_windows, 2); + } +} + + + +static const char * +iconName(const char * const s) { +/*---------------------------------------------------------------------------- + Return an icon name suitable for an image titled 's'. + + s == NULL means untitled. +-----------------------------------------------------------------------------*/ + const char * retval; + char buf[BUFSIZ]; + + if (!s) + STRSCPY(buf, "Unnamed"); + else { + char * t; + + STRSCPY(buf, s); + /* strip off stuff following 1st word. This strips + info added by processing functions too. + */ + t = index(buf, ' '); + if (t) + *t = '\0'; + + /* Strip off leading path. if you don't use unix-style paths, + You might want to change this. + */ + + t= rindex(buf, '/'); + if (t) { + char * p; + for (p = buf, ++t; *t; ++p, ++t) + *p = *t; + *p = '\0'; + } + /* look for an extension and strip it off */ + t = index(buf, '.'); + if (t) + *t = '\0'; + } + + retval = strdup(buf); + if (retval == NULL) + pm_error("Out of memory"); + return retval; +} + + + +/* visual class to name table */ + +static struct visual_class_name { + int class; /* numerical value of class */ + const char * name; /* actual name of class */ +} const VisualClassName[] = { + {TrueColor, "TrueColor" } , + {DirectColor, "DirectColor" } , + {PseudoColor, "PseudoColor" } , + {StaticColor, "StaticColor" } , + {GrayScale, "GrayScale" } , + {StaticGray, "StaticGray" } , + {StaticGray, "StaticGrey" } , + {-1, NULL } +}; + + + +int +visualClassFromName(const char * const name) { + + unsigned int a; + int class; + bool found; + + for (a = 0, found = FALSE; VisualClassName[a].name; ++a) { + if (STRCASEEQ(VisualClassName[a].name, name)) { + /* Check for uniqueness. We special-case StaticGray + because we have two spellings but they are unique if + we find either. + */ + if (found && class != StaticGray) + pm_error("'%s' does not uniquely describe a visual class", + name); + + class = VisualClassName[a].class; + found = TRUE; + } + } + if (!found) + pm_error("'%s' is not a visual class name", name); + + return class; +} + + + +static const char * +nameOfVisualClass(int const class) { + + unsigned int a; + const char * name; + bool found; + + for (a = 0, found = FALSE; VisualClassName[a].name; ++a) { + if (VisualClassName[a].class == class) { + name = VisualClassName[a].name; + found = TRUE; + } + } + if (found) + return name; + else + return "[Unknown Visual Class]"; +} + + + +/* find the best visual of a particular class with a particular depth + */ + +static Visual * +bestVisualOfClassAndDepth(Display * const disp, + int const scrn, + int const class, + unsigned int const depth) { + + long const vinfoMask = + VisualScreenMask | VisualClassMask | VisualDepthMask; + + Visual * best; + XVisualInfo template; + XVisualInfo * infoP; + unsigned int nvisuals; + + best = NULL; /* initial value */ + + template.screen = scrn; + template.class = class; + template.depth = depth; + infoP = XGetVisualInfo(disp, vinfoMask, &template, (int*)&nvisuals); + if (infoP) { + /* There are visuals with this depth */ + + /* Not sure what to do if this gives more than one visual of a + particular class and depth, so just return the first one. + */ + + best = infoP->visual; + XFree((char *)infoP); + } + return best; +} + + + +static void +bestVisual(Display * const disp, + int const scrn, + Image * const imageP, + Visual ** const rvisualPP, + unsigned int * const rdepthP) { +/*---------------------------------------------------------------------------- + Try to determine the best available visual to use for a particular + image +-----------------------------------------------------------------------------*/ + unsigned int depth, a; + Screen * screen; + Visual * visualP; + Visual * default_visualP; + + /* Figure out the best depth the server supports. note that some servers + (such as the HP 11.3 server) actually say they support some depths but + have no visuals that support that depth. Seems silly to me ... + */ + + depth = 0; + screen = ScreenOfDisplay(disp, scrn); + for (a = 0; a < screen->ndepths; ++a) { + if (screen->depths[a].nvisuals && + ((!depth || + ((depth < imageP->depth) && (screen->depths[a].depth > depth)) || + ((screen->depths[a].depth >= imageP->depth) && + (screen->depths[a].depth < depth))))) + depth = screen->depths[a].depth; + } + if (!depth) { + /* this shouldn't happen */ + pm_message("bestVisual: didn't find any depths?!?"); + depth = DefaultDepth(disp, scrn); + } + + /* given this depth, find the best possible visual + */ + + default_visualP = DefaultVisual(disp, scrn); + switch (imageP->type) { + case ITRUE: { + /* If the default visual is DirectColor or TrueColor prioritize such + that we use the default type if it exists at this depth + */ + + if (default_visualP->class == TrueColor) { + visualP = bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, + DirectColor, depth); + } else { + visualP = bestVisualOfClassAndDepth(disp, scrn, DirectColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, TrueColor, + depth); + } + + if (!visualP || ((depth <= 8) && + bestVisualOfClassAndDepth(disp, scrn, PseudoColor, + depth))) + visualP = bestVisualOfClassAndDepth(disp, scrn, PseudoColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, StaticColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, GrayScale, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, StaticGray, depth); + } break; + + case IRGB: { + /* if it's an RGB image, we want PseudoColor if we can get it */ + + visualP = bestVisualOfClassAndDepth(disp, scrn, PseudoColor, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, DirectColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, StaticColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, GrayScale, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, StaticGray, depth); + } break; + + case IBITMAP: { + visualP = bestVisualOfClassAndDepth(disp, scrn, PseudoColor, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, StaticColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, GrayScale, depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, StaticGray, depth); + + /* It seems pretty wasteful to use a TrueColor or DirectColor visual + to display a bitmap (2-color) image, so we look for those last + */ + + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, DirectColor, + depth); + if (!visualP) + visualP = bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth); + } break; + } + + if (!visualP) { /* this shouldn't happen */ + pm_message("bestVisual: couldn't find one?!?"); + depth = DefaultDepth(disp, scrn); + visualP = DefaultVisual(disp, scrn); + } + *rvisualPP = visualP; + *rdepthP = depth; +} + + + +static void +bestVisualOfClass(Display * const disp, + int const scrn, + Image * const image, + int const visual_class, + Visual ** const rvisual, + unsigned int * const rdepth) { +/*---------------------------------------------------------------------------- + Given a visual class, try to find the best visual of that class at + the best depth. Return a null visual and depth if we couldn't find + any visual of that type at any depth. +-----------------------------------------------------------------------------*/ + Visual *visual; + Screen *screen; + unsigned int a, b, depth; + + /* loop through depths looking for a visual of a good depth which matches + * our visual class. + */ + + screen= ScreenOfDisplay(disp, scrn); + visual= (Visual *)NULL; + depth= 0; + for (a= 0; a < screen->ndepths; a++) { + for (b= 0; b < screen->depths[a].nvisuals; b++) { + if ((screen->depths[a].visuals[b].class == visual_class) && + (!depth || + ((depth < image->depth) && (screen->depths[a].depth > depth)) || + ((screen->depths[a].depth >= image->depth) && + (screen->depths[a].depth < depth)))) { + depth= screen->depths[a].depth; + visual= &(screen->depths[a].visuals[b]); + } + } + } + *rvisual= visual; + *rdepth= depth; +} + + + +static void +getImageDispDimensions(viewer * const viewerP, + Image * const imageP, + unsigned int * const widthP, + unsigned int * const heightP) { + + if (viewerP->userChoseGeometry) { + *widthP = viewerP->width; + *heightP = viewerP->height; + } else { + unsigned int const displayWidth = + DisplayWidth(viewerP->dispP, viewerP->scrn); + unsigned int const displayHeight = + DisplayHeight(viewerP->dispP, viewerP->scrn); + + /* We don't use more than 90% of display real estate unless user + explicitly asked for it. + */ + *widthP = MIN(imageP->width, (unsigned)(displayWidth * 0.9)); + *heightP = MIN(imageP->height, (unsigned)(displayHeight * 0.9)); + } +} + + + +static void +getVisualAndDepth(Image * const imageP, + Display * const dispP, + int const scrn, + bool const fit, + bool const visualSpec, + unsigned int const visualClass, + Visual ** const visualPP, + unsigned int * const depthP) { + + + /* If the user told us to fit the colormap, we must use the default + visual. + */ + + if (fit) { + *visualPP = DefaultVisual(dispP, scrn); + *depthP = DefaultDepth(dispP, scrn); + } else { + Visual * visualP; + unsigned int depth; + + visualP = NULL; + if (!visualSpec) { + /* Try to pick the best visual for the image. */ + + bestVisual(dispP, scrn, imageP, &visualP, &depth); + } else { + /* Try to find a visual of the specified class */ + + bestVisualOfClass(dispP, scrn, imageP, visualClass, + &visualP, &depth); + if (!visualP) { + bestVisual(dispP, scrn, imageP, &visualP, &depth); + pm_message("Server does not provide %s visual, using %s", + nameOfVisualClass(visualClass), + nameOfVisualClass(visualP->class)); + } + } + *visualPP = visualP; + *depthP = depth; + } +} + + + +static void +setNormalSizeHints(viewer * const viewerP, + Image * const imageP) { + + XSizeHints sh; + + sh.width = viewerP->width; + sh.height = viewerP->height; + if (viewerP->fullscreen) { + sh.min_width = sh.max_width = viewerP->width; + sh.min_height = sh.max_height = viewerP->height; + } else { + sh.min_width = 1; + sh.min_height = 1; + sh.max_width = imageP->width; + sh.max_height = imageP->height; + } + sh.width_inc = 1; + sh.height_inc = 1; + + sh.flags = PMinSize | PMaxSize | PResizeInc; + if (viewerP->userChoseGeometry) + sh.flags |= USSize; + else + sh.flags |= PSize; + + if (viewerP->userChoseGeometry) { + sh.x = viewerP->xpos; + sh.y = viewerP->ypos; + sh.flags |= USPosition; + } + XSetNormalHints(viewerP->dispP, viewerP->viewportWin, &sh); + + sh.min_width = sh.max_width; + sh.min_height = sh.max_height; + XSetNormalHints(viewerP->dispP, viewerP->imageWin, &sh); + /* Image doesn't shrink */ +} + + + +static void +setWMHints(viewer * const viewerP) { + + XWMHints wmh; + + wmh.input = TRUE; + wmh.flags = InputHint; + XSetWMHints(viewerP->dispP, viewerP->viewportWin, &wmh); +} + + +#define CTL_C '\003' + +typedef enum exitReason { + EXIT_NONE, + EXIT_QUIT, + EXIT_WM_KILL, + EXIT_DESTROYED +} exitReason; + + + +static void +run(viewer * const viewerP, + Image * const imageP, + XImageInfo * const ximageInfoP, + int const initPixx, + int const initPixy, + bool const install, + exitReason * const exitReasonP) { + + int lastx, lasty; + int pixx, pixy; + union { + XEvent event; + XAnyEvent any; + XButtonEvent button; + XKeyEvent key; + XConfigureEvent configure; + XExposeEvent expose; + XMotionEvent motion; + XResizeRequestEvent resize; + XClientMessageEvent message; + } event; + exitReason exitReason; + + lastx = lasty = -1; + pixx = initPixx; + pixy = initPixy; + + exitReason = EXIT_NONE; /* No reason to exit yet */ + + while (exitReason == EXIT_NONE) { + XNextEvent(viewerP->dispP, &event.event); + + switch (event.any.type) { + case ButtonPress: + if (event.button.button == 1) { + lastx = event.button.x; + lasty = event.button.y; + } + break; + + case KeyPress: { + char buf[128]; + KeySym ks; + XComposeStatus status; + Status rc; + + rc = XLookupString(&event.key, buf, 128, &ks, &status); + if (rc == 1) { + char const ret = buf[0]; + char const lowerRet = tolower(ret); + + switch (lowerRet) { + case CTL_C: + case 'q': + exitReason = EXIT_QUIT; + break; + } + } + } break; + + case MotionNotify: { + int mousex, mousey; + + if (imageP->width <= viewerP->width && + imageP->height <= viewerP->height) { + /* we're AT&T */ + } else { + mousex = event.button.x; + mousey = event.button.y; + while (XCheckTypedEvent(viewerP->dispP, MotionNotify, + (XEvent*)&event)) { + mousex = event.button.x; + mousey = event.button.y; + } + pixx -= (lastx - mousex); + pixy -= (lasty - mousey); + lastx = mousex; + lasty = mousey; + placeImage(viewerP, imageP->width, imageP->height, + &pixx, &pixy); + } + } break; + + case ConfigureNotify: + viewerP->width = event.configure.width; + viewerP->height = event.configure.height; + + placeImage(viewerP, imageP->width, imageP->height, + &pixx, &pixy); + + /* Configure the cursor to indicate which directions we can drag + */ + + changeCursor(viewerP, imageP->width, imageP->height); + break; + + case DestroyNotify: + exitReason = EXIT_DESTROYED; + break; + + case Expose: + blitImage(ximageInfoP, imageP->width, imageP->height, + event.expose.x, event.expose.y, + event.expose.width, event.expose.height); + break; + + case EnterNotify: + if (install) + XInstallColormap(viewerP->dispP, ximageInfoP->cmap); + break; + + case LeaveNotify: + if (install) + XUninstallColormap(viewerP->dispP, ximageInfoP->cmap); + break; + + case ClientMessage: + /* If we get a client message for the viewport window + which has the value of the delete atom, it means the + window manager wants us to die. + */ + + if ((event.message.window == viewerP->viewportWin) && + (event.message.data.l[0] == viewerP->deleteAtom)) { + exitReason = EXIT_WM_KILL; + } + break; + } + } + *exitReasonP = exitReason; +} + + + +static int +retvalueFromExitReason(exitReason const exitReason) { + + int retval; + + switch (exitReason) { + case EXIT_NONE: assert(false); break; + case EXIT_QUIT: retval = 0; break; + case EXIT_WM_KILL: retval = 10; break; + case EXIT_DESTROYED: retval = 20; break; + } + return retval; +} + + + +static void +resizeViewer(viewer * const viewerP, + unsigned int const newWidth, + unsigned int const newHeight) { + + if (viewerP->width != newWidth || viewerP->height != newHeight) { + XResizeWindow(viewerP->dispP, viewerP->viewportWin, + newWidth, newHeight); + viewerP->width = newWidth; + viewerP->height = newHeight; + } +} + + + +void +displayInViewer(viewer * const viewerP, + struct Image * const imageP, + bool const install, + bool const userWantsPrivateCmap, + bool const fit, + bool const userWantsPixmap, + bool const visualSpec, + unsigned int const visualClass, + const char * const title, + bool const verbose, + int * const retvalP) { + + XImageInfo * ximageInfoP; + Visual * visual; + unsigned int depth; + bool privateCmap; + int pixx, pixy; + exitReason exitReason; + unsigned int windowWidth, windowHeight; + + pixx = -1; pixy = -1; /* initial values */ + + getImageDispDimensions(viewerP, imageP, &windowWidth, &windowHeight); + + resizeViewer(viewerP, windowWidth, windowHeight); + + getVisualAndDepth(imageP, viewerP->dispP, viewerP->scrn, + fit, visualSpec, visualClass, + &visual, &depth); + + if (verbose && (visual != DefaultVisual(viewerP->dispP, viewerP->scrn))) + pm_message("Using %s visual", nameOfVisualClass(visual->class)); + + /* If we're in slideshow mode and the user told us to fit the colormap, + free it here. + */ + + if (viewerP->blank) { + /* For the first image we display we can use the default cmap. + subsequent images use a private colormap (unless they're + bitmaps) so we don't get color erosion when switching + images. + */ + + if (fit) { + XDestroyWindow(viewerP->dispP, viewerP->imageWin); + viewerP->imageWin = 0; + viewerP->imageColormap = 0; + privateCmap = userWantsPrivateCmap; + } else if (!BITMAPP(imageP)) + privateCmap = TRUE; + } else + privateCmap = userWantsPrivateCmap; + + ximageInfoP = imageToXImage(viewerP->dispP, viewerP->scrn, visual, depth, + imageP, privateCmap, fit, verbose); + if (!ximageInfoP) + pm_error("INTERNAL ERROR: Cannot convert Image to XImage"); + + destroyImageWindow(viewerP); + + createImageWindow(viewerP, ximageInfoP, imageP, visual, + userWantsPixmap, verbose); + + if (title) + XStoreName(viewerP->dispP, viewerP->viewportWin, title); + else + XStoreName(viewerP->dispP, viewerP->viewportWin, "Unnamed"); + + { + const char * const name = iconName(title); + XSetIconName(viewerP->dispP, viewerP->viewportWin, name); + strfree(name); + } + setNormalSizeHints(viewerP, imageP); + + setWMHints(viewerP); + + setViewportColormap(viewerP, visual); + + /* Map (display) windows */ + + XMapWindow(viewerP->dispP, viewerP->imageWin); + XMapWindow(viewerP->dispP, viewerP->viewportWin); + + /* Start displaying image */ + + placeImage(viewerP, imageP->width, imageP->height, &pixx, &pixy); + if (!viewerP->blank) { + XResizeWindow(viewerP->dispP, viewerP->imageWin, + imageP->width, imageP->height); + /* Clear the image window. Ask for exposure if there is no tile. */ + XClearArea(viewerP->dispP, viewerP->imageWin, 0, 0, 0, 0, + (viewerP->pixmap == None)); + viewerP->blank = FALSE; + } + + changeCursor(viewerP, imageP->width, imageP->height); + + /* Process X events, continuously */ + run(viewerP, imageP, ximageInfoP, pixx, pixy, install, &exitReason); + + freeXImage(imageP, ximageInfoP); + + *retvalP = retvalueFromExitReason(exitReason); +} diff --git a/other/pamx/window.h b/other/pamx/window.h new file mode 100644 index 00000000..2efc54b5 --- /dev/null +++ b/other/pamx/window.h @@ -0,0 +1,38 @@ +#ifndef WINDOW_H_INCLUDED +#define WINDOW_H_INCLUDED + +#include <X11/Xlib.h> + +#include "pm_c_util.h" + +struct Image; +struct viewer; +typedef struct viewer viewer; + +int +visualClassFromName(const char * const name); + +void +createViewer(viewer ** const viewerPP, + Display * const dispP, + int const scrn, + const char * const geometryString, + bool const fullscreen); + +void +destroyViewer(viewer * const viewerP); + +void +displayInViewer(viewer * const viewerP, + struct Image * const imageP, + bool const install, + bool const userWantsPrivateCmap, + bool const fit, + bool const userWantsPixmap, + bool const visualSpec, + unsigned int const visualClass, + const char * const title, + bool const verbose, + int * const retvalP); + +#endif diff --git a/other/pamx/ximageinfo.h b/other/pamx/ximageinfo.h new file mode 100644 index 00000000..b0b54ee1 --- /dev/null +++ b/other/pamx/ximageinfo.h @@ -0,0 +1,25 @@ +#ifndef XIMAGEINFO_H_INCLUDED +#define XIMAGEINFO_H_INCLUDED + +#include <X11/Xlib.h> + + + +typedef unsigned long Pixel; /* what X thinks a pixel is */ +typedef unsigned short Intensity; /* what X thinks an RGB intensity is */ + +typedef struct { + /* This struct holds the X-client side bits for a rendered image. */ + Display * disp; /* destination display */ + int scrn; /* destination screen */ + int depth; /* depth of drawable we want/have */ + Drawable drawable; /* drawable to send image to */ + Pixel foreground; + /* foreground and background pixels for mono images */ + Pixel background; + Colormap cmap; /* colormap used for image */ + GC gc; /* cached gc for sending image */ + XImage * ximageP; /* ximage structure */ +} XImageInfo; + +#endif diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c new file mode 100644 index 00000000..c4776001 --- /dev/null +++ b/other/pnmcolormap.c @@ -0,0 +1,973 @@ +/****************************************************************************** + pnmcolormap.c +******************************************************************************* + + Create a colormap file (a PPM image containing one pixel of each of a set + of colors). Base the set of colors on an input image. + + For PGM input, do the equivalent for grayscale and produce a PGM graymap. + + By Bryan Henderson, San Jose, CA 2001.12.17 + + Derived from ppmquant, originally by Jef Poskanzer. + + Copyright (C) 1989, 1991 by Jef Poskanzer. + Copyright (C) 2001 by Bryan Henderson. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. + +******************************************************************************/ + +#include <math.h> + +#include "pam.h" +#include "pammap.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "nstring.h" + +enum methodForLargest {LARGE_NORM, LARGE_LUM}; + +enum methodForRep {REP_CENTER_BOX, REP_AVERAGE_COLORS, REP_AVERAGE_PIXELS}; + +typedef struct box* boxVector; +struct box { + int ind; + int colors; + int sum; +}; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespec of input file */ + unsigned int allcolors; /* boolean: select all colors from the input */ + unsigned int newcolors; + /* Number of colors argument; meaningless if allcolors true */ + enum methodForLargest methodForLargest; + /* -spreadintensity/-spreadluminosity options */ + enum methodForRep methodForRep; + /* -center/-meancolor/-meanpixel options */ + unsigned int sort; + unsigned int square; + unsigned int verbose; +}; + + + +static void +parseCommandLine (int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int spreadbrightness, spreadluminosity; + unsigned int center, meancolor, meanpixel; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "spreadbrightness", OPT_FLAG, + NULL, &spreadbrightness, 0); + OPTENT3(0, "spreadluminosity", OPT_FLAG, + NULL, &spreadluminosity, 0); + OPTENT3(0, "center", OPT_FLAG, + NULL, ¢er, 0); + OPTENT3(0, "meancolor", OPT_FLAG, + NULL, &meancolor, 0); + OPTENT3(0, "meanpixel", OPT_FLAG, + NULL, &meanpixel, 0); + OPTENT3(0, "sort", OPT_FLAG, NULL, + &cmdlineP->sort, 0 ); + OPTENT3(0, "square", OPT_FLAG, NULL, + &cmdlineP->square, 0 ); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0 ); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + + if (spreadbrightness && spreadluminosity) + pm_error("You cannot specify both -spreadbrightness and " + "spreadluminosity."); + if (spreadluminosity) + cmdlineP->methodForLargest = LARGE_LUM; + else + cmdlineP->methodForLargest = LARGE_NORM; + + if (center + meancolor + meanpixel > 1) + pm_error("You can specify only one of -center, -meancolor, and " + "-meanpixel."); + if (meancolor) + cmdlineP->methodForRep = REP_AVERAGE_COLORS; + else if (meanpixel) + cmdlineP->methodForRep = REP_AVERAGE_PIXELS; + else + cmdlineP->methodForRep = REP_CENTER_BOX; + + if (argc-1 > 2) + pm_error("Program takes at most two arguments: number of colors " + "and input file specification. " + "You specified %d arguments.", argc-1); + else { + if (argc-1 < 2) + cmdlineP->inputFilespec = "-"; + else + cmdlineP->inputFilespec = argv[2]; + + if (argc-1 < 1) + pm_error("You must specify the number of colors in the " + "output as an argument."); + else { + if (strcmp(argv[1], "all") == 0) + cmdlineP->allcolors = TRUE; + else { + char * tail; + long int const newcolors = strtol(argv[1], &tail, 10); + if (*tail != '\0') + pm_error("The number of colors argument '%s' is not " + "a number or 'all'", argv[1]); + else if (newcolors < 1) + pm_error("The number of colors must be positive"); + else if (newcolors == 1) + pm_error("The number of colors must be greater than 1."); + else { + cmdlineP->newcolors = newcolors; + cmdlineP->allcolors = FALSE; + } + } + } + } +} + + +typedef int qsort_comparison_fn(const void *, const void *); + /* A collation function to be used as argument to qsort() */ + +static qsort_comparison_fn compareplane; + +static unsigned int compareplanePlane; + /* This is a parameter to compareplane(). We use this global variable + so that compareplane() can be called by qsort(), to compare two + tuples. qsort() doesn't pass any arguments except the two tuples. + */ +static int +compareplane(const void * const arg1, + const void * const arg2) { + + const struct tupleint * const * const comparandPP = arg1; + const struct tupleint * const * const comparatorPP = arg2; + + return (*comparandPP)->tuple[compareplanePlane] - + (*comparatorPP)->tuple[compareplanePlane]; +} + + + +static qsort_comparison_fn sumcompare; + +static int +sumcompare(const void * const b1, const void * const b2) { + return(((boxVector)b2)->sum - ((boxVector)b1)->sum); +} + + + + +/* +** Here is the fun part, the median-cut colormap generator. This is based +** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer +** Display", SIGGRAPH '82 Proceedings, page 297. +*/ + +static tupletable2 +newColorMap(unsigned int const newcolors, + unsigned int const depth) { + + tupletable2 colormap; + unsigned int i; + struct pam pam; + + pam.depth = depth; + + colormap.table = pnm_alloctupletable(&pam, newcolors); + + for (i = 0; i < newcolors; ++i) { + unsigned int plane; + for (plane = 0; plane < depth; ++plane) + colormap.table[i]->tuple[plane] = 0; + } + colormap.size = newcolors; + + return colormap; +} + + + +static boxVector +newBoxVector(int const colors, int const sum, int const newcolors) { + + boxVector bv; + MALLOCARRAY(bv, newcolors); + if (bv == NULL) + pm_error("out of memory allocating box vector table"); + + /* Set up the initial box. */ + bv[0].ind = 0; + bv[0].colors = colors; + bv[0].sum = sum; + + return bv; +} + + + +static void +findBoxBoundaries(tupletable2 const colorfreqtable, + unsigned int const depth, + unsigned int const boxStart, + unsigned int const boxSize, + sample minval[], + sample maxval[]) { +/*---------------------------------------------------------------------------- + Go through the box finding the minimum and maximum of each + component - the boundaries of the box. +-----------------------------------------------------------------------------*/ + unsigned int plane; + unsigned int i; + + for (plane = 0; plane < depth; ++plane) { + minval[plane] = colorfreqtable.table[boxStart]->tuple[plane]; + maxval[plane] = minval[plane]; + } + + for (i = 1; i < boxSize; ++i) { + unsigned int plane; + for (plane = 0; plane < depth; ++plane) { + sample const v = colorfreqtable.table[boxStart + i]->tuple[plane]; + if (v < minval[plane]) minval[plane] = v; + if (v > maxval[plane]) maxval[plane] = v; + } + } +} + + + +static unsigned int +largestByNorm(sample minval[], sample maxval[], unsigned int const depth) { + + unsigned int largestDimension; + unsigned int plane; + + sample largestSpreadSoFar = 0; + largestDimension = 0; + for (plane = 0; plane < depth; ++plane) { + sample const spread = maxval[plane]-minval[plane]; + if (spread > largestSpreadSoFar) { + largestDimension = plane; + largestSpreadSoFar = spread; + } + } + return largestDimension; +} + + + +static unsigned int +largestByLuminosity(sample minval[], sample maxval[], + unsigned int const depth) { +/*---------------------------------------------------------------------------- + This subroutine presumes that the tuple type is either + BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3). + To save time, we don't actually check it. +-----------------------------------------------------------------------------*/ + unsigned int retval; + + if (depth == 1) + retval = 0; + else { + /* An RGB tuple */ + unsigned int largestDimension; + unsigned int plane; + double largestSpreadSoFar; + + largestSpreadSoFar = 0.0; + + for (plane = 0; plane < 3; ++plane) { + double const spread = + pnm_lumin_factor[plane] * (maxval[plane]-minval[plane]); + if (spread > largestSpreadSoFar) { + largestDimension = plane; + largestSpreadSoFar = spread; + } + } + retval = largestDimension; + } + return retval; +} + + + +static void +centerBox(int const boxStart, + int const boxSize, + tupletable2 const colorfreqtable, + unsigned int const depth, + tuple const newTuple) { + + unsigned int plane; + + for (plane = 0; plane < depth; ++plane) { + int minval, maxval; + unsigned int i; + + minval = maxval = colorfreqtable.table[boxStart]->tuple[plane]; + + for (i = 1; i < boxSize; ++i) { + int const v = colorfreqtable.table[boxStart + i]->tuple[plane]; + minval = MIN( minval, v); + maxval = MAX( maxval, v); + } + newTuple[plane] = (minval + maxval) / 2; + } +} + + + +static void +averageColors(int const boxStart, + int const boxSize, + tupletable2 const colorfreqtable, + unsigned int const depth, + tuple const newTuple) { + + unsigned int plane; + + for (plane = 0; plane < depth; ++plane) { + sample sum; + int i; + + sum = 0; + + for (i = 0; i < boxSize; ++i) + sum += colorfreqtable.table[boxStart+i]->tuple[plane]; + + newTuple[plane] = sum / boxSize; + } +} + + + +static void +averagePixels(int const boxStart, + int const boxSize, + tupletable2 const colorfreqtable, + unsigned int const depth, + tuple const newTuple) { + + unsigned int n; + /* Number of tuples represented by the box */ + unsigned int plane; + unsigned int i; + + /* Count the tuples in question */ + n = 0; /* initial value */ + for (i = 0; i < boxSize; ++i) + n += colorfreqtable.table[boxStart + i]->value; + + + for (plane = 0; plane < depth; ++plane) { + sample sum; + int i; + + sum = 0; + + for (i = 0; i < boxSize; ++i) + sum += colorfreqtable.table[boxStart+i]->tuple[plane] + * colorfreqtable.table[boxStart+i]->value; + + newTuple[plane] = sum / n; + } +} + + + +static tupletable2 +colormapFromBv(unsigned int const newcolors, + boxVector const bv, + unsigned int const boxes, + tupletable2 const colorfreqtable, + unsigned int const depth, + enum methodForRep const methodForRep) { + /* + ** Ok, we've got enough boxes. Now choose a representative color for + ** each box. There are a number of possible ways to make this choice. + ** One would be to choose the center of the box; this ignores any structure + ** within the boxes. Another method would be to average all the colors in + ** the box - this is the method specified in Heckbert's paper. A third + ** method is to average all the pixels in the box. + */ + tupletable2 colormap; + unsigned int bi; + + colormap = newColorMap(newcolors, depth); + + for (bi = 0; bi < boxes; ++bi) { + switch (methodForRep) { + case REP_CENTER_BOX: + centerBox(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, + colormap.table[bi]->tuple); + break; + case REP_AVERAGE_COLORS: + averageColors(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, + colormap.table[bi]->tuple); + break; + case REP_AVERAGE_PIXELS: + averagePixels(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, + colormap.table[bi]->tuple); + break; + default: + pm_error("Internal error: invalid value of methodForRep: %d", + methodForRep); + } + } + return colormap; +} + + + +static unsigned int +freqTotal(tupletable2 const freqtable) { + + unsigned int i; + unsigned int sum; + + sum = 0; + + for (i = 0; i < freqtable.size; ++i) + sum += freqtable.table[i]->value; + + return sum; +} + + +static void +splitBox(boxVector const bv, + unsigned int * const boxesP, + unsigned int const bi, + tupletable2 const colorfreqtable, + unsigned int const depth, + enum methodForLargest const methodForLargest) { +/*---------------------------------------------------------------------------- + Split Box 'bi' in the box vector bv (so that bv contains one more box + than it did as input). Split it so that each new box represents about + half of the pixels in the distribution given by 'colorfreqtable' for + the colors in the original box, but with distinct colors in each of the + two new boxes. + + Assume the box contains at least two colors. +-----------------------------------------------------------------------------*/ + unsigned int const boxStart = bv[bi].ind; + unsigned int const boxSize = bv[bi].colors; + unsigned int const sm = bv[bi].sum; + + sample * minval; /* malloc'ed array */ + sample * maxval; /* malloc'ed array */ + + unsigned int largestDimension; + /* number of the plane with the largest spread */ + unsigned int medianIndex; + int lowersum; + /* Number of pixels whose value is "less than" the median */ + + MALLOCARRAY_NOFAIL(minval, depth); + MALLOCARRAY_NOFAIL(maxval, depth); + + findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize, + minval, maxval); + + /* Find the largest dimension, and sort by that component. I have + included two methods for determining the "largest" dimension; + first by simply comparing the range in RGB space, and second by + transforming into luminosities before the comparison. + */ + switch (methodForLargest) { + case LARGE_NORM: + largestDimension = largestByNorm(minval, maxval, depth); + break; + case LARGE_LUM: + largestDimension = largestByLuminosity(minval, maxval, depth); + break; + } + + /* TODO: I think this sort should go after creating a box, + not before splitting. Because you need the sort to use + the REP_CENTER_BOX method of choosing a color to + represent the final boxes + */ + + /* Set the gross global variable 'compareplanePlane' as a + parameter to compareplane(), which is called by qsort(). + */ + compareplanePlane = largestDimension; + qsort((char*) &colorfreqtable.table[boxStart], boxSize, + sizeof(colorfreqtable.table[boxStart]), + compareplane); + + { + /* Now find the median based on the counts, so that about half + the pixels (not colors, pixels) are in each subdivision. */ + + unsigned int i; + + lowersum = colorfreqtable.table[boxStart]->value; /* initial value */ + for (i = 1; i < boxSize - 1 && lowersum < sm/2; ++i) { + lowersum += colorfreqtable.table[boxStart + i]->value; + } + medianIndex = i; + } + /* Split the box, and sort to bring the biggest boxes to the top. */ + + bv[bi].colors = medianIndex; + bv[bi].sum = lowersum; + bv[*boxesP].ind = boxStart + medianIndex; + bv[*boxesP].colors = boxSize - medianIndex; + bv[*boxesP].sum = sm - lowersum; + ++(*boxesP); + qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare); + + free(minval); free(maxval); +} + + + +static void +mediancut(tupletable2 const colorfreqtable, + unsigned int const depth, + int const newcolors, + enum methodForLargest const methodForLargest, + enum methodForRep const methodForRep, + tupletable2 * const colormapP) { +/*---------------------------------------------------------------------------- + Compute a set of only 'newcolors' colors that best represent an + image whose pixels are summarized by the histogram + 'colorfreqtable'. Each tuple in that table has depth 'depth'. + colorfreqtable.table[i] tells the number of pixels in the subject image + have a particular color. + + As a side effect, sort 'colorfreqtable'. +-----------------------------------------------------------------------------*/ + boxVector bv; + unsigned int bi; + unsigned int boxes; + bool multicolorBoxesExist; + /* There is at least one box that contains at least 2 colors; ergo, + there is more splitting we can do. + */ + + bv = newBoxVector(colorfreqtable.size, freqTotal(colorfreqtable), + newcolors); + boxes = 1; + multicolorBoxesExist = (colorfreqtable.size > 1); + + /* Main loop: split boxes until we have enough. */ + while (boxes < newcolors && multicolorBoxesExist) { + /* Find the first splittable box. */ + for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi); + if (bi >= boxes) + multicolorBoxesExist = FALSE; + else + splitBox(bv, &boxes, bi, colorfreqtable, depth, methodForLargest); + } + *colormapP = colormapFromBv(newcolors, bv, boxes, colorfreqtable, depth, + methodForRep); + + free(bv); +} + + + + +static void +validateCompatibleImage(struct pam * const inpamP, + struct pam * const firstPamP, + unsigned int const imageSeq) { + + if (inpamP->depth != firstPamP->depth) + pm_error("Image %u depth (%u) is not the same as Image 0 (%u)", + imageSeq, inpamP->depth, firstPamP->depth); + if (inpamP->maxval != firstPamP->maxval) + pm_error("Image %u maxval (%u) is not the same as Image 0 (%u)", + imageSeq, + (unsigned)inpamP->maxval, (unsigned)firstPamP->maxval); + if (inpamP->format != firstPamP->format) + pm_error("Image %u format (%d) is not the same as Image 0 (%d)", + imageSeq, inpamP->format, firstPamP->format); + if (!STREQ(inpamP->tuple_type, firstPamP->tuple_type)) + pm_error("Image %u tuple type (%s) is not the same as Image 0 (%s)", + imageSeq, inpamP->tuple_type, firstPamP->tuple_type); +} + + + +static void +addImageColorsToHash(struct pam * const pamP, + tuplehash const tuplehash, + unsigned int * const colorCountP) { + + tuple * tuplerow; + unsigned int row; + + tuplerow = pnm_allocpamrow(pamP); + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + + pnm_readpamrow(pamP, tuplerow); + + for (col = 0; col < pamP->width; ++col) { + bool firstOccurrence; + + pnm_addtuplefreqoccurrence(pamP, tuplerow[col], tuplehash, + &firstOccurrence); + + if (firstOccurrence) + ++(*colorCountP); + } + } + pnm_freepamrow(tuplerow); +} + + + +static void +computeHistogram(FILE * const ifP, + int * const formatP, + struct pam * const freqPamP, + tupletable2 * const colorfreqtableP) { +/*---------------------------------------------------------------------------- + Make a histogram of the colors in the image stream in the file '*ifP'. + + Return as *freqPamP a description of the tuple values in the histogram. + Only the fields of *freqPamP that describe individual tuples are + meaningful (depth, maxval, tuple type); + + As a fringe benefit, also return the format of the input file as + *formatP. +----------------------------------------------------------------------------*/ + unsigned int imageSeq; + struct pam firstPam; + tuplehash tuplehash; + unsigned int colorCount; + bool eof; + + pm_message("making histogram..."); + + tuplehash = pnm_createtuplehash(); + colorCount = 0; + + eof = FALSE; + + for (imageSeq = 0; !eof; ++imageSeq) { + struct pam inpam; + + pm_message("Scanning image %u", imageSeq); + + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + if (imageSeq == 0) + firstPam = inpam; + else + validateCompatibleImage(&inpam, &firstPam, imageSeq); + + addImageColorsToHash(&inpam, tuplehash, &colorCount); + + pm_message("%u colors so far", colorCount); + + pnm_nextimage(ifP, &eof); + } + colorfreqtableP->table = + pnm_tuplehashtotable(&firstPam, tuplehash, colorCount); + colorfreqtableP->size = colorCount; + + pnm_destroytuplehash(tuplehash); + + pm_message("%u colors found", colorfreqtableP->size); + + freqPamP->size = sizeof(*freqPamP); + freqPamP->len = PAM_STRUCT_SIZE(tuple_type); + freqPamP->maxval = firstPam.maxval; + freqPamP->bytes_per_sample = pnm_bytespersample(freqPamP->maxval); + freqPamP->depth = firstPam.depth; + STRSCPY(freqPamP->tuple_type, firstPam.tuple_type); + + *formatP = firstPam.format; +} + + + +static void +computeColorMapFromInput(FILE * const ifP, + bool const allColors, + int const reqColors, + enum methodForLargest const methodForLargest, + enum methodForRep const methodForRep, + int * const formatP, + struct pam * const freqPamP, + tupletable2 * const colormapP) { +/*---------------------------------------------------------------------------- + Produce a colormap containing the best colors to represent the + image stream in file 'ifP'. Figure it out using the median cut + technique. + + The colormap will have 'reqcolors' or fewer colors in it, unless + 'allcolors' is true, in which case it will have all the colors that + are in the input. + + The colormap has the same maxval as the input. + + Put the colormap in newly allocated storage as a tupletable2 + and return its address as *colormapP. Return the number of colors in + it as *colorsP and its maxval as *colormapMaxvalP. + + Return the characteristics of the input file as + *formatP and *freqPamP. (This information is not really + relevant to our colormap mission; just a fringe benefit). +-----------------------------------------------------------------------------*/ + tupletable2 colorfreqtable; + + computeHistogram(ifP, formatP, freqPamP, &colorfreqtable); + + if (allColors) { + *colormapP = colorfreqtable; + } else { + if (colorfreqtable.size <= reqColors) { + pm_message("Image already has few enough colors (<=%d). " + "Keeping same colors.", reqColors); + *colormapP = colorfreqtable; + } else { + pm_message("choosing %d colors...", reqColors); + mediancut(colorfreqtable, freqPamP->depth, + reqColors, methodForLargest, methodForRep, + colormapP); + pnm_freetupletable2(freqPamP, colorfreqtable); + } + } +} + + + +static void +sortColormap(tupletable2 const colormap, + sample const depth) { +/*---------------------------------------------------------------------------- + Sort the colormap in place, in order of ascending Plane 0 value, + the Plane 1 value, etc. + + Use insertion sort. +-----------------------------------------------------------------------------*/ + int i; + + pm_message("Sorting %u colors...", colormap.size); + + for (i = 0; i < colormap.size; ++i) { + int j; + for (j = i+1; j < colormap.size; ++j) { + unsigned int plane; + bool iIsGreater, iIsLess; + + iIsGreater = FALSE; iIsLess = FALSE; + for (plane = 0; + plane < depth && !iIsGreater && !iIsLess; + ++plane) { + if (colormap.table[i]->tuple[plane] > + colormap.table[j]->tuple[plane]) + iIsGreater = TRUE; + else if (colormap.table[i]->tuple[plane] < + colormap.table[j]->tuple[plane]) + iIsLess = TRUE; + } + if (iIsGreater) { + for (plane = 0; plane < depth; ++plane) { + sample const temp = colormap.table[i]->tuple[plane]; + colormap.table[i]->tuple[plane] = + colormap.table[j]->tuple[plane]; + colormap.table[j]->tuple[plane] = temp; + } + } + } + } +} + + + +static void +colormapToSquare(struct pam * const pamP, + tupletable2 const colormap, + tuple *** const outputRasterP) { + { + unsigned int const intsqrt = (int)sqrt((float)colormap.size); + if (intsqrt * intsqrt == colormap.size) + pamP->width = intsqrt; + else + pamP->width = intsqrt + 1; + } + { + unsigned int const intQuotient = colormap.size / pamP->width; + if (pamP->width * intQuotient == colormap.size) + pamP->height = intQuotient; + else + pamP->height = intQuotient + 1; + } + { + tuple ** outputRaster; + unsigned int row; + unsigned int colormapIndex; + + outputRaster = pnm_allocpamarray(pamP); + + colormapIndex = 0; /* initial value */ + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) { + outputRaster[row][col][plane] = + colormap.table[colormapIndex]->tuple[plane]; + } + if (colormapIndex < colormap.size-1) + ++colormapIndex; + } + } + *outputRasterP = outputRaster; + } +} + + + +static void +colormapToSingleRow(struct pam * const pamP, + tupletable2 const colormap, + tuple *** const outputRasterP) { + + tuple ** outputRaster; + unsigned int col; + + pamP->width = colormap.size; + pamP->height = 1; + + outputRaster = pnm_allocpamarray(pamP); + + for (col = 0; col < pamP->width; ++col) { + int plane; + for (plane = 0; plane < pamP->depth; ++plane) + outputRaster[0][col][plane] = colormap.table[col]->tuple[plane]; + } + *outputRasterP = outputRaster; +} + + + +static void +colormapToImage(int const format, + const struct pam * const colormapPamP, + tupletable2 const colormap, + bool const sort, + bool const square, + struct pam * const outpamP, + tuple *** const outputRasterP) { +/*---------------------------------------------------------------------------- + Create a tuple array and pam structure for an image which includes + one pixel of each of the colors in the colormap 'colormap'. + + May rearrange the contents of 'colormap'. +-----------------------------------------------------------------------------*/ + outpamP->size = sizeof(*outpamP); + outpamP->len = PAM_STRUCT_SIZE(tuple_type); + outpamP->format = format, + outpamP->plainformat = FALSE; + outpamP->depth = colormapPamP->depth; + outpamP->maxval = colormapPamP->maxval; + outpamP->bytes_per_sample = pnm_bytespersample(outpamP->maxval); + STRSCPY(outpamP->tuple_type, colormapPamP->tuple_type); + + if (sort) + sortColormap(colormap, outpamP->depth); + + if (square) + colormapToSquare(outpamP, colormap, outputRasterP); + else + colormapToSingleRow(outpamP, colormap, outputRasterP); +} + + + +int +main(int argc, char * argv[] ) { + + struct cmdlineInfo cmdline; + FILE * ifP; + int format; + struct pam colormapPam; + struct pam outpam; + tuple ** colormapRaster; + tupletable2 colormap; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + computeColorMapFromInput(ifP, + cmdline.allcolors, cmdline.newcolors, + cmdline.methodForLargest, + cmdline.methodForRep, + &format, &colormapPam, &colormap); + + pm_close(ifP); + + colormapToImage(format, &colormapPam, colormap, + cmdline.sort, cmdline.square, &outpam, &colormapRaster); + + if (cmdline.verbose) + pm_message("Generating %u x %u image", outpam.width, outpam.height); + + outpam.file = stdout; + + pnm_writepam(&outpam, colormapRaster); + + pnm_freetupletable2(&colormapPam, colormap); + + pnm_freepamarray(colormapRaster, &outpam); + + pm_close(stdout); + + return 0; +} diff --git a/other/ppmdcfont.c b/other/ppmdcfont.c new file mode 100644 index 00000000..701277a9 --- /dev/null +++ b/other/ppmdcfont.c @@ -0,0 +1,200 @@ +#include <stdio.h> +#include <assert.h> + +#include "ppm.h" +#include "nstring.h" +#include "ppmdfont.h" + + + +static void +generateHeader(struct ppmd_fontHeader const fontHeader) { + + fprintf(stdout, " {/* .header */\n"); + fprintf(stdout, " {'p','p','m','d','f','o','n','t'},\n"); + fprintf(stdout, " 0x%02x,\n", fontHeader.format); + fprintf(stdout, " %u,\n", fontHeader.characterCount); + fprintf(stdout, " %u\n", fontHeader.firstCodePoint); + fprintf(stdout, " }\n"); +} + + + +static void +generateGlyphCommand(struct ppmd_glyphCommand const glyphCommand) { + + const char * verb; + + switch (glyphCommand.verb) { + case CMD_NOOP: verb = "CMD_NOOP"; break; + case CMD_DRAWLINE: verb = "CMD_DRAWLINE"; break; + case CMD_MOVEPEN: verb = "CMD_MOVEPEN"; break; + } + + fprintf(stdout, " {/* glyphCommand */ %s, %u, %u }\n", + verb, glyphCommand.x, glyphCommand.y); + +} + + + +static void +generateCommandTable(struct ppmd_glyph const glyph, + const char * const variableName) { + + unsigned int commandNum; + + fprintf(stdout, "struct ppmd_glyphCommand const %s[%u] = {\n", + variableName, glyph.header.commandCount); + + for (commandNum = 0; + commandNum < glyph.header.commandCount; + ++commandNum) { + + generateGlyphCommand(glyph.commandList[commandNum]); + + if (commandNum < glyph.header.commandCount-1) + fprintf(stdout, " ,\n"); + } +} + + + +static void +generateCommandTables(const struct ppmd_font * const fontP, + const char * const glyphTableVariableName) { + + unsigned int relativeCodePoint; + + for (relativeCodePoint = 0; + relativeCodePoint < fontP->header.characterCount; + ++relativeCodePoint) { + + if (fontP->glyphTable[relativeCodePoint].header.commandCount > 0) { + const char * commandTableVariableName; + + asprintfN(&commandTableVariableName, "%s_cmd_%u", + glyphTableVariableName, + fontP->header.firstCodePoint + relativeCodePoint); + + generateCommandTable(fontP->glyphTable[relativeCodePoint], + commandTableVariableName); + + strfree(commandTableVariableName); + + fprintf(stdout, "};\n"); + fprintf(stdout, "\n"); + } + } +} + + + +static void +generateGlyph( + struct ppmd_glyph const glyph, + const char * const commandTableVariableName) { + + fprintf(stdout, " { /* glyph */\n"); + fprintf(stdout, " { /* header */ %u, %u, %u}\n", + glyph.header.commandCount, + glyph.header.skipBefore, + glyph.header.skipAfter + ); + + fprintf(stdout, " ,\n"); + if (glyph.header.commandCount == 0) + fprintf(stdout, " NULL\n"); + else + fprintf(stdout, " %s\n", commandTableVariableName); + + fprintf(stdout, " }\n"); +} + + + +static void +generateGlyphTable(const struct ppmd_font * const fontP, + const char * const variableName) { + + unsigned int relativeCodePoint; + + generateCommandTables(fontP, variableName); + + fprintf(stdout, "struct ppmd_glyph const %s[%u] = {\n", + variableName, fontP->header.characterCount); + + for (relativeCodePoint = 0; + relativeCodePoint < fontP->header.characterCount; + ++relativeCodePoint) { + + const char * commandTableVariableName; + + asprintfN(&commandTableVariableName, "%s_cmd_%u", + variableName, + fontP->header.firstCodePoint + relativeCodePoint); + + generateGlyph(fontP->glyphTable[relativeCodePoint], + commandTableVariableName); + + strfree(commandTableVariableName); + + if (relativeCodePoint < fontP->header.characterCount - 1) + fprintf(stdout, " ,\n"); + } + fprintf(stdout, "};\n"); + fprintf(stdout, "\n"); +} + + + +static void +generateFont(const struct ppmd_font * const fontP, + const char * const fontVariableName, + const char * const glyphTableVariableName) { + + fprintf(stdout, "struct ppmd_font const %s = {\n", fontVariableName); + + generateHeader(fontP->header); + + fprintf(stdout, " ,\n"); + + fprintf(stdout, " /* .glyphTable: */ %s\n", glyphTableVariableName); + + fprintf(stdout, "};\n"); +} + + + +int +main(int argc, char **argv) { + + const char fontVariableName[] = "ppmd_standardfont"; + const struct ppmd_font * fontP; + const char * glyphTableVariableName; + + ppm_init(&argc, argv); + + ppmd_read_font(stdin, &fontP); + + fprintf(stdout, "/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a " + "ppmdfont file. */\n"); + + fprintf(stdout, "\n"); + + fprintf(stdout, "#include \"ppmdfont.h\"\n\n"); + + asprintfN(&glyphTableVariableName, "%s_glyphTable", fontVariableName); + + generateGlyphTable(fontP, glyphTableVariableName); + + fprintf(stdout, "\n"); + + generateFont(fontP, fontVariableName, glyphTableVariableName); + + strfree(glyphTableVariableName); + + ppmd_free_font(fontP); + + return 0; +} diff --git a/other/ppmddumpfont.c b/other/ppmddumpfont.c new file mode 100644 index 00000000..3ab477ab --- /dev/null +++ b/other/ppmddumpfont.c @@ -0,0 +1,89 @@ +#include <stdio.h> +#include <assert.h> + +#include "ppm.h" +#include "ppmdfont.h" + + + +static int +untwos(unsigned char const arg) { + + if (arg >= 128) + return arg - 256; + else + return arg; +} + + + +static void +dumpHeader(struct ppmd_fontHeader const fontHeader) { + + pm_message("Font has %u characters", fontHeader.characterCount); + pm_message("Font has code points %u through %u", + fontHeader.firstCodePoint, + fontHeader.firstCodePoint + fontHeader.characterCount - 1); +} + + + +static void +dumpGlyph(struct ppmd_glyph const glyph) { + + unsigned int commandNum; + + pm_message(" skip before: %u pixels; skip after: %u pixels; " + "%u commands:", + glyph.header.skipBefore, + glyph.header.skipAfter, + glyph.header.commandCount); + + for (commandNum = 0; + commandNum < glyph.header.commandCount; + ++commandNum) { + + struct ppmd_glyphCommand const glyphCommand = + glyph.commandList[commandNum]; + + const char * verbDisp; + + switch (glyphCommand.verb) { + case CMD_NOOP: verbDisp = "NOOP"; break; + case CMD_DRAWLINE: verbDisp = "DRAWLINE"; break; + case CMD_MOVEPEN: verbDisp = "MOVEPEN"; break; + } + + pm_message(" %s %d %d", + verbDisp, untwos(glyphCommand.x), untwos(glyphCommand.y)); + } +} + + + +int +main(int argc, char **argv) { + + const struct ppmd_font * fontP; + unsigned int relativeCodePoint; + + ppm_init(&argc, argv); + + ppmd_read_font(stdin, &fontP); + + dumpHeader(fontP->header); + + for (relativeCodePoint = 0; + relativeCodePoint < fontP->header.characterCount; + ++relativeCodePoint) { + + pm_message("Code point %u:", + fontP->header.firstCodePoint + relativeCodePoint); + + dumpGlyph(fontP->glyphTable[relativeCodePoint]); + } + + ppmd_free_font(fontP); + + return 0; +} diff --git a/other/ppmdmkfont.c b/other/ppmdmkfont.c new file mode 100644 index 00000000..7cf1256f --- /dev/null +++ b/other/ppmdmkfont.c @@ -0,0 +1,705 @@ +#include <stdio.h> +#include <assert.h> + +#include "ppm.h" +#include "mallocvar.h" +#include "nstring.h" +#include "ppmdfont.h" + + +/* Stroke character definitions + + The following character definitions are derived from the (public + domain) Hershey plotter font database, using the single-stroke + Roman font. + + Each character definition begins with 3 bytes which specify the + number of X, Y plot pairs which follow, the negative of the skip + before starting to draw the characters, and the skip after the + character. The first plot pair moves the pen to that location and + subsequent pairs draw to the location given. A pair of 192, 0 + raises the pen, moves to the location given by the following pair, + and resumes drawing with the pair after that. + + The values in the definition tables are 8-bit two's complement + signed numbers. We declare the table as "unsigned char" and + manually sign-extend the values because C compilers differ as to + whether the type "char" is signed or unsigned, and some compilers + don't accept the qualifier "signed" which we would like to use for + these items. We specify negative numbers as their unsigned two's + complements to avoid complaints from compilers which don't like + initialising unsigned data with signed values. Ahhh, portability. +*/ + +static unsigned char char32[] = +{ 0, 0, 21 }; + +static unsigned char char33[] = +{ 8, 251, 5, + 0, 244, 0, 2, 192, 0, 0, 7, 255, 8, 0, 9, 1, 8, 0, 7 }; + +static unsigned char char34[] = +{ 17, 253, 15, + 2, 244, 1, 245, 0, 244, 1, 243, 2, 244, 2, 246, 1, + 248, 0, 249, 192, 0, 10, 244, 9, 245, 8, 244, 9, 243, 10, 244, + 10, 246, 9, 248, 8, 249, }; + +static unsigned char char35[] = +{ 11, 246, 11, + 1, 240, 250, 16, 192, 0, 7, 240, 0, 16, 192, 0, 250, + 253, 8, 253, 192, 0, 249, 3, 7, 3 }; + +static unsigned char char36[] = +{ 26, 246, 10, + 254, 240, 254, 13, 192, 0, 2, 240, 2, 13, 192, 0, 7, + 247, 5, 245, 2, 244, 254, 244, 251, 245, 249, 247, 249, 249, 250, + 251, 251, 252, 253, 253, 3, 255, 5, 0, 6, 1, 7, 3, 7, 6, 5, 8, 2, + 9, 254, 9, 251, 8, 249, 6 }; + +static unsigned char char37[] = +{ 31, 244, 12, + 9, 244, 247, 9, 192, 0, 252, 244, 254, 246, 254, + 248, 253, 250, 251, 251, 249, 251, 247, 249, 247, 247, 248, 245, + 250, 244, 252, 244, 254, 245, 1, 246, 4, 246, 7, 245, 9, 244, + 192, 0, 5, 2, 3, 3, 2, 5, 2, 7, 4, 9, 6, 9, 8, 8, 9, 6, 9, 4, 7, + 2, 5, 2 }; + +static unsigned char char38[] = +{ 34, 243, 13, + 10, 253, 10, 252, 9, 251, 8, 251, 7, 252, 6, 254, 4, + 3, 2, 6, 0, 8, 254, 9, 250, 9, 248, 8, 247, 7, 246, 5, 246, 3, + 247, 1, 248, 0, 255, 252, 0, 251, 1, 249, 1, 247, 0, 245, 254, + 244, 252, 245, 251, 247, 251, 249, 252, 252, 254, 255, 3, 6, 5, + 8, 7, 9, 9, 9, 10, 8, 10, 7 }; + +static unsigned char char39[] = +{ 7, 251, 5, + 0, 246, 255, 245, 0, 244, 1, 245, 1, 247, 0, 249, 255, + 250 }; + +static unsigned char char40[] = +{ 10, 249, 7, + 4, 240, 2, 242, 0, 245, 254, 249, 253, 254, 253, 2, + 254, 7, 0, 11, 2, 14, 4, 16 }; + +static unsigned char char41[] = +{ 10, 249, 7, + 252, 240, 254, 242, 0, 245, 2, 249, 3, 254, 3, 2, 2, + 7, 0, 11, 254, 14, 252, 16 }; + +static unsigned char char42[] = +{ 8, 248, 8, + 0, 250, 0, 6, 192, 0, 251, 253, 5, 3, 192, 0, 5, 253, + 251, 3 }; + +static unsigned char char43[] = +{ 5, 243, 13, + 0, 247, 0, 9, 192, 0, 247, 0, 9, 0 }; + +static unsigned char char44[] = +{ 8, 251, 5, + 1, 8, 0, 9, 255, 8, 0, 7, 1, 8, 1, 10, 0, 12, 255, 13 +}; + +static unsigned char char45[] = +{ 2, 243, 13, + 247, 0, 9, 0 }; + +static unsigned char char46[] = +{ 5, 251, 5, + 0, 7, 255, 8, 0, 9, 1, 8, 0, 7 }; + +static unsigned char char47[] = +{ 2, 245, 11, + 9, 240, 247, 16 }; + +static unsigned char char48[] = +{ 17, 246, 10, + 255, 244, 252, 245, 250, 248, 249, 253, 249, 0, 250, + 5, 252, 8, 255, 9, 1, 9, 4, 8, 6, 5, 7, 0, 7, 253, 6, 248, 4, + 245, 1, 244, 255, 244 }; + +static unsigned char char49[] = +{ 4, 246, 10, + 252, 248, 254, 247, 1, 244, 1, 9 }; + +static unsigned char char50[] = +{ 14, 246, 10, + 250, 249, 250, 248, 251, 246, 252, 245, 254, 244, 2, + 244, 4, 245, 5, 246, 6, 248, 6, 250, 5, 252, 3, 255, 249, 9, 7, 9 +}; + +static unsigned char char51[] = +{ 15, 246, 10, + 251, 244, 6, 244, 0, 252, 3, 252, 5, 253, 6, 254, 7, + 1, 7, 3, 6, 6, 4, 8, 1, 9, 254, 9, 251, 8, 250, 7, 249, 5 }; + + +static unsigned char char52[] = +{ 6, 246, 10, + 3, 244, 249, 2, 8, 2, 192, 0, 3, 244, 3, 9 }; + +static unsigned char char53[] = +{ 17, 246, 10, + 5, 244, 251, 244, 250, 253, 251, 252, 254, 251, 1, + 251, 4, 252, 6, 254, 7, 1, 7, 3, 6, 6, 4, 8, 1, 9, 254, 9, 251, + 8, 250, 7, 249, 5 }; + +static unsigned char char54[] = +{ 23, 246, 10, + 6, 247, 5, 245, 2, 244, 0, 244, 253, 245, 251, 248, + 250, 253, 250, 2, 251, 6, 253, 8, 0, 9, 1, 9, 4, 8, 6, 6, 7, 3, + 7, 2, 6, 255, 4, 253, 1, 252, 0, 252, 253, 253, 251, 255, 250, 2 +}; + +static unsigned char char55[] = +{ 5, 246, 10, + 7, 244, 253, 9, 192, 0, 249, 244, 7, 244 }; + +static unsigned char char56[] = +{ 29, 246, 10, + 254, 244, 251, 245, 250, 247, 250, 249, 251, 251, + 253, 252, 1, 253, 4, 254, 6, 0, 7, 2, 7, 5, 6, 7, 5, 8, 2, 9, + 254, 9, 251, 8, 250, 7, 249, 5, 249, 2, 250, 0, 252, 254, 255, + 253, 3, 252, 5, 251, 6, 249, 6, 247, 5, 245, 2, 244, 254, 244 }; + +static unsigned char char57[] = +{ 23, 246, 10, + 6, 251, 5, 254, 3, 0, 0, 1, 255, 1, 252, 0, 250, + 254, 249, 251, 249, 250, 250, 247, 252, 245, 255, 244, 0, 244, 3, + 245, 5, 247, 6, 251, 6, 0, 5, 5, 3, 8, 0, 9, 254, 9, 251, 8, 250, + 6 }; + +static unsigned char char58[] = +{ 11, 251, 5, + 0, 251, 255, 252, 0, 253, 1, 252, 0, 251, 192, 0, 0, + 7, 255, 8, 0, 9, 1, 8, 0, 7 }; + +static unsigned char char59[] = +{ 14, 251, 5, + 0, 251, 255, 252, 0, 253, 1, 252, 0, 251, 192, 0, 1, + 8, 0, 9, 255, 8, 0, 7, 1, 8, 1, 10, 0, 12, 255, 13 }; + +static unsigned char char60[] = +{ 3, 244, 12, + 8, 247, 248, 0, 8, 9 }; + +static unsigned char char61[] = +{ 5, 243, 13, + 247, 253, 9, 253, 192, 0, 247, 3, 9, 3 }; + +static unsigned char char62[] = +{ 3, 244, 12, + 248, 247, 8, 0, 248, 9 }; + +static unsigned char char63[] = +{ 20, 247, 9, + 250, 249, 250, 248, 251, 246, 252, 245, 254, 244, 2, + 244, 4, 245, 5, 246, 6, 248, 6, 250, 5, 252, 4, 253, 0, 255, 0, + 2, 192, 0, 0, 7, 255, 8, 0, 9, 1, 8, 0, 7 }; + +static unsigned char char64[] = +{ 55, 243, 14, + 5, 252, 4, 250, 2, 249, 255, 249, 253, 250, 252, + 251, 251, 254, 251, 1, 252, 3, 254, 4, 1, 4, 3, 3, 4, 1, 192, 0, + 255, 249, 253, 251, 252, 254, 252, 1, 253, 3, 254, 4, 192, 0, 5, + 249, 4, 1, 4, 3, 6, 4, 8, 4, 10, 2, 11, 255, 11, 253, 10, 250, 9, + 248, 7, 246, 5, 245, 2, 244, 255, 244, 252, 245, 250, 246, 248, + 248, 247, 250, 246, 253, 246, 0, 247, 3, 248, 5, 250, 7, 252, 8, + 255, 9, 2, 9, 5, 8, 7, 7, 8, 6, 192, 0, 6, 249, 5, 1, 5, 3, 6, 4 +}; + +static unsigned char char65[] = +{ 8, 247, 9, + 0, 244, 248, 9, 192, 0, 0, 244, 8, 9, 192, 0, 251, 2, + 5, 2 }; + +static unsigned char char66[] = +{ 23, 245, 10, + 249, 244, 249, 9, 192, 0, 249, 244, 2, 244, 5, 245, + 6, 246, 7, 248, 7, 250, 6, 252, 5, 253, 2, 254, 192, 0, 249, 254, + 2, 254, 5, 255, 6, 0, 7, 2, 7, 5, 6, 7, 5, 8, 2, 9, 249, 9 }; + +static unsigned char char67[] = +{ 18, 246, 11, + 8, 249, 7, 247, 5, 245, 3, 244, 255, 244, 253, 245, + 251, 247, 250, 249, 249, 252, 249, 1, 250, 4, 251, 6, 253, 8, + 255, 9, 3, 9, 5, 8, 7, 6, 8, 4 }; + +static unsigned char char68[] = +{ 15, 245, 10, + 249, 244, 249, 9, 192, 0, 249, 244, 0, 244, 3, 245, + 5, 247, 6, 249, 7, 252, 7, 1, 6, 4, 5, 6, 3, 8, 0, 9, 249, 9 }; + +static unsigned char char69[] = +{ 11, 246, 9, + 250, 244, 250, 9, 192, 0, 250, 244, 7, 244, 192, 0, + 250, 254, 2, 254, 192, 0, 250, 9, 7, 9 }; + +static unsigned char char70[] = +{ 8, 246, 8, + 250, 244, 250, 9, 192, 0, 250, 244, 7, 244, 192, 0, + 250, 254, 2, 254 }; + +static unsigned char char71[] = +{ 22, 246, 11, + 8, 249, 7, 247, 5, 245, 3, 244, 255, 244, 253, 245, + 251, 247, 250, 249, 249, 252, 249, 1, 250, 4, 251, 6, 253, 8, + 255, 9, 3, 9, 5, 8, 7, 6, 8, 4, 8, 1, 192, 0, 3, 1, 8, 1 }; + +static unsigned char char72[] = +{ 8, 245, 11, + 249, 244, 249, 9, 192, 0, 7, 244, 7, 9, 192, 0, 249, + 254, 7, 254 }; + +static unsigned char char73[] = +{ 2, 252, 4, + 0, 244, 0, 9 }; + +static unsigned char char74[] = +{ 10, 248, 8, + 4, 244, 4, 4, 3, 7, 2, 8, 0, 9, 254, 9, 252, 8, 251, + 7, 250, 4, 250, 2 }; + +static unsigned char char75[] = +{ 8, 245, 10, + 249, 244, 249, 9, 192, 0, 7, 244, 249, 2, 192, 0, + 254, 253, 7, 9 }; + +static unsigned char char76[] = +{ 3, 246, 7, + 250, 244, 250, 9, 6, 9 }; + +static unsigned char char77[] = +{ 11, 244, 12, + 248, 244, 248, 9, 192, 0, 248, 244, 0, 9, 192, 0, 8, + 244, 0, 9, 192, 0, 8, 244, 8, 9 }; + +static unsigned char char78[] = +{ 8, 245, 11, + 249, 244, 249, 9, 192, 0, 249, 244, 7, 9, 192, 0, 7, + 244, 7, 9 }; + +static unsigned char char79[] = +{ 21, 245, 11, + 254, 244, 252, 245, 250, 247, 249, 249, 248, 252, + 248, 1, 249, 4, 250, 6, 252, 8, 254, 9, 2, 9, 4, 8, 6, 6, 7, 4, + 8, 1, 8, 252, 7, 249, 6, 247, 4, 245, 2, 244, 254, 244 }; + +static unsigned char char80[] = +{ 13, 245, 10, + 249, 244, 249, 9, 192, 0, 249, 244, 2, 244, 5, 245, + 6, 246, 7, 248, 7, 251, 6, 253, 5, 254, 2, 255, 249, 255 }; + +static unsigned char char81[] = +{ 24, 245, 11, + 254, 244, 252, 245, 250, 247, 249, 249, 248, 252, + 248, 1, 249, 4, 250, 6, 252, 8, 254, 9, 2, 9, 4, 8, 6, 6, 7, 4, + 8, 1, 8, 252, 7, 249, 6, 247, 4, 245, 2, 244, 254, 244, 192, 0, + 1, 5, 7, 11 }; + +static unsigned char char82[] = +{ 16, 245, 10, + 249, 244, 249, 9, 192, 0, 249, 244, 2, 244, 5, 245, + 6, 246, 7, 248, 7, 250, 6, 252, 5, 253, 2, 254, 249, 254, 192, 0, + 0, 254, 7, 9 }; + +static unsigned char char83[] = +{ 20, 246, 10, + 7, 247, 5, 245, 2, 244, 254, 244, 251, 245, 249, + 247, 249, 249, 250, 251, 251, 252, 253, 253, 3, 255, 5, 0, 6, 1, + 7, 3, 7, 6, 5, 8, 2, 9, 254, 9, 251, 8, 249, 6 }; + +static unsigned char char84[] = +{ 5, 248, 8, + 0, 244, 0, 9, 192, 0, 249, 244, 7, 244 }; + +static unsigned char char85[] = +{ 10, 245, 11, + 249, 244, 249, 3, 250, 6, 252, 8, 255, 9, 1, 9, 4, + 8, 6, 6, 7, 3, 7, 244 }; + +static unsigned char char86[] = +{ 5, 247, 9, + 248, 244, 0, 9, 192, 0, 8, 244, 0, 9 }; + +static unsigned char char87[] = +{ 11, 244, 12, + 246, 244, 251, 9, 192, 0, 0, 244, 251, 9, 192, 0, 0, + 244, 5, 9, 192, 0, 10, 244, 5, 9 }; + +static unsigned char char88[] = +{ 5, 246, 10, + 249, 244, 7, 9, 192, 0, 7, 244, 249, 9 }; + +static unsigned char char89[] = +{ 6, 247, 9, + 248, 244, 0, 254, 0, 9, 192, 0, 8, 244, 0, 254 }; + +static unsigned char char90[] = +{ 8, 246, 10, + 7, 244, 249, 9, 192, 0, 249, 244, 7, 244, 192, 0, + 249, 9, 7, 9 }; + +static unsigned char char91[] = +{ 11, 249, 7, + 253, 240, 253, 16, 192, 0, 254, 240, 254, 16, 192, 0, + 253, 240, 4, 240, 192, 0, 253, 16, 4, 16 }; + +static unsigned char char92[] = +{ 2, 245, 11, + 9, 16, 247, 240 }; + +static unsigned char char93[] = +{ 11, 249, 7, + 2, 240, 2, 16, 192, 0, 3, 240, 3, 16, 192, 0, 252, + 240, 3, 240, 192, 0, 252, 16, 3, 16 }; + +static unsigned char char94[] = +{ 7, 245, 11, + 248, 2, 0, 253, 8, 2, 192, 0, 248, 2, 0, 254, 8, 2 }; + +static unsigned char char95[] = +{ 2, 253, 22, + 0, 9, 20, 9 }; + +static unsigned char char96[] = +{ 7, 251, 5, + 1, 244, 0, 245, 255, 247, 255, 249, 0, 250, 1, 249, 0, 248 }; + +static unsigned char char97[] = +{ 17, 247, 10, + 6, 251, 6, 9, 192, 0, 6, 254, 4, 252, 2, 251, 255, + 251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, + 2, 9, 4, 8, 6, 6 }; + +static unsigned char char98[] = +{ 17, 246, 9, + 250, 244, 250, 9, 192, 0, 250, 254, 252, 252, 254, + 251, 1, 251, 3, 252, 5, 254, 6, 1, 6, 3, 5, 6, 3, 8, 1, 9, 254, + 9, 252, 8, 250, 6 }; + +static unsigned char char99[] = +{ 14, 247, 9, + 6, 254, 4, 252, 2, 251, 255, 251, 253, 252, 251, 254, + 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, 2, 9, 4, 8, 6, 6 }; + +static unsigned char char100[] = +{ 17, 247, 10, + 6, 244, 6, 9, 192, 0, 6, 254, 4, 252, 2, 251, 255, + 251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, + 2, 9, 4, 8, 6, 6 }; + +static unsigned char char101[] = +{ 17, 247, 9, + 250, 1, 6, 1, 6, 255, 5, 253, 4, 252, 2, 251, 255, + 251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, + 2, 9, 4, 8, 6, 6 }; + +static unsigned char char102[] = +{ 8, 251, 7, + 5, 244, 3, 244, 1, 245, 0, 248, 0, 9, 192, 0, 253, + 251, 4, 251 }; + +static unsigned char char103[] = +{ 22, 247, 10, + 6, 251, 6, 11, 5, 14, 4, 15, 2, 16, 255, 16, 253, + 15, 192, 0, 6, 254, 4, 252, 2, 251, 255, 251, 253, 252, 251, + 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, 2, 9, 4, 8, 6, 6 }; + +static unsigned char char104[] = +{ 10, 247, 10, + 251, 244, 251, 9, 192, 0, 251, 255, 254, 252, 0, + 251, 3, 251, 5, 252, 6, 255, 6, 9 }; + +static unsigned char char105[] = +{ 8, 252, 4, + 255, 244, 0, 245, 1, 244, 0, 243, 255, 244, 192, 0, + 0, 251, 0, 9 }; + +static unsigned char char106[] = +{ 11, 251, 5, + 0, 244, 1, 245, 2, 244, 1, 243, 0, 244, 192, 0, 1, + 251, 1, 12, 0, 15, 254, 16, 252, 16 }; + +static unsigned char char107[] = +{ 8, 247, 8, + 251, 244, 251, 9, 192, 0, 5, 251, 251, 5, 192, 0, 255, 1, 6, 9 }; + +static unsigned char char108[] = +{ 2, 252, 4, + 0, 244, 0, 9 }; + +static unsigned char char109[] = +{ 18, 241, 15, + 245, 251, 245, 9, 192, 0, 245, 255, 248, 252, 250, + 251, 253, 251, 255, 252, 0, 255, 0, 9, 192, 0, 0, 255, 3, 252, + 5, 251, 8, 251, 10, 252, 11, 255, 11, 9 }; + +static unsigned char char110[] = +{ 10, 247, 10, + 251, 251, 251, 9, 192, 0, 251, 255, 254, 252, 0, + 251, 3, 251, 5, 252, 6, 255, 6, 9 }; + +static unsigned char char111[] = +{ 17, 247, 10, + 255, 251, 253, 252, 251, 254, 250, 1, 250, 3, 251, + 6, 253, 8, 255, 9, 2, 9, 4, 8, 6, 6, 7, 3, 7, 1, 6, 254, 4, 252, + 2, 251, 255, 251 }; + +static unsigned char char112[] = +{ 17, 246, 9, + 250, 251, 250, 16, 192, 0, 250, 254, 252, 252, 254, + 251, 1, 251, 3, 252, 5, 254, 6, 1, 6, 3, 5, 6, 3, 8, 1, 9, 254, + 9, 252, 8, 250, 6 }; + +static unsigned char char113[] = +{ 17, 247, 10, + 6, 251, 6, 16, 192, 0, 6, 254, 4, 252, 2, 251, 255, + 251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, + 2, 9, 4, 8, 6, 6 }; + +static unsigned char char114[] = +{ 8, 249, 6, + 253, 251, 253, 9, 192, 0, 253, 1, 254, 254, 0, 252, + 2, 251, 5, 251 }; + +static unsigned char char115[] = +{ 17, 248, 9, + 6, 254, 5, 252, 2, 251, 255, 251, 252, 252, 251, + 254, 252, 0, 254, 1, 3, 2, 5, 3, 6, 5, 6, 6, 5, 8, 2, 9, 255, 9, + 252, 8, 251, 6 }; + +static unsigned char char116[] = +{ 8, 251, 7, + 0, 244, 0, 5, 1, 8, 3, 9, 5, 9, 192, 0, 253, 251, 4, 251 }; + +static unsigned char char117[] = +{ 10, 247, 10, + 251, 251, 251, 5, 252, 8, 254, 9, 1, 9, 3, 8, 6, 5, + 192, 0, 6, 251, 6, 9 }; + +static unsigned char char118[] = +{ 5, 248, 8, + 250, 251, 0, 9, 192, 0, 6, 251, 0, 9 }; + +static unsigned char char119[] = +{ 11, 245, 11, + 248, 251, 252, 9, 192, 0, 0, 251, 252, 9, 192, 0, + 0, 251, 4, 9, 192, 0, 8, 251, 4, 9 }; + +static unsigned char char120[] = +{ 5, 248, 9, + 251, 251, 6, 9, 192, 0, 6, 251, 251, 9 }; + +static unsigned char char121[] = +{ 9, 248, 8, + 250, 251, 0, 9, 192, 0, 6, 251, 0, 9, 254, 13, 252, + 15, 250, 16, 249, 16 }; + +static unsigned char char122[] = +{ 8, 248, 9, + 6, 251, 251, 9, 192, 0, 251, 251, 6, 251, 192, 0, + 251, 9, 6, 9 }; + +static unsigned char char123[] = +{ 39, 249, 7, + 2, 240, 0, 241, 255, 242, 254, 244, 254, 246, 255, + 248, 0, 249, 1, 251, 1, 253, 255, 255, 192, 0, 0, 241, 255, 243, + 255, 245, 0, 247, 1, 248, 2, 250, 2, 252, 1, 254, 253, 0, 1, 2, + 2, 4, 2, 6, 1, 8, 0, 9, 255, 11, 255, 13, 0, 15, 192, 0, 255, 1, + 1, 3, 1, 5, 0, 7, 255, 8, 254, 10, 254, 12, 255, 14, 0, 15, 2, 16 }; + +static unsigned char char124[] = +{ 2, 252, 4, + 0, 240, 0, 16 }; + +static unsigned char char125[] = +{ 39, 249, 7, + 254, 240, 0, 241, 1, 242, 2, 244, 2, 246, 1, 248, 0, + 249, 255, 251, 255, 253, 1, 255, 192, 0, 0, 241, 1, 243, 1, 245, + 0, 247, 255, 248, 254, 250, 254, 252, 255, 254, 3, 0, 255, 2, + 254, 4, 254, 6, 255, 8, 0, 9, 1, 11, 1, 13, 0, 15, 192, 0, 1, 1, + 255, 3, 255, 5, 0, 7, 1, 8, 2, 10, 2, 12, 1, 14, 0, 15, 254, 16 }; + +static unsigned char char126[] = +{ 23, 255, 21, + 2, 1, 0, 255, 1, 253, 3, 251, 5, 251, 7, 252, + 11, 255, 13, 0, 15, 0, 17, 255, 18, 254, 192, 0, 2, 0, 1, + 254, 3, 253, 5, 253, 7, 254, 11, 1, 13, 2, 15, 2, 17, 1, 18, + 255, 18, 252 }; + +/* Pointers to character definition tables. */ + +static unsigned char * fontData[] = { + char32, char33, char34, char35, char36, char37, char38, char39, char40, + char41, char42, char43, char44, char45, char46, char47, char48, char49, + char50, char51, char52, char53, char54, char55, char56, char57, char58, + char59, char60, char61, char62, char63, char64, char65, char66, char67, + char68, char69, char70, char71, char72, char73, char74, char75, char76, + char77, char78, char79, char80, char81, char82, char83, char84, char85, + char86, char87, char88, char89, char90, char91, char92, char93, char94, + char95, char96, char97, char98, char99, char100, char101, char102, + char103, char104, char105, char106, char107, char108, char109, char110, + char111, char112, char113, char114, char115, char116, char117, char118, + char119, char120, char121, char122, char123, char124, char125, char126 +}; + + + +static void +writeGlyphCommand(FILE * const ofP, + struct ppmd_glyphCommand const glyphCommand) { + + fputc(glyphCommand.verb, ofP); + fputc(glyphCommand.x, ofP); + fputc(glyphCommand.y, ofP); +} + + + +static void +writeMovePen(FILE * const ofP, + const unsigned char * const glyphData) { + + struct ppmd_glyphCommand glyphCommand; + + glyphCommand.verb = CMD_MOVEPEN; + glyphCommand.x = glyphData[0]; + glyphCommand.y = glyphData[1]; + + writeGlyphCommand(ofP, glyphCommand); +} + + + +static void +writeMovePenNoop(FILE * const ofP, + const unsigned char * const glyphData) { + + struct ppmd_glyphCommand glyphCommand; + + glyphCommand.verb = CMD_MOVEPEN; + glyphCommand.x = glyphData[0]; + glyphCommand.y = glyphData[1]; + + writeGlyphCommand(ofP, glyphCommand); + + glyphCommand.verb = CMD_NOOP; + glyphCommand.x = 0; + glyphCommand.y = 0; + + writeGlyphCommand(ofP, glyphCommand); +} + + + +static void +writeDrawLine(FILE * const ofP, + const unsigned char * const glyphData) { + + struct ppmd_glyphCommand glyphCommand; + + glyphCommand.verb = CMD_DRAWLINE; + glyphCommand.x = glyphData[0]; + glyphCommand.y = glyphData[1]; + + writeGlyphCommand(ofP, glyphCommand); +} + + + +static void +writeGlyphHeader(FILE * const ofP, + struct ppmd_glyphHeader const glyphHeader) { + + fputc(glyphHeader.commandCount, ofP); + fputc(glyphHeader.skipBefore, ofP); + fputc(glyphHeader.skipAfter, ofP); +} + + + +static void +writeBuiltinCharacter(FILE * const ofP, + unsigned int const relativeCodePoint) { + + const unsigned char * const glyphData = fontData[relativeCodePoint]; + + struct ppmd_glyphHeader glyphHeader; + unsigned int commandNum; + + glyphHeader.commandCount = glyphData[0]; + glyphHeader.skipBefore = glyphData[1]; + glyphHeader.skipAfter = glyphData[2]; + + writeGlyphHeader(ofP, glyphHeader); + + commandNum = 0; + + while (commandNum < glyphHeader.commandCount) { + + if (commandNum == 0) { + writeMovePen(ofP, &glyphData[3 + commandNum * 2]); + commandNum += 1; + } else if (glyphData[3 + commandNum*2] == 192) { + + assert(commandNum + 1 < glyphHeader.commandCount); + + writeMovePenNoop(ofP, &glyphData[3 + (commandNum + 1) * 2]); + + commandNum += 2; + } else { + writeDrawLine(ofP, &glyphData[3 + commandNum * 2]); + commandNum += 1; + } + } +} + + + +static void +writeFontHeader(FILE * const ofP, + struct ppmd_fontHeader const fontHeader) { + + fwrite(fontHeader.signature, 1, sizeof(fontHeader.signature), ofP); + fputc(fontHeader.format, ofP); + fputc(fontHeader.characterCount, ofP); + fputc(fontHeader.firstCodePoint, ofP); +} + + + +static void +writeBuiltinFont(FILE * const ofP) { + + unsigned int relativeCodePoint; + + struct ppmd_fontHeader fontHeader; + + memcpy(fontHeader.signature, "ppmdfont", sizeof(fontHeader.signature)); + fontHeader.format = 0x01; + fontHeader.characterCount = 95; + fontHeader.firstCodePoint = 32; + + writeFontHeader(ofP, fontHeader); + + for (relativeCodePoint = 0; + relativeCodePoint < fontHeader.characterCount; + ++relativeCodePoint) { + + writeBuiltinCharacter(ofP,relativeCodePoint); + } +} + + + +int +main(int argc, char **argv) { + + ppm_init(&argc, argv); + + writeBuiltinFont(stdout); + + return 0; +} diff --git a/other/ppmsvgalib.c b/other/ppmsvgalib.c new file mode 100644 index 00000000..67cc2b1a --- /dev/null +++ b/other/ppmsvgalib.c @@ -0,0 +1,283 @@ +/****************************************************************************** + ppmsvgalib +******************************************************************************* + Display a PPM image on a Linux console using Svgalib. + + By Bryan Henderson, San Jose CA 2002.01.06. + + Contributed to the public domain. + +******************************************************************************/ + +#define _XOPEN_SOURCE /* Make sure modern signal stuff is in signal.h */ +#include <stdio.h> +#include <vga.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include "ppm.h" +#include "shhopt.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespec of input file */ + unsigned int mode; + unsigned int verbose; +}; + + + +static void +parseCommandLine (int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc( 100*sizeof( optEntry ) ); + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int modeSpec; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "mode", OPT_UINT, + &cmdlineP->mode, &modeSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, + NULL, &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (!modeSpec) + pm_error("You must specify the -mode option."); + + if (argc-1 > 1) + pm_error("Program takes at most one argument: the input file " + "specification. " + "You specified %d arguments.", argc-1); + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else + cmdlineP->inputFilespec = argv[1]; +} + + + +static void +displayImage(FILE * const ifP, + int const cols, + int const rows, + pixval const maxval, + int const format, + int const originCol, + int const originRow) { +/*---------------------------------------------------------------------------- + Draw the PPM image which is in file 'ifP', which is positioned after the + PPM header on the screen with its upper left corner (originCol, originRow). + + The image is 'cols' x 'rows' with maxval 'maxval' and PNM format 'format'. + + Svgalib is initialized and the mode selected. + + The image fits on the screen. +-----------------------------------------------------------------------------*/ + unsigned int svgalibMaxval = 255; + /* This is the maxval for intensity values passed to Svgalib */ + unsigned int row; + pixel * pixelrow; + + pixelrow = ppm_allocrow(cols); + + /* Implementation note: It might be faster to use + vga_drawscansegment() instead of vga_drawpixel() + */ + + for (row = 0; row < rows; ++row) { + unsigned int col; + ppm_readppmrow(ifP, pixelrow, cols, maxval, format); + for (col = 0; col < cols; ++col) { + pixel const p = pixelrow[col]; + int const red = PPM_GETR(p) * svgalibMaxval / maxval; + int const grn = PPM_GETG(p) * svgalibMaxval / maxval; + int const blu = PPM_GETB(p) * svgalibMaxval / maxval; + + vga_setrgbcolor(red, grn, blu); + vga_drawpixel(originCol + col, originRow + row); + } + } + ppm_freerow(pixelrow); +} + + + +static void +sigintHandler(int const signal) { +/*---------------------------------------------------------------------------- + This is a signal handler for the SIGINT signal (Control-C). + + It does nothing; The handler exists only to replace the default action, + which is to terminate the process. Though the handler does nothing, + the signal still causes the wait() system call, assuming it's in progress, + to terminate so that this program can terminate. +-----------------------------------------------------------------------------*/ +} + + + +static void +waitforSigint(void) { + + struct sigaction oldsigaction; + struct sigaction newsigaction; + int rc; + + newsigaction.sa_handler = &sigintHandler; + sigemptyset(&newsigaction.sa_mask); + newsigaction.sa_flags = 0; + rc = sigaction(SIGINT, &newsigaction, &oldsigaction); + if (rc != 0) + pm_error("Unable to set up SIGINTR signal handler. Errno=%d (%s)", + errno, strerror(errno)); + + pause(); /* Wait for a signal, e.g. control-C */ + + sigaction(SIGINT, &oldsigaction, NULL); +} + + + +static void +display(FILE * const ifP, + int const cols, + int const rows, + pixval const maxval, + int const format, + int const videoMode, + bool const verbose) { + + int xmax, ymax; + vga_modeinfo *modeinfo; + + modeinfo = vga_getmodeinfo(videoMode); + + if (verbose) { + pm_message("Screen Width: %d Height: %d Colors: %d", + modeinfo->width, + modeinfo->height, + modeinfo->colors); + pm_message("DisplayStartRange: %xh Maxpixels: %d Blit: %s", + modeinfo->startaddressrange, + modeinfo->maxpixels, + modeinfo->haveblit ? "YES" : "NO"); + } + + if (modeinfo->colors <= 256) + pm_error("This video mode has %d or fewer colors, which means " + "it is colormapped (aka paletted, aka pseudocolor). " + "This program cannot drive colormapped modes.", + modeinfo->colors); + + if (cols > modeinfo->width) + pm_error("Image is too wide (%d columns) for screen (%d columns). " + "Use Pamcut to select part to display.", + cols, modeinfo->width); + if (rows > modeinfo->height) + pm_error("Image is too tall (%d rows) for screen (%d rows). " + "Use Pamcut to select part to display.", + rows, modeinfo->height); + + /* The program must not terminate after we set the video mode and before + we reset it to text mode. Note that vga_setmode() sets up handlers + for signals such as SIGINT that attempt to restore modes and then exit + the program. + */ + + vga_setmode(videoMode); + + vga_screenoff(); + + xmax = vga_getxdim() - 1; + ymax = vga_getydim() - 1; + + /* Draw white border */ + + vga_setcolor(vga_white()); + vga_drawline(0, 0, xmax, 0); + vga_drawline(xmax, 0, xmax, ymax); + vga_drawline(xmax, ymax, 0, ymax); + vga_drawline(0, ymax, 0, 0); + + vga_screenon(); + + { + int const originCol = (modeinfo->width - cols) / 2; + int const originRow = (modeinfo->height - rows) / 2; + displayImage(ifP, cols, rows, maxval, format, originCol, originRow); + } + + waitforSigint(); + + vga_setmode(TEXT); +} + + + +int +main(int argc, char *argv[]) { + + FILE * ifP; + struct cmdlineInfo cmdline; + int cols, rows; + pixval maxval; + int format; + int rc; + + ppm_init( &argc, argv ); + + rc = vga_init(); /* Initialize. */ + if (rc < 0) + pm_error("Svgalib unable to allocate a virtual console."); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + ppm_readppminit(ifP, &cols, &rows, &maxval, &format); + + { + enum pm_check_code checkResult; + ppm_check(ifP, PM_CHECK_BASIC, format, cols, rows, maxval, + &checkResult); + } + + if (vga_hasmode(cmdline.mode)) + display(ifP, cols, rows, maxval, format, + cmdline.mode, cmdline.verbose); + else { + pm_error("Svgalib video mode #%d not available. Either the " + "video controller isn't capable of that mode or the " + "Svgalib video driver doesn't know how to use it.", + cmdline.mode); + } + + pm_close(ifP); + + return 0; +} diff --git a/other/ppmtomap b/other/ppmtomap new file mode 100755 index 00000000..1d7ed940 --- /dev/null +++ b/other/ppmtomap @@ -0,0 +1,5 @@ +#! /bin/sh + +# This program exists for backward compatibility. + +pnmcolormap all $@ |