diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/Makefile | 17 | ||||
-rw-r--r-- | editor/pamcat.c | 1303 | ||||
-rw-r--r-- | editor/pamlevels.c | 1 | ||||
-rw-r--r-- | editor/pnmcat.c | 879 | ||||
-rwxr-xr-x | editor/pnmindex.csh | 189 | ||||
-rwxr-xr-x | editor/pnmindex.sh | 214 | ||||
-rwxr-xr-x | editor/pnmmargin | 4 | ||||
-rwxr-xr-x | editor/pnmquantall | 4 | ||||
-rw-r--r-- | editor/specialty/pnmindex.c | 106 |
9 files changed, 1380 insertions, 1337 deletions
diff --git a/editor/Makefile b/editor/Makefile index 8798cf6e..0027832c 100644 --- a/editor/Makefile +++ b/editor/Makefile @@ -16,7 +16,8 @@ SUBDIRS = pamflip specialty # This package is so big, it's useful even when some parts won't # build. -PORTBINARIES = pamaddnoise pamaltsat pambackground pambrighten pamcomp pamcut \ +PORTBINARIES = pamaddnoise pamaltsat pambackground pambrighten \ + pamcat pamcomp pamcut \ pamdice pamditherbw pamedge pamenlarge \ pamfunc pamhomography pamhue pamlevels \ pammasksharpen pammixmulti \ @@ -26,7 +27,7 @@ PORTBINARIES = pamaddnoise pamaltsat pambackground pambrighten pamcomp pamcut \ pbmclean pbmmask pbmpscale pbmreduce \ pgmdeshadow pgmenhance \ pgmmedian \ - pnmalias pnmcat pnmconvol pnmcrop \ + pnmalias pnmconvol pnmcrop \ pnmgamma \ pnmhisteq pnminvert pnmmontage \ pnmnlfilt pnmnorm pnmpad pnmpaste \ @@ -63,10 +64,15 @@ install.bin install.merge: install.bin.local .PHONY: install.bin.local install.bin.local: $(PKGDIR)/bin # Remember that $(SYMLINK) might just be a copy command. -# backward compatibility: program used to be pnminterp + +# In December 2001, pamstretch replaced pnminterp and pamstretch-getn +# replaced pnminterp-gen cd $(PKGDIR)/bin ; \ rm -f pnminterp$(EXE); \ $(SYMLINK) pamstretch$(EXE) pnminterp$(EXE) + cd $(PKGDIR)/bin ; \ + rm -f pnminterp-gen$(EXE); \ + $(SYMLINK) pamstretch-gen$(EXE) pnminterp-gen$(EXE) # In March 2002, pnmnorm replaced ppmnorm and pgmnorm cd $(PKGDIR)/bin ; \ rm -f ppmnorm$(EXE) ; \ @@ -100,6 +106,10 @@ install.bin.local: $(PKGDIR)/bin cd $(PKGDIR)/bin ; \ rm -f pnmcomp$(EXE) ; \ $(SYMLINK) pamcomp$(EXE) pnmcomp$(EXE) +# In August 2022, pamcat replaced pnmcat + cd $(PKGDIR)/bin ; \ + rm -f pnmcat$(EXE) ; \ + $(SYMLINK) pamcat$(EXE) pnmcat$(EXE) mergecomptrylist: cat /dev/null >$@ @@ -111,4 +121,5 @@ mergecomptrylist: echo "TRY(\"pnmcut\", main_pamcut);" >>$@ echo "TRY(\"pnmscale\", main_pamscale);" >>$@ echo "TRY(\"pnmcomp\", main_pamcomp);" >>$@ + echo "TRY(\"pnmcat\", main_pamcomp);" >>$@ diff --git a/editor/pamcat.c b/editor/pamcat.c new file mode 100644 index 00000000..c7f0482d --- /dev/null +++ b/editor/pamcat.c @@ -0,0 +1,1303 @@ +/*============================================================================= + pamcat +=============================================================================== + + Concatenate images. + + By Bryan Henderson and Akira Urushibata. Contributed to the public domain + by its authors. + +=============================================================================*/ + +#include <assert.h> + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "shhopt.h" +#include "bitarith.h" +#include "nstring.h" +#include "pam.h" +#include "pbm.h" + +#define LEFTBITS pm_byteLeftBits +#define RIGHTBITS pm_byteRightBits + +enum PadColorMethod {PAD_BLACK, PAD_WHITE, PAD_AUTO}; + /* The method of determining the color of padding when images are not the + same height or width. Always white (maxval samples) always black (zero + samples) or determined from what looks like background for the image in + question. + */ + + +enum Orientation {TOPBOTTOM, LEFTRIGHT}; + /* Direction of concatenation */ + +enum Justification {JUST_CENTER, JUST_MIN, JUST_MAX}; + /* Justification of images in concatenation */ + +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 fileCt; + enum PadColorMethod padColorMethod; + enum Orientation orientation; + enum Justification justification; + unsigned int verbose; +}; + + + +static void +parseCommandLine(int argc, const 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; + /* Instructions to OptParseOptions3() on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int leftright, topbottom; + unsigned int black, white; + unsigned int jtop, jbottom, jleft, jright, jcenter; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "leftright", OPT_FLAG, NULL, &leftright, 0); + OPTENT3(0, "lr", OPT_FLAG, NULL, &leftright, 0); + OPTENT3(0, "topbottom", OPT_FLAG, NULL, &topbottom, 0); + OPTENT3(0, "tb", OPT_FLAG, NULL, &topbottom, 0); + OPTENT3(0, "black", OPT_FLAG, NULL, &black, 0); + OPTENT3(0, "white", OPT_FLAG, NULL, &white, 0); + OPTENT3(0, "jtop", OPT_FLAG, NULL, &jtop, 0); + OPTENT3(0, "jbottom", OPT_FLAG, NULL, &jbottom, 0); + OPTENT3(0, "jleft", OPT_FLAG, NULL, &jleft, 0); + OPTENT3(0, "jright", OPT_FLAG, NULL, &jright, 0); + OPTENT3(0, "jcenter", OPT_FLAG, NULL, &jcenter, 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 */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + free(option_def); + + if (leftright + topbottom > 1) + pm_error("You may specify only one of -topbottom (-tb) and " + "-leftright (-lr)"); + else if (leftright) + cmdlineP->orientation = LEFTRIGHT; + else if (topbottom) + cmdlineP->orientation = TOPBOTTOM; + else + pm_error("You must specify either -leftright or -topbottom"); + + if (black + white > 1) + pm_error("You may specify only one of -black and -white"); + else if (black) + cmdlineP->padColorMethod = PAD_BLACK; + else if (white) + cmdlineP->padColorMethod = PAD_WHITE; + else + cmdlineP->padColorMethod = PAD_AUTO; + + if (jtop + jbottom + jleft + jright + jcenter > 1) + pm_error("You may specify only one of -jtop, -jbottom, " + "-jleft, and -jright"); + else { + switch (cmdlineP->orientation) { + case LEFTRIGHT: + if (jleft) + pm_error("-jleft is invalid with -leftright"); + if (jright) + pm_error("-jright is invalid with -leftright"); + if (jtop) + cmdlineP->justification = JUST_MIN; + else if (jbottom) + cmdlineP->justification = JUST_MAX; + else if (jcenter) + cmdlineP->justification = JUST_CENTER; + else + cmdlineP->justification = JUST_CENTER; + break; + case TOPBOTTOM: + if (jtop) + pm_error("-jtop is invalid with -topbottom"); + if (jbottom) + pm_error("-jbottom is invalid with -topbottom"); + if (jleft) + cmdlineP->justification = JUST_MIN; + else if (jright) + cmdlineP->justification = JUST_MAX; + else if (jcenter) + cmdlineP->justification = JUST_CENTER; + else + cmdlineP->justification = JUST_CENTER; + break; + } + } + + if (argc-1 < 1) { + MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, 1); + cmdlineP->inputFileName[0] = "-"; + cmdlineP->fileCt = 1; + } else { + unsigned int i; + unsigned int stdinCt; + /* Number of input files user specified as Standard Input */ + + MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, argc-1); + + for (i = 0, stdinCt = 0; i < argc-1; ++i) { + cmdlineP->inputFileName[i] = argv[1+i]; + if (streq(argv[1+i], "-")) + ++stdinCt; + } + cmdlineP->fileCt = argc-1; + if (stdinCt > 1) + pm_error("At most one input image can come from Standard Input. " + "You specified %u", stdinCt); + } +} + + + +static const char * +tupletypeX(bool const allVisual, + unsigned int const colorDepth, + sample const maxMaxval, + bool const haveOpacity) { + + const char * retval; + + if (allVisual) { + switch (colorDepth) { + case 1: + if (maxMaxval == 1) + retval = haveOpacity ? "BLACKANDWHITE_ALPHA" : "BLACKANDWHITE"; + else + retval = haveOpacity ? "GRAYSCALE_ALPHA" : "GRAYSCALE"; + break; + case 3: + retval = haveOpacity ? "RGB_ALPHA" : "RGB"; + break; + default: + assert(false); + } + } else + retval = ""; + + return retval; +} + + + +typedef struct { + /* This describes a transformation from one tuple type to another, + e.g. from BLACKANDWHITE to GRAY_ALPHA. + + For transformations bewteen the defined ones for visual images, + only the "up" transformations are covered. + */ + bool mustPromoteColor; + /* Plane 0, which is the black/white or grayscale plane and also + the red plane must be copied as the red, green, and blue planes + (0, 1, and 2). + */ + bool mustPromoteOpacity; + /* Plane 1, which is the opacity plane for black and white or + grayscale tuples, must be copied as the RGB opacity plane (3). + */ + bool mustCreateOpacity; + /* The opacity plane value must be set to opaque */ + + bool mustPadZero; + /* Where the target tuple type is deeper than the source tuple + type, all higher numbered planes must be cleared to zero. + + This is mutually exclusive with the rest of the musts. + */ + +} TtTransform; + + + +static TtTransform +ttXformForImg(const struct pam * const inpamP, + const struct pam * const outpamP) { +/*---------------------------------------------------------------------------- + The transform required to transform tuples of the kind described by *inpamP + to tuples of the kind described by *outpamP (e.g. from grayscale to RGB, + which involves replicating one plane into three). + + We assume *outpamP tuples are of a type that is at least as expressive as + *inpamP tuples. So e.g. outpamP->tuple_type cannot be "GRAYSCALE" if + inpamP->tuple_type is "RGB". +-----------------------------------------------------------------------------*/ + TtTransform retval; + + if (inpamP->visual && outpamP->visual) { + retval.mustPromoteColor = + (outpamP->color_depth > inpamP->color_depth); + retval.mustPromoteOpacity = + (outpamP->color_depth > inpamP->color_depth && + (outpamP->have_opacity && inpamP->have_opacity)); + retval.mustCreateOpacity = + (outpamP->have_opacity && !inpamP->have_opacity); + retval.mustPadZero = false; + } else { + retval.mustPromoteColor = false; + retval.mustPromoteOpacity = false; + retval.mustCreateOpacity = false; + retval.mustPadZero = true; + } + return retval; +} + + + +static void +reportPlans(unsigned int const fileCt, + const struct pam * const outpamP) { + + pm_message("Concatenating %u input images", fileCt); + + pm_message("Output width, height, depth: %u x %u x %u", + outpamP->width, outpamP->height, outpamP->depth); + + if (outpamP->format == RPBM_FORMAT) + pm_message("Using PBM fast path and producing raw PBM output"); + else if (outpamP->format == PBM_FORMAT) + pm_message("Output format: Plain PBM"); + else { + pm_message("Output maxval (max of all inputs): %lu", outpamP->maxval); + + switch (outpamP->format) { + case PGM_FORMAT: + pm_message("Output format: Plain PGM"); + break; + case RPGM_FORMAT: + pm_message("Output format: Raw PGM"); + break; + case PPM_FORMAT: + pm_message("Output format: Plain PPM"); + break; + case RPPM_FORMAT: + pm_message("Output format: Raw PPM"); + break; + case PAM_FORMAT: + pm_message("Output format: PAM"); + + if (strlen(outpamP->tuple_type) > 0) + pm_message("Output tuple type: '%s'", outpamP->tuple_type); + else + pm_message("Output tuple type is null string because " + "input images have various non-visual tuple types"); + break; + } + } +} + + + +static void +computeOutputParms(unsigned int const fileCt, + enum Orientation const orientation, + const struct pam * const inpam, /* array */ + bool const verbose, + struct pam * const outpamP) { + + double newCols, newRows; + unsigned int maxDepth; + sample maxMaxval; + int newFormat; + const char * firstTupletype; + bool allSameTt; + bool allVisual; + unsigned int maxColorDepth; + bool haveOpacity; + unsigned int fileSeq; + + for (fileSeq = 0, newCols = 0, newRows = 0, maxDepth = 0, maxMaxval = 0, + newFormat = 0, + allVisual = true, maxColorDepth = 0, haveOpacity = false, + firstTupletype = NULL, allSameTt = true; + fileSeq < fileCt; + ++fileSeq) { + + const struct pam * const inpamP = &inpam[fileSeq]; + + switch (orientation) { + case LEFTRIGHT: + newCols += inpamP->width; + newRows = MAX(newRows, inpamP->height); + break; + case TOPBOTTOM: + newRows += inpamP->height; + newCols = MAX(newCols, inpamP->width); + break; + } + + if (!firstTupletype) + firstTupletype = inpamP->tuple_type; + if (inpamP->tuple_type != firstTupletype) + allSameTt = false; + + if (inpamP->visual) { + maxColorDepth = MAX(maxColorDepth, inpamP->color_depth); + + if (inpamP->have_opacity) + haveOpacity = true; + } else + allVisual = false; + + maxDepth = MAX(maxDepth, inpamP->depth); + maxMaxval = MAX(maxMaxval, inpamP->maxval); + + if (PAM_FORMAT_TYPE(inpamP->format) > PAM_FORMAT_TYPE(newFormat)) + newFormat = inpamP->format; + } + assert(newCols > 0); + assert(newRows > 0); + assert(maxMaxval > 0); + assert(newFormat > 0); + + if (newCols > INT_MAX) + pm_error("Output width too large: %.0f.", newCols); + if (newRows > INT_MAX) + pm_error("Output height too large: %.0f.", newRows); + + outpamP->size = sizeof(*outpamP); + outpamP->len = PAM_STRUCT_SIZE(tuple_type); + + /* Note that while 'double' is not in general a precise numerical type, + in the case of a sum of integers which is less than INT_MAX, it + is exact, because double's precision is greater than int's. + */ + outpamP->height = (unsigned int)newRows; + outpamP->width = (unsigned int)newCols; + if (allVisual) + outpamP->depth = MAX(maxDepth, + maxColorDepth + (haveOpacity ? 1 : 0)); + else + outpamP->depth = maxDepth; + outpamP->allocation_depth = 0; /* This means same as depth */ + outpamP->maxval = maxMaxval; + outpamP->format = newFormat; + if (allSameTt) + STRSCPY(outpamP->tuple_type, firstTupletype); + else + STRSCPY(outpamP->tuple_type, + tupletypeX(allVisual, maxColorDepth, maxMaxval, haveOpacity)); + outpamP->comment_p = NULL; + outpamP->plainformat = false; + + if (verbose) + reportPlans(fileCt, outpamP); +} + + + +static void +copyBitrow(const unsigned char * const source, + unsigned char * const destBitrow, + unsigned int const cols, + unsigned int const offset) { +/*---------------------------------------------------------------------------- + Copy from source to destBitrow, without shifting. Preserve + surrounding image data. +-----------------------------------------------------------------------------*/ + unsigned char * const dest = & destBitrow[ offset/8 ]; + /* Copy destination, with leading full bytes ignored. */ + unsigned int const rs = offset % 8; + /* The "little offset", as measured from start of dest. Source + is already shifted by this value. + */ + unsigned int const trs = (cols + rs) % 8; + /* The number of partial bits in the final char. */ + unsigned int const colByteCnt = pbm_packed_bytes(cols + rs); + /* # bytes to process, including partial ones on both ends. */ + unsigned int const last = colByteCnt - 1; + + unsigned char const origHead = dest[0]; + unsigned char const origEnd = dest[last]; + + unsigned int i; + + assert(colByteCnt >= 1); + + for (i = 0; i < colByteCnt; ++i) + dest[i] = source[i]; + + if (rs > 0) + dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); + + if (trs > 0) + dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); +} + + + +static void +padFillBitrow(unsigned char * const destBitrow, + unsigned char const padColor, + unsigned int const cols, + unsigned int const offset) { +/*---------------------------------------------------------------------------- + Fill destBitrow, starting at offset, with padColor. padColor is a + byte -- 0x00 or 0xff -- not a single bit. +-----------------------------------------------------------------------------*/ + unsigned char * const dest = &destBitrow[offset/8]; + unsigned int const rs = offset % 8; + unsigned int const trs = (cols + rs) % 8; + unsigned int const colByteCnt = pbm_packed_bytes(cols + rs); + unsigned int const last = colByteCnt - 1; + + unsigned char const origHead = dest[0]; + unsigned char const origEnd = dest[last]; + + unsigned int i; + + assert(colByteCnt > 0); + + for (i = 0; i < colByteCnt; ++i) + dest[i] = padColor; + + if (rs > 0) + dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); + + if (trs > 0) + dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); +} + + + +/* concatenateLeftRightPbm() and concatenateLeftRightGen() + employ almost identical algorithms. + The difference is in the data types and functions. + + Same for concatenateTopBottomPbm() and concatenateTopBottomGen(). +*/ + + +typedef struct { + /* Information about one image */ + unsigned char * proberow; + /* Top row of image, when background color is + auto-determined. + */ + unsigned int offset; + /* start position of image, in bits, counting from left + edge + */ + unsigned char background; + /* Background color. 0x00 means white; 0xff means black */ + unsigned int padtop; + /* Top padding amount */ +} LrImgCtlPbm; + + + +static void +createLrImgCtlPbm(const struct pam * const inpam, /* array */ + unsigned int const fileCt, + unsigned int const outHeight, + enum Justification const justification, + enum PadColorMethod const padColorMethod, + LrImgCtlPbm ** const imgCtlP) { +/*---------------------------------------------------------------------------- + Read the first row of each image in inpam[] and return that and additional + information about images as *imgCtlP. +-----------------------------------------------------------------------------*/ + LrImgCtlPbm * imgCtl; /* array, size 'fileCt' */ + unsigned int fileSeq; + + MALLOCARRAY_NOFAIL(imgCtl, fileCt); + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + LrImgCtlPbm * const imgCtlP = &imgCtl[fileSeq]; + const struct pam * const inpamP = &inpam[fileSeq]; + + switch (justification) { + case JUST_MIN: + imgCtlP->padtop = 0; + break; + case JUST_MAX: + imgCtlP->padtop = outHeight - inpam[fileSeq].height; + break; + case JUST_CENTER: + imgCtlP->padtop = (outHeight - inpamP->height) / 2; + break; + } + + imgCtlP->offset = + (fileSeq == 0) ? + 0 : imgCtl[fileSeq-1].offset + inpam[fileSeq-1].width; + + if (inpamP->height == outHeight) /* no padding */ + imgCtlP->proberow = NULL; + else { /* determine pad color for image i */ + switch (padColorMethod) { + case PAD_AUTO: { + bit bgBit; + imgCtlP->proberow = + pbm_allocrow_packed((unsigned int)inpamP->width + 7); + pbm_readpbmrow_bitoffset( + inpamP->file, imgCtlP->proberow, + inpamP->width, inpamP->format, imgCtlP->offset % 8); + + bgBit = pbm_backgroundbitrow( + imgCtlP->proberow, inpamP->width, + imgCtlP->offset % 8); + + imgCtlP->background = bgBit == PBM_BLACK ? 0xff : 0x00; + } break; + case PAD_BLACK: + imgCtlP->proberow = NULL; + imgCtlP->background = 0xff; + break; + case PAD_WHITE: + imgCtlP->proberow = NULL; + imgCtlP->background = 0x00; + break; + } + } + } + *imgCtlP = imgCtl; +} + + + +static void +destroyPbmImgCtl(LrImgCtlPbm * const imgCtl, /* array */ + unsigned int const fileCt) { + + unsigned int i; + + for (i = 0; i < fileCt; ++i) { + if (imgCtl[i].proberow) + free(imgCtl[i].proberow); + } + free(imgCtl); +} + + + +static void +concatenateLeftRightPbm(struct pam * const outpamP, + const struct pam * const inpam, /* array */ + unsigned int const fileCt, + enum Justification const justification, + enum PadColorMethod const padColorMethod) { + + unsigned char * const outrow = pbm_allocrow_packed(outpamP->width); + /* We use just one outrow. All padding and image data (with the + exception of following imgCtl.proberow) goes directly into this + packed PBM row. + */ + + LrImgCtlPbm * imgCtl; + /* malloc'ed array, one element per image. Shadows inpam[] */ + unsigned int row; + + createLrImgCtlPbm(inpam, fileCt, outpamP->height, + justification, padColorMethod, + &imgCtl); + + outrow[pbm_packed_bytes(outpamP->width)-1] = 0x00; + + for (row = 0; row < outpamP->height; ++row) { + unsigned int fileSeq; + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + const LrImgCtlPbm * const imgCtlP = &imgCtl[fileSeq]; + const struct pam * const inpamP = &inpam[fileSeq]; + + if ((row == 0 && imgCtlP->padtop > 0) || + row == imgCtlP->padtop + inpamP->height) { + + /* This row begins a run of padding, either above or below + file 'i', so set 'outrow' to padding. + */ + padFillBitrow(outrow, imgCtlP->background, inpamP->width, + imgCtlP->offset); + } + + if (row == imgCtlP->padtop && imgCtlP->proberow != NULL) { + /* Top row has been read to proberow[] to determine + background. Copy it to outrow[]. + */ + copyBitrow(imgCtlP->proberow, outrow, + inpamP->width, imgCtlP->offset); + } else if (row >= imgCtlP->padtop && + row < imgCtlP->padtop + inpamP->height) { + pbm_readpbmrow_bitoffset( + inpamP->file, outrow, inpamP->width, inpamP->format, + imgCtlP->offset); + } else { + /* It's a row of padding, so outrow[] is already set + appropriately. + */ + } + } + pbm_writepbmrow_packed(outpamP->file, outrow, outpamP->width, 0); + } + + destroyPbmImgCtl(imgCtl, fileCt); + + pbm_freerow_packed(outrow); +} + + + +static void +concatenateTopBottomPbm(const struct pam * const outpamP, + const struct pam * const inpam, /* array */ + unsigned int const fileCt, + enum Justification const justification, + enum PadColorMethod const padColorMethod) { + + unsigned char * const outrow = pbm_allocrow_packed(outpamP->width); + /* Like the left-right PBM case, all padding and image data + goes directly into outrow. There is no proberow. + */ + unsigned char background, backgroundPrev; + /* 0x00 means white; 0xff means black */ + unsigned int padleft; + bool backChange; + /* Background color is different from that of the previous + input image. + */ + + unsigned int fileSeq; + unsigned int row, startRow; + + outrow[pbm_packed_bytes(outpamP->width)-1] = 0x00; + + switch (padColorMethod){ + case PAD_AUTO: /* do nothing */ break; + case PAD_BLACK: background = 0xff; break; + case PAD_WHITE: background = 0x00; break; + } + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + const struct pam * const inpamP = &inpam[fileSeq]; + + if (inpamP->width == outpamP->width) { + /* No padding */ + startRow = 0; + backChange = FALSE; + padleft = 0; + outrow[pbm_packed_bytes(outpamP->width)-1] = 0x00; + } else { + /* Determine amount of padding and color */ + switch (justification) { + case JUST_MIN: + padleft = 0; + break; + case JUST_MAX: + padleft = outpamP->width - inpamP->width; + break; + case JUST_CENTER: + padleft = (outpamP->width - inpamP->width) / 2; + break; + } + + switch (padColorMethod) { + case PAD_AUTO: { + bit bgBit; + + startRow = 1; + + pbm_readpbmrow_bitoffset( + inpamP->file, outrow, inpamP->width, inpamP->format, + padleft); + + bgBit = pbm_backgroundbitrow(outrow, inpamP->width, padleft); + background = bgBit == PBM_BLACK ? 0xff : 0x00; + + backChange = (fileSeq == 0 || background != backgroundPrev); + } break; + case PAD_WHITE: + case PAD_BLACK: + startRow = 0; + backChange = (fileSeq == 0); + break; + } + + if (backChange || + (fileSeq > 0 && inpam[fileSeq-1].width > inpamP->width)) { + unsigned int const padright = + outpamP->width - padleft - inpamP->width; + + if (padleft > 0) + padFillBitrow(outrow, background, padleft, 0); + + if (padright > 0) + padFillBitrow(outrow, background, padright, + padleft + inpamP->width); + + } + } + + if (startRow == 1) + /* Top row already read for auto background color + determination. Write it out. + */ + pbm_writepbmrow_packed(outpamP->file, outrow, outpamP->width, 0); + + for (row = startRow; row < inpamP->height; ++row) { + pbm_readpbmrow_bitoffset(inpamP->file, outrow, inpamP->width, + inpamP->format, padleft); + pbm_writepbmrow_packed(outpamP->file, outrow, outpamP->width, 0); + } + + backgroundPrev = background; + } + pbm_freerow_packed(outrow); +} + + + +static void +padPlanesRow(const struct pam * const inpamP, + tuple * const outrow, + const struct pam * const outpamP) { +/*---------------------------------------------------------------------------- + Rearrange the planes of *outrow as needed to transform them into tuples + as described by *outpamP from tuples as described by *inpamP. +-----------------------------------------------------------------------------*/ + TtTransform const ttTransform = ttXformForImg(inpamP, outpamP); + + assert(inpamP->allocation_depth >= outpamP->depth); + + if (ttTransform.mustPromoteOpacity) { + unsigned int col; + + assert(outpamP->depth >= PAM_TRN_PLANE); + + for (col = 0; col < inpamP->width; ++col) { + outrow[col][outpamP->opacity_plane] = + outrow[col][inpamP->opacity_plane]; + } + } + if (ttTransform.mustPromoteColor) { + unsigned int col; + + assert(outpamP->depth >= PAM_GRN_PLANE); + assert(outpamP->depth >= PAM_BLU_PLANE); + + for (col = 0; col < inpamP->width; ++col) { + assert(PAM_RED_PLANE == 0); + outrow[col][PAM_GRN_PLANE] = outrow[col][0]; + outrow[col][PAM_BLU_PLANE] = outrow[col][0]; + } + } + + if (ttTransform.mustCreateOpacity) { + unsigned int col; + + for (col = 0; col < inpamP->width; ++col) + outrow[col][outpamP->opacity_plane] = outpamP->maxval; + } + + if (ttTransform.mustPadZero) { + unsigned int plane; + + for (plane = inpamP->depth; plane < outpamP->depth; ++plane) { + unsigned int col; + + for (col = 0; col < inpamP->width; ++col) + outrow[col][plane] = 0; + } + } +} + + + +typedef struct { +/*---------------------------------------------------------------------------- + Parameters and state for placing a row of a particular input image in + the output in a left-right concatenation. +-----------------------------------------------------------------------------*/ + tuple * cachedRow; + /* Contents of the current row of the image, with depth and maxval + adjusted for output, in malloc'ed space belonging to this object. + Input file is positioned past this row. Null if data not present + and input file is positioned to the current row. + */ + tuple * out; + /* Point in output row buffer where the row from this image goes */ + tuple background; + unsigned int padtop; + /* Number of rows of padding that go above this image in the output */ +} LrImgCtl; + + + +static void +createLrImgCtlArray(const struct pam * const inpam, /* array */ + unsigned int const fileCt, + tuple * const newTuplerow, + const struct pam * const outpamP, + enum Justification const justification, + enum PadColorMethod const padColorMethod, + LrImgCtl ** const imgCtlP) { + + LrImgCtl * imgCtl; /* array */ + unsigned int fileSeq; + + MALLOCARRAY_NOFAIL(imgCtl, fileCt); + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + LrImgCtl * const thisEntryP = &imgCtl[fileSeq]; + const struct pam * const inpamP = &inpam[fileSeq]; + + switch (justification) { /* Determine top padding */ + case JUST_MIN: + thisEntryP->padtop = 0; + break; + case JUST_MAX: + thisEntryP->padtop = outpamP->height - inpamP->height; + break; + case JUST_CENTER: + thisEntryP->padtop = (outpamP->height - inpamP->height) / 2; + break; + } + + thisEntryP->out = + (fileSeq == 0 ? + &newTuplerow[0] : imgCtl[fileSeq-1].out + inpam[fileSeq-1].width); + + if (inpamP->height == outpamP->height) { /* no vertical padding */ + thisEntryP->cachedRow = NULL; + pnm_createBlackTuple(outpamP, &thisEntryP->background); + /* Meaningless because no padding */ + } else { + /* Determine pad color */ + switch (padColorMethod){ + case PAD_AUTO: + thisEntryP->cachedRow = pnm_allocpamrow(inpamP); + pnm_readpamrow(inpamP, thisEntryP->cachedRow); + pnm_scaletuplerow(inpamP, thisEntryP->cachedRow, + thisEntryP->cachedRow, outpamP->maxval); + padPlanesRow(inpamP, thisEntryP->cachedRow, outpamP); + { + struct pam cachedRowPam; + cachedRowPam = *outpamP; + cachedRowPam.width = inpamP->width; + thisEntryP->background = pnm_backgroundtuplerow( + &cachedRowPam, thisEntryP->cachedRow); + } + break; + case PAD_BLACK: + thisEntryP->cachedRow = NULL; + pnm_createBlackTuple(outpamP, &thisEntryP->background); + break; + case PAD_WHITE: + thisEntryP->cachedRow = NULL; + pnm_createWhiteTuple(outpamP, &thisEntryP->background); + break; + } + } + if (outpamP->visual) { + /* Any opacity sample in background color tuple is meaningless at + this point; make it opaque. + */ + if (outpamP->have_opacity) { + thisEntryP->background[outpamP->opacity_plane] = + outpamP->maxval; + } + } + + } + *imgCtlP = imgCtl; +} + + + +static void +destroyLrImgCtlArray(LrImgCtl * const imgCtl, /* array */ + unsigned int const fileCt) { + + unsigned int fileSeq; + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + LrImgCtl * const thisEntryP = &imgCtl[fileSeq]; + + pnm_freepamtuple(thisEntryP->background); + pnm_freepamrow(thisEntryP->cachedRow); + } + + free(imgCtl); +} + + + +static void +concatenateLeftRightGen(const struct pam * const outpamP, + const struct pam * const inpam, /* array */ + unsigned int const fileCt, + enum Justification const justification, + enum PadColorMethod const padColorMethod) { + + tuple * const outrow = pnm_allocpamrow(outpamP); + + LrImgCtl * imgCtl; + unsigned int row; + + createLrImgCtlArray(inpam, fileCt, outrow, outpamP, + justification, padColorMethod, + &imgCtl); + + for (row = 0; row < outpamP->height; ++row) { + unsigned int fileSeq; + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + LrImgCtl * const thisEntryP = &imgCtl[fileSeq]; + const struct pam * const inpamP = &inpam[fileSeq]; + + if ((row == 0 && thisEntryP->padtop > 0) || + row == thisEntryP->padtop + inpamP->height) { + /* This row begins a run of padding, either above or below + image 'fileSeq', so set its part of outrow[] to padding. + */ + unsigned int col; + for (col = 0; col < inpamP->width; ++col) { + pnm_assigntuple(outpamP, thisEntryP->out[col], + thisEntryP->background); + } + } + if (row == thisEntryP->padtop && thisEntryP->cachedRow) { + /* We're at the top row of image 'fileSeq', and that row + has already been read to cachedRow[] to determine + background. Copy it to image fileseq's part of outrow[]. + */ + unsigned int col; + for (col = 0; col < inpamP->width; ++col) { + pnm_assigntuple(outpamP, thisEntryP->out[col], + thisEntryP->cachedRow[col]); + } + free(thisEntryP->cachedRow); + thisEntryP->cachedRow = NULL; + } else if (row >= thisEntryP->padtop && + row < thisEntryP->padtop + inpamP->height) { + pnm_readpamrow(inpamP, thisEntryP->out); + pnm_scaletuplerow(inpamP, thisEntryP->out, + thisEntryP->out, outpamP->maxval); + padPlanesRow(inpamP, thisEntryP->out, outpamP); + } else { + /* It's a row of padding, so image filesSeq's part of outrow[] + is already set appropriately. + */ + } + } + /* Note that imgCtl[N].out[] is an alias to part of outrow[], so + outrow[] has been set. + */ + pnm_writepamrow(outpamP, outrow); + } + destroyLrImgCtlArray(imgCtl, fileCt); + + pnm_freepamrow(outrow); +} + + + +static tuple +initialBackgroundColor(const struct pam * const outpamP, + enum PadColorMethod const padColorMethod) { + + tuple retval; + + switch (padColorMethod) { + case PAD_AUTO: + /* Background is different for each input image */ + retval = pnm_allocpamtuple(outpamP); + /* Dummy value; just need something to free */ + break; + case PAD_BLACK: + pnm_createBlackTuple(outpamP, &retval); + break; + case PAD_WHITE: + pnm_createWhiteTuple(outpamP, &retval); + break; + } + + if (outpamP->visual) { + /* Any opacity sample in background color tuple is meaningless at this + point; make it opaque. + */ + if (outpamP->have_opacity) + retval[outpamP->opacity_plane] = outpamP->maxval; + } + + return retval; +} + + + +static unsigned int +leftPadAmount(const struct pam * const outpamP, + const struct pam * const inpamP, + enum Justification const justification) { + + switch (justification) { + case JUST_MIN: return 0; + case JUST_MAX: return outpamP->width - inpamP->width; + case JUST_CENTER: return (outpamP->width - inpamP->width) / 2; + } + assert(false); +} + + + +static void +setHorizPadding(tuple * const newTuplerow, + const struct pam * const outpamP, + bool const backChanged, + const struct pam * const inpam, /* array */ + unsigned int const imageSeq, + unsigned int const padLeft, + tuple const background) { +/*---------------------------------------------------------------------------- + Set the left and right padding for an output row in a top-bottom + concatenation. + + 'newTuplerow' is where we set the padding (and also assumed to hold the + contents of the previous output row). *outpamP describes it. + + 'backChanged' means the background color is different for the current row + from that of the previous row. + + inpam[] is the array of descriptors for all the input images. 'imageSeq' + is the index into this array for the current image. + + 'background' is the background color to set. +-----------------------------------------------------------------------------*/ + if (backChanged || + (imageSeq > 0 && inpam[imageSeq-1].width > inpam[imageSeq].width)) { + unsigned int col; + + for (col = 0; col < padLeft; ++col) + pnm_assigntuple(outpamP, newTuplerow[col], background); + for (col = padLeft + inpam[imageSeq].width; + col < outpamP->width; + ++col) { + pnm_assigntuple(outpamP, newTuplerow[col], background); + } + } else { + /* No need to pad because newTuplerow[] already contains the + correct padding from the previous row. + */ + } +} + + + +static void +readFirstTBRowAndDetermineBackground(const struct pam * const inpamP, + const struct pam * const outpamP, + tuple * const out, + tuple * const backgroundP) { +/*---------------------------------------------------------------------------- + Read the first row of an input image into 'out', adjusting it to conform + to the output depth and maxval described by *outpamP. + + The image is positioned to the first row at entry. + + From this row, determine the background color for the input image and + return it as *backgroundP (a newly malloced tuple). +-----------------------------------------------------------------------------*/ + pnm_readpamrow(inpamP, out); + + pnm_scaletuplerow(inpamP, out, out, outpamP->maxval); + + padPlanesRow(inpamP, out, outpamP); + + { + struct pam partialOutpam; + /* Descriptor for the input image with depth and maxval adjusted to + that of the output image. + */ + tuple background; + + partialOutpam = *outpamP; + partialOutpam.width = inpamP->width; + + background = pnm_backgroundtuplerow(&partialOutpam, out); + + if (outpamP->visual) { + /* Make the background opaque. */ + if (outpamP->have_opacity) + background[outpamP->opacity_plane] = outpamP->maxval; + } + + *backgroundP = background; + } +} + + + +static void +concatenateTopBottomGen(const struct pam * const outpamP, + const struct pam * const inpam, /* array */ + unsigned int const fileCt, + enum Justification const justification, + enum PadColorMethod const padColorMethod) { + + tuple * const newTuplerow = pnm_allocpamrow(outpamP); + tuple * out; + /* The location in newTuplerow[] that the row from the current + input image goes. + */ + unsigned int fileSeq; + tuple background; + tuple backgroundPrev; + + background = initialBackgroundColor(outpamP, padColorMethod); + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) { + const struct pam * const inpamP = &inpam[fileSeq]; + + unsigned int row; + unsigned int startRow; + bool backChanged; + /* The background color is different from that of the previous + input image. + */ + + if (inpamP->width == outpamP->width) { + /* no padding */ + startRow = 0; + backChanged = false; + out = &newTuplerow[0]; + } else { + unsigned int const padLeft = + leftPadAmount(outpamP, inpamP, justification); + + if (padColorMethod == PAD_AUTO) { + out = &newTuplerow[padLeft]; + backgroundPrev = background; + readFirstTBRowAndDetermineBackground( + inpamP, outpamP, out, &background); + + backChanged = + fileSeq == 0 || + pnm_tupleequal(outpamP, background, backgroundPrev); + pnm_freepamtuple(backgroundPrev); + + startRow = 1; + } else { + /* Background color is constant: black or white */ + startRow = 0; + out = &newTuplerow[padLeft]; + backChanged = (fileSeq == 0); + } + + setHorizPadding(newTuplerow, outpamP, backChanged, inpam, fileSeq, + padLeft, background); + } + + if (startRow == 1) + /* Top row was already read for auto background color + determination. Write it out. + */ + pnm_writepamrow(outpamP, newTuplerow); + + for (row = startRow; row < inpamP->height; ++row) { + pnm_readpamrow(inpamP, out); + + pnm_scaletuplerow(inpamP, out, out, outpamP->maxval); + + padPlanesRow(inpamP, out, outpamP); + + pnm_writepamrow(outpamP, newTuplerow); + } + } + pnm_freepamtuple(background); + pnm_freepamrow(newTuplerow); +} + + + +int +main(int argc, + const char ** argv) { + + struct CmdlineInfo cmdline; + struct pam * inpam; /* malloc'ed array */ + struct pam outpam; + unsigned int i; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + MALLOCARRAY_NOFAIL(inpam, cmdline.fileCt); + + for (i = 0; i < cmdline.fileCt; ++i) { + FILE * const ifP = pm_openr(cmdline.inputFileName[i]); + inpam[i].comment_p = NULL; /* Don't want to see the comments */ + pnm_readpaminit(ifP, &inpam[i], PAM_STRUCT_SIZE(opacity_plane)); + } + + computeOutputParms(cmdline.fileCt, cmdline.orientation, inpam, + cmdline.verbose, &outpam); + + outpam.file = stdout; + + for (i = 0; i < cmdline.fileCt; ++i) + pnm_setminallocationdepth(&inpam[i], outpam.depth); + + pnm_writepaminit(&outpam); + + if (outpam.format == RPBM_FORMAT) { + switch (cmdline.orientation) { + case LEFTRIGHT: + concatenateLeftRightPbm(&outpam, inpam, cmdline.fileCt, + cmdline.justification, + cmdline.padColorMethod); + break; + case TOPBOTTOM: + concatenateTopBottomPbm(&outpam, inpam, cmdline.fileCt, + cmdline.justification, + cmdline.padColorMethod); + break; + } + } else { + switch (cmdline.orientation) { + case LEFTRIGHT: + concatenateLeftRightGen(&outpam, inpam, cmdline.fileCt, + cmdline.justification, + cmdline.padColorMethod); + break; + case TOPBOTTOM: + concatenateTopBottomGen(&outpam, inpam, cmdline.fileCt, + cmdline.justification, + cmdline.padColorMethod); + break; + } + } + for (i = 0; i < cmdline.fileCt; ++i) + pm_close(inpam[i].file); + free(cmdline.inputFileName); + free(inpam); + pm_close(stdout); + + return 0; +} + + + diff --git a/editor/pamlevels.c b/editor/pamlevels.c index a282751a..de2afc45 100644 --- a/editor/pamlevels.c +++ b/editor/pamlevels.c @@ -5,7 +5,6 @@ #include <stdlib.h> #include "netpbm/pam.h" -#include "netpbm/pm_system.h" #include "netpbm/pm_gamma.h" #include "netpbm/nstring.h" #include "netpbm/ppm.h" diff --git a/editor/pnmcat.c b/editor/pnmcat.c deleted file mode 100644 index fea80181..00000000 --- a/editor/pnmcat.c +++ /dev/null @@ -1,879 +0,0 @@ -/* pnmcat.c - concatenate PNM images -** -** Copyright (C) 1989, 1991 by Jef Poskanzer. -** -** 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 <assert.h> - -#include "pm_c_util.h" -#include "mallocvar.h" -#include "shhopt.h" -#include "bitarith.h" -#include "nstring.h" -#include "pnm.h" - -#define LEFTBITS pm_byteLeftBits -#define RIGHTBITS pm_byteRightBits - -enum backcolor {BACK_WHITE, BACK_BLACK, BACK_AUTO}; - -enum orientation {TOPBOTTOM, LEFTRIGHT}; - -enum justification {JUST_CENTER, JUST_MIN, JUST_MAX}; - -typedef struct { - /* This obviously should be a struct pam. We should convert this - to 'pamcat'. - */ - FILE * ifP; - int cols; - int rows; - int format; - xelval maxval; -} ImgInfo; - - - -struct CmdlineInfo { - /* All the information the user supplied in the command line, - in a form easy for the program to use. - */ - const char ** inputFilespec; - unsigned int nfiles; - enum backcolor backcolor; - enum orientation orientation; - enum justification justification; -}; - - - -static void -parseCommandLine(int argc, const 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; - /* Instructions to OptParseOptions3() on how to parse our options. - */ - optStruct3 opt; - - unsigned int option_def_index; - - unsigned int leftright, topbottom, black, white, jtop, jbottom, - jleft, jright, jcenter; - - MALLOCARRAY_NOFAIL(option_def, 100); - - option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3(0, "leftright", OPT_FLAG, NULL, &leftright, 0); - OPTENT3(0, "lr", OPT_FLAG, NULL, &leftright, 0); - OPTENT3(0, "topbottom", OPT_FLAG, NULL, &topbottom, 0); - OPTENT3(0, "tb", OPT_FLAG, NULL, &topbottom, 0); - OPTENT3(0, "black", OPT_FLAG, NULL, &black, 0); - OPTENT3(0, "white", OPT_FLAG, NULL, &white, 0); - OPTENT3(0, "jtop", OPT_FLAG, NULL, &jtop, 0); - OPTENT3(0, "jbottom", OPT_FLAG, NULL, &jbottom, 0); - OPTENT3(0, "jleft", OPT_FLAG, NULL, &jleft, 0); - OPTENT3(0, "jright", OPT_FLAG, NULL, &jright, 0); - OPTENT3(0, "jcenter", OPT_FLAG, NULL, &jcenter, 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 */ - - pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); - /* Uses and sets argc, argv, and some of *cmdlineP and others. */ - - free(option_def); - - if (leftright + topbottom > 1) - pm_error("You may specify only one of -topbottom (-tb) and " - "-leftright (-lr)"); - else if (leftright) - cmdlineP->orientation = LEFTRIGHT; - else if (topbottom) - cmdlineP->orientation = TOPBOTTOM; - else - pm_error("You must specify either -leftright or -topbottom"); - - if (black + white > 1) - pm_error("You may specify only one of -black and -white"); - else if (black) - cmdlineP->backcolor = BACK_BLACK; - else if (white) - cmdlineP->backcolor = BACK_WHITE; - else - cmdlineP->backcolor = BACK_AUTO; - - if (jtop + jbottom + jleft + jright + jcenter > 1) - pm_error("You may specify only one of -jtop, -jbottom, " - "-jleft, and -jright"); - else { - switch (cmdlineP->orientation) { - case LEFTRIGHT: - if (jleft) - pm_error("-jleft is invalid with -leftright"); - if (jright) - pm_error("-jright is invalid with -leftright"); - if (jtop) - cmdlineP->justification = JUST_MIN; - else if (jbottom) - cmdlineP->justification = JUST_MAX; - else if (jcenter) - cmdlineP->justification = JUST_CENTER; - else - cmdlineP->justification = JUST_CENTER; - break; - case TOPBOTTOM: - if (jtop) - pm_error("-jtop is invalid with -topbottom"); - if (jbottom) - pm_error("-jbottom is invalid with -topbottom"); - if (jleft) - cmdlineP->justification = JUST_MIN; - else if (jright) - cmdlineP->justification = JUST_MAX; - else if (jcenter) - cmdlineP->justification = JUST_CENTER; - else - cmdlineP->justification = JUST_CENTER; - break; - } - } - - if (argc-1 < 1) { - MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, 1); - cmdlineP->inputFilespec[0] = "-"; - cmdlineP->nfiles = 1; - } else { - unsigned int i; - unsigned int stdinCt; - /* Number of input files user specified as Standard Input */ - - MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, argc-1); - - for (i = 0, stdinCt = 0; i < argc-1; ++i) { - cmdlineP->inputFilespec[i] = argv[1+i]; - if (streq(argv[1+i], "-")) - ++stdinCt; - } - cmdlineP->nfiles = argc-1; - if (stdinCt > 1) - pm_error("At most one input image can come from Standard Input. " - "You specified %u", stdinCt); - } -} - - - -static void -computeOutputParms(unsigned int const nfiles, - enum orientation const orientation, - ImgInfo const img[], - unsigned int * const newcolsP, - unsigned int * const newrowsP, - xelval * const newmaxvalP, - int * const newformatP) { - - double newcols, newrows; - int newformat; - xelval newmaxval; - - unsigned int i; - - newcols = 0; - newrows = 0; - - for (i = 0; i < nfiles; ++i) { - const ImgInfo * const imgP = &img[i]; - - if (i == 0) { - newmaxval = imgP->maxval; - newformat = imgP->format; - } else { - if (PNM_FORMAT_TYPE(imgP->format) > PNM_FORMAT_TYPE(newformat)) - newformat = imgP->format; - if (imgP->maxval > newmaxval) - newmaxval = imgP->maxval; - } - switch (orientation) { - case LEFTRIGHT: - newcols += imgP->cols; - if (imgP->rows > newrows) - newrows = imgP->rows; - break; - case TOPBOTTOM: - newrows += imgP->rows; - if (imgP->cols > newcols) - newcols = imgP->cols; - break; - } - } - - /* Note that while 'double' is not in general a precise numerical type, - in the case of a sum of integers which is less than INT_MAX, it - is exact, because double's precision is greater than int's. - */ - if (newcols > INT_MAX) - pm_error("Output width too large: %.0f.", newcols); - if (newrows > INT_MAX) - pm_error("Output height too large: %.0f.", newrows); - - *newrowsP = (unsigned int)newrows; - *newcolsP = (unsigned int)newcols; - *newmaxvalP = newmaxval; - *newformatP = newformat; -} - - - -static void -copyBitrow(const unsigned char * const source, - unsigned char * const destBitrow, - unsigned int const cols, - unsigned int const offset) { -/*---------------------------------------------------------------------------- - Copy from source to destBitrow, without shifting. Preserve - surrounding image data. ------------------------------------------------------------------------------*/ - unsigned char * const dest = & destBitrow[ offset/8 ]; - /* Copy destination, with leading full bytes ignored. */ - unsigned int const rs = offset % 8; - /* The "little offset", as measured from start of dest. Source - is already shifted by this value. - */ - unsigned int const trs = (cols + rs) % 8; - /* The number of partial bits in the final char. */ - unsigned int const colByteCnt = pbm_packed_bytes(cols + rs); - /* # bytes to process, including partial ones on both ends. */ - unsigned int const last = colByteCnt - 1; - - unsigned char const origHead = dest[0]; - unsigned char const origEnd = dest[last]; - - unsigned int i; - - assert(colByteCnt >= 1); - - for (i = 0; i < colByteCnt; ++i) - dest[i] = source[i]; - - if (rs > 0) - dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); - - if (trs > 0) - dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); -} - - - -static void -padFillBitrow(unsigned char * const destBitrow, - unsigned char const padColor, - unsigned int const cols, - unsigned int const offset) { -/*---------------------------------------------------------------------------- - Fill destBitrow, starting at offset, with padColor. padColor is a - byte -- 0x00 or 0xff -- not a single bit. ------------------------------------------------------------------------------*/ - unsigned char * const dest = &destBitrow[offset/8]; - unsigned int const rs = offset % 8; - unsigned int const trs = (cols + rs) % 8; - unsigned int const colByteCnt = pbm_packed_bytes(cols + rs); - unsigned int const last = colByteCnt - 1; - - unsigned char const origHead = dest[0]; - unsigned char const origEnd = dest[last]; - - unsigned int i; - - assert(colByteCnt > 0); - - for (i = 0; i < colByteCnt; ++i) - dest[i] = padColor; - - if (rs > 0) - dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); - - if (trs > 0) - dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); -} - - - -/* concatenateLeftRightPBM() and concatenateLeftRightGen() - employ almost identical algorithms. - The difference is in the data types and functions. - - Same for concatenateTopBottomPBM() and concatenateTopBottomGen(). -*/ - - -typedef struct { - /* Information about one image */ - unsigned char * proberow; - /* Top row of image, when background color is - auto-determined. - */ - unsigned int offset; - /* start position of image, in bits, counting from left - edge - */ - unsigned char background; - /* Background color. 0x00 means white; 0xff means black */ - unsigned int padtop; - /* Top padding amount */ -} ImgInfoPbm2; - - - -static void -getPbmImageInfo(ImgInfo const img[], - unsigned int const nfiles, - unsigned int const newrows, - enum justification const justification, - enum backcolor const backcolor, - ImgInfoPbm2 ** const img2P) { -/*---------------------------------------------------------------------------- - Read the first row of each image in img[] and return that and additional - information about images as *img2P. ------------------------------------------------------------------------------*/ - ImgInfoPbm2 * img2; - unsigned int i; - - MALLOCARRAY_NOFAIL(img2, nfiles); - - for (i = 0; i < nfiles; ++i) { - switch (justification) { - case JUST_MIN: img2[i].padtop = 0; break; - case JUST_MAX: img2[i].padtop = newrows - img[i].rows; break; - case JUST_CENTER: img2[i].padtop = (newrows - img[i].rows) / 2; break; - } - - img2[i].offset = (i == 0) ? 0 : img2[i-1].offset + img[i-1].cols; - - if (img[i].rows == newrows) /* no padding */ - img2[i].proberow = NULL; - else { /* determine pad color for image i */ - switch (backcolor) { - case BACK_AUTO: { - bit bgBit; - img2[i].proberow = - pbm_allocrow_packed((unsigned int)img[i].cols + 7); - pbm_readpbmrow_bitoffset( - img[i].ifP, img2[i].proberow, - img[i].cols, img[i].format, img2[i].offset % 8); - - bgBit = pbm_backgroundbitrow( - img2[i].proberow, img[i].cols, img2[i].offset % 8); - - img2[i].background = bgBit == PBM_BLACK ? 0xff : 0x00; - } break; - case BACK_BLACK: - img2[i].proberow = NULL; - img2[i].background = 0xff; - break; - case BACK_WHITE: - img2[i].proberow = NULL; - img2[i].background = 0x00; - break; - } - } - } - *img2P = img2; -} - - - -static void -destroyPbmImg2(ImgInfoPbm2 * const img2, - unsigned int const nfiles) { - - unsigned int i; - - for (i = 0; i < nfiles; ++i) { - if (img2[i].proberow) - free(img2[i].proberow); - } - free(img2); -} - - - -static void -concatenateLeftRightPbm(FILE * const ofP, - unsigned int const nfiles, - unsigned int const newcols, - unsigned int const newrows, - enum justification const justification, - ImgInfo const img[], - enum backcolor const backcolor) { - - unsigned char * const outrow = pbm_allocrow_packed(newcols); - /* We use just one outrow. All padding and image data (with the - exception of following img2.proberow) goes directly into this - packed PBM row. - */ - - ImgInfoPbm2 * img2; - /* malloc'ed array, one element per image. Shadows img[] */ - unsigned int row; - - getPbmImageInfo(img, nfiles, newrows, justification, backcolor, &img2); - - outrow[pbm_packed_bytes(newcols)-1] = 0x00; - - for (row = 0; row < newrows; ++row) { - unsigned int i; - - for (i = 0; i < nfiles; ++i) { - - if ((row == 0 && img2[i].padtop > 0) || - row == img2[i].padtop + img[i].rows) { - - /* This row begins a run of padding, either above or below - file 'i', so set 'outrow' to padding. - */ - padFillBitrow(outrow, img2[i].background, img[i].cols, - img2[i].offset); - } - - if (row == img2[i].padtop && img2[i].proberow != NULL) { - /* Top row has been read to proberow[] to determine - background. Copy it to outrow[]. - */ - copyBitrow(img2[i].proberow, outrow, - img[i].cols, img2[i].offset); - } else if (row >= img2[i].padtop && - row < img2[i].padtop + img[i].rows) { - pbm_readpbmrow_bitoffset( - img[i].ifP, outrow, img[i].cols, img[i].format, - img2[i].offset); - } else { - /* It's a row of padding, so outrow[] is already set - appropriately. - */ - } - } - pbm_writepbmrow_packed(ofP, outrow, newcols, 0); - } - - destroyPbmImg2(img2, nfiles); - - pbm_freerow_packed(outrow); -} - - - -static void -concatenateTopBottomPbm(FILE * const ofP, - unsigned int const nfiles, - int const newcols, - int const newrows, - enum justification const justification, - ImgInfo const img[], - enum backcolor const backcolor) { - - unsigned char * const outrow = pbm_allocrow_packed(newcols); - /* Like the left-right PBM case, all padding and image data - goes directly into outrow. There is no proberow. - */ - unsigned char background, backgroundPrev; - /* 0x00 means white; 0xff means black */ - unsigned int padleft; - bool backChange; - /* Background color is different from that of the previous - input image. - */ - - unsigned int i; - unsigned int row, startRow; - - outrow[pbm_packed_bytes(newcols)-1] = 0x00; - - switch (backcolor){ - case BACK_AUTO: /* do nothing */ break; - case BACK_BLACK: background = 0xff; break; - case BACK_WHITE: background = 0x00; break; - } - - for (i = 0; i < nfiles; ++i) { - if (img[i].cols == newcols) { - /* No padding */ - startRow = 0; - backChange = FALSE; - padleft = 0; - outrow[pbm_packed_bytes(newcols)-1] = 0x00; - } else { - /* Determine amount of padding and color */ - switch (justification) { - case JUST_MIN: padleft = 0; break; - case JUST_MAX: padleft = newcols - img[i].cols; break; - case JUST_CENTER: padleft = (newcols - img[i].cols) / 2; break; - } - - switch (backcolor) { - case BACK_AUTO: { - bit bgBit; - - startRow = 1; - - pbm_readpbmrow_bitoffset(img[i].ifP, - outrow, img[i].cols, img[i].format, - padleft); - - bgBit = pbm_backgroundbitrow(outrow, img[i].cols, padleft); - background = bgBit == PBM_BLACK ? 0xff : 0x00; - - backChange = (i == 0 || background != backgroundPrev); - } break; - case BACK_WHITE: - case BACK_BLACK: - startRow = 0; - backChange = (i==0); - break; - } - - if (backChange || (i > 0 && img[i-1].cols > img[i].cols)) { - unsigned int const padright = newcols - padleft - img[i].cols; - - if (padleft > 0) - padFillBitrow(outrow, background, padleft, 0); - - if (padright > 0) - padFillBitrow(outrow, background, padright, - padleft + img[i].cols); - - } - } - - if (startRow == 1) - /* Top row already read for auto background color - determination. Write it out. - */ - pbm_writepbmrow_packed(ofP, outrow, newcols, 0); - - for (row = startRow; row < img[i].rows; ++row) { - pbm_readpbmrow_bitoffset(img[i].ifP, outrow, img[i].cols, - img[i].format, padleft); - pbm_writepbmrow_packed(ofP, outrow, newcols, 0); - } - - backgroundPrev = background; - } - pbm_freerow_packed(outrow); -} - - - -typedef struct { - xel * xelrow; - xel * inrow; - xel background; - int padtop; -} ImgGen2; - - - -static void -getGenImgInfo(ImgInfo const img[], - unsigned int const nfiles, - xel * const newxelrow, - unsigned int const newrows, - xelval const newmaxval, - int const newformat, - enum justification const justification, - enum backcolor const backcolor, - ImgGen2 ** const img2P) { - - ImgGen2 * img2; - unsigned int i; - - MALLOCARRAY_NOFAIL(img2, nfiles); - - for (i = 0; i < nfiles; ++i) { - switch (justification) { /* Determine top padding */ - case JUST_MIN: - img2[i].padtop = 0; - break; - case JUST_MAX: - img2[i].padtop = newrows - img[i].rows; - break; - case JUST_CENTER: - img2[i].padtop = (newrows - img[i].rows) / 2; - break; - } - - img2[i].inrow = - (i == 0 ? &newxelrow[0] : img2[i-1].inrow + img[i-1].cols); - - if (img[i].rows == newrows) /* no padding */ - img2[i].xelrow = NULL; - else { - /* Determine pad color */ - switch (backcolor){ - case BACK_AUTO: - img2[i].xelrow = pnm_allocrow(img[i].cols); - pnm_readpnmrow(img[i].ifP, img2[i].xelrow, - img[i].cols, img[i].maxval, img[i].format); - pnm_promoteformatrow(img2[i].xelrow, img[i].cols, - img[i].maxval, img[i].format, - newmaxval, newformat); - img2[i].background = pnm_backgroundxelrow( - img2[i].xelrow, img[i].cols, newmaxval, newformat); - break; - case BACK_BLACK: - img2[i].xelrow = NULL; - img2[i].background = pnm_blackxel(newmaxval, newformat); - break; - case BACK_WHITE: - img2[i].xelrow = NULL; - img2[i].background = pnm_whitexel(newmaxval, newformat); - break; - } - } - } - *img2P = img2; -} - - - -static void -concatenateLeftRightGen(FILE * const ofP, - unsigned int const nfiles, - unsigned int const newcols, - unsigned int const newrows, - xelval const newmaxval, - int const newformat, - enum justification const justification, - ImgInfo const img[], - enum backcolor const backcolor) { - - xel * const outrow = pnm_allocrow(newcols); - - ImgGen2 * img2; - unsigned int row; - - getGenImgInfo(img, nfiles, outrow, newrows, - newmaxval, newformat, justification, backcolor, &img2); - - for (row = 0; row < newrows; ++row) { - unsigned int i; - - for (i = 0; i < nfiles; ++i) { - if ((row == 0 && img2[i].padtop > 0) || - row == img2[i].padtop + img[i].rows) { - /* This row begins a run of padding, either above or below - file 'i', so set 'outrow' to padding. - */ - unsigned int col; - for (col = 0; col < img[i].cols; ++col) - img2[i].inrow[col] = img2[i].background; - } - if (row == img2[i].padtop && img2[i].xelrow) { - /* We're at the top row of file 'i', and that row - has already been read to xelrow[] to determine - background. Copy it to 'outrow'. - */ - unsigned int col; - for (col = 0; col < img[i].cols; ++col) - img2[i].inrow[col] = img2[i].xelrow[col]; - - free(img2[i].xelrow); - } else if (row >= img2[i].padtop && - row < img2[i].padtop + img[i].rows) { - pnm_readpnmrow( - img[i].ifP, img2[i].inrow, img[i].cols, img[i].maxval, - img[i].format); - pnm_promoteformatrow( - img2[i].inrow, img[i].cols, img[i].maxval, - img[i].format, newmaxval, newformat); - } else { - /* It's a row of padding, so outrow[] is already set - appropriately. - */ - } - } - /* Note that img2[N].inrow{] is an alias to part of outrow[], so - outrow[] has been set. - */ - pnm_writepnmrow(ofP, outrow, newcols, newmaxval, newformat, 0); - } - pnm_freerow(outrow); -} - - - -static void -concatenateTopBottomGen(FILE * const ofP, - unsigned int const nfiles, - int const newcols, - int const newrows, - xelval const newmaxval, - int const newformat, - enum justification const justification, - ImgInfo const img[], - enum backcolor const backcolor) { - - xel * const newxelrow = pnm_allocrow(newcols); - xel * inrow; - unsigned int padleft; - unsigned int i; - unsigned int row, startRow; - xel background, backgroundPrev; - bool backChange; - /* The background color is different from that of the previous - input image. - */ - - switch (backcolor) { - case BACK_AUTO: /* do nothing now, determine at start of each image */ - break; - case BACK_BLACK: background = pnm_blackxel(newmaxval, newformat); - break; - case BACK_WHITE: background = pnm_whitexel(newmaxval, newformat); - break; - } - - for ( i = 0; i < nfiles; ++i, backgroundPrev = background) { - if (img[i].cols == newcols) { - /* no padding */ - startRow = 0; - backChange = FALSE; - inrow = newxelrow; - } else { /* Calculate left padding amount */ - switch (justification) { - case JUST_MIN: padleft = 0; break; - case JUST_MAX: padleft = newcols - img[i].cols; break; - case JUST_CENTER: padleft = (newcols - img[i].cols) / 2; break; - } - - if (backcolor == BACK_AUTO) { - /* Determine background color */ - - startRow = 1; - inrow = &newxelrow[padleft]; - - pnm_readpnmrow(img[i].ifP, inrow, - img[i].cols, img[i].maxval, img[i].format); - pnm_promoteformatrow(inrow, img[i].cols, img[i].maxval, - img[i].format, - newmaxval, newformat); - background = pnm_backgroundxelrow( - inrow, img[i].cols, newmaxval, newformat); - - backChange = i==0 || !PNM_EQUAL(background, backgroundPrev); - } else { - /* background color is constant: black or white */ - startRow = 0; - inrow = &newxelrow[padleft]; - backChange = (i==0); - } - - if (backChange || (i > 0 && img[i-1].cols > img[i].cols)) { - unsigned int col; - - for (col = 0; col < padleft; ++col) - newxelrow[col] = background; - for (col = padleft + img[i].cols; col < newcols; ++col) - newxelrow[col] = background; - } - } - - if (startRow == 1) - /* Top row already read for auto background - color determination. Write it out. */ - pnm_writepnmrow(ofP, newxelrow, newcols, newmaxval, newformat, 0); - - for (row = startRow; row < img[i].rows; ++row) { - pnm_readpnmrow(img[i].ifP, - inrow, img[i].cols, img[i].maxval, img[i].format); - pnm_promoteformatrow( - inrow, img[i].cols, img[i].maxval, img[i].format, - newmaxval, newformat); - - pnm_writepnmrow(ofP, newxelrow, newcols, newmaxval, newformat, 0); - } - } - pnm_freerow(newxelrow); -} - - - -int -main(int argc, - const char ** argv) { - - struct CmdlineInfo cmdline; - ImgInfo * img; /* malloc'ed array */ - xelval newmaxval; - int newformat; - unsigned int i; - unsigned int newrows, newcols; - - pm_proginit(&argc, argv); - - parseCommandLine(argc, argv, &cmdline); - - MALLOCARRAY_NOFAIL(img, cmdline.nfiles); - - for (i = 0; i < cmdline.nfiles; ++i) { - img[i].ifP = pm_openr(cmdline.inputFilespec[i]); - pnm_readpnminit(img[i].ifP, &img[i].cols, &img[i].rows, - &img[i].maxval, &img[i].format); - } - - computeOutputParms(cmdline.nfiles, cmdline.orientation, img, - &newcols, &newrows, &newmaxval, &newformat); - - pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0); - - if (PNM_FORMAT_TYPE(newformat) == PBM_TYPE) { - switch (cmdline.orientation) { - case LEFTRIGHT: - concatenateLeftRightPbm(stdout, cmdline.nfiles, - newcols, newrows, cmdline.justification, - img, cmdline.backcolor); - break; - case TOPBOTTOM: - concatenateTopBottomPbm(stdout, cmdline.nfiles, - newcols, newrows, cmdline.justification, - img, cmdline.backcolor); - break; - } - } else { - switch (cmdline.orientation) { - case LEFTRIGHT: - concatenateLeftRightGen(stdout, cmdline.nfiles, - newcols, newrows, newmaxval, newformat, - cmdline.justification, img, - cmdline.backcolor); - break; - case TOPBOTTOM: - concatenateTopBottomGen(stdout, cmdline.nfiles, - newcols, newrows, newmaxval, newformat, - cmdline.justification, img, - cmdline.backcolor); - break; - } - } - for (i = 0; i < cmdline.nfiles; ++i) - pm_close(img[i].ifP); - free(cmdline.inputFilespec); - free(img); - pm_close(stdout); - - return 0; -} - - - diff --git a/editor/pnmindex.csh b/editor/pnmindex.csh deleted file mode 100755 index c6f1e844..00000000 --- a/editor/pnmindex.csh +++ /dev/null @@ -1,189 +0,0 @@ -#!/bin/csh -f -# -# pnmindex - build a visual index of a bunch of anymaps -# -# Copyright (C) 1991 by Jef Poskanzer. -# -# 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. - -# -title and -quant added by John Heidemann 13-Sep-00. - -set size=100 # make the images about this big -set across=6 # show this many images per row -set colors=256 # quantize results to this many colors -set back="-white" # default background color -set doquant=true # quantize or not -set title="" # default title (none) - -while ( 1 ) - switch ( "$1" ) - - case -s*: - if ( $#argv < 2 ) goto usage - set size="$2" - shift - shift - breaksw - - case -a*: - if ( $#argv < 2 ) goto usage - set across="$2" - shift - shift - breaksw - - case -t*: - if ( $#argv < 2 ) goto usage - set title="$2" - shift - shift - breaksw - - case -c*: - set colors="$2" - shift - shift - breaksw - - case -noq*: - set doquant=false - shift - breaksw - - case -q*: - set doquant=true - shift - breaksw - - case -b*: - set back="-black" - shift - breaksw - - case -w*: - set back="-white" - shift - breaksw - - case -*: - goto usage - breaksw - - default: - break - breaksw - - endsw -end - -if ( $#argv == 0 ) then - goto usage -endif - -set tmpfile=/tmp/pi.tmp.$$ -rm -f $tmpfile -set maxformat=PBM - -set rowfiles=() -set imagefiles=() -@ row = 1 -@ col = 1 - -if ( "$title" != "" ) then - set rowfile=/tmp/pi.${row}.$$ - rm -f $rowfile - pbmtext "$title" > $rowfile - set rowfiles=( $rowfiles $rowfile ) - @ row += 1 -endif - -foreach i ( $argv ) - - set description=`pnmfile $i` - if ( $description[4] <= $size && $description[6] <= $size ) then - cat $i > $tmpfile - else - switch ( $description[2] ) - case PBM: - pnmscale -quiet -xysize $size $size $i | pgmtopbm > $tmpfile - breaksw - - case PGM: - pnmscale -quiet -xysize $size $size $i > $tmpfile - if ( $maxformat == PBM ) then - set maxformat=PGM - endif - breaksw - - default: - if ( $doquant == false ) then - pnmscale -quiet -xysize $size $size $i > $tmpfile - else - pnmscale -quiet -xysize $size $size $i | ppmquant -quiet $colors > $tmpfile - endif - set maxformat=PPM - breaksw - endsw - endif - set imagefile=/tmp/pi.${row}.${col}.$$ - rm -f $imagefile - if ( "$back" == "-white" ) then - pbmtext "$i" | pnmcat $back -tb $tmpfile - > $imagefile - else - pbmtext "$i" | pnminvert | pnmcat $back -tb $tmpfile - > $imagefile - endif - rm -f $tmpfile - set imagefiles=( $imagefiles $imagefile ) - - if ( $col >= $across ) then - set rowfile=/tmp/pi.${row}.$$ - rm -f $rowfile - if ( $maxformat != PPM || $doquant == false ) then - pnmcat $back -lr -jbottom $imagefiles > $rowfile - else - pnmcat $back -lr -jbottom $imagefiles | ppmquant -quiet $colors > $rowfile - endif - rm -f $imagefiles - set imagefiles=() - set rowfiles=( $rowfiles $rowfile ) - @ col = 1 - @ row += 1 - else - @ col += 1 - endif - -end - -if ( $#imagefiles > 0 ) then - set rowfile=/tmp/pi.${row}.$$ - rm -f $rowfile - if ( $maxformat != PPM || $doquant == false ) then - pnmcat $back -lr -jbottom $imagefiles > $rowfile - else - pnmcat $back -lr -jbottom $imagefiles | ppmquant -quiet $colors > $rowfile - endif - rm -f $imagefiles - set rowfiles=( $rowfiles $rowfile ) -endif - -if ( $#rowfiles == 1 ) then - cat $rowfiles -else - if ( $maxformat != PPM || $doquant == false ) then - pnmcat $back -tb $rowfiles - else - pnmcat $back -tb $rowfiles | ppmquant -quiet $colors - endif -endif -rm -f $rowfiles - -exit 0 - -usage: -echo "usage: $0 [-size N] [-across N] [-colors N] [-black] pnmfile ..." -exit 1 diff --git a/editor/pnmindex.sh b/editor/pnmindex.sh deleted file mode 100755 index dfc5b82a..00000000 --- a/editor/pnmindex.sh +++ /dev/null @@ -1,214 +0,0 @@ -#!/bin/sh -# -# pnmindex - build a visual index of a bunch of PNM images -# -# Copyright (C) 1991 by Jef Poskanzer. -# -# 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. - -size=100 # make the images about this big -across=6 # show this many images per row -colors=256 # quantize results to this many colors -back="-white" # default background color -doquant=true # quantize or not -title="" # default title (none) - -usage () -{ - echo "usage: $0 [-size N] [-across N] [-colors N] [-black] pnmfile ..." - exit 1 -} - -while :; do - case "$1" in - - -s*) - if [ $# -lt 2 ]; then usage; fi - size="$2" - shift - shift - ;; - - -a*) - if [ $# -lt 2 ]; then usage; fi - across="$2" - shift - shift - ;; - - -t*) - if [ $# -lt 2 ]; then usage; fi - title="$2" - shift - shift - ;; - - -c*) - if [ $# -lt 2 ]; then usage; fi - colors="$2" - shift - shift - ;; - - -b*) - back="-black" - shift - ;; - - -w*) - back="-white" - shift - ;; - - -noq*) - doquant=false - shift - ;; - - -q*) - doquant=true - shift - ;; - - -*) - usage - ;; - - *) - break - ;; - esac -done - -if [ $# -eq 0 ]; then - usage -fi - -tempdir="${TMPDIR-/tmp}/pnmindex.$$" -mkdir -m 0700 $tempdir || \ - { echo "Could not create temporary file. Exiting."; exit 1;} -trap 'rm -rf $tempdir' 0 1 3 15 - -tmpfile=$tempdir/pi.tmp -maxformat=PBM - -rowfiles=() -imagefiles=() -row=1 -col=1 - -if [ "$title"x != ""x ] ; then -# rowfile=`tempfile -p pirow -m 600` - rowfile=$tempdir/pi.${row} - pbmtext "$title" > $rowfile - rowfiles=(${rowfiles[*]} $rowfile ) - row=$(($row + 1)) -fi - -for i in "$@"; do - - description=(`pnmfile $i`) - - format=${description[1]} - width=${description[3]} - height=${description[5]} - - if [ $? -ne 0 ]; then - echo pnmfile returned an error - exit $? - fi - - if [ $width -le $size ] && \ - [ $height -le $size ]; then - cat $i > $tmpfile - else - case $format in - - PBM) - pamscale -quiet -xysize $size $size $i | pgmtopbm > $tmpfile - ;; - - PGM) - pamscale -quiet -xysize $size $size $i > $tmpfile - if [ $maxformat = PBM ]; then - maxformat=PGM - fi - ;; - - *) - if [ "$doquant" = "true" ] ; then - pamscale -quiet -xysize $size $size $i | \ - pnmquant -quiet $colors > $tmpfile - else - pamscale -quiet -xysize $size $size $i > $tmpfile - fi - maxformat=PPM - ;; - esac - fi - - imagefile=$tempdir/pi.${row}.${col} - rm -f $imagefile - if [ "$back" = "-white" ]; then - pbmtext "$i" | pnmcat $back -tb $tmpfile - > $imagefile - else - pbmtext "$i" | pnminvert | pnmcat $back -tb $tmpfile - > $imagefile - fi - imagefiles=( ${imagefiles[*]} $imagefile ) - - if [ $col -ge $across ]; then - rowfile=$tempdir/pi.${row} - rm -f $rowfile - - if [ $maxformat != PPM -o "$doquant" = "false" ]; then - pnmcat $back -lr -jbottom ${imagefiles[*]} > $rowfile - else - pnmcat $back -lr -jbottom ${imagefiles[*]} | \ - pnmquant -quiet $colors > $rowfile - fi - - rm -f ${imagefiles[*]} - unset imagefiles - imagefiles=() - rowfiles=( ${rowfiles[*]} $rowfile ) - col=1 - row=$(($row + 1)) - else - col=$(($col + 1)) - fi -done - -# All the full rows have been put in row files. -# Now put the final partial row in its row file. - -if [ ${#imagefiles[*]} -gt 0 ]; then - rowfile=$tempdir/pi.${row} - rm -f $rowfile - if [ $maxformat != PPM -o "$doquant" = "false" ]; then - pnmcat $back -lr -jbottom ${imagefiles[*]} > $rowfile - else - pnmcat $back -lr -jbottom ${imagefiles[*]} | \ - pnmquant -quiet $colors > $rowfile - fi - rm -f ${imagefiles[*]} - rowfiles=( ${rowfiles[*]} $rowfile ) -fi - -if [ ${#rowfiles[*]} -eq 1 ]; then - cat $rowfiles -else - if [ $maxformat != PPM -o "$doquant" = "false" ]; then - pnmcat $back -tb ${rowfiles[*]} - else - pnmcat $back -tb ${rowfiles[*]} | pnmquant -quiet $colors - fi -fi -rm -f ${rowfiles[*]} - -exit 0 - diff --git a/editor/pnmmargin b/editor/pnmmargin index a62e5e44..94321c7f 100755 --- a/editor/pnmmargin +++ b/editor/pnmmargin @@ -108,8 +108,8 @@ else pamflip -rotate90 $tmp2 > $tmp3 # Cat things together. - pnmcat -lr $tmp2 $tmp1 $tmp2 > $tmp4 - pnmcat -tb $plainopt $tmp3 $tmp4 $tmp3 + pamcat -leftright $tmp2 $tmp1 $tmp2 > $tmp4 + pamcat -topbottom $plainopt $tmp3 $tmp4 $tmp3 fi diff --git a/editor/pnmquantall b/editor/pnmquantall index 5f434fc2..594e8f7b 100755 --- a/editor/pnmquantall +++ b/editor/pnmquantall @@ -145,11 +145,11 @@ sub tempFile($) { sub makeColorMap($$$$) { my ($fileNamesR, $newColorCt, $colorMapFileName, $errorR) = @_; - my $pnmcatCmd = "pnmcat -topbottom -white -jleft @{$fileNamesR}"; + my $pamcatCmd = "pamcat -topbottom -white -jleft @{$fileNamesR}"; my $pnmcolormapCmd = "pnmcolormap $newColorCt"; - my $makeMapCmd = "$pnmcatCmd | $pnmcolormapCmd >$colorMapFileName"; + my $makeMapCmd = "$pamcatCmd | $pnmcolormapCmd >$colorMapFileName"; my $termStatus = system($makeMapCmd); diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c index 438fe058..2b39e4ec 100644 --- a/editor/specialty/pnmindex.c +++ b/editor/specialty/pnmindex.c @@ -1,5 +1,5 @@ /*============================================================================ - pnmindex + pnmindex ============================================================================== build a visual index of a bunch of PNM images @@ -32,7 +32,7 @@ #include "nstring.h" #include "pnm.h" -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -56,11 +56,11 @@ systemf(const char * const fmt, ...) { va_list varargs; - + size_t dryRunLen; - + va_start(varargs, fmt); - + pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen); va_end(varargs); @@ -83,7 +83,7 @@ systemf(const char * const fmt, va_start(varargs, fmt); pm_vsnprintf(shellCommand, allocSize, fmt, varargs, &realLen); - + assert(realLen == dryRunLen); va_end(varargs); @@ -94,12 +94,12 @@ systemf(const char * const fmt, if (rc != 0) pm_error("shell command '%s' failed. rc %d", shellCommand, rc); - + pm_strfree(shellCommand); } } } - + static const char * @@ -168,8 +168,8 @@ shellQuote(const char * const arg) { static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { +parseCommandLine(int argc, char ** argv, + struct CmdlineInfo * const cmdlineP) { unsigned int option_def_index; optEntry *option_def; @@ -183,13 +183,13 @@ parseCommandLine(int argc, char ** argv, MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ - OPTENT3(0, "black", OPT_FLAG, NULL, + OPTENT3(0, "black", OPT_FLAG, NULL, &cmdlineP->black, 0); - OPTENT3(0, "noquant", OPT_FLAG, NULL, + OPTENT3(0, "noquant", OPT_FLAG, NULL, &cmdlineP->noquant, 0); - OPTENT3(0, "quant", OPT_FLAG, NULL, + OPTENT3(0, "quant", OPT_FLAG, NULL, &quant, 0); - OPTENT3(0, "verbose", OPT_FLAG, NULL, + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); OPTENT3(0, "size", OPT_UINT, &cmdlineP->size, &sizeSpec, 0); @@ -202,7 +202,7 @@ parseCommandLine(int argc, char ** argv, opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ - opt.allowNegNum = FALSE; + opt.allowNegNum = FALSE; pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdline_p and others. */ @@ -212,7 +212,7 @@ parseCommandLine(int argc, char ** argv, if (!colorsSpec) cmdlineP->colors = 256; - + if (!sizeSpec) cmdlineP->size = 100; @@ -246,7 +246,7 @@ parseCommandLine(int argc, char ** argv, static void -freeCmdline(struct cmdlineInfo const cmdline) { +freeCmdline(struct CmdlineInfo const cmdline) { unsigned int i; @@ -326,9 +326,9 @@ rowFileName(const char * const dirName, unsigned int const row) { const char * fileName; - + pm_asprintf(&fileName, "%s/pi.%u", dirName, row); - + return fileName; } @@ -368,7 +368,7 @@ copyImage(const char * const inputFileName, systemf("cat %s > %s", inputFileNmToken, outputFileName); pm_strfree(inputFileNmToken); -} +} @@ -386,26 +386,26 @@ copyScaleQuantImage(const char * const inputFileName, switch (PNM_FORMAT_TYPE(format)) { case PBM_TYPE: - pm_asprintf(&scaleCommand, + pm_asprintf(&scaleCommand, "pamscale -quiet -xysize %u %u %s " "| pgmtopbm > %s", size, size, inputFileNmToken, outputFileName); break; - + case PGM_TYPE: - pm_asprintf(&scaleCommand, + pm_asprintf(&scaleCommand, "pamscale -quiet -xysize %u %u %s >%s", size, size, inputFileNmToken, outputFileName); break; - + case PPM_TYPE: if (quant) - pm_asprintf(&scaleCommand, + pm_asprintf(&scaleCommand, "pamscale -quiet -xysize %u %u %s " "| pnmquant -quiet %u > %s", size, size, inputFileNmToken, colors, outputFileName); else - pm_asprintf(&scaleCommand, + pm_asprintf(&scaleCommand, "pamscale -quiet -xysize %u %u %s >%s", size, size, inputFileNmToken, outputFileName); break; @@ -426,7 +426,7 @@ formatTypeMax(int const typeA, int const typeB) { if (typeA == PPM_TYPE || typeB == PPM_TYPE) - return PPM_TYPE; + return PPM_TYPE; else if (typeA == PGM_TYPE || typeB == PGM_TYPE) return PGM_TYPE; else @@ -441,9 +441,9 @@ thumbnailFileName(const char * const dirName, unsigned int const col) { const char * fileName; - + pm_asprintf(&fileName, "%s/pi.%u.%u", dirName, row, col); - + return fileName; } @@ -464,7 +464,7 @@ thumbnailFileList(const char * const dirName, pm_error("Unable to allocate %u bytes for file list", maxListSize); list[0] = '\0'; - + for (col = 0; col < cols; ++col) { const char * const fileName = thumbnailFileName(dirName, row, col); @@ -487,19 +487,28 @@ makeImageFile(const char * const thumbnailFileName, const char * const inputFileName, bool const blackBackground, const char * const outputFileName) { +/*---------------------------------------------------------------------------- + Create one thumbnail image. It consists of the image in the file named + 'thumbnailFileName' with text of that name appended to the bottom. + Write the result to the file named 'outputFileName'. + + 'blackBackground' means give the image a black background where padding + is necessary and make the text white on black. If false, give the image + a white background instead. +-----------------------------------------------------------------------------*/ const char * const blackWhiteOpt = blackBackground ? "-black" : "-white"; const char * const invertStage = blackBackground ? "| pnminvert " : ""; const char * inputFileNmToken = shellQuote(inputFileName); systemf("pbmtext %s %s" - "| pnmcat %s -topbottom %s - " + "| pamcat %s -topbottom %s - " "> %s", inputFileNmToken, invertStage, blackWhiteOpt, thumbnailFileName, outputFileName); pm_strfree(inputFileNmToken); -} +} @@ -519,21 +528,21 @@ makeThumbnail(const char * const inputFileName, xelval maxval; const char * tmpfile; const char * fileName; - + ifP = pm_openr(inputFileName); pnm_readpnminit(ifP, &imageCols, &imageRows, &maxval, &format); pm_close(ifP); - + pm_asprintf(&tmpfile, "%s/pi.tmp", tempDir); if (imageCols < size && imageRows < size) copyImage(inputFileName, tmpfile); else - copyScaleQuantImage(inputFileName, tmpfile, format, + copyScaleQuantImage(inputFileName, tmpfile, format, size, quant, colors); fileName = thumbnailFileName(tempDir, row, col); - + makeImageFile(tmpfile, inputFileName, black, fileName); unlink(tmpfile); @@ -543,7 +552,7 @@ makeThumbnail(const char * const inputFileName, *formatP = format; } - + static void @@ -552,7 +561,7 @@ unlinkThumbnailFiles(const char * const dirName, unsigned int const cols) { unsigned int col; - + for (col = 0; col < cols; ++col) { const char * const fileName = thumbnailFileName(dirName, row, col); @@ -569,7 +578,7 @@ unlinkRowFiles(const char * const dirName, unsigned int const rows) { unsigned int row; - + for (row = 0; row < rows; ++row) { const char * const fileName = rowFileName(dirName, row); @@ -595,7 +604,7 @@ combineIntoRowAndDelete(unsigned int const row, const char * fileName; const char * quantStage; const char * fileList; - + fileName = rowFileName(tempDir, row); unlink(fileName); @@ -607,7 +616,7 @@ combineIntoRowAndDelete(unsigned int const row, fileList = thumbnailFileList(tempDir, row, cols); - systemf("pnmcat %s -leftright -jbottom %s " + systemf("pamcat %s -leftright -jbottom %s " "%s" ">%s", blackWhiteOpt, fileList, quantStage, fileName); @@ -641,7 +650,7 @@ rowFileList(const char * const dirName, if (strlen(list) + strlen(fileName) + 1 > maxListSize - 1) pm_error("File name list too long for this program to handle."); - + else { strcat(list, " "); strcat(list, fileName); @@ -666,7 +675,7 @@ writeRowsAndDelete(unsigned int const rows, const char * quantStage; const char * fileList; - + if (maxFormatType == PPM_TYPE && quant) pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colors); else @@ -674,7 +683,7 @@ writeRowsAndDelete(unsigned int const rows, fileList = rowFileList(tempDir, rows); - systemf("pnmcat %s -topbottom %s %s", + systemf("pamcat %s -topbottom %s %s", blackWhiteOpt, fileList, quantStage); pm_strfree(fileList); @@ -687,7 +696,7 @@ writeRowsAndDelete(unsigned int const rows, int main(int argc, char *argv[]) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; const char * tempDir; int maxFormatType; unsigned int colsInRow; @@ -699,7 +708,7 @@ main(int argc, char *argv[]) { parseCommandLine(argc, argv, &cmdline); verbose = cmdline.verbose; - + makeTempDir(&tempDir); maxFormatType = PBM_TYPE; @@ -714,7 +723,7 @@ main(int argc, char *argv[]) { int format; - makeThumbnail(inputFileName, cmdline.size, cmdline.black, + makeThumbnail(inputFileName, cmdline.size, cmdline.black, !cmdline.noquant, cmdline.colors, tempDir, rowsDone, colsInRow, &format); @@ -742,3 +751,6 @@ main(int argc, char *argv[]) { return 0; } + + + |