diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2009-03-02 01:05:47 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2009-03-02 01:05:47 +0000 |
commit | 150c1c5999ddf9fb1f0a638bf628c000649e8735 (patch) | |
tree | 644a9cd73339d60c34bf51fdfe1d0fb7c66603ae | |
parent | e4f137017d17bc98ef50d9f7ada38a4d65bcfdff (diff) | |
download | netpbm-mirror-150c1c5999ddf9fb1f0a638bf628c000649e8735.tar.gz netpbm-mirror-150c1c5999ddf9fb1f0a638bf628c000649e8735.tar.xz netpbm-mirror-150c1c5999ddf9fb1f0a638bf628c000649e8735.zip |
Add PBM fastpath, fix various bugs
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@849 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r-- | doc/HISTORY | 4 | ||||
-rw-r--r-- | editor/pnmcrop.c | 654 |
2 files changed, 454 insertions, 204 deletions
diff --git a/doc/HISTORY b/doc/HISTORY index 372fb1af..b37d32bd 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -13,12 +13,16 @@ not yet BJH Release 10.46.00 xwdtopnm: Add warning about 2 byte sample output. + pnmcrop: fast path for PBM. + pnmscale: finally make it just an alias of pamscale, which obsoleted it in 10.20. pnmcut: finally make it just an alias of pamcut, which obsoleted it in 9.20. + pnmcrop: various fixes. + pnmcat: fix array bound violation with PBM top/bottom concatenation. Introduced in 10.44. diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c index a98c6f60..e9dcbe4a 100644 --- a/editor/pnmcrop.c +++ b/editor/pnmcrop.c @@ -31,13 +31,25 @@ enum bg_choice {BG_BLACK, BG_WHITE, BG_DEFAULT, BG_SIDES}; +typedef enum { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3} edgeLocation; + +static const char * const edgeName[] = { + "left", + "right", + "top", + "bottom" +}; + + + struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFilespec; enum bg_choice background; - unsigned int left, right, top, bottom; + bool wantCrop[4]; + /* User wants crop of left, right, top, bottom, resp. */ unsigned int verbose; unsigned int margin; const char * borderfile; /* NULL if none */ @@ -46,7 +58,7 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that @@ -59,6 +71,7 @@ parseCommandLine(int argc, char ** argv, unsigned int blackOpt, whiteOpt, sidesOpt; unsigned int marginSpec, borderfileSpec; + unsigned int leftOpt, rightOpt, topOpt, bottomOpt; unsigned int option_def_index; @@ -68,10 +81,10 @@ parseCommandLine(int argc, char ** argv, OPTENT3(0, "black", OPT_FLAG, NULL, &blackOpt, 0); OPTENT3(0, "white", OPT_FLAG, NULL, &whiteOpt, 0); OPTENT3(0, "sides", OPT_FLAG, NULL, &sidesOpt, 0); - OPTENT3(0, "left", OPT_FLAG, NULL, &cmdlineP->left, 0); - OPTENT3(0, "right", OPT_FLAG, NULL, &cmdlineP->right, 0); - OPTENT3(0, "top", OPT_FLAG, NULL, &cmdlineP->top, 0); - OPTENT3(0, "bottom", OPT_FLAG, NULL, &cmdlineP->bottom, 0); + OPTENT3(0, "left", OPT_FLAG, NULL, &leftOpt, 0); + OPTENT3(0, "right", OPT_FLAG, NULL, &rightOpt, 0); + OPTENT3(0, "top", OPT_FLAG, NULL, &topOpt, 0); + OPTENT3(0, "bottom", OPT_FLAG, NULL, &bottomOpt, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); OPTENT3(0, "margin", OPT_UINT, &cmdlineP->margin, &marginSpec, 0); @@ -82,8 +95,10 @@ parseCommandLine(int argc, char ** 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. */ + + free(option_def); if (argc-1 == 0) cmdlineP->inputFilespec = "-"; /* stdin */ @@ -106,12 +121,16 @@ parseCommandLine(int argc, char ** argv, else cmdlineP->background = BG_DEFAULT; - if (!cmdlineP->left && !cmdlineP->right && !cmdlineP->top - && !cmdlineP->bottom) { - cmdlineP->left = cmdlineP->right = cmdlineP->top - = cmdlineP->bottom = TRUE; + if (!leftOpt && !rightOpt && !topOpt && !bottomOpt) { + unsigned int i; + for (i = 0; i < 4; ++i) + cmdlineP->wantCrop[i] = true; + } else { + cmdlineP->wantCrop[LEFT] = !!leftOpt; + cmdlineP->wantCrop[RIGHT] = !!rightOpt; + cmdlineP->wantCrop[TOP] = !!topOpt; + cmdlineP->wantCrop[BOTTOM] = !!bottomOpt; } - if (!marginSpec) cmdlineP->margin = 0; @@ -121,6 +140,22 @@ parseCommandLine(int argc, char ** argv, +typedef struct { +/*---------------------------------------------------------------------------- + This describes a cropping operation of a single border (top, bottom, + left, or right). + + Our definition of cropping includes padding to make a margin as well as + chopping stuff out. +-----------------------------------------------------------------------------*/ + unsigned int removeSize; + /* Size in pixels of the border to remove */ + unsigned int padSize; + /* Size in pixels of the border to add */ +} cropOp; + + + static xel background3Corners(FILE * const ifP, int const rows, @@ -204,10 +239,10 @@ computeBackground(FILE * const ifP, switch (backgroundChoice) { case BG_WHITE: - background = pnm_whitexel(maxval, format); + background = pnm_whitexel(maxval, format); break; case BG_BLACK: - background = pnm_blackxel(maxval, format); + background = pnm_blackxel(maxval, format); break; case BG_SIDES: background = @@ -238,33 +273,28 @@ findBordersInImage(FILE * const ifP, xel const backgroundColor, bool const verbose, bool * const hasBordersP, - unsigned int * const leftP, - unsigned int * const rightP, - unsigned int * const topP, - unsigned int * const bottomP) { + unsigned int * const borderSize) { /*---------------------------------------------------------------------------- Find the left, right, top, and bottom borders in the image 'ifP'. - Return their sizes in pixels as *leftP, *rightP, *topP, and *bottomP. + Return their sizes in pixels as borderSize[n]. Iff the image is all background, *hasBordersP == FALSE. Expect the input file to be positioned to the beginning of the image raster and leave it positioned arbitrarily. -----------------------------------------------------------------------------*/ - xel* xelrow; /* A row of the input image */ + xel * xelrow; /* A row of the input image */ int row; bool gottop; int left, right, bottom, top; - /* leftmost, etc. nonbackground pixel found so far. - Can be just off the edge of the image, so -1 or 'cols' or 'rows' - */ + /* leftmost, etc. nonbackground pixel found so far; -1 for none */ xelrow = pnm_allocrow(cols); left = cols; /* initial value */ - right = -1; /* initial value */ - top = rows; /* initial value */ - bottom = -1; /* initial value */ + right = -1; /* initial value */ + top = rows; /* initial value */ + bottom = -1; /* initial value */ gottop = FALSE; for (row = 0; row < rows; ++row) { @@ -275,7 +305,7 @@ findBordersInImage(FILE * const ifP, pnm_readpnmrow(ifP, xelrow, cols, maxval, format); col = 0; - while (PNM_EQUAL(xelrow[col], backgroundColor) && col < cols) + while (col < cols && PNM_EQUAL(xelrow[col], backgroundColor)) ++col; thisRowLeft = col; @@ -297,46 +327,45 @@ findBordersInImage(FILE * const ifP, bottom = row + 1; /* New candidate */ } } + + free(xelrow); if (right == -1) *hasBordersP = FALSE; else { *hasBordersP = TRUE; assert(right <= cols); assert(bottom <= rows); - *leftP = left - 0; - *rightP = cols - right; - *topP = top - 0; - *bottomP = rows - bottom; + borderSize[LEFT] = left - 0; + borderSize[RIGHT] = cols - right; + borderSize[TOP] = top - 0; + borderSize[BOTTOM] = rows - bottom; } } static void -findBordersInFile(const char * const borderFileName, +findBordersInFile(unsigned int const icols, + unsigned int const irows, + FILE * const borderFileP, xel const backgroundColor, bool const verbose, bool * const hasBordersP, - unsigned int * const leftP, - unsigned int * const rightP, - unsigned int * const topP, - unsigned int * const bottomP) { - - FILE * borderFileP; - int cols; - int rows; + unsigned int * const borderSize) { + + int bcols; /* cols and rows in the borderfile */ + int brows; xelval maxval; int format; - borderFileP = pm_openr(borderFileName); - - pnm_readpnminit(borderFileP, &cols, &rows, &maxval, &format); - - findBordersInImage(borderFileP, cols, rows, maxval, format, - backgroundColor, verbose, hasBordersP, - leftP, rightP, topP, bottomP); + pnm_readpnminit(borderFileP, &bcols, &brows, &maxval, &format); - pm_close(borderFileP); + if (bcols != icols || brows != irows) + pm_error("Image file and border file differ in size: %ux%u %ux%u", + icols, irows, bcols, brows); + + findBordersInImage(borderFileP, bcols, brows, maxval, format, + backgroundColor, verbose, hasBordersP, borderSize); } @@ -350,45 +379,24 @@ ending(unsigned int const n) { static void -reportOneEdge(unsigned int const oldBorderSize, - unsigned int const newBorderSize, - const char * const place) { - - if (newBorderSize > oldBorderSize) - pm_message("Adding %u pixel%s to the %u-pixel %s border", - newBorderSize - oldBorderSize, - ending(newBorderSize - oldBorderSize), - oldBorderSize, place); - else if (newBorderSize < oldBorderSize) - pm_message("Cropping %u pixel%s from the %u-pixel %s border", - oldBorderSize - newBorderSize, - ending(oldBorderSize - newBorderSize), - oldBorderSize, place); - else - pm_message("Leaving %s border unchanged at %u pixel%s", - place, oldBorderSize, ending(oldBorderSize)); -} - +reportCroppingParameters(cropOp const crop[]) { + unsigned int i; -static void -reportCroppingParameters(unsigned int const oldLeftBorderSize, - unsigned int const oldRightBorderSize, - unsigned int const oldTopBorderSize, - unsigned int const oldBottomBorderSize, - unsigned int const newLeftBorderSize, - unsigned int const newRightBorderSize, - unsigned int const newTopBorderSize, - unsigned int const newBottomBorderSize) { - - if (oldLeftBorderSize == 0 && oldRightBorderSize == 0 && - oldTopBorderSize == 0 && oldBottomBorderSize == 0) - pm_message("No Border found."); - - reportOneEdge(oldLeftBorderSize, newLeftBorderSize, "left" ); - reportOneEdge(oldRightBorderSize, newRightBorderSize, "right" ); - reportOneEdge(oldTopBorderSize, newTopBorderSize, "top" ); - reportOneEdge(oldBottomBorderSize, newBottomBorderSize, "bottom"); + for (i = 0; i < 4; ++i) { + if (crop[i].removeSize == 0 && crop[i].padSize == 0) + pm_message("Not cropping %s edge", edgeName[i]); + else { + if (crop[i].padSize > 0) + pm_message("Adding %u pixel%s to the %s border", + crop[i].padSize, ending(crop[i].padSize), + edgeName[i]); + if (crop[i].removeSize > 0) + pm_message("Cropping %u pixel%s from the %s border", + crop[i].removeSize, ending(crop[i].removeSize), + edgeName[i]); + } + } } @@ -408,21 +416,60 @@ fillRow(xel * const xelrow, static void -writeCropped(FILE * const ifP, - unsigned int const cols, - unsigned int const rows, - xelval const maxval, - int const format, - unsigned int const oldLeftBorder, - unsigned int const oldRightBorder, - unsigned int const oldTopBorder, - unsigned int const oldBottomBorder, - unsigned int const newLeftBorder, - unsigned int const newRightBorder, - unsigned int const newTopBorder, - unsigned int const newBottomBorder, - xel const backgroundColor, - FILE * const ofP) { +readOffBorderNonPbm(unsigned int const height, + FILE * const ifP, + unsigned int const cols, + xelval const maxval, + int const format) { + + xel * xelrow; + unsigned int i; + + xelrow = pnm_allocrow(cols); + + for (i = 0; i < height; ++i) + pnm_readpnmrow(ifP, xelrow, cols, maxval, format); + + pnm_freerow(xelrow); +} + + + +static void +outputNewBorderNonPbm(unsigned int const height, + unsigned int const width, + xel const color, + FILE * const ofP, + xelval const maxval, + int const format) { +/*---------------------------------------------------------------------------- + Output to 'ofP' a horizontal border (i.e. top or bottom) + of color 'backgroundColor', height 'height', width 'width'. +-----------------------------------------------------------------------------*/ + xel * xelrow; + unsigned int i; + + xelrow = pnm_allocrow(width); + + fillRow(xelrow, width, color); + + for (i = 0; i < height; ++i) + pnm_writepnmrow(ofP, xelrow, width, maxval, format, 0); + + pnm_freerow(xelrow); +} + + + +static void +writeCroppedNonPbm(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + cropOp const crop[], + xel const backgroundColor, + FILE * const ofP) { /* In order to do cropping, padding or both at the same time, we have a rather complicated row buffer: @@ -431,6 +478,11 @@ writeCropped(FILE * const ifP, the foreground pixels, the original border pixels, and the new border pixels. + We're calling foreground everything that isn't being cropped out + or padded in. So the "foreground" may include some of what is really + a background border in the original image -- because the user can + choose to retain part of that border as a margin. + The foreground pixels are in the center of the buffer, starting at Column 'foregroundLeft' and going to 'foregroundRight'. @@ -453,72 +505,65 @@ writeCropped(FILE * const ifP, That's for the middle rows. For the top and bottom, we just use the left portion of xelrow[], starting at 0. + + This is the general case. Enhancement for PBM appears below. + (Logic works for PBM). */ unsigned int const foregroundCols = - cols - oldLeftBorder - oldRightBorder; + cols - crop[LEFT].removeSize - crop[RIGHT].removeSize; unsigned int const outputCols = - foregroundCols + newLeftBorder + newRightBorder; + foregroundCols + crop[LEFT].padSize + crop[RIGHT].padSize; unsigned int const foregroundRows = - rows - oldTopBorder - oldBottomBorder; + rows - crop[TOP].removeSize - crop[BOTTOM].removeSize; unsigned int const outputRows = - foregroundRows + newTopBorder + newBottomBorder; + foregroundRows + crop[TOP].padSize + crop[BOTTOM].padSize; - unsigned int const foregroundLeft = MAX(oldLeftBorder, newLeftBorder); + unsigned int const foregroundLeft = + MAX(crop[LEFT].removeSize, crop[LEFT].padSize); /* Index into xelrow[] of leftmost pixel of foreground */ unsigned int const foregroundRight = foregroundLeft + foregroundCols; /* Index into xelrow[] just past rightmost pixel of foreground */ unsigned int const allocCols = - foregroundRight + MAX(oldRightBorder, newRightBorder); + foregroundRight + MAX(crop[RIGHT].removeSize, crop[RIGHT].padSize); - xel *xelrow; + xel * xelrow; unsigned int i; - assert(outputCols == newLeftBorder + foregroundCols + newRightBorder); - assert(outputRows == newTopBorder + foregroundRows + newBottomBorder); - pnm_writepnminit(ofP, outputCols, outputRows, maxval, format, 0); xelrow = pnm_allocrow(allocCols); - /* Read off existing top border */ - for (i = 0; i < oldTopBorder; ++i) - pnm_readpnmrow(ifP, xelrow, cols, maxval, format); + readOffBorderNonPbm(crop[TOP].removeSize, ifP, cols, maxval, format); + outputNewBorderNonPbm(crop[TOP].padSize, outputCols, backgroundColor, + ofP, maxval, format); - /* Output new top border */ - fillRow(xelrow, outputCols, backgroundColor); - for (i = 0; i < newTopBorder; ++i) - pnm_writepnmrow(ofP, xelrow, outputCols, maxval, format, 0); + /* Set left border pixels */ + fillRow(&xelrow[foregroundLeft - crop[LEFT].padSize], crop[LEFT].padSize, + backgroundColor); + /* Set right border pixels */ + fillRow(&xelrow[foregroundRight], crop[RIGHT].padSize, backgroundColor); /* Read and output foreground rows */ for (i = 0; i < foregroundRows; ++i) { - /* Set left border pixels */ - fillRow(&xelrow[foregroundLeft - newLeftBorder], newLeftBorder, - backgroundColor); - + /* Read foreground pixels */ - pnm_readpnmrow(ifP, &(xelrow[foregroundLeft - oldLeftBorder]), cols, - maxval, format); - - /* Set right border pixels */ - fillRow(&xelrow[foregroundRight], newRightBorder, backgroundColor); + pnm_readpnmrow(ifP, &(xelrow[foregroundLeft - crop[LEFT].removeSize]), + cols, maxval, format); pnm_writepnmrow(ofP, - &(xelrow[foregroundLeft - newLeftBorder]), outputCols, - maxval, format, 0); + &(xelrow[foregroundLeft - crop[LEFT].padSize]), + outputCols, maxval, format, 0); } - /* Read off existing bottom border */ - for (i = 0; i < oldBottomBorder; ++i) - pnm_readpnmrow(ifP, xelrow, cols, maxval, format); - - /* Output new bottom border */ - fillRow(xelrow, outputCols, backgroundColor); - for (i = 0; i < newBottomBorder; ++i) - pnm_writepnmrow(ofP, xelrow, outputCols, maxval, format, 0); + readOffBorderNonPbm(crop[BOTTOM].removeSize, ifP, cols, maxval, format); + + outputNewBorderNonPbm(crop[BOTTOM].padSize, outputCols, + backgroundColor, + ofP, maxval, format); pnm_freerow(xelrow); } @@ -526,103 +571,304 @@ writeCropped(FILE * const ifP, static void -determineNewBorders(struct cmdlineInfo const cmdline, - unsigned int const leftBorderSize, - unsigned int const rightBorderSize, - unsigned int const topBorderSize, - unsigned int const bottomBorderSize, - unsigned int * const newLeftSizeP, - unsigned int * const newRightSizeP, - unsigned int * const newTopSizeP, - unsigned int * const newBottomSizeP) { - - *newLeftSizeP = cmdline.left ? cmdline.margin : leftBorderSize ; - *newRightSizeP = cmdline.right ? cmdline.margin : rightBorderSize ; - *newTopSizeP = cmdline.top ? cmdline.margin : topBorderSize ; - *newBottomSizeP = cmdline.bottom ? cmdline.margin : bottomBorderSize ; -} +fillRowPBM(unsigned char * const bitrow, + unsigned int const cols, + unsigned int const blackWhite) { +/*---------------------------------------------------------------------------- + Fill the packed PBM row buffer bitrow[] with 'cols' columns of + black or white: black if 'blackWhite' is 1; white if it is '0'. + 'blackWhite' cannot be anything else. +-----------------------------------------------------------------------------*/ + unsigned int const colChars = pbm_packed_bytes(cols); + unsigned int i; + + assert(blackWhite == 0 || blackWhite == 1); + + for (i = 0; i < colChars; ++i) + bitrow[i] = blackWhite * 0xff; + if (cols % 8 > 0) + bitrow[colChars-1] <<= 8 - cols % 8; +} -int -main(int argc, char *argv[]) { - struct cmdlineInfo cmdline; - FILE * ifP; - /* The program's regular input file. Could be a seekable copy of it - in a temporary file. +static void +readOffBorderPbm(unsigned int const height, + FILE * const ifP, + unsigned int const cols, + int const format) { + + unsigned char * bitrow; + unsigned int i; + + bitrow = pbm_allocrow_packed(cols); + + for (i = 0; i < height; ++i) + pbm_readpbmrow_packed(ifP, bitrow, cols, format); + + pbm_freerow_packed(bitrow); +} + + + +static void +outputNewBorderPbm(unsigned int const height, + unsigned int const width, + unsigned int const blackWhite, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Output to 'ofP' a horizontal border (i.e. top or bottom) + of height 'height', width 'width'. Make it black if 'blackWhite' is + 1; white if 'blackWhite' is 0. 'blackWhite' can't be anything else. +-----------------------------------------------------------------------------*/ + unsigned char * bitrow; + unsigned int i; + + bitrow = pbm_allocrow_packed(width); + + fillRowPBM(bitrow, width, blackWhite); + + for (i = 0; i < height; ++i) + pbm_writepbmrow_packed(ofP, bitrow, width, 0); + + pbm_freerow_packed(bitrow); +} + + + +static void +writeCroppedPBM(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + int const format, + cropOp const crop[], + xel const backgroundColor, + FILE * const ofP) { + + /* See comments for writeCroppedNonPBM(), which uses identical logic flow. + Uses pbm functions instead of general pnm functions. + */ + + unsigned int const foregroundCols = + cols - crop[LEFT].removeSize - crop[RIGHT].removeSize; + unsigned int const outputCols = + foregroundCols + crop[LEFT].padSize + crop[RIGHT].padSize; + unsigned int const foregroundRows = + rows - crop[TOP].removeSize - crop[BOTTOM].removeSize; + unsigned int const outputRows = + foregroundRows + crop[TOP].padSize + crop[BOTTOM].padSize; + + unsigned int const foregroundLeft = + MAX(crop[LEFT].removeSize, crop[LEFT].padSize); + unsigned int const foregroundRight = foregroundLeft + foregroundCols; + + unsigned int const allocCols = + foregroundRight + MAX(crop[RIGHT].removeSize, crop[RIGHT].padSize); + + unsigned int const backgroundBlackWhite = + PNM_EQUAL(backgroundColor, pnm_whitexel(1, PBM_TYPE)) ? 0: 1; + + unsigned int const readOffset = foregroundLeft - crop[LEFT].removeSize; + unsigned int const writeOffset = foregroundLeft - crop[LEFT].padSize; + unsigned int const lastWriteChar = writeOffset/8 + (outputCols-1)/8; + unsigned char * bitrow; + unsigned int i; + + pbm_writepbminit(ofP, outputCols, outputRows, 0); + + bitrow = pbm_allocrow_packed(allocCols); + + readOffBorderPbm(crop[TOP].removeSize, ifP, cols, format); + + outputNewBorderPbm(crop[TOP].padSize, outputCols, backgroundBlackWhite, + ofP); + + /* Prepare padding: left and/or right */ + fillRowPBM(bitrow, allocCols, backgroundBlackWhite); + + /* Read and output foreground rows */ + for (i = 0; i < foregroundRows; ++i) { + /* Read foreground pixels */ + pbm_readpbmrow_bitoffset(ifP, bitrow, cols, format, readOffset); + + pbm_writepbmrow_bitoffset(ofP, + bitrow, outputCols, format, writeOffset); + + /* If there is right-side padding, repair the write buffer + distorted by pbm_writepbmrow_bitoffset() + (No need to mend any left-side padding) */ - bool eof; /* no more images in input stream */ + if (crop[RIGHT].padSize > 0) + bitrow[lastWriteChar] = backgroundBlackWhite * 0xff; + } + + readOffBorderPbm(crop[BOTTOM].removeSize, ifP, cols, format); + + outputNewBorderPbm(crop[BOTTOM].padSize, outputCols, backgroundBlackWhite, + ofP); + + pbm_freerow_packed(bitrow); +} + + + +static void +determineCrops(struct cmdlineInfo const cmdline, + unsigned int const oldBorderSize[], + cropOp * const cropArray) { + + edgeLocation i; + + for (i = 0; i < 4; ++i) { + if (cmdline.wantCrop[i]) { + if (oldBorderSize[i] > cmdline.margin) { + cropArray[i].removeSize = oldBorderSize[i] - cmdline.margin; + cropArray[i].padSize = 0; + } else { + cropArray[i].removeSize = 0; + cropArray[i].padSize = cmdline.margin - oldBorderSize[i]; + } + } else { + cropArray[i].removeSize = 0; + cropArray[i].padSize = 0; + } + } +} + + +static void +validateComputableSize(unsigned int const cols, + unsigned int const rows, + cropOp const crop[]) { + + double const newcols = + (double)cols + + (double)crop[LEFT].padSize + (double)crop[RIGHT].padSize; + + double const newrows = + (double)rows + + (double)crop[TOP].padSize + (double)crop[BOTTOM].padSize; + + if (newcols > INT_MAX) + pm_error("Output width too large: %.0f.", newcols); + if (newrows > INT_MAX) + pm_error("Output height too large: %.0f.", newrows); +} + + + +static void +cropOneImage(struct cmdlineInfo const cmdline, + FILE * const ifP, + FILE * const bdfP, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Crop the image to which the stream *ifP is presently positioned + and write the results to *ofP. If bdfP is non-null, use the image + to which stream *bdfP is presently positioned as the borderfile + (the file that tells us where the existing borders are in the input + image). Leave *ifP and *bdfP positioned after the image. + + *ifP is seekable; *bdfP may not be. +-----------------------------------------------------------------------------*/ xelval maxval; int format; int rows, cols; /* dimensions of input image */ bool hasBorders; - unsigned int oldLeftBorder, oldRightBorder, oldTopBorder, oldBottomBorder; + unsigned int oldBorder[4]; /* The sizes of the borders in the input image */ - unsigned int newLeftBorder, newRightBorder, newTopBorder, newBottomBorder; - /* The sizes of the borders in the output image */ + cropOp crop[4]; + /* The crops we have to do on each side */ + xel background; pm_filepos rasterpos; - pnm_init(&argc, argv); + pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); - parseCommandLine(argc, argv, &cmdline); + pm_tell2(ifP, &rasterpos, sizeof(rasterpos)); - ifP = pm_openr_seekable(cmdline.inputFilespec); + background = computeBackground(ifP, cols, rows, maxval, format, + cmdline.background, cmdline.verbose); - eof = FALSE; - while (!eof) { - pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); + if (cmdline.borderfile) { + findBordersInFile(cols, rows, bdfP, background, + cmdline.verbose, &hasBorders, oldBorder); + } else { + pm_seek2(ifP, &rasterpos, sizeof(rasterpos)); - pm_tell2(ifP, &rasterpos, sizeof(rasterpos)); + findBordersInImage(ifP, cols, rows, maxval, format, + background, cmdline.verbose, &hasBorders, + oldBorder); + } + if (!hasBorders) + pm_error("The image is entirely background; " + "there is nothing to crop."); - background = computeBackground(ifP, cols, rows, maxval, format, - cmdline.background, cmdline.verbose); + determineCrops(cmdline, oldBorder, crop); - if (cmdline.borderfile) { - findBordersInFile(cmdline.borderfile, - background, cmdline.verbose, &hasBorders, - &oldLeftBorder, &oldRightBorder, - &oldTopBorder, &oldBottomBorder); - } else { - pm_seek2(ifP, &rasterpos, sizeof(rasterpos)); + validateComputableSize(cols, rows, crop); - findBordersInImage(ifP, cols, rows, maxval, format, - background, cmdline.verbose, &hasBorders, - &oldLeftBorder, &oldRightBorder, - &oldTopBorder, &oldBottomBorder); - } - if (!hasBorders) - pm_error("The image is entirely background; " - "there is nothing to crop."); - - determineNewBorders(cmdline, - oldLeftBorder, oldRightBorder, - oldTopBorder, oldBottomBorder, - &newLeftBorder, &newRightBorder, - &newTopBorder, &newBottomBorder); - - if (cmdline.verbose) - reportCroppingParameters(oldLeftBorder, oldRightBorder, - oldTopBorder, oldBottomBorder, - newLeftBorder, newRightBorder, - newTopBorder, newBottomBorder); + if (cmdline.verbose) + reportCroppingParameters(crop); - pm_seek2(ifP, &rasterpos, sizeof(rasterpos)); + pm_seek2(ifP, &rasterpos, sizeof(rasterpos)); - writeCropped(ifP, cols, rows, maxval, format, - oldLeftBorder, oldRightBorder, - oldTopBorder, oldBottomBorder, - newLeftBorder, newRightBorder, - newTopBorder, newBottomBorder, - background, stdout); + if (PNM_FORMAT_TYPE(format) == PBM_TYPE) + writeCroppedPBM(ifP, cols, rows, format, crop, background, ofP); + else + writeCroppedNonPbm(ifP, cols, rows, maxval, format, crop, + background, ofP); +} + + + +int +main(int argc, const char *argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + /* The program's regular input file. Could be a seekable copy of + it in a temporary file. + */ + FILE * bdfP; /* The borderfile; NULL if none. No seeks. */ + bool eof; /* no more images in input stream */ + bool beof; /* no more images in borderfile stream */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr_seekable(cmdline.inputFilespec); + + if (cmdline.borderfile) + bdfP = pm_openr(cmdline.borderfile); + else + bdfP = NULL; + + eof = beof = FALSE; + while (!eof) { + cropOneImage(cmdline, ifP, bdfP, stdout); pnm_nextimage(ifP, &eof); + + if (bdfP) { + pnm_nextimage(bdfP, &beof); + + if (eof != beof) { + if (!eof) + pm_error("Input file has more images than border file."); + else + pm_error("Border file has more images than image file."); + } + } } pm_close(stdout); pm_close(ifP); + if (bdfP) + pm_close(bdfP); return 0; } |