diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2008-09-27 02:59:58 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2008-09-27 02:59:58 +0000 |
commit | 4b4b88014200757f6f7f8864c85138a56835c794 (patch) | |
tree | cea2a438b384f7a4fbf85aabb9fbd23093908d49 /editor | |
parent | f04d41f61e5cf83835c6bc85122e65cfe2cf9857 (diff) | |
download | netpbm-mirror-4b4b88014200757f6f7f8864c85138a56835c794.tar.gz netpbm-mirror-4b4b88014200757f6f7f8864c85138a56835c794.tar.xz netpbm-mirror-4b4b88014200757f6f7f8864c85138a56835c794.zip |
Rebase to 10.44.00
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@738 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r-- | editor/pamcomp.c | 124 | ||||
-rw-r--r-- | editor/pamcut.c | 276 | ||||
-rw-r--r-- | editor/pamperspective.c | 534 | ||||
-rw-r--r-- | editor/pnmcat.c | 821 | ||||
-rw-r--r-- | editor/pnmcrop.c | 4 | ||||
-rw-r--r-- | editor/pnmmontage.c | 503 | ||||
-rw-r--r-- | editor/pnmpad.c | 205 | ||||
-rw-r--r-- | editor/pnmpaste.c | 577 | ||||
-rw-r--r-- | editor/pnmstitch.c | 14 |
9 files changed, 2083 insertions, 975 deletions
diff --git a/editor/pamcomp.c b/editor/pamcomp.c index fc43d9c5..45220486 100644 --- a/editor/pamcomp.c +++ b/editor/pamcomp.c @@ -22,14 +22,16 @@ additional work by multiple authors. -----------------------------------------------------------------------------*/ -#define _BSD_SOURCE /* Make sure strcasecmp() is in string.h */ +#define _BSD_SOURCE /* Make sure strcaseceq() is in nstring.h */ +#include <assert.h> #include <string.h> #include <math.h> -#include "pam.h" -#include "pm_gamma.h" -#include "shhopt.h" #include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pm_gamma.h" +#include "pam.h" enum horizPos {BEYONDLEFT, LEFT, CENTER, RIGHT, BEYONDRIGHT}; enum vertPos {ABOVE, TOP, MIDDLE, BOTTOM, BELOW}; @@ -64,7 +66,7 @@ struct cmdlineInfo { static void parseCommandLine(int argc, - char ** argv, + const char ** argv, struct cmdlineInfo * const cmdlineP ) { /*---------------------------------------------------------------------------- Parse program command line described in Unix standard form by argc @@ -111,7 +113,7 @@ parseCommandLine(int argc, 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); + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ @@ -123,15 +125,15 @@ parseCommandLine(int argc, cmdlineP->alphaFilespec = NULL; if (alignSpec) { - if (strcasecmp(align, "BEYONDLEFT") == 0) + if (strcaseeq(align, "BEYONDLEFT")) cmdlineP->align = BEYONDLEFT; - else if (strcasecmp(align, "LEFT") == 0) + else if (strcaseeq(align, "LEFT")) cmdlineP->align = LEFT; - else if (strcasecmp(align, "CENTER") == 0) + else if (strcaseeq(align, "CENTER")) cmdlineP->align = CENTER; - else if (strcasecmp(align, "RIGHT") == 0) + else if (strcaseeq(align, "RIGHT")) cmdlineP->align = RIGHT; - else if (strcasecmp(align, "BEYONDRIGHT") == 0) + else if (strcaseeq(align, "BEYONDRIGHT")) cmdlineP->align = BEYONDRIGHT; else pm_error("Invalid value for align option: '%s'. Only LEFT, " @@ -141,15 +143,15 @@ parseCommandLine(int argc, cmdlineP->align = LEFT; if (valignSpec) { - if (strcasecmp(valign, "ABOVE") == 0) + if (strcaseeq(valign, "ABOVE")) cmdlineP->valign = ABOVE; - else if (strcasecmp(valign, "TOP") == 0) + else if (strcaseeq(valign, "TOP")) cmdlineP->valign = TOP; - else if (strcasecmp(valign, "MIDDLE") == 0) + else if (strcaseeq(valign, "MIDDLE")) cmdlineP->valign = MIDDLE; - else if (strcasecmp(valign, "BOTTOM") == 0) + else if (strcaseeq(valign, "BOTTOM")) cmdlineP->valign = BOTTOM; - else if (strcasecmp(valign, "BELOW") == 0) + else if (strcaseeq(valign, "BELOW")) cmdlineP->valign = BELOW; else pm_error("Invalid value for valign option: '%s'. Only TOP, " @@ -268,12 +270,12 @@ determineOutputType(struct pam * const composedPamP, static void -warnOutOfFrame( int const originLeft, - int const originTop, - int const overCols, - int const overRows, - int const underCols, - int const underRows ) { +warnOutOfFrame(int const originLeft, + int const originTop, + int const overCols, + int const overRows, + int const underCols, + int const underRows) { if (originLeft >= underCols) pm_message("WARNING: the overlay is entirely off the right edge " @@ -339,6 +341,10 @@ computeOverlayPosition(int const underCols, The origin may be outside the underlying image (so e.g. *originLeftP may be negative or > image width). That means not all of the overlay image actually gets used. In fact, there may be no overlap at all. + + But we insist that the span from the topmost row of the two images + to the bottommost row be less than INT_MAX so that Caller can + use an integer for a row number in the combination. -----------------------------------------------------------------------------*/ int xalign, yalign; @@ -470,6 +476,44 @@ adaptRowToOutputFormat(struct pam * const inpamP, static void +composeRow(int const originleft, + struct pam * const underlayPamP, + struct pam * const overlayPamP, + bool const invertAlpha, + float const masterOpacity, + bool const overlayHasOpacity, + unsigned int const opacityPlane, + struct pam * const composedPamP, + enum sampleScale const sampleScale, + const tuple * const underlayTuplerow, + const tuple * const overlayTuplerow, + const tuplen * const alphaTuplerown, + tuple * const composedTuplerow) { + + unsigned int col; + for (col = 0; col < composedPamP->width; ++col) { + int const ovlcol = col - originleft; + + if (ovlcol >= 0 && ovlcol < overlayPamP->width) { + tuplen const alphaTuplen = + alphaTuplerown ? alphaTuplerown[ovlcol] : NULL; + + overlayPixel(overlayTuplerow[ovlcol], overlayPamP, + underlayTuplerow[col], underlayPamP, + alphaTuplen, invertAlpha, + overlayHasOpacity, opacityPlane, + composedTuplerow[col], composedPamP, + masterOpacity, sampleScale); + } else + /* Overlay image does not touch this column. */ + pnm_assigntuple(composedPamP, composedTuplerow[col], + underlayTuplerow[col]); + } +} + + + +static void composite(int const originleft, int const origintop, struct pam * const underlayPamP, @@ -495,6 +539,9 @@ composite(int const originleft, go. It is not necessarily inside the underlying image (in fact, may be negative). Only the part of the overlay that actually intersects the underlying image, if any, gets into the output. + + We assume that the span from the topmost row of the two images to + the bottommost row is less than INT_MAX. -----------------------------------------------------------------------------*/ enum sampleScale const sampleScale = assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE; @@ -515,9 +562,13 @@ composite(int const originleft, overlayTuplerow = pnm_allocpamrow(overlayPamP); if (alphaPamP) alphaTuplerown = pnm_allocpamrown(alphaPamP); + else + alphaTuplerown = NULL; pnm_writepaminit(composedPamP); + assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */ + for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop); underlayRow < MAX(underlayPamP->height, origintop + overlayPamP->height); @@ -541,25 +592,12 @@ composite(int const originleft, pnm_writepamrow(composedPamP, underlayTuplerow); } else { - unsigned int col; - for (col = 0; col < composedPamP->width; ++col) { - int const ovlcol = col - originleft; - - if (ovlcol >= 0 && ovlcol < overlayPamP->width) { - tuplen const alphaTuplen = - alphaPamP ? alphaTuplerown[ovlcol] : NULL; - - overlayPixel(overlayTuplerow[ovlcol], overlayPamP, - underlayTuplerow[col], underlayPamP, - alphaTuplen, invertAlpha, - overlayHasOpacity, opacityPlane, - composedTuplerow[col], composedPamP, - masterOpacity, sampleScale); - } else - /* Overlay image does not touch this column. */ - pnm_assigntuple(composedPamP, composedTuplerow[col], - underlayTuplerow[col]); - } + composeRow(originleft, underlayPamP, overlayPamP, + invertAlpha, masterOpacity, overlayHasOpacity, + opacityPlane, composedPamP, sampleScale, + underlayTuplerow, overlayTuplerow, alphaTuplerown, + composedTuplerow); + pnm_writepamrow(composedPamP, composedTuplerow); } } @@ -574,7 +612,7 @@ composite(int const originleft, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; FILE * underlayFileP; @@ -586,7 +624,7 @@ main(int argc, char *argv[]) { struct pam composedPam; int originLeft, originTop; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/editor/pamcut.c b/editor/pamcut.c index a555ad58..76a57f8e 100644 --- a/editor/pamcut.c +++ b/editor/pamcut.c @@ -271,44 +271,51 @@ computeCutBounds(const int cols, const int rows, static void -rejectOutOfBounds(const int cols, const int rows, - const int leftcol, const int rightcol, - const int toprow, const int bottomrow) { - - /* Reject coordinates off the edge */ - - if (leftcol < 0) - pm_error("You have specified a left edge (%d) that is beyond\n" - "the left edge of the image (0)", leftcol); - if (leftcol > cols-1) - pm_error("You have specified a left edge (%d) that is beyond\n" - "the right edge of the image (%d)", leftcol, cols-1); - if (rightcol < 0) - pm_error("You have specified a right edge (%d) that is beyond\n" - "the left edge of the image (0)", rightcol); - if (rightcol > cols-1) - pm_error("You have specified a right edge (%d) that is beyond\n" - "the right edge of the image (%d)", rightcol, cols-1); - if (leftcol > rightcol) - pm_error("You have specified a left edge (%d) that is to the right\n" - "of the right edge you specified (%d)", +rejectOutOfBounds(unsigned int const cols, + unsigned int const rows, + int const leftcol, + int const rightcol, + int const toprow, + int const bottomrow, + bool const pad) { + + /* Reject coordinates off the edge */ + + if (!pad) { + if (leftcol < 0) + pm_error("You have specified a left edge (%d) that is beyond " + "the left edge of the image (0)", leftcol); + if (leftcol > (int)(cols-1)) + pm_error("You have specified a left edge (%d) that is beyond " + "the right edge of the image (%u)", leftcol, cols-1); + if (rightcol < 0) + pm_error("You have specified a right edge (%d) that is beyond " + "the left edge of the image (0)", rightcol); + if (rightcol > (int)(cols-1)) + pm_error("You have specified a right edge (%d) that is beyond " + "the right edge of the image (%u)", rightcol, cols-1); + if (toprow < 0) + pm_error("You have specified a top edge (%d) that is above " + "the top edge of the image (0)", toprow); + if (toprow > (int)(rows-1)) + pm_error("You have specified a top edge (%d) that is below " + "the bottom edge of the image (%u)", toprow, rows-1); + if (bottomrow < 0) + pm_error("You have specified a bottom edge (%d) that is above " + "the top edge of the image (0)", bottomrow); + if (bottomrow > (int)(rows-1)) + pm_error("You have specified a bottom edge (%d) that is below " + "the bottom edge of the image (%u)", bottomrow, rows-1); + } + + if (leftcol > rightcol) + pm_error("You have specified a left edge (%d) that is to the right of " + "the right edge you specified (%d)", leftcol, rightcol); - - if (toprow < 0) - pm_error("You have specified a top edge (%d) that is above the top " - "edge of the image (0)", toprow); - if (toprow > rows-1) - pm_error("You have specified a top edge (%d) that is below the\n" - "bottom edge of the image (%d)", toprow, rows-1); - if (bottomrow < 0) - pm_error("You have specified a bottom edge (%d) that is above the\n" - "top edge of the image (0)", bottomrow); - if (bottomrow > rows-1) - pm_error("You have specified a bottom edge (%d) that is below the\n" - "bottom edge of the image (%d)", bottomrow, rows-1); - if (toprow > bottomrow) - pm_error("You have specified a top edge (%d) that is below\n" - "the bottom edge you specified (%d)", + + if (toprow > bottomrow) + pm_error("You have specified a top edge (%d) that is below " + "the bottom edge you specified (%d)", toprow, bottomrow); } @@ -400,13 +407,15 @@ struct rowCutter { create a new one then. */ + + static void -createRowCutter(struct pam * const inpamP, - struct pam * const outpamP, +createRowCutter(const struct pam * const inpamP, + const struct pam * const outpamP, int const leftcol, int const rightcol, struct rowCutter ** const rowCutterPP) { - + struct rowCutter * rowCutterP; tuple * inputPointers; tuple * outputPointers; @@ -414,7 +423,7 @@ createRowCutter(struct pam * const inpamP, tuple blackTuple; tuple discardTuple; int col; - + assert(inpamP->depth >= outpamP->depth); /* Entry condition. If this weren't true, we could not simply treat an input tuple as an output tuple. @@ -485,15 +494,144 @@ destroyRowCutter(struct rowCutter * const rowCutterP) { static void +extractRowsGen(const struct pam * const inpamP, + const struct pam * const outpamP, + int const leftcol, + int const rightcol, + int const toprow, + int const bottomrow) { + + struct rowCutter * rowCutterP; + int row; + + /* Write out top padding */ + if (0 - toprow > 0) + writeBlackRows(outpamP, 0 - toprow); + + createRowCutter(inpamP, outpamP, leftcol, rightcol, &rowCutterP); + + /* Read input and write out rows extracted from it */ + for (row = 0; row < inpamP->height; ++row) { + if (row >= toprow && row <= bottomrow){ + pnm_readpamrow(inpamP, rowCutterP->inputPointers); + pnm_writepamrow(outpamP, rowCutterP->outputPointers); + } else /* row < toprow || row > bottomrow */ + pnm_readpamrow(inpamP, NULL); + + /* Note that we may be tempted just to quit after reaching the bottom + of the extracted image, but that would cause a broken pipe problem + for the process that's feeding us the image. + */ + } + + destroyRowCutter(rowCutterP); + + /* Write out bottom padding */ + if ((bottomrow - (inpamP->height-1)) > 0) + writeBlackRows(outpamP, bottomrow - (inpamP->height-1)); +} + + + +static void +makeBlackPBMRow(unsigned char * const bitrow, + unsigned int const cols) { + + unsigned int const colByteCnt = pbm_packed_bytes(cols); + + unsigned int i; + + for (i = 0; i < colByteCnt; ++i) + bitrow[i] = PBM_BLACK * 0xff; + + if (PBM_BLACK != 0 && cols % 8 > 0) + bitrow[colByteCnt-1] <<= (8 - cols % 8); +} + + + +static void +extractRowsPBM(const struct pam * const inpamP, + const struct pam * const outpamP, + int const leftcol, + int const rightcol, + int const toprow, + int const bottomrow) { + + unsigned char * bitrow; + int readOffset, writeOffset; + int row; + unsigned int totalWidth; + + assert(0 <= leftcol && leftcol <= rightcol && rightcol < inpamP->width); + assert(toprow <= bottomrow); + + if (leftcol > 0) { + totalWidth = MAX(rightcol+1, inpamP->width) + 7; + if (totalWidth > INT_MAX) + /* Prevent overflows in pbm_allocrow_packed() */ + pm_error("Specified right edge is too far " + "from the right end of input image"); + + readOffset = 0; + writeOffset = leftcol; + } else { + totalWidth = -leftcol + MAX(rightcol+1, inpamP->width); + if (totalWidth > INT_MAX) + pm_error("Specified left/right edge is too far " + "from the left/right end of input image"); + + readOffset = -leftcol; + writeOffset = 0; + } + + bitrow = pbm_allocrow_packed(totalWidth); + + if (toprow < 0 || leftcol < 0 || rightcol >= inpamP->width){ + makeBlackPBMRow(bitrow, totalWidth); + if (toprow < 0) { + int row; + for (row=0; row < 0 - toprow; ++row) + pbm_writepbmrow_packed(outpamP->file, bitrow, + outpamP->width, 0); + } + } + + for (row = 0; row < inpamP->height; ++row){ + if (row >= toprow && row <= bottomrow) { + pbm_readpbmrow_bitoffset(inpamP->file, bitrow, inpamP->width, + inpamP->format, readOffset); + + pbm_writepbmrow_bitoffset(outpamP->file, bitrow, outpamP->width, + 0, writeOffset); + + if (rightcol >= inpamP->width) + /* repair right padding */ + bitrow[writeOffset/8 + pbm_packed_bytes(outpamP->width) - 1] = + 0xff * PBM_BLACK; + } else + pnm_readpamrow(inpamP, NULL); /* read and discard */ + } + + if (bottomrow - (inpamP->height-1) > 0) { + int row; + makeBlackPBMRow(bitrow, outpamP->width); + for (row = 0; row < bottomrow - (inpamP->height-1); ++row) + pbm_writepbmrow_packed(outpamP->file, bitrow, outpamP->width, 0); + } + pbm_freerow_packed(bitrow); +} + + + +static void cutOneImage(FILE * const ifP, struct cmdlineInfo const cmdline, FILE * const ofP) { - int row; int leftcol, rightcol, toprow, bottomrow; struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PAM image */ - struct rowCutter * rowCutterP; pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); @@ -503,22 +641,11 @@ cutOneImage(FILE * const ifP, cmdline.width, cmdline.height, &leftcol, &rightcol, &toprow, &bottomrow); - if (!cmdline.pad) - rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, - toprow, bottomrow); - else { - if (cmdline.left > cmdline.right) - pm_error("You have specified a left edge (%d) that is to the right\n" - "of the right edge you specified (%d)", - cmdline.left, cmdline.right); - - if (cmdline.top > cmdline.bottom) - pm_error("You have specified a top edge (%d) that is below\n" - "the bottom edge you specified (%d)", - cmdline.top, cmdline.bottom); - } + rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, + toprow, bottomrow, cmdline.pad); + if (cmdline.verbose) { - pm_message("Image goes from Row 0, Column 0 through Row %d, Column %d", + pm_message("Image goes from Row 0, Column 0 through Row %u, Column %u", inpam.height-1, inpam.width-1); pm_message("Cutting from Row %d, Column %d through Row %d Column %d", toprow, leftcol, bottomrow, rightcol); @@ -526,36 +653,15 @@ cutOneImage(FILE * const ifP, outpam = inpam; /* Initial value -- most fields should be same */ outpam.file = ofP; - outpam.width = rightcol-leftcol+1; - outpam.height = bottomrow-toprow+1; + outpam.width = rightcol - leftcol + 1; + outpam.height = bottomrow - toprow + 1; pnm_writepaminit(&outpam); - /* Write out top padding */ - if (0 - toprow > 0) - writeBlackRows(&outpam, 0 - toprow); - - createRowCutter(&inpam, &outpam, leftcol, rightcol, &rowCutterP); - - /* Read input and write out rows extracted from it */ - for (row = 0; row < inpam.height; ++row) { - if (row >= toprow && row <= bottomrow){ - pnm_readpamrow(&inpam, rowCutterP->inputPointers); - pnm_writepamrow(&outpam, rowCutterP->outputPointers); - } else /* row < toprow || row > bottomrow */ - pnm_readpamrow(&inpam, NULL); - - /* Note that we may be tempted just to quit after reaching the bottom - of the extracted image, but that would cause a broken pipe problem - for the process that's feeding us the image. - */ - } - - destroyRowCutter(rowCutterP); - - /* Write out bottom padding */ - if ((bottomrow - (inpam.height-1)) > 0) - writeBlackRows(&outpam, bottomrow - (inpam.height-1)); + if (PNM_FORMAT_TYPE(outpam.format) == PBM_TYPE) + extractRowsPBM(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow); + else + extractRowsGen(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow); } diff --git a/editor/pamperspective.c b/editor/pamperspective.c index 0839a98b..744f123c 100644 --- a/editor/pamperspective.c +++ b/editor/pamperspective.c @@ -20,12 +20,14 @@ #define _BSD_SOURCE /* Make sure strdup is int string.h */ +#include <assert.h> +#include <stdlib.h> #include <math.h> #include <string.h> -#include "pam.h" -#include "shhopt.h" #include "mallocvar.h" +#include "shhopt.h" +#include "pam.h" typedef double number; @@ -184,25 +186,26 @@ typedef struct { } world_data; -/* - Internal infile buffer - - This is a cyclic in random access out buffer, just large enough - to store all input lines that are still in use. -*/ typedef struct { - - unsigned int num_rows; - unsigned int last_physical; - unsigned int last_logical; - /* Row number of the input row most recently read from input - file into buffer (i.e. one less than the number of rows we - have read from the input) +/*---------------------------------------------------------------------------- + A buffer of image input. This holds a vertical window of the input. +-----------------------------------------------------------------------------*/ + unsigned int numRows; + /* Height of buffer window */ + unsigned int nextImageRow; + /* Row number of the next image row that will go into the buffer. + The 'numRows' rows before (above) that are in the buffer now. + */ + unsigned int nextBufferRow; + /* Row number in the physical buffer (index of rows[]) where + the next row read will go (hence where the oldest/highest + row in the buffer is now). */ tuple ** rows; + /* The rows of the window, as a cyclic buffer */ const struct pam * inpamP; - + /* The image from which we fill the buffer */ } buffer; @@ -315,45 +318,53 @@ static int parse_enum (const char *const text, -static number parse_float (char *const text) +static number +parseFloat(const char * const text) { /*---------------------------------------------------------------------------- Parse an argument given to a float command line option. We cannot just call strtod, because we want to parse fractions like "5/3" -----------------------------------------------------------------------------*/ -{ - bool error; - char* end; - char* denstart; - number num,den; - - error = FALSE; - num = strtod (text, &end); /* try strtod anyway */ - switch (*end) { - case 0: /* It is a plain number */ - break; - case '/': /* It might be a fraction */ - /* (Try to) parse the numerator */ - *end = 0; - num = strtod (text, &end); - error = (*end) != 0; - if (!error) { - /* Undo the above change */ - *end = '/'; - /* (Try to) parse the denominator */ - denstart = end+1; - den = strtod (denstart, &end); - error = (fabs(den)<eps) || ((*end) != 0); - if (!error) - num /= den; + bool error; + char * end; + number num; + char * buffer; + + buffer = strdup(text); + if (!buffer) + pm_error("Out of memory"); + + error = FALSE; + num = strtod(buffer, &end); /* try strtod anyway */ + switch(*end) { + case 0: /* It is a plain number */ + break; + case '/': /* It might be a fraction */ + /* (Try to) parse the numerator */ + *end = 0; + num = strtod(text, &end); + error = (*end != '\0'); + if (!error) { + char * const denStart = end + 1; + number denominator; + + /* Undo the above change */ + *end = '/'; + /* (Try to) parse the denominator */ + denominator = strtod(denStart, &end); + error = (fabs(denominator) < eps) || (*end != '\0'); + if (!error) + num /= denominator; + }; + break; + default: /* It is no number format we know */ + error = TRUE; }; - break; - default: /* It is no number format we know */ - error = TRUE; - }; - if (error) - pm_error ("Invalid number format: %s", text); + if (error) + pm_error("Invalid number format: %s", text); - return num; + free(buffer); + + return num; } @@ -382,8 +393,8 @@ static void parse_include_point(char * specification, if (*comma_seek == 0) pm_error ("Invalid format for --include point: '%s'", specification); *comma_seek = 0; /* separate the two parts for parsing purposes */ - new_point->xi = (number) parse_float(specification); - new_point->yi = (number) parse_float(comma_seek+1); + new_point->xi = (number) parseFloat(specification); + new_point->yi = (number) parseFloat(comma_seek+1); *comma_seek = ','; } @@ -431,9 +442,12 @@ static void parse_include_points(const char * const include_opt, } -static void parse_command_line (int argc, char* argv[], option *const options) -{ - char* float_text[num_float_options]; + +static void +parseCommandLine(int argc, const char * argv[], + option * const options) { + + const char* float_text[num_float_options]; unsigned int float_spec[num_float_options]; char* enum_text[num_enum_options]; unsigned int enum_spec[num_enum_options]; @@ -446,6 +460,8 @@ static void parse_command_line (int argc, char* argv[], option *const options) unsigned int option_def_index; optEntry* option_def; + set_command_line_defaults(options); + /* Let shhopt try its best */ option_def_index = 0; @@ -469,7 +485,7 @@ static void parse_command_line (int argc, char* argv[], option *const options) opt.opt_table = option_def; opt.short_allowed = FALSE; opt.allowNegNum = TRUE; - optParseOptions3 (&argc, argv, opt, sizeof(opt), 0); + optParseOptions3 (&argc, (char **)argv, opt, sizeof(opt), 0); /* The non-option arguments are optionally all eight coordinates and optionally the input filename @@ -502,7 +518,7 @@ static void parse_command_line (int argc, char* argv[], option *const options) for (i=0; i<num_float_options; i++) if (float_spec[i]) - options->floats[i] = parse_float (float_text[i]); + options->floats[i] = parseFloat (float_text[i]); /* Parse enum options -- shhopt retrieved them as strings */ @@ -620,7 +636,7 @@ static bool solve_3_linear_equations (number* x1, number* x2, number* x3, a11*x1 + a12*x2 + a13*x3 = b1 a21*x1 + a22*x2 + a23*x3 = b2 a31*x1 + a32*x2 + a33*x3 = b3 - The return value is wether the system is solvable + The return value is whether the system is solvable ----------------------------------------------------------------------------*/ { number c11,c12,d1,c21,c22,d2,e,f; @@ -715,18 +731,18 @@ static bool solve_3_linear_equations (number* x1, number* x2, number* x3, static void determine_world_parallelogram (world_data *const world, const option *const options) /*---------------------------------------------------------------------------- - constructs xw_ul,...,zw_lr from xi_ul,...,yi_lr + Construct xw_ul,...,zw_lr from xi_ul,...,yi_lr - Actually this is a solution of a linear equation system. + This is a solution of a linear equation system. - We first solve 4 variables (the 4 z-coordinates) against 4 - equations: Each z-coordinate determines the corresponding x- and - y-coordinates in a linear fashion, where the coefficients are taken - from the image coordinates. This corresponds to the fact that a - point of an image determines a line in the world. + We first solve 4 equations for 4 variables (the 4 z-coordinates): + Each z-coordinate determines the corresponding x- and y-coordinates + in a linear fashion, where the coefficients are taken from the image + coordinates. This corresponds to the fact that a point of an image + determines a line in the world. 3 equations state that the 4 points form a parallelogram. The 4th - equation is for normalization and states, that the center of the + equation is for normalization and states that the center of the parallelogram has a z-coordinate of 1. -----------------------------------------------------------------------------*/ { @@ -894,9 +910,11 @@ static void determine_world_parallelogram (world_data *const world, -static int diff (int const a, int const b) -{ - return MAX (b-a, a-b); +static unsigned int +distance(unsigned int const a, + unsigned int const b) { + + return a > b ? a - b : b - a; } @@ -1063,40 +1081,46 @@ static void determine_coefficients_pixel (world_data *const world, -static void outpixel_to_inpixel (int const xo, int const yo, - number* const xi, number* const yi, - const world_data *const world) -{ - number xof,yof,xw,yw,zw; - - xof = (number) xo; - yof = (number) yo; - xw = world->ax + world->bx*xof + world->cx*yof; - yw = world->ay + world->by*xof + world->cy*yof; - zw = world->az + world->bz*xof + world->cz*yof; - *xi = xw/zw; - *yi = yw/zw; +static void +outpixelToInPos(int const outCol, + int const outRow, + number * const inColP, + number * const inRowP, + const world_data * const worldP) { +/*---------------------------------------------------------------------------- + For a pixel of the output image at Column 'outCol', row 'outRow', + determine the position in the input image that corresponds to the + center of that pixel. + + This position is not a pixel position -- it's a position in + continuous space, for example Row 9.2, Column 0.1. And it isn't + necessarily within the input image, for example Column 600 even though + the input image is only 500 pixels wide, and a coordinate might even + be negative. +-----------------------------------------------------------------------------*/ + number const outColF = (number) outCol; + number const outRowF = (number) outRow; + + number const xw = worldP->ax + worldP->bx * outColF + worldP->cx * outRowF; + number const yw = worldP->ay + worldP->by * outColF + worldP->cy * outRowF; + number const zw = worldP->az + worldP->bz * outColF + worldP->cz * outRowF; + + *inColP = xw/zw; + *inRowP = yw/zw; } -static int outpixel_to_iny (int xo, int yo, const world_data *const world) -{ - number xi,yi; - outpixel_to_inpixel (xo,yo,&xi,&yi,world); - return (int) yi; -} +static int +outpixelToInRow(int const outCol, + int const outRow, + const world_data * const worldP) { -static int clean_y (int const y, const struct pam *const outpam) -{ - return MIN(MAX(0, y), outpam->height-1); -} + number xi, yi; -static unsigned int -distance(unsigned int const a, - unsigned int const b) { + outpixelToInPos(outCol, outRow, &xi, &yi, worldP); - return a > b ? a - b : b - a; + return (int) yi; } @@ -1110,6 +1134,71 @@ boundedRow(int const unboundedRow, +#if 0 +/* This is the original calculation of window height. It's + mysterious, and doesn't work. It looks like it basically wants to + take the greater of vertical displacement of the top edge of the + input quadrilateral and that of the bottom edge. In simple + scenarios, that is in fact what it does, and I can see how those + edges might be where the most stretching takes place. However, it + the calculation is obviously more complex than that. + + It doesn't work because the actual image generation produces rows + in the middle that are derived from lines in the input quadrilateral + with greater slope than either the top or bottom edge. I.e. to + compute one output row, it needs more rows of input than this + calculation provides. + + I don't know if that means the computation of the output is wrong + or the computation of the window height is wrong. The code is too + opaque. But just to make a viable computation, I replaced the + window height calculation with the brute force computation you + see below: it determines the vertical displacement of every line + of the input quadrilateral that is used to generate an output row + and takes the greatest of them for the window height. + + - Bryan Henderson 08.07.27. +*/ + + +static unsigned int +windowHeight(const world_data * const worldP, + const struct pam * const inpamP, + const struct pam * const outpamP, + const option * const optionsP) { + + unsigned int numRows; + int yul, yur, yll, ylr, y_min; + + yul = outpixelToInRow(0, 0, worldP); + yur = outpixelToInRow(outpamP->width-1, 0, worldP); + yll = outpixelToInRow(0, outpamP->height-1, worldP); + ylr = outpixelToInRow(outpamP->width-1, outpamP->height-1, worldP); + + y_min = MIN(MIN(yul, yur), MIN(yll, ylr)); + numRows = MAX(MAX(diff(yul, yur), + diff(yll, ylr)), + MAX(diff(boundedRow(yul, outpamP), + boundedRow(y_min, outpamP)), + diff(boundedRow(yur, outpamP), + boundedRow(y_min, outpamP)))) + + 2; + switch (optionsP->enums[3]) { /* --interpolation */ + case interp_nearest: + break; + case interp_linear: + numRows += 1; + break; + } + if (numRows > inpamP->height) + numRows = inpamP->height; + + return numRows; +} +#endif + + + static unsigned int windowHeight(const world_data * const worldP, const struct pam * const inpamP, @@ -1125,9 +1214,9 @@ windowHeight(const world_data * const worldP, unsigned int const leftCol = 0; unsigned int const rghtCol = outpamP->width - 1; unsigned int const leftInRow = - boundedRow(outpixel_to_iny(leftCol, outRow, worldP), outpamP); + boundedRow(outpixelToInRow(leftCol, outRow, worldP), outpamP); unsigned int const rghtInRow = - boundedRow(outpixel_to_iny(rghtCol, outRow, worldP), outpamP); + boundedRow(outpixelToInRow(rghtCol, outRow, worldP), outpamP); unsigned int const rowWindowHeight = distance(leftInRow, rghtInRow); @@ -1142,56 +1231,84 @@ windowHeight(const world_data * const worldP, static void -init_buffer(buffer * const bufferP, +buffer_init(buffer * const bufferP, const world_data * const worldP, const option * const optionsP, const struct pam * const inpamP, const struct pam * const outpamP) { - unsigned int const num_rows = + unsigned int const numRows = windowHeight(worldP, inpamP, outpamP, optionsP); - MALLOCARRAY_SAFE(bufferP->rows, num_rows); - bufferP->num_rows = num_rows; - { - unsigned int row; - for (row = 0; row < num_rows; ++row) { - bufferP->rows[row] = pnm_allocpamrow(inpamP); - pnm_readpamrow(inpamP, bufferP->rows[row]); - } + unsigned int row; + + MALLOCARRAY_SAFE(bufferP->rows, numRows); + + for (row = 0; row < numRows; ++row) { + bufferP->rows[row] = pnm_allocpamrow(inpamP); + pnm_readpamrow(inpamP, bufferP->rows[row]); } - bufferP->last_logical = num_rows-1; - bufferP->last_physical = num_rows-1; + + bufferP->nextImageRow = numRows; + bufferP->nextBufferRow = 0; + bufferP->numRows = numRows; + bufferP->inpamP = inpamP; } -static tuple * -read_buffer(buffer * const bufferP, - unsigned int const logical_y) { +static const tuple * +buffer_getRow(buffer * const bufferP, + unsigned int const imageRow) { +/*---------------------------------------------------------------------------- + Return row 'imageRow' of an image. - int y; - - while (logical_y > bufferP->last_logical) { - ++bufferP->last_physical; - if (bufferP->last_physical == bufferP->num_rows) - bufferP->last_physical = 0; - pnm_readpamrow(bufferP->inpamP, bufferP->rows[bufferP->last_physical]); - ++bufferP->last_logical; + The return value is a pointer into storage that belongs to *bufferP. + + *bufferP remembers only a window of the image, and the window + cannot move up, so 'imageRow' cannot be higher in the image than + the lowest row read so far through *bufferP plus *bufferP's maximum + window height. We assume that. +-----------------------------------------------------------------------------*/ + unsigned int bufferRow; + /* The row of the buffer that holds row 'imageRow' of the image */ + unsigned int n; + /* Number of rows our row is before the bottom of the window */ + + assert(imageRow >= bufferP->nextImageRow - bufferP->numRows); + /* The requested row is not one that's already been bumped out + of the buffer. + */ + + while (imageRow >= bufferP->nextImageRow) { + pnm_readpamrow(bufferP->inpamP, bufferP->rows[bufferP->nextBufferRow]); + + ++bufferP->nextBufferRow; + if (bufferP->nextBufferRow == bufferP->numRows) + bufferP->nextBufferRow = 0; + + ++bufferP->nextImageRow; } + + n = bufferP->nextImageRow - imageRow; + + assert(n <= bufferP->numRows); - y = logical_y - bufferP->last_logical + bufferP->last_physical; - if (y < 0) - y += bufferP->num_rows; + if (n <= bufferP->nextBufferRow) + bufferRow = bufferP->nextBufferRow - n; + else + bufferRow = bufferP->nextBufferRow + bufferP->numRows - n; - return bufferP->rows[y]; + assert(bufferRow < bufferP->numRows); + + return bufferP->rows[bufferRow]; } static void -term_buffer(buffer * const bufferP) { +buffer_term(buffer * const bufferP) { unsigned int i; @@ -1201,12 +1318,12 @@ term_buffer(buffer * const bufferP) { through. */ - while (bufferP->last_logical < bufferP->inpamP->height-1) { + while (bufferP->nextImageRow < bufferP->inpamP->height) { pnm_readpamrow(bufferP->inpamP, bufferP->rows[0]); - ++bufferP->last_logical; + ++bufferP->nextImageRow; } - for (i = 0; i < bufferP->num_rows; ++i) + for (i = 0; i < bufferP->numRows; ++i) pnm_freepamrow(bufferP->rows[i]); free(bufferP->rows); @@ -1215,91 +1332,110 @@ term_buffer(buffer * const bufferP) { -/* The following variables are global for speed reasons. - In this way they do not have to be passed to each call of the +struct interpContext { + tuple background; + buffer* indata; + int width,height,depth; +}; + +/* The following is global for speed reasons. + In this way it does not have to be passed to each call of the interpolation functions Think of this as Schönfinkeling (aka Currying). */ -static tuple background; -static buffer* indata; -static int width,height,depth; +static struct interpContext ictx; -static void init_interpolation_global_vars (buffer* const inbuffer, - const struct pam *const inpam, - const struct pam *const outpam) -{ - pnm_createBlackTuple (outpam, &background); - indata = inbuffer; - width = inpam->width; - height = inpam->height; - depth = outpam->depth; +static void +init_interpolation_global_vars(buffer * const inbufferP, + const struct pam * const inpamP, + const struct pam * const outpamP) { + + pnm_createBlackTuple(outpamP, &ictx.background); + ictx.indata = inbufferP; + ictx.width = inpamP->width; + ictx.height = inpamP->height; + ictx.depth = outpamP->depth; } -static void clean_interpolation_global_vars (void) -{ - free (background); +static void +clean_interpolation_global_vars(void) { + + free(ictx.background); } /* These functions perform the interpolation */ -static tuple attempt_read (int const x, int const y) -{ - if ((x<0) || (x>=width) || (y<0) || (y>=height)) - return background; - else - return read_buffer(indata, y)[x]; +static tuple +getPixel(int const col, + int const row) { +/*---------------------------------------------------------------------------- + Get the pixel at Row 'row', Column 'col' of the image which is the + context of the interpolation in which we are called. + + Consider the image to go on forever in all directions (even negative + column/row numbers), being the background color everywhere outside + the actual image. +-----------------------------------------------------------------------------*/ + if ((col < 0) || (col >= ictx.width) || (row < 0) || (row >= ictx.height)) + return ictx.background; + else + return buffer_getRow(ictx.indata, row)[col]; } -static void take_nearest (tuple const dest, number const x, number const y) -{ - int xx,yy,entry; - tuple p; - - xx = (int)floor(x+0.5); - yy = (int)floor(y+0.5); - p = attempt_read (xx, yy); - for (entry=0; entry<depth; entry++) { - dest[entry]=p[entry]; - } +static void +takeNearest(tuple const dest, + number const x, + number const y) { + + int const xx = (int)floor(x+0.5); + int const yy = (int)floor(y+0.5); + tuple const p = getPixel(xx, yy); + + unsigned int entry; + + for (entry = 0; entry < ictx.depth; ++entry) { + dest[entry] = p[entry]; + } } -static void linear_interpolation (tuple const dest, - number const x, number const y) -{ - int xx,yy,entry; - number xf,yf,a,b,c,d; - tuple p1,p2,p3,p4; - - xx = (int)floor(x); - yy = (int)floor(y); - xf = x-(number)xx; - yf = y-(number)yy; - p1 = attempt_read (xx, yy); - p2 = attempt_read (xx+1, yy); - p3 = attempt_read (xx, yy+1); - p4 = attempt_read (xx+1, yy+1); - a = (1.0-xf)*(1.0-yf); - b = xf*(1.0-yf); - c = (1.0-xf)*yf; - d = xf*yf; - for (entry=0; entry<depth; entry++) { - dest[entry]=(sample) floor( - a*((number) p1[entry]) + - b*((number) p2[entry]) + - c*((number) p3[entry]) + - d*((number) p4[entry]) + - 0.5); - } +static void +linearInterpolation(tuple const dest, + number const x, + number const y) { + + int const xx = (int)floor(x); + int const yy = (int)floor(y); + number const xf = x - (number)xx; + number const yf = y - (number)yy; + tuple const p1 = getPixel(xx, yy); + tuple const p2 = getPixel(xx+1, yy); + tuple const p3 = getPixel(xx, yy+1); + tuple const p4 = getPixel(xx+1, yy+1); + number const a = (1.0-xf) * (1.0-yf); + number const b = xf * (1.0-yf); + number const c = (1.0-xf) * yf; + number const d = xf * yf; + + unsigned int entry; + + for (entry=0; entry < ictx.depth; ++entry) { + dest[entry] = floor( + a * (number) p1[entry] + + b * (number) p2[entry] + + c * (number) p3[entry] + + d * (number) p4[entry] + + 0.5); + } } @@ -1318,8 +1454,8 @@ perspective(struct pam * const outpamP, unsigned int col; for (col = 0; col < outpamP->width; ++col) { - number xi,yi; - outpixel_to_inpixel(col, row, &xi, &yi, worldP); + number xi, yi; + outpixelToInPos(col, row, &xi, &yi, worldP); interpolater(outrow[col], xi, yi); } pnm_writepamrow(outpamP, outrow); @@ -1330,7 +1466,7 @@ perspective(struct pam * const outpamP, int -main(int argc, char* argv[]) { +main(int argc, const char * argv[]) { FILE * ifP; struct pam inpam; @@ -1340,11 +1476,9 @@ main(int argc, char* argv[]) { world_data world; interpolateFn * interpolater; - pnm_init(&argc, argv); - - set_command_line_defaults(&options); + pm_proginit(&argc, argv); - parse_command_line(argc, argv, &options); + parseCommandLine(argc, argv, &options); ifP = pm_openr(options.infilename); @@ -1378,21 +1512,21 @@ main(int argc, char* argv[]) { /* Initialize the actual calculation */ - init_buffer(&inbuffer, &world, &options, &inpam, &outpam); + buffer_init(&inbuffer, &world, &options, &inpam, &outpam); init_interpolation_global_vars(&inbuffer, &inpam, &outpam); switch (options.enums[3]) { /* --interpolation */ case interp_nearest: - interpolater = take_nearest; + interpolater = takeNearest; break; case interp_linear: - interpolater = linear_interpolation; + interpolater = linearInterpolation; break; }; perspective(&outpam, &world, interpolater); clean_interpolation_global_vars(); - term_buffer(&inbuffer); + buffer_term(&inbuffer); free_option(&options); pm_close(ifP); pm_close(stdout); diff --git a/editor/pnmcat.c b/editor/pnmcat.c index cc86520f..5ede0bbc 100644 --- a/editor/pnmcat.c +++ b/editor/pnmcat.c @@ -1,4 +1,4 @@ -/* pnmcat.c - concatenate portable anymaps +/* pnmcat.c - concatenate PNM images ** ** Copyright (C) 1989, 1991 by Jef Poskanzer. ** @@ -10,22 +10,40 @@ ** implied warranty. */ -#include "pnm.h" +#include <assert.h> + #include "mallocvar.h" #include "shhopt.h" +#include "bitarith.h" +#include "pnm.h" +#define LEFTBITS pm_byteLeftBits +#define RIGHTBITS pm_byteRightBits -enum backcolor {BACK_BLACK, BACK_WHITE, BACK_AUTO}; +enum backcolor {BACK_WHITE, BACK_BLACK, BACK_AUTO}; enum orientation {TOPBOTTOM, LEFTRIGHT}; enum justification {JUST_CENTER, JUST_MIN, JUST_MAX}; +struct imgInfo { + /* This obviously should be a struct pam. We should convert this + to 'pamcat'. + */ + FILE * ifP; + int cols; + int rows; + int format; + xelval maxval; +}; + + + struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char **inputFilespec; + const char ** inputFilespec; unsigned int nfiles; enum backcolor backcolor; enum orientation orientation; @@ -35,22 +53,24 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** const argv, +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 = malloc(100*sizeof(optEntry)); - /* Instructions to OptParseOptions2 on how to parse our options. + 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); @@ -68,7 +88,7 @@ parseCommandLine(int argc, char ** const argv, 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); + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (leftright + topbottom > 1) @@ -134,24 +154,21 @@ parseCommandLine(int argc, char ** const argv, unsigned int i; MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, argc-1); - + for (i = 0; i < argc-1; ++i) cmdlineP->inputFilespec[i] = argv[1+i]; cmdlineP->nfiles = argc-1; } -} +} static void computeOutputParms(unsigned int const nfiles, enum orientation const orientation, - int cols[], - int rows[], - xelval maxval[], - int format[], - int * const newcolsP, - int * const newrowsP, + struct imgInfo const img[], + unsigned int * const newcolsP, + unsigned int * const newrowsP, xelval * const newmaxvalP, int * const newformatP) { @@ -164,29 +181,31 @@ computeOutputParms(unsigned int const nfiles, newcols = 0; newrows = 0; - for (i = 0; i < nfiles; ++i) { + for (i = 0; i < nfiles; ++i) { + const struct imgInfo * const imgP = &img[i]; + if (i == 0) { - newmaxval = maxval[i]; - newformat = format[i]; - } else { - if (PNM_FORMAT_TYPE(format[i]) > PNM_FORMAT_TYPE(newformat)) - newformat = format[i]; - if (maxval[i] > newmaxval) - newmaxval = maxval[i]; - } + 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 += cols[i]; - if (rows[i] > newrows) - newrows = rows[i]; + newcols += imgP->cols; + if (imgP->rows > newrows) + newrows = imgP->rows; break; case TOPBOTTOM: - newrows += rows[i]; - if (cols[i] > newcols) - newcols = cols[i]; + 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 @@ -196,9 +215,9 @@ computeOutputParms(unsigned int const nfiles, pm_error("Output width too large: %.0f.", newcols); if (newrows > INT_MAX) pm_error("Output height too large: %.0f.", newrows); - - *newrowsP = (int) newrows; - *newcolsP = (int) newcols; + + *newrowsP = (unsigned int)newrows; + *newcolsP = (unsigned int)newcols; *newmaxvalP = newmaxval; *newformatP = newformat; } @@ -206,231 +225,631 @@ computeOutputParms(unsigned int const nfiles, static void -concatenateLeftRight(FILE * const ofp, - unsigned int const nfiles, - int const newcols, - int const newrows, - xelval const newmaxval, - int const newformat, - enum justification const justification, - FILE * ifp[], - int cols[], - int rows[], - xelval maxval[], - int format[], - xel * xelrow[], - xel background[]) { +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(). +*/ + + +struct imgInfoPbm2 { + /* 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 */ +}; + + + +static void +getPbmImageInfo(struct imgInfo const img[], + unsigned int const nfiles, + unsigned int const newrows, + enum justification const justification, + enum backcolor const backcolor, + struct imgInfoPbm2 ** const img2P) { +/*---------------------------------------------------------------------------- + Read the first row of each image in img[] and return that and additional + information about images as *img2P. +-----------------------------------------------------------------------------*/ + struct 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(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(struct 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, + struct 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 + exeption of following img2.proberow) goes directly into this + packed PBM row. + */ + + struct imgInfoPbm2 * img2; + /* malloc'ed array, one element per image. Shadows img[] */ unsigned int row; - - xel * const newxelrow = pnm_allocrow(newcols); + + getPbmImageInfo(img, nfiles, newrows, justification, backcolor, &img2); for (row = 0; row < newrows; ++row) { - unsigned int new; unsigned int i; - new = 0; for (i = 0; i < nfiles; ++i) { - int padtop; + 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, + struct 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[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; + } + free(outrow); +} + + + +struct imgGen2 { + xel * xelrow; + xel * inrow; + xel background; + int padtop; +}; + + + +static void +getGenImgInfo(struct 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, + struct imgGen2 ** const img2P) { + + struct imgGen2 * img2; + unsigned int i; + + MALLOCARRAY_NOFAIL(img2, nfiles); + + for (i = 0; i < nfiles; ++i) { + switch (justification) { /* Determine top padding */ case JUST_MIN: - padtop = 0; + img2[i].padtop = 0; break; case JUST_MAX: - padtop = newrows - rows[i]; + img2[i].padtop = newrows - img[i].rows; break; case JUST_CENTER: - padtop = ( newrows - rows[i] ) / 2; + 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, + struct imgInfo const img[], + enum backcolor const backcolor) { + + xel * const outrow = pnm_allocrow(newcols); + struct 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 < padtop || row >= padtop + rows[i]) { + 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 < cols[i]; ++col) - newxelrow[new+col] = background[i]; + 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 { - if (row != padtop) { - /* first row already read */ - pnm_readpnmrow( - ifp[i], xelrow[i], cols[i], maxval[i], format[i] ); - pnm_promoteformatrow( - xelrow[i], cols[i], maxval[i], format[i], - newmaxval, newformat ); - } - { - unsigned int col; - for (col = 0; col < cols[i]; ++col) - newxelrow[new+col] = xelrow[i][col]; - } + /* It's a row of padding, so outrow[] is already set + appropriately. + */ } - new += cols[i]; } - pnm_writepnmrow(ofp, newxelrow, newcols, newmaxval, newformat, 0); + pnm_writepnmrow(ofP, outrow, newcols, newmaxval, newformat, 0); } + pnm_freerow(outrow); } static void -concatenateTopBottom(FILE * const ofp, - unsigned int const nfiles, - int const newcols, - int const newrows, - xelval const newmaxval, - int const newformat, - enum justification const justification, - FILE * ifp[], - int cols[], - int rows[], - xelval maxval[], - int format[], - xel * xelrow[], - xel background[]) { - - int new; +concatenateTopBottomGen(FILE * const ofP, + unsigned int const nfiles, + int const newcols, + int const newrows, + xelval const newmaxval, + int const newformat, + enum justification const justification, + struct imgInfo const img[], + enum backcolor const backcolor) { + xel * const newxelrow = pnm_allocrow(newcols); - int padleft; + xel * inrow; + unsigned int padleft; unsigned int i; - unsigned int row; - - i = 0; - switch (justification) { - case JUST_MIN: - padleft = 0; - break; - case JUST_MAX: - padleft = newcols - cols[i]; - break; - case JUST_CENTER: - padleft = (newcols - cols[i]) / 2; - break; + 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; } - new = 0; - - for (row = 0; row < newrows; ++row) { - if (row - new >= rows[i]) { - new += rows[i]; - ++i; - if (i >= nfiles) - pm_error("INTERNAL ERROR: i > nfiles"); + 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 - cols[i]; - break; - case JUST_CENTER: - padleft = (newcols - cols[i]) / 2; - break; + 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 (row - new > 0) { - pnm_readpnmrow( - ifp[i], xelrow[i], cols[i], maxval[i], format[i]); + + 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( - xelrow[i], cols[i], maxval[i], format[i], + inrow, img[i].cols, img[i].maxval, img[i].format, newmaxval, newformat); + + pnm_writepnmrow(ofP, newxelrow, newcols, newmaxval, newformat, 0); } - { - unsigned int col; - - for (col = 0; col < padleft; ++col) - newxelrow[col] = background[i]; - for (col = 0; col < cols[i]; ++col) - newxelrow[padleft+col] = xelrow[i][col]; - for (col = padleft + cols[i]; col < newcols; ++col) - newxelrow[col] = background[i]; - } - pnm_writepnmrow(ofp, - newxelrow, newcols, newmaxval, newformat, 0); - } + } + pnm_freerow(newxelrow); } int -main(int argc, char ** argv) { +main(int argc, + const char ** argv) { struct cmdlineInfo cmdline; - FILE** ifp; - xel** xelrow; - xel* background; - xelval* maxval; + struct imgInfo * img; /* malloc'ed array */ xelval newmaxval; - int* rows; - int* cols; - int* format; int newformat; unsigned int i; - int newrows, newcols; + unsigned int newrows, newcols; - pnm_init( &argc, argv ); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - - MALLOCARRAY_NOFAIL(ifp, cmdline.nfiles); - MALLOCARRAY_NOFAIL(xelrow, cmdline.nfiles); - MALLOCARRAY_NOFAIL(background, cmdline.nfiles); - MALLOCARRAY_NOFAIL(maxval, cmdline.nfiles); - MALLOCARRAY_NOFAIL(rows, cmdline.nfiles); - MALLOCARRAY_NOFAIL(cols, cmdline.nfiles); - MALLOCARRAY_NOFAIL(format, cmdline.nfiles); + + MALLOCARRAY_NOFAIL(img, cmdline.nfiles); for (i = 0; i < cmdline.nfiles; ++i) { - ifp[i] = pm_openr(cmdline.inputFilespec[i]); - pnm_readpnminit(ifp[i], &cols[i], &rows[i], &maxval[i], &format[i]); - xelrow[i] = pnm_allocrow(cols[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, - cols, rows, maxval, format, + computeOutputParms(cmdline.nfiles, cmdline.orientation, img, &newcols, &newrows, &newmaxval, &newformat); - for (i = 0; i < cmdline.nfiles; ++i) { - /* Read first row just to get a good guess at the background. */ - pnm_readpnmrow(ifp[i], xelrow[i], cols[i], maxval[i], format[i]); - pnm_promoteformatrow( - xelrow[i], cols[i], maxval[i], format[i], newmaxval, newformat); - switch (cmdline.backcolor) { - case BACK_AUTO: - background[i] = - pnm_backgroundxelrow( - xelrow[i], cols[i], 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; - case BACK_BLACK: - background[i] = pnm_blackxel(newmaxval, newformat); + } + } else { + switch (cmdline.orientation) { + case LEFTRIGHT: + concatenateLeftRightGen(stdout, cmdline.nfiles, + newcols, newrows, newmaxval, newformat, + cmdline.justification, img, + cmdline.backcolor); break; - case BACK_WHITE: - background[i] = pnm_whitexel(newmaxval, newformat); + case TOPBOTTOM: + concatenateTopBottomGen(stdout, cmdline.nfiles, + newcols, newrows, newmaxval, newformat, + cmdline.justification, img, + cmdline.backcolor); break; } - } - - pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0); - - switch (cmdline.orientation) { - case LEFTRIGHT: - concatenateLeftRight(stdout, cmdline.nfiles, - newcols, newrows, newmaxval, newformat, - cmdline.justification, - ifp, cols, rows, maxval, format, xelrow, - background); - break; - case TOPBOTTOM: - concatenateTopBottom(stdout, cmdline.nfiles, - newcols, newrows, newmaxval, newformat, - cmdline.justification, - ifp, cols, rows, maxval, format, xelrow, - background); - break; } - free(cmdline.inputFilespec); - for (i = 0; i < cmdline.nfiles; ++i) - pm_close(ifp[i]); - + pm_close(img[i].ifP); + free(cmdline.inputFilespec); pm_close(stdout); return 0; diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c index 114e3a3d..a98c6f60 100644 --- a/editor/pnmcrop.c +++ b/editor/pnmcrop.c @@ -255,7 +255,9 @@ findBordersInImage(FILE * const ifP, int row; bool gottop; int left, right, bottom, top; - /* leftmost, etc. nonbackground pixel found so far; -1 for none */ + /* leftmost, etc. nonbackground pixel found so far. + Can be just off the edge of the image, so -1 or 'cols' or 'rows' + */ xelrow = pnm_allocrow(cols); diff --git a/editor/pnmmontage.c b/editor/pnmmontage.c index 7c43a082..a7b74510 100644 --- a/editor/pnmmontage.c +++ b/editor/pnmmontage.c @@ -20,11 +20,125 @@ #include "shhopt.h" #include "pam.h" -typedef struct { int f[sizeof(int) * 8 + 1]; } factorset; -typedef struct { int x; int y; } coord; -static int qfactor; -static int quality = 5; + +struct cmdlineInfo { + const char * header; + const char * data; + const char * prefix; + unsigned int quality; + unsigned int quality2; + unsigned int nFiles; + const char ** inFileName; +}; + + + +static void +parseCommandLine(int argc, const 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 dataSpec, headerSpec, prefixSpec, qualitySpec; + unsigned int option_def_index; + unsigned int i; + unsigned int q[10]; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3( 0, "data", OPT_STRING, &cmdlineP->data, &dataSpec, 0); + OPTENT3( 0, "header", OPT_STRING, &cmdlineP->header, &headerSpec, 0); + OPTENT3('q', "quality", OPT_UINT, &cmdlineP->quality, &qualitySpec, 0); + OPTENT3('p', "prefix", OPT_STRING, &cmdlineP->prefix, &prefixSpec, 0); + OPTENT3('0', "0", OPT_FLAG, NULL, &q[0], 0); + OPTENT3('1', "1", OPT_FLAG, NULL, &q[1], 0); + OPTENT3('2', "2", OPT_FLAG, NULL, &q[2], 0); + OPTENT3('3', "3", OPT_FLAG, NULL, &q[3], 0); + OPTENT3('4', "4", OPT_FLAG, NULL, &q[4], 0); + OPTENT3('5', "5", OPT_FLAG, NULL, &q[5], 0); + OPTENT3('6', "6", OPT_FLAG, NULL, &q[6], 0); + OPTENT3('7', "7", OPT_FLAG, NULL, &q[7], 0); + OPTENT3('8', "8", OPT_FLAG, NULL, &q[8], 0); + OPTENT3('9', "9", OPT_FLAG, NULL, &q[9], 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; + opt.allowNegNum = FALSE; + + optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0); + + if (!dataSpec) + cmdlineP->data = NULL; + if (!headerSpec) + cmdlineP->header = NULL; + if (!prefixSpec) + cmdlineP->prefix = ""; + if (!qualitySpec) + cmdlineP->quality = 200; + + + /* cmdlineP->quality2 is the greatest number from the --1, --2, etc. + options, or 5 if none of those are specified. + */ + cmdlineP->quality2 = 5; /* initial value */ + for (i = 0; i < 10; ++i) { + if (q[i]) + cmdlineP->quality2 = i; + } + + cmdlineP->nFiles = argc-1; + + MALLOCARRAY_NOFAIL(cmdlineP->inFileName, argc-1); + + for (i = 0; i < argc-1; ++i) { + if (cmdlineP->data && strchr(argv[i+1], ':')) + pm_error("Filename '%s' contains a \":\", which is forbidden " + "with -data", argv[i+1]); + else + cmdlineP->inFileName[i] = strdup(argv[1+1]); + } +} + + + +typedef struct { + int f[sizeof(int) * 8 + 1]; +} factorset; + +typedef struct { + int x; int y; +} coord; + +typedef struct { + coord ul; + coord size; +} rectangle; + +static coord +lr(rectangle const r) { +/*---------------------------------------------------------------------------- + Return the coordinates of the lower right corner of 'r' + (i.e. the pixel just beyond the lowest rightmost one). +-----------------------------------------------------------------------------*/ + coord retval; + + retval.x = r.ul.x + r.size.x; + retval.y = r.ul.y + r.size.y; + + return retval; +} static factorset factor(int n) @@ -68,69 +182,87 @@ gcd(int n, int m) static bool -collides(const coord * const locs, - const coord * const szs, - const coord * const cloc, - const coord * const csz, - unsigned int const n) { +overlaps(rectangle const a, + rectangle const b) { + + return + (a.ul.x < lr(b).x && a.ul.y < lr(b).y) && + (lr(a).x > b.ul.x && lr(a).y > b.ul.y); +} + + +static bool +collides(rectangle const test, + const rectangle * const fieldList, + unsigned int const n) { +/*---------------------------------------------------------------------------- + Return true iff the rectangle 'test' overlaps any of the 'n' rectangles + fieldList[]. +-----------------------------------------------------------------------------*/ unsigned int i; - for (i = 0; i < n; ++i) { - if ((locs[i].x < cloc->x + csz->x) && - (locs[i].y < cloc->y + csz->y) && - (locs[i].x + szs[i].x > cloc->x) && - (locs[i].y + szs[i].y > cloc->y)) + for (i = 0; i < n; ++i) + if (overlaps(fieldList[i], test)) return true; - } + return false; } static void -recursefindpack(coord * const current, +recursefindpack(rectangle * const current, coord const currentsz, - coord * const set, coord * const best, unsigned int const minarea, unsigned int * const maxareaP, unsigned int const depth, unsigned int const n, unsigned int const xinc, - unsigned int const yinc) { + unsigned int const yinc, + unsigned int const quality, + unsigned int const qfactor) { if (depth == n) { if (currentsz.x * currentsz.y < *maxareaP) { unsigned int i; for (i = 0; i < n; ++i) - best[i] = current[i]; + best[i] = current[i].ul; *maxareaP = currentsz.x * currentsz.y; } } else { unsigned int i; + rectangle * const newP = ¤t[depth]; + for (i = 0; ; ++i) { - for (current[depth].x = 0, current[depth].y = i * yinc; - current[depth].y <= i * yinc;) { + for (newP->ul.x = 0, newP->ul.y = i * yinc; + newP->ul.y <= i * yinc;) { coord c; - c.x = MAX(current[depth].x + set[depth].x, currentsz.x); - c.y = MAX(current[depth].y + set[depth].y, currentsz.y); - if (!collides(current, set, ¤t[depth], - &set[depth], depth)) { - recursefindpack(current, c, set, best, minarea, maxareaP, - depth + 1, n, xinc, yinc); + c.x = MAX(lr(*newP).x, currentsz.x); + c.y = MAX(lr(*newP).y, currentsz.y); + pm_message("current = (%u.%u, %u.%u) new = (%u.%u, %u.%u)", + current[0].ul.x, current[0].size.x, + current[0].ul.y, current[0].size.y, + newP->ul.x, newP->size.x, + newP->ul.y, newP->size.y); + if (!collides(*newP, current, depth)) { + pm_message("Depth %u: Doesn't collide at i=%u", depth,i); + recursefindpack(current, c, best, minarea, maxareaP, + depth + 1, n, xinc, yinc, + quality, qfactor); if (*maxareaP <= minarea) return; } - if (current[depth].x == (i - 1) * xinc) - current[depth].y = 0; - if (current[depth].x < i * xinc) - current[depth].x += xinc; + if (newP->ul.x == (i - 1) * xinc) + newP->ul.y = 0; + if (newP->ul.x < i * xinc) + newP->ul.x += xinc; else - current[depth].y += yinc; + newP->ul.y += yinc; } } } @@ -139,52 +271,59 @@ recursefindpack(coord * const current, static void -findpack(struct pam *imgs, int n, coord *coords) -{ - int minarea; - int i; - int rdiv; - int cdiv; - int minx = -1; - int miny = -1; - coord *current; - coord *set; - unsigned int z = UINT_MAX; - coord c = { 0, 0 }; - - if (quality > 1) - { - for (minarea = i = 0; i < n; ++i) - minarea += imgs[i].height * imgs[i].width, - minx = MAX(minx, imgs[i].width), - miny = MAX(miny, imgs[i].height); +findpack(struct pam * const imgs, + unsigned int const n, + coord * const coords, + unsigned int const quality, + unsigned int const qfactor) { - minarea = minarea * qfactor / 100; - } - else - { - minarea = INT_MAX - 1; - } + int minarea; + int i; + int rdiv; + int cdiv; + int minx; + int miny; + rectangle * current; + unsigned int z; + coord c; + + minx = -1; miny = -1; /* initial value */ + z = UINT_MAX; /* initial value */ + c.x = 0; c.y = 0; /* initial value */ + + if (quality > 1) { + unsigned int realMinarea; + for (realMinarea = i = 0; i < n; ++i) + realMinarea += imgs[i].height * imgs[i].width, + minx = MAX(minx, imgs[i].width), + miny = MAX(miny, imgs[i].height); + + minarea = realMinarea * qfactor / 100; + } else { + minarea = INT_MAX - 1; + } + + /* It's relatively easy to show that, if all the images + * are multiples of a particular size, then a best + * packing will always align the images on a grid of + * that size. + * + * This speeds computation immensely. + */ + for (rdiv = imgs[0].height, i = 1; i < n; ++i) + rdiv = gcd(imgs[i].height, rdiv); + + for (cdiv = imgs[0].width, i = 1; i < n; ++i) + cdiv = gcd(imgs[i].width, cdiv); - /* It's relatively easy to show that, if all the images - * are multiples of a particular size, then a best - * packing will always align the images on a grid of - * that size. - * - * This speeds computation immensely. - */ - for (rdiv = imgs[0].height, i = 1; i < n; ++i) - rdiv = gcd(imgs[i].height, rdiv); - - for (cdiv = imgs[0].width, i = 1; i < n; ++i) - cdiv = gcd(imgs[i].width, cdiv); - - MALLOCARRAY(current, n); - MALLOCARRAY(set, n); - for (i = 0; i < n; ++i) - set[i].x = imgs[i].width, - set[i].y = imgs[i].height; - recursefindpack(current, c, set, coords, minarea, &z, 0, n, cdiv, rdiv); + MALLOCARRAY(current, n); + + for (i = 0; i < n; ++i) { + current[i].size.x = imgs[i].width; + current[i].size.y = imgs[i].height; + } + recursefindpack(current, c, coords, minarea, &z, 0, n, cdiv, rdiv, + quality, qfactor); } @@ -419,159 +558,101 @@ computeOutputDimensions(int * const widthP, -struct cmdlineInfo { - const char * header; - const char * data; - const char * prefix; - unsigned int quality; - unsigned int q[10]; -}; - - - int main(int argc, const char **argv) { - struct cmdlineInfo cmdline; - struct pam *imgs; - struct pam outimg; - int nfiles; - coord *coords; - FILE *header; - FILE *data; - const char **names; - unsigned int i; - - optEntry * option_def; - /* Instructions to OptParseOptions3 on how to parse our options. - */ - optStruct3 opt; - unsigned int dataSpec, headerSpec, prefixSpec, qualitySpec; - unsigned int option_def_index; - - pm_proginit(&argc, argv); - - MALLOCARRAY_NOFAIL(option_def, 100); - - option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3( 0, "data", OPT_STRING, &cmdline.data, &dataSpec, 0); - OPTENT3( 0, "header", OPT_STRING, &cmdline.header, &headerSpec, 0); - OPTENT3('q', "quality", OPT_UINT, &cmdline.quality, &qualitySpec, 0); - OPTENT3('p', "prefix", OPT_STRING, &cmdline.prefix, &prefixSpec, 0); - OPTENT3('0', "0", OPT_FLAG, NULL, &cmdline.q[0], 0); - OPTENT3('1', "1", OPT_FLAG, NULL, &cmdline.q[1], 0); - OPTENT3('2', "2", OPT_FLAG, NULL, &cmdline.q[2], 0); - OPTENT3('3', "3", OPT_FLAG, NULL, &cmdline.q[3], 0); - OPTENT3('4', "4", OPT_FLAG, NULL, &cmdline.q[4], 0); - OPTENT3('5', "5", OPT_FLAG, NULL, &cmdline.q[5], 0); - OPTENT3('6', "6", OPT_FLAG, NULL, &cmdline.q[6], 0); - OPTENT3('7', "7", OPT_FLAG, NULL, &cmdline.q[7], 0); - OPTENT3('8', "8", OPT_FLAG, NULL, &cmdline.q[8], 0); - OPTENT3('9', "9", OPT_FLAG, NULL, &cmdline.q[9], 0); - - opt.opt_table = option_def; - opt.short_allowed = FALSE; - opt.allowNegNum = FALSE; - - /* Check for flags. */ - optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0); - - if (!dataSpec) - cmdline.data = NULL; - if (!headerSpec) - cmdline.header = NULL; - if (!prefixSpec) - cmdline.prefix = ""; - if (!qualitySpec) - cmdline.quality = 200; - - header = cmdline.header ? pm_openw(cmdline.header) : NULL; - data = cmdline.data ? pm_openw(cmdline.data) : NULL; - qfactor = cmdline.quality; - - for (i = 0; i < 10; ++i) - { - if (cmdline.q[i]) - { - quality = i; - switch (quality) - { - case 0: case 1: break; - case 2: case 3: case 4: case 5: case 6: - qfactor = 100 * (8 - quality); - break; - case 7: qfactor = 150; break; - case 8: qfactor = 125; break; - case 9: qfactor = 100; break; - } + + struct cmdlineInfo cmdline; + struct pam * imgs; + struct pam outimg; + unsigned int nfiles; + coord * coords; + FILE * header; + FILE * data; + const char ** names; + unsigned int i; + unsigned int qfactor; /* In per cent */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + header = cmdline.header ? pm_openw(cmdline.header) : NULL; + data = cmdline.data ? pm_openw(cmdline.data) : NULL; + + switch (cmdline.quality2) { + case 0: case 1: + qfactor = cmdline.quality; + break; + case 2: case 3: case 4: case 5: case 6: + qfactor = 100 * (8 - cmdline.quality2); + break; + case 7: qfactor = 150; break; + case 8: qfactor = 125; break; + case 9: qfactor = 100; break; + default: pm_error("Internal error - impossible value of 'quality2': %u", + cmdline.quality2); } - } - if (1 < argc) - nfiles = argc - 1; - else - nfiles = 1; + nfiles = cmdline.nFiles > 0 ? cmdline.nFiles : 1; - MALLOCARRAY(imgs, nfiles); - MALLOCARRAY(coords, nfiles); - MALLOCARRAY(names, nfiles); + MALLOCARRAY(imgs, nfiles); + MALLOCARRAY(coords, nfiles); + MALLOCARRAY(names, nfiles); - if (!imgs || !coords || !names) - pm_error("out of memory"); + if (!imgs || !coords || !names) + pm_error("out of memory"); - if (1 < argc) - { - for (i = 0; i < nfiles; ++i) - { - if (cmdline.data && strchr(argv[i+1], ':')) - pm_error("filenames containing \":\" (%s) are forbidden when -data is specified", - argv[i+1]); - imgs[i].file = pm_openr(argv[i+1]); - names[i] = argv[i+1]; + if (cmdline.nFiles > 0) { + unsigned int i; + + for (i = 0; i < cmdline.nFiles; ++i) { + imgs[i].file = pm_openr(cmdline.inFileName[i]); + names[i] = strdup(cmdline.inFileName[i]); + } + } else { + imgs[0].file = stdin; + names[0] = strdup("stdin"); } - } - else - { - imgs[0].file = stdin; - } - for (i = 0; i < nfiles; ++i) - pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type)); + for (i = 0; i < nfiles; ++i) + pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type)); - sortImagesByArea(nfiles, imgs, names); + sortImagesByArea(nfiles, imgs, names); - findpack(imgs, nfiles, coords); + findpack(imgs, nfiles, coords, cmdline.quality2, qfactor); - computeOutputType(&outimg.maxval, &outimg.format, outimg.tuple_type, - &outimg.depth, nfiles, imgs); + computeOutputType(&outimg.maxval, &outimg.format, outimg.tuple_type, + &outimg.depth, nfiles, imgs); - computeOutputDimensions(&outimg.width, &outimg.height, nfiles, imgs, coords); + computeOutputDimensions(&outimg.width, &outimg.height, nfiles, + imgs, coords); - pnm_setminallocationdepth(&outimg, outimg.depth); + pnm_setminallocationdepth(&outimg, outimg.depth); - outimg.size = sizeof(outimg); - outimg.len = sizeof(outimg); - outimg.file = stdout; - outimg.bytes_per_sample = 0; - for (i = outimg.maxval; i; i >>= 8) - ++outimg.bytes_per_sample; + outimg.size = sizeof(outimg); + outimg.len = sizeof(outimg); + outimg.file = stdout; + outimg.bytes_per_sample = 0; + for (i = outimg.maxval; i; i >>= 8) + ++outimg.bytes_per_sample; - writePam(&outimg, nfiles, coords, imgs); + writePam(&outimg, nfiles, coords, imgs); - if (data) - writeData(data, outimg.width, outimg.height, - nfiles, names, coords, imgs); - - if (header) - writeHeader(header, cmdline.prefix, outimg.width, outimg.height, + if (data) + writeData(data, outimg.width, outimg.height, nfiles, names, coords, imgs); - for (i = 0; i < nfiles; ++i) - pm_close(imgs[i].file); - pm_close(stdout); - if (header) - pm_close(header); - if (data) - pm_close(data); + if (header) + writeHeader(header, cmdline.prefix, outimg.width, outimg.height, + nfiles, names, coords, imgs); - return 0; + for (i = 0; i < nfiles; ++i) + pm_close(imgs[i].file); + pm_close(stdout); + if (header) + pm_close(header); + if (data) + pm_close(data); + + return 0; } diff --git a/editor/pnmpad.c b/editor/pnmpad.c index 979fe017..f1541e96 100644 --- a/editor/pnmpad.c +++ b/editor/pnmpad.c @@ -1,13 +1,13 @@ /* pnmpad.c - add border to sides of a portable anymap - ** AJCD 4/9/90 + ** AJCD 4/9/90 */ #include <string.h> #include <stdio.h> -#include "pnm.h" -#include "shhopt.h" #include "mallocvar.h" +#include "shhopt.h" +#include "pnm.h" #define MAX_WIDTHHEIGHT INT_MAX-10 @@ -55,7 +55,7 @@ parseCommandLine(int argc, const char ** argv, MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ - OPTENT3(0, "xsize", OPT_UINT, &cmdlineP->xsize, + OPTENT3(0, "xsize", OPT_UINT, &cmdlineP->xsize, &cmdlineP->xsizeSpec, 0); OPTENT3(0, "width", OPT_UINT, &cmdlineP->xsize, &cmdlineP->xsizeSpec, 0); @@ -63,13 +63,13 @@ parseCommandLine(int argc, const char ** argv, &cmdlineP->ysizeSpec, 0); OPTENT3(0, "height", OPT_UINT, &cmdlineP->ysize, &cmdlineP->ysizeSpec, 0); - OPTENT3(0, "left", OPT_UINT, &cmdlineP->left, + OPTENT3(0, "left", OPT_UINT, &cmdlineP->left, &cmdlineP->leftSpec, 0); - OPTENT3(0, "right", OPT_UINT, &cmdlineP->right, + OPTENT3(0, "right", OPT_UINT, &cmdlineP->right, &cmdlineP->rightSpec, 0); - OPTENT3(0, "top", OPT_UINT, &cmdlineP->top, + OPTENT3(0, "top", OPT_UINT, &cmdlineP->top, &cmdlineP->topSpec, 0); - OPTENT3(0, "bottom", OPT_UINT, &cmdlineP->bottom, + OPTENT3(0, "bottom", OPT_UINT, &cmdlineP->bottom, &cmdlineP->bottomSpec, 0); OPTENT3(0, "xalign", OPT_FLOAT, &cmdlineP->xalign, &xalignSpec, 0); @@ -79,7 +79,7 @@ parseCommandLine(int argc, const char ** argv, &yalignSpec, 0); OPTENT3(0, "valign", OPT_FLOAT, &cmdlineP->yalign, &yalignSpec, 0); - OPTENT3(0, "black", OPT_FLAG, NULL, + OPTENT3(0, "black", OPT_FLAG, NULL, &blackOpt, 0); OPTENT3(0, "white", OPT_FLAG, NULL, &cmdlineP->white, 0); @@ -128,20 +128,20 @@ parseCommandLine(int argc, const char ** argv, if (xalignSpec) { if (cmdlineP->xalign < 0) - pm_error("You have specified a negative -halign value (%f)", + pm_error("You have specified a negative -halign value (%f)", cmdlineP->xalign); if (cmdlineP->xalign > 1) - pm_error("You have specified a -halign value (%f) greater than 1", + pm_error("You have specified a -halign value (%f) greater than 1", cmdlineP->xalign); } else cmdlineP->xalign = 0.5; if (yalignSpec) { if (cmdlineP->yalign < 0) - pm_error("You have specified a negative -halign value (%f)", + pm_error("You have specified a negative -halign value (%f)", cmdlineP->yalign); if (cmdlineP->yalign > 1) - pm_error("You have specified a -valign value (%f) greater than 1", + pm_error("You have specified a -valign value (%f) greater than 1", cmdlineP->yalign); } else cmdlineP->yalign = 0.5; @@ -150,9 +150,9 @@ parseCommandLine(int argc, const char ** argv, if (argc-1 > 1) pm_error("This program takes at most 1 parameter. You specified %d", argc-1); - else if (argc-1 == 1) + else if (argc-1 == 1) cmdlineP->input_filespec = argv[1]; - else + else cmdlineP->input_filespec = "-"; } @@ -177,7 +177,7 @@ parseCommandLineOld(int argc, const char ** argv, case 'l': if (atoi(argv[1]+2) < 0) pm_error("left border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) + else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) pm_error("left border too large"); else cmdlineP->left = atoi(argv[1]+2); @@ -185,7 +185,7 @@ parseCommandLineOld(int argc, const char ** argv, case 'r': if (atoi(argv[1]+2) < 0) pm_error("right border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) + else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) pm_error("right border too large"); else cmdlineP->right = atoi(argv[1]+2); @@ -193,7 +193,7 @@ parseCommandLineOld(int argc, const char ** argv, case 'b': if (atoi(argv[1]+2) < 0) pm_error("bottom border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) + else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) pm_error("bottom border too large"); else cmdlineP->bottom = atoi(argv[1]+2); @@ -201,7 +201,7 @@ parseCommandLineOld(int argc, const char ** argv, case 't': if (atoi(argv[1]+2) < 0) pm_error("top border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) + else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) pm_error("top border too large"); else cmdlineP->top = atoi(argv[1]+2); @@ -239,12 +239,12 @@ validateHorizontalSize(struct cmdlineInfo const cmdline, if (lpad > MAX_WIDTHHEIGHT) pm_error("The left padding value you specified is too large."); - + if (rpad > MAX_WIDTHHEIGHT) pm_error("The right padding value you specified is too large."); - + if ((double) cols + (double) lpad + (double) rpad > MAX_WIDTHHEIGHT) - pm_error("Given padding value(s) makes output width too large."); + pm_error("Given padding value(s) makes output width too large."); } @@ -256,7 +256,7 @@ computeHorizontalPadSizes(struct cmdlineInfo const cmdline, unsigned int * const rpadP) { validateHorizontalSize(cmdline, cols); - + if (cmdline.xsizeSpec) { if (cmdline.leftSpec && cmdline.rightSpec) { if (cmdline.left + cols + cmdline.right < cmdline.xsize) { @@ -270,7 +270,7 @@ computeHorizontalPadSizes(struct cmdlineInfo const cmdline, } } else if (cmdline.leftSpec) { *lpadP = cmdline.left; - *rpadP = MAX(cmdline.xsize, cmdline.left + cols) - + *rpadP = MAX(cmdline.xsize, cmdline.left + cols) - (cmdline.left + cols); } else if (cmdline.rightSpec) { *rpadP = cmdline.right; @@ -303,15 +303,15 @@ validateVerticalSize(struct cmdlineInfo const cmdline, if (ysize > MAX_WIDTHHEIGHT) pm_error("The height value you specified is too large."); - + if (tpad > MAX_WIDTHHEIGHT) pm_error("The top padding value you specified is too large."); - + if (bpad > MAX_WIDTHHEIGHT) pm_error("The bottom padding value you specified is too large."); - + if ((double) rows + (double) tpad + (double) bpad > MAX_WIDTHHEIGHT) - pm_error("Given padding value(s) makes output height too large."); + pm_error("Given padding value(s) makes output height too large."); } @@ -337,11 +337,11 @@ computeVerticalPadSizes(struct cmdlineInfo const cmdline, } } else if (cmdline.topSpec) { *tpadP = cmdline.top; - *bpadP = MAX(cmdline.ysize, cmdline.top + rows) - + *bpadP = MAX(cmdline.ysize, cmdline.top + rows) - (cmdline.top + rows); } else if (cmdline.bottomSpec) { *bpadP = cmdline.bottom; - *tpadP = MAX(cmdline.ysize, rows + cmdline.bottom) - + *tpadP = MAX(cmdline.ysize, rows + cmdline.bottom) - (rows + cmdline.bottom); } else { if (cmdline.ysize > rows) { @@ -380,14 +380,117 @@ computePadSizes(struct cmdlineInfo const cmdline, +static void +padPbm(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + int const format, + unsigned int const newcols, + unsigned int const lpad, + unsigned int const rpad, + unsigned int const tpad, + unsigned int const bpad, + bool const colorWhite) { +/*---------------------------------------------------------------------------- + Fast padding routine for PBM +-----------------------------------------------------------------------------*/ + unsigned char * const bgrow = pbm_allocrow_packed(newcols); + unsigned char * const newrow = pbm_allocrow_packed(newcols); + + unsigned char const padChar = + 0xff * (colorWhite ? PBM_WHITE : PBM_BLACK); + + unsigned int const newColChars = pbm_packed_bytes(newcols); + + unsigned int row; + unsigned int charCnt; + + /* Set up margin row, input-output buffer */ + for (charCnt = 0; charCnt < newColChars; ++charCnt) + bgrow[charCnt] = newrow[charCnt] = padChar; + + if (newcols % 8 > 0) { + bgrow[newColChars-1] <<= 8 - newcols % 8; + newrow[newColChars-1] <<= 8 - newcols % 8; + } + + pbm_writepbminit(stdout, newcols, rows + tpad + bpad, 0); + + /* Write top margin */ + for (row = 0; row < tpad; ++row) + pbm_writepbmrow_packed(stdout, bgrow, newcols, 0); + + /* Read rows, shift and write with left and right margins added */ + for (row = 0; row < rows; ++row) { + pbm_readpbmrow_bitoffset(ifP, newrow, cols, format, lpad); + pbm_writepbmrow_packed(stdout, newrow, newcols, 0); + } + + pnm_freerow(newrow); + + /* Write bottom margin */ + for (row = 0; row < bpad; ++row) + pbm_writepbmrow_packed(stdout, bgrow, newcols, 0); + + pnm_freerow(bgrow); +} + + +static void +padGeneral(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + unsigned int const newcols, + unsigned int const lpad, + unsigned int const rpad, + unsigned int const tpad, + unsigned int const bpad, + bool const colorWhite) { +/*---------------------------------------------------------------------------- + General padding routine (logic works for PBM) +-----------------------------------------------------------------------------*/ + xel * const bgrow = pnm_allocrow(newcols); + xel * const xelrow = pnm_allocrow(newcols); + xel background; + unsigned int row, col; + + if (colorWhite) + background = pnm_whitexel(maxval, format); + else + background = pnm_blackxel(maxval, format); + + for (col = 0; col < newcols; ++col) + xelrow[col] = bgrow[col] = background; + + pnm_writepnminit(stdout, newcols, rows + tpad + bpad, maxval, format, 0); + + for (row = 0; row < tpad; ++row) + pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0); + + for (row = 0; row < rows; ++row) { + pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format); + pnm_writepnmrow(stdout, xelrow, newcols, maxval, format, 0); + } + + for (row = 0; row < bpad; ++row) + pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0); + + pnm_freerow(xelrow); + pnm_freerow(bgrow); +} + + + int main(int argc, const char ** argv) { struct cmdlineInfo cmdline; - FILE *ifP; - xel *xelrow, *bgrow, background; + FILE * ifP; + xelval maxval; - int rows, cols, newcols, row, col, format; + int rows, cols, newcols, format; bool depr_cmd; /* use deprecated commandline interface */ unsigned int lpad, rpad, tpad, bpad; @@ -401,7 +504,7 @@ main(int argc, const char ** argv) { if (argv[1][2] >= '0' && argv[1][2] <= '9') depr_cmd = TRUE; } - } + } if (argc > 2 && argv[2][0] == '-') { if (argv[2][1] == 't' || argv[2][1] == 'b' || argv[2][1] == 'l' || argv[2][1] == 'r') { @@ -410,45 +513,27 @@ main(int argc, const char ** argv) { } } - if (depr_cmd) + if (depr_cmd) parseCommandLineOld(argc, argv, &cmdline); - else + else parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.input_filespec); pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); - if (cmdline.white) - background = pnm_whitexel(maxval, format); - else - background = pnm_blackxel(maxval, format); if (cmdline.verbose) pm_message("image WxH = %dx%d", cols, rows); computePadSizes(cmdline, cols, rows, &lpad, &rpad, &tpad, &bpad); newcols = cols + lpad + rpad; - xelrow = pnm_allocrow(newcols); - bgrow = pnm_allocrow(newcols); - for (col = 0; col < newcols; col++) - xelrow[col] = bgrow[col] = background; - - pnm_writepnminit(stdout, newcols, rows + tpad + bpad, maxval, format, 0); - - for (row = 0; row < tpad; row++) - pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0); - - for (row = 0; row < rows; row++) { - pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format); - pnm_writepnmrow(stdout, xelrow, newcols, maxval, format, 0); - } - - for (row = 0; row < bpad; row++) - pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0); - - pnm_freerow(xelrow); - pnm_freerow(bgrow); + if (PNM_FORMAT_TYPE(format) == PBM_TYPE) + padPbm(ifP, cols, rows, format, newcols, lpad, rpad, tpad, bpad, + !!cmdline.white); + else + padGeneral(ifP, cols, rows, maxval, format, + newcols, lpad, rpad, tpad, bpad, !!cmdline.white); pm_close(ifP); diff --git a/editor/pnmpaste.c b/editor/pnmpaste.c index 38b316c6..3fd9d521 100644 --- a/editor/pnmpaste.c +++ b/editor/pnmpaste.c @@ -10,176 +10,419 @@ ** implied warranty. */ +#include <assert.h> + #include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" #include "pnm.h" + +enum boolOp {REPLACE, AND, OR, XOR /*, NAND, NOR, NXOR */ }; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * baseFilename; + const char * insetFilename; + int insertCol; /* Negative means from right edge */ + int insertRow; /* Negative means from bottom edge */ + enum boolOp operation; +}; + + + +static void +parseCommandLine(int argc, const 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 replaceOpt, andOpt, orOpt, xorOpt; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "replace", OPT_FLAG, NULL, + &replaceOpt, 0); + OPTENT3(0, "and", OPT_FLAG, NULL, + &andOpt, 0); + OPTENT3(0, "or", OPT_FLAG, NULL, + &orOpt, 0); + OPTENT3(0, "xor", OPT_FLAG, NULL, + &xorOpt, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = TRUE; /* We have parms that are negative numbers */ + + optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (replaceOpt + andOpt + orOpt + xorOpt > 1) + pm_error("You may specify only one of -replace, -and, -or, and -xor"); + + cmdlineP->operation = + replaceOpt ? REPLACE : + andOpt ? AND : + orOpt ? OR : + xorOpt ? XOR : + replaceOpt; + + + if (argc-1 >= 3) { + cmdlineP->insetFilename = argv[1]; + cmdlineP->insertCol = atoi(argv[2]); + cmdlineP->insertRow = atoi(argv[3]); + + if (argc-1 >= 4) { + cmdlineP->baseFilename = argv[4]; + + if (argc-1 > 4) + pm_error("Too many arguments: %u. This program takes " + "at most 4", argc-1); + } else + cmdlineP->baseFilename = "-"; + } else + pm_error("You must specify at least 3 arguments: \"from\" file " + "name, insert-at column, and insert-at row. " + "You specified %u", argc-1); + + if (streq(cmdlineP->baseFilename, "-") && + streq(cmdlineP->insetFilename, "-")) + pm_error("You can't use Standard Input for both the input images"); +} + + + +static unsigned char +leftBits(unsigned char const x, + unsigned int const n){ +/*---------------------------------------------------------------------------- + Clear rightmost (8-n) bits, retain leftmost (=high) n bits. +-----------------------------------------------------------------------------*/ + assert(n <= 8); + + return (x >> (8-n)) << (8-n); +} + + + +static unsigned char +rightBits(unsigned char const x, + unsigned int const n){ +/*---------------------------------------------------------------------------- + Return rightmost (=low) n bits of x. (Clear the rest). +-----------------------------------------------------------------------------*/ + assert(n <= 8); + + return (x << (8-n)) >> (8-n); +} + + + +static void +insertDirect(FILE * const ifP, + unsigned char * const destrow, + unsigned int const cols, + int const format, + enum boolOp const operation, + unsigned char * const buffer) { +/*---------------------------------------------------------------------------- + Read the next row from PBM file 'ifP' and merge it according to + 'operation' into 'destrow', flush left in packed PBM format. + + 'cols' and 'format' describe the 'ifP' image. + + 'buffer' is a scratch buffer for our use, at least wide enough to hold + a packed PBM row of 'ifP'. +-----------------------------------------------------------------------------*/ + unsigned int const colBytes = pbm_packed_bytes(cols); + unsigned int const last = colBytes - 1; + unsigned char const origRight = destrow[last]; + + if (operation == REPLACE) + pbm_readpbmrow_packed(ifP, destrow, cols, format); + else { + unsigned int i; + + pbm_readpbmrow_packed(ifP, buffer, cols, format); + + for (i = 0; i < colBytes; ++i) { + switch (operation) { + case AND: destrow[i] |= buffer[i]; break; + case OR : destrow[i] &= buffer[i]; break; + case XOR: destrow[i] = ~( destrow[i] ^ buffer[i] ) ; break; + /* + case NAND: destrow[i] = ~( destrow[i] | buffer[i] ) ; break; + case NOR : destrow[i] = ~( destrow[i] & buffer[i] ) ; break; + case NXOR: destrow[i] ^= buffer[i] ; break; + */ + case REPLACE: assert(false); break; + } + } + } + + if (cols % 8 > 0) + destrow[last] = leftBits(destrow[last], cols % 8) + | rightBits(origRight, 8 - cols % 8); +} + + + +static void +insertShift(FILE * const ifP, + unsigned char * const destrow, + unsigned int const cols, + unsigned int const format, + unsigned int const offset, + enum boolOp const operation, + unsigned char * const buffer) { +/*---------------------------------------------------------------------------- + Same as insertDirect(), but start merging 'offset' bits from the left + end of 'destrow'. 'offset' is less than 8. + + buffer[] is wide enough to hold a packed PBM row of *ifP plus one + byte of margin. +-----------------------------------------------------------------------------*/ + unsigned int const shiftBytes = pbm_packed_bytes(cols + offset); + unsigned int const last = shiftBytes - 1; + unsigned char const origLeft = destrow[0]; + unsigned char const origRight = destrow[last]; + + unsigned int const padOffset = (cols + offset) % 8; + + unsigned int i; + + assert(offset < 8); + + pbm_readpbmrow_packed(ifP, &buffer[1], cols, format); + + /* Note that buffer[0] is undefined. */ + + for (i = 0; i < shiftBytes; ++i) { + unsigned int const rsh = offset; + unsigned int const lsh = 8-rsh; + unsigned char const t = buffer[i] << lsh | buffer[i+1] >> rsh; + + switch (operation) { + case REPLACE: destrow[i] = t; break; + case AND: destrow[i] |= t; break; + case OR : destrow[i] &= t; break; + case XOR: destrow[i] = ~ (destrow[i] ^ t); break; + /* + case NAND: destrow[i] = ~ (destrow[i] | t); break; + case NOR : destrow[i] = ~ (destrow[i] & t); break; + case NXOR: destrow[i] ^= t; break; + */ + } + } + + /* destrow[] now contains garbage in the 'offset' leftmost bits + and 8-offset rightmost bits. Those are supposed to be unchanged + from the input, so we restore them now. + */ + + destrow[0] = leftBits(origLeft, offset) | + rightBits(destrow[0], 8-offset); + + if (padOffset % 8 > 0) + destrow[last] = leftBits(destrow[last], padOffset) | + rightBits(origRight , 8-padOffset); +} + + + +static void +pastePbm(FILE * const fpInset, + FILE * const fpBase, + int const insetFormat, + int const baseFormat, + unsigned int const insetRows, + unsigned int const baseRows, + unsigned int const insetCols, + unsigned int const baseCols, + unsigned int const insertCol, + unsigned int const insertRow, + enum boolOp const operation) { +/*---------------------------------------------------------------------------- + Fast paste for PBM +-----------------------------------------------------------------------------*/ + unsigned char * const baserow = pbm_allocrow_packed(baseCols); + unsigned char * const buffer = pbm_allocrow_packed(insetCols+8); + int const shiftBytes = insertCol / 8; + unsigned int const shiftOffset = insertCol % 8; + int const baseColBytes = pbm_packed_bytes(baseCols); + + unsigned int row; + + pbm_writepbminit(stdout, baseCols, baseRows, 0); + + for (row = 0; row < baseRows; ++row) { + pbm_readpbmrow_packed(fpBase, baserow, baseCols, baseFormat); + + if (row >= insertRow && row < insertRow + insetRows) { + if (shiftOffset == 0) + insertDirect(fpInset, &baserow[shiftBytes], insetCols, + insetFormat, operation, buffer); + else + insertShift(fpInset, &baserow[shiftBytes], insetCols, + insetFormat, shiftOffset, operation, buffer); + } + + if (baseCols % 8 > 0) + baserow[baseColBytes-1] + = leftBits(baserow[baseColBytes-1] , baseCols % 8); + + pbm_writepbmrow_packed(stdout, baserow, baseCols, 0); + } + pbm_freerow_packed(buffer); + pbm_freerow_packed(baserow); +} + + + +static void +pasteNonPbm(FILE * const fpInset, + FILE * const fpBase, + int const formatInset, + int const formatBase, + int const newformat, + xelval const maxvalInset, + xelval const maxvalBase, + unsigned int const rowsInset, + unsigned int const rowsBase, + unsigned int const colsInset, + unsigned int const colsBase, + unsigned int const insertCol, + unsigned int const insertRow) { + + /* Logic works for PBM, but cannot do bitwise operations */ + + xelval const newmaxval = MAX(maxvalInset, maxvalBase); + + xel * const xelrowInset = pnm_allocrow(colsInset); + xel * const xelrowBase = pnm_allocrow(colsBase); + + unsigned int row; + + pnm_writepnminit(stdout, colsBase, rowsBase, newmaxval, newformat, 0); + + for (row = 0; row < rowsBase; ++row) { + pnm_readpnmrow(fpBase, xelrowBase, colsBase, maxvalBase, formatBase); + pnm_promoteformatrow(xelrowBase, colsBase, maxvalBase, formatBase, + newmaxval, newformat); + + if (row >= insertRow && row < insertRow + rowsInset) { + unsigned int colInset; + + pnm_readpnmrow(fpInset, xelrowInset, colsInset, maxvalInset, + formatInset); + pnm_promoteformatrow(xelrowInset, colsInset, maxvalInset, + formatInset, newmaxval, newformat ); + for (colInset = 0; colInset < colsInset; ++colInset) + xelrowBase[insertCol + colInset] = xelrowInset[colInset]; + } + pnm_writepnmrow(stdout, xelrowBase, colsBase, newmaxval, newformat, 0); + } + + pnm_freerow(xelrowBase); + pnm_freerow(xelrowInset); +} + + + int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp1; - FILE* ifp2; - register xel* xelrow1; - register xel* xelrow2; - register xel* x1P; - register xel* x2P; - xelval maxval1, maxval2, newmaxval; - int argn, rows1, cols1, format1, x, y; - int rows2, cols2, format2, newformat, row; - register int col; - char function; - const char* const usage = "[-replace|-or|-and|-xor] frompnmfile x y [intopnmfile]"; - - pnm_init( &argc, argv ); - - argn = 1; - function = 'r'; - - /* Check for flags. */ - if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) - { - if ( pm_keymatch( argv[argn], "-replace", 2 ) ) - function = 'r'; - else if ( pm_keymatch( argv[argn], "-or", 2 ) ) - function = 'o'; - else if ( pm_keymatch( argv[argn], "-and", 2 ) ) - function = 'a'; - else if ( pm_keymatch( argv[argn], "-xor", 2 ) ) - function = 'x'; - else - pm_usage( usage ); - ++argn; - } - - if ( argn == argc ) - pm_usage( usage ); - ifp1 = pm_openr( argv[argn] ); - ++argn; - - if ( argn == argc ) - pm_usage( usage ); - if ( sscanf( argv[argn], "%d", &x ) != 1 ) - pm_usage( usage ); - ++argn; - if ( argn == argc ) - pm_usage( usage ); - if ( sscanf( argv[argn], "%d", &y ) != 1 ) - pm_usage( usage ); - ++argn; - - if ( argn != argc ) - { - ifp2 = pm_openr( argv[argn] ); - ++argn; - } +main(int argc, const char ** argv) { + + struct cmdlineInfo cmdline; + FILE * fpInset; + FILE * fpBase; + xelval maxvalInset, maxvalBase; + int rowsInset, colsInset; + int formatInset; + int rowsBase, colsBase; + int formatBase; + int newformat; + unsigned int insertRow, insertCol; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + fpInset = pm_openr(cmdline.insetFilename); + fpBase = pm_openr(cmdline.baseFilename); + + pnm_readpnminit(fpInset, &colsInset, &rowsInset, + &maxvalInset, &formatInset); + pnm_readpnminit(fpBase, &colsBase, &rowsBase, &maxvalBase, &formatBase); + + if (colsBase < colsInset) + pm_error( + "Image to paste is wider than base image by %u cols", + colsInset - colsBase); + else if (cmdline.insertCol <= -colsBase) + pm_error( + "x is too negative -- the second image has only %u cols", + colsBase); + else if (cmdline.insertCol >= colsBase) + pm_error( + "x is too large -- the second image has only %u cols", + colsBase); + + if (rowsBase < rowsInset) + pm_error( + "Image to paste is taller than base image by %u rows", + rowsInset - rowsBase); + else if (cmdline.insertRow <= -rowsBase) + pm_error( + "y is too negative -- the second image has only %u rows", + rowsBase); + else if (cmdline.insertRow >= rowsBase) + pm_error( + "y is too large -- the second image has only %d rows", + rowsBase); + + insertCol = cmdline.insertCol < 0 ? + colsBase + cmdline.insertCol : cmdline.insertCol; + insertRow = cmdline.insertRow < 0 ? + rowsBase + cmdline.insertRow : cmdline.insertRow; + + if (insertCol + colsInset > colsBase) + pm_error("Extends over right edge by %u pixels", + (insertCol + colsInset) - colsBase); + if (insertRow + rowsInset > rowsBase) + pm_error("Extends over bottom edge by %u pixels", + (insertRow + rowsInset) - rowsBase); + + newformat = MAX(PNM_FORMAT_TYPE(formatInset), PNM_FORMAT_TYPE(formatBase)); + + if (cmdline.operation != REPLACE && newformat != PBM_TYPE) + pm_error("no logical operations allowed for a non-PBM image"); + + if (newformat == PBM_TYPE) + pastePbm(fpInset, fpBase, formatInset, formatBase, + rowsInset, rowsBase, colsInset, colsBase, + insertCol, insertRow, cmdline.operation); else - ifp2 = stdin; - - if ( argn != argc ) - pm_usage( usage ); - - pnm_readpnminit( ifp1, &cols1, &rows1, &maxval1, &format1 ); - xelrow1 = pnm_allocrow(cols1); - pnm_readpnminit( ifp2, &cols2, &rows2, &maxval2, &format2 ); - xelrow2 = pnm_allocrow(cols2); - - if ( x <= -cols2 ) - pm_error( - "x is too negative -- the second anymap has only %d cols", - cols2 ); - else if ( x >= cols2 ) - pm_error( - "x is too large -- the second anymap has only %d cols", - cols2 ); - if ( y <= -rows2 ) - pm_error( - "y is too negative -- the second anymap has only %d rows", - rows2 ); - else if ( y >= rows2 ) - pm_error( - "y is too large -- the second anymap has only %d rows", - rows2 ); - - if ( x < 0 ) - x += cols2; - if ( y < 0 ) - y += rows2; - - if ( x + cols1 > cols2 ) - pm_error( "x + width is too large by %d pixels", x + cols1 - cols2 ); - if ( y + rows1 > rows2 ) - pm_error( "y + height is too large by %d pixels", y + rows1 - rows2 ); - - newformat = MAX( PNM_FORMAT_TYPE(format1), PNM_FORMAT_TYPE(format2) ); - newmaxval = MAX( maxval1, maxval2 ); - - if ( function != 'r' && newformat != PBM_TYPE ) - pm_error( "no logical operations allowed for non-bitmaps" ); - - pnm_writepnminit( stdout, cols2, rows2, newmaxval, newformat, 0 ); - - for ( row = 0; row < rows2; ++row ) - { - pnm_readpnmrow( ifp2, xelrow2, cols2, maxval2, format2 ); - pnm_promoteformatrow( xelrow2, cols2, maxval2, format2, - newmaxval, newformat ); - - if ( row >= y && row < y + rows1 ) - { - pnm_readpnmrow( ifp1, xelrow1, cols1, maxval1, format1 ); - pnm_promoteformatrow( xelrow1, cols1, maxval1, format1, - newmaxval, newformat ); - for ( col = 0, x1P = xelrow1, x2P = &(xelrow2[x]); - col < cols1; ++col, ++x1P, ++x2P ) - { - register xelval b1, b2; - - switch ( function ) - { - case 'r': - *x2P = *x1P; - break; - - case 'o': - b1 = PNM_GET1( *x1P ); - b2 = PNM_GET1( *x2P ); - if ( b1 != 0 || b2 != 0 ) - PNM_ASSIGN1( *x2P, newmaxval ); - else - PNM_ASSIGN1( *x2P, 0 ); - break; - - case 'a': - b1 = PNM_GET1( *x1P ); - b2 = PNM_GET1( *x2P ); - if ( b1 != 0 && b2 != 0 ) - PNM_ASSIGN1( *x2P, newmaxval ); - else - PNM_ASSIGN1( *x2P, 0 ); - break; - - case 'x': - b1 = PNM_GET1( *x1P ); - b2 = PNM_GET1( *x2P ); - if ( ( b1 != 0 && b2 == 0 ) || ( b1 == 0 && b2 != 0 ) ) - PNM_ASSIGN1( *x2P, newmaxval ); - else - PNM_ASSIGN1( *x2P, 0 ); - break; - - default: - pm_error( "can't happen" ); - } - } - } - - pnm_writepnmrow( stdout, xelrow2, cols2, newmaxval, newformat, 0 ); - } - - pm_close( ifp1 ); - pm_close( ifp2 ); - pm_close( stdout ); + pasteNonPbm(fpInset, fpBase, + formatInset, formatBase, newformat, + maxvalInset, maxvalBase, + rowsInset, rowsBase, colsInset, colsBase, + insertCol, insertRow); - exit( 0 ); - } + pm_close(fpInset); + pm_close(fpBase); + pm_close(stdout); + + return 0; +} diff --git a/editor/pnmstitch.c b/editor/pnmstitch.c index 61f02a04..45aee2f4 100644 --- a/editor/pnmstitch.c +++ b/editor/pnmstitch.c @@ -216,7 +216,7 @@ struct cmdlineInfo { unsigned int verbose; }; -static char minus[] = "-"; + static void parseCommandLine ( int argc, char ** argv, @@ -293,10 +293,10 @@ parseCommandLine ( int argc, char ** argv, /* NOTREACHED */ } else { if (argc-1 == 0) { - cmdlineP->leftFilespec = minus; - cmdlineP->rightFilespec = minus; + cmdlineP->leftFilespec = "-"; + cmdlineP->rightFilespec = "-"; } else if (argc-1 == 1) { - cmdlineP->leftFilespec = minus; + cmdlineP->leftFilespec = "-"; cmdlineP->rightFilespec = argv[1]; } else { cmdlineP->leftFilespec = argv[1]; @@ -311,7 +311,7 @@ parseCommandLine ( int argc, char ** argv, } else if (outputSpec) { cmdlineP->outputFilespec = outputOpt; } else { - cmdlineP->outputFilespec = minus; + cmdlineP->outputFilespec = "-"; } } } /* parseCommandLine() - end */ @@ -439,7 +439,7 @@ readinit(const char * const name) else { FILE * ifP; - if (strcmp(name,minus) == 0) { + if (streq(name, "-")) { ifP = stdin; image->name = strdup("<stdin>"); } else { @@ -468,7 +468,7 @@ readinit(const char * const name) static bool writeinit(Image * image) { - if (strcmp(image->name,minus) == 0) { + if (streq(image->name, "-")) { image->pam.file = stdout; strfree(image->name); image->name = strdup("<stdout>"); |