diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2008-07-08 02:46:20 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2008-07-08 02:46:20 +0000 |
commit | 9dba55a7d0d16072377a5c7a9e32961932019efd (patch) | |
tree | d0cbb621147f917853de58bee490ec13ff5ee905 /editor | |
parent | fbc42f59d42c846ad2ee6a6ba99d77f5efb10dfa (diff) | |
download | netpbm-mirror-9dba55a7d0d16072377a5c7a9e32961932019efd.tar.gz netpbm-mirror-9dba55a7d0d16072377a5c7a9e32961932019efd.tar.xz netpbm-mirror-9dba55a7d0d16072377a5c7a9e32961932019efd.zip |
Add fast PBM path
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@665 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r-- | editor/pnmpaste.c | 568 |
1 files changed, 401 insertions, 167 deletions
diff --git a/editor/pnmpaste.c b/editor/pnmpaste.c index 38b316c6..0bf404d6 100644 --- a/editor/pnmpaste.c +++ b/editor/pnmpaste.c @@ -10,176 +10,410 @@ ** 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. +-----------------------------------------------------------------------------*/ + 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. +-----------------------------------------------------------------------------*/ + 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, cols, format); + + for (i = 0; i < shiftBytes; ++i) { + unsigned int const rsh = offset; + unsigned int const lsh = 8-rsh; + unsigned char const t = buffer[i-1] << lsh | buffer[i] >> 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[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 buffer0 = pbm_allocrow_packed(insetCols+8); + unsigned char * const buffer = buffer0 + 1; + 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(buffer0); + 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; +} |