diff options
Diffstat (limited to 'editor/pamundice.c')
-rw-r--r-- | editor/pamundice.c | 459 |
1 files changed, 299 insertions, 160 deletions
diff --git a/editor/pamundice.c b/editor/pamundice.c index dbe0a8df..60360f17 100644 --- a/editor/pamundice.c +++ b/editor/pamundice.c @@ -18,30 +18,28 @@ #include "nstring.h" #include "mallocvar.h" -#define MAXFILENAMELEN 80 - /* Maximum number of characters we accept in filenames */ - -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFilePattern; - /* null-terminated string, max MAXFILENAMELEN-10 characters */ unsigned int across; unsigned int down; - unsigned int hoverlap; - unsigned int voverlap; + unsigned int hoverlap; + unsigned int voverlap; + const char * listfile; + unsigned int listfileSpec; unsigned int verbose; }; static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP ) { +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. + 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. @@ -53,7 +51,7 @@ parseCommandLine(int argc, char ** argv, /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; - + unsigned int acrossSpec, downSpec; unsigned int hoverlapSpec, voverlapSpec; unsigned int option_def_index; @@ -69,6 +67,8 @@ parseCommandLine(int argc, char ** argv, &hoverlapSpec, 0); OPTENT3(0, "voverlap", OPT_UINT, &cmdlineP->voverlap, &voverlapSpec, 0); + OPTENT3(0, "listfile", OPT_STRING, &cmdlineP->listfile, + &cmdlineP->listfileSpec, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); @@ -76,13 +76,19 @@ 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 */ - pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); + pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 ); /* Uses and sets argc, argv, and some of *cmdline_p and others. */ - if (!acrossSpec) + if (acrossSpec) { + if (cmdlineP->across == 0) + pm_error ("-across value must be positive"); + } else cmdlineP->across = 1; - - if (!downSpec) + + if (downSpec) { + if (cmdlineP->down == 0) + pm_error ("-down value must be positive"); + } else cmdlineP->down = 1; if (!hoverlapSpec) @@ -91,16 +97,23 @@ parseCommandLine(int argc, char ** argv, if (!voverlapSpec) cmdlineP->voverlap = 0; - if (argc-1 < 1) - pm_error("You must specify one argument: the input file name " - "pattern (e.g. 'myimage%%2a%%2d.pnm')"); - else { - cmdlineP->inputFilePattern = argv[1]; - - if (argc-1 > 1) - pm_error("Progam takes at most one parameter: input file name. " - "You specified %u", argc-1); + if (cmdlineP->listfileSpec) { + if (argc-1 > 0) + pm_error("Program takes no parameters when -listfile is " + "specified. You specified %u", argc-1); + else + cmdlineP->inputFilePattern = NULL; + } else { + if (argc-1 < 1) + pm_error("You must specify one argument, the input file name " + "pattern (e.g. 'myimage%%2a%%2d.pnm'), or -listfile"); + else if (argc-1 > 1) + pm_error("Program takes at most one parameter: " + "the input file name pattern. You specified %u", argc-1); + else + cmdlineP->inputFilePattern = argv[1]; } + free(option_def); } @@ -128,7 +141,7 @@ buffer_init(struct buffer * const bufferP) { static void buffer_term(struct buffer * const bufferP) { - + free(bufferP->string); } @@ -184,7 +197,7 @@ getPrecision(const char * const pattern, inCursor = startInCursor; /* Start right after the '%' */ precision = 0; - + while (isdigit(pattern[inCursor])) { precision = 10 * precision + digitValue(pattern[inCursor]); ++inCursor; @@ -202,13 +215,24 @@ getPrecision(const char * const pattern, +typedef struct { + /* Context of % substitutions as we progress through a file name pattern */ + bool downSub; + /* There has been a %d (down) substitution */ + bool acrossSub; + /* There has been a %a (across) substitution */ +} SubstContext; + + + static void doSubstitution(const char * const pattern, unsigned int const startInCursor, unsigned int const rank, unsigned int const file, struct buffer * const bufferP, - unsigned int * const newInCursorP) { + unsigned int * const newInCursorP, + SubstContext * const substContextP) { unsigned int inCursor; @@ -219,7 +243,7 @@ doSubstitution(const char * const pattern, ++inCursor; } else { unsigned int precision; - + getPrecision(pattern, inCursor, &precision, &inCursor); if (pattern[inCursor] == '\0') @@ -232,16 +256,28 @@ doSubstitution(const char * const pattern, switch (pattern[inCursor]) { case 'a': - pm_asprintf(&substString, "%0*u", precision, file); - pm_asprintf(&desc, "file (across)"); + if (substContextP->acrossSub) + pm_error("Format specifier 'a' appears more than " + "once in input file pattern '%s'", pattern); + else { + pm_asprintf(&substString, "%0*u", precision, file); + pm_asprintf(&desc, "file (across)"); + substContextP->acrossSub = true; + } break; case 'd': - pm_asprintf(&substString, "%0*u", precision, rank); - pm_asprintf(&desc, "rank (down)"); + if (substContextP->downSub) + pm_error("Format specifier 'd' appears more than " + "once in input file pattern '%s'", pattern); + else { + pm_asprintf(&substString, "%0*u", precision, rank); + pm_asprintf(&desc, "rank (down)"); + substContextP->downSub = true; + } break; default: pm_error("Unknown format specifier '%c' in input file " - "pattern '%s'. Recognized format specifier s are " + "pattern '%s'. Recognized format specifiers are " "'%%a' (across) and '%%d (down)'", pattern[inCursor], pattern); } @@ -252,7 +288,7 @@ doSubstitution(const char * const pattern, desc, (unsigned)strlen(substString), precision); else buffer_addString(bufferP, substString); - + pm_strfree(desc); pm_strfree(substString); @@ -268,25 +304,32 @@ static void computeInputFileName(const char * const pattern, unsigned int const rank, unsigned int const file, - const char ** const fileNameP) { + const char ** const fileNameP, + bool * const rankFileIndependentP) { struct buffer buffer; unsigned int inCursor; + SubstContext substContext; buffer_init(&buffer); inCursor = 0; + substContext.downSub = 0; + substContext.acrossSub = 0; while (pattern[inCursor] != '\0') { if (pattern[inCursor] == '%') { ++inCursor; - doSubstitution(pattern, inCursor, rank, file, &buffer, &inCursor); + doSubstitution(pattern, inCursor, rank, file, &buffer, &inCursor, + &substContext); } else buffer_addChar(&buffer, pattern[inCursor++]); } + *rankFileIndependentP = !substContext.downSub && !substContext.acrossSub; + pm_asprintf(fileNameP, "%s", buffer.string); buffer_term(&buffer); @@ -295,8 +338,127 @@ computeInputFileName(const char * const pattern, + +static void +createInFileListFmFile(const char * const listFile, + unsigned int const nRank, + unsigned int const nFile, + const char *** const inputFileListP) { + + FILE * const lfP = pm_openr(listFile); + unsigned int const fileCt = nRank * nFile; + + const char ** inputFileList; + unsigned int fileSeq; + + MALLOCARRAY_NOFAIL(inputFileList, nRank * nFile); + + for (fileSeq = 0; fileSeq < fileCt; ) { + int eof; + size_t lineLen; + char * buf = NULL; /* initial value */ + size_t bufferSz = 0; /* initial value */ + + pm_getline(lfP, &buf, &bufferSz, &eof, &lineLen); + + if (eof) + pm_error("Premature EOF reading list file. " + "Read %u files. Should be %u.", fileSeq, fileCt); + else if (lineLen > 0) { + inputFileList[fileSeq] = buf; + ++fileSeq; + } + } + pm_close(lfP); + + *inputFileListP = inputFileList; + +} + + + +static void +createInFileListFmPattern(const char * const pattern, + unsigned int const nRank, + unsigned int const nFile, + const char *** const inputFileListP) { + + const char ** inputFileList; + unsigned int rank, file; + bool warnedSingleFile; + + MALLOCARRAY_NOFAIL(inputFileList, nRank * nFile); + + for (rank = 0, warnedSingleFile = false; rank < nRank ; ++rank) { + for (file = 0; file < nFile ; ++file) { + const unsigned int idx = rank * nFile + file; + + bool fileNmIsRankFileIndependent; + + computeInputFileName(pattern, rank, file, &inputFileList[idx], + &fileNmIsRankFileIndependent); + + if (fileNmIsRankFileIndependent && !warnedSingleFile) { + pm_message("Warning: No grid location (%%a/%%d) specified " + "in input file pattern '%s'. " + "Input is single file", pattern); + warnedSingleFile = true; + } + } + } + *inputFileListP = inputFileList; +} + + + +static void +destroyInFileList(const char ** const inputFileList, + unsigned int const nRank, + unsigned int const nFile) { + + unsigned int const fileCt = nRank * nFile; + + unsigned int fileSeq; + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) + pm_strfree(inputFileList[fileSeq]); + + free(inputFileList); +} + + + +typedef struct { + unsigned int nRank; /* Number of images in the vertical direction */ + unsigned int nFile; /* Number of images in the horizontal direction */ + unsigned int hoverlap; /* horizontal overlap */ + unsigned int voverlap; /* vertical overlap */ + const char ** list; /* List (1-dimensional array) of filenames */ + /* Row-major, top to bottom, left to right */ +} InputFiles; + + + +static const char * +inputFileName(InputFiles const inputFiles, + unsigned int const rank, + unsigned int const file) { +/*---------------------------------------------------------------------------- + A selected entry from "inputFiles.list" based on "rank" and "file". + + Currently we assume that the list is a one-dimensional represetation + of an array, row-major, top to bottom and left to right in each row. +----------------------------------------------------------------------------*/ + assert(rank < inputFiles.nRank); + assert(file < inputFiles.nFile); + + return inputFiles.list[rank * inputFiles.nFile + file]; +} + + + static void -getCommonInfo(const char * const inputFilePattern, +getCommonInfo(InputFiles const inputFiles, int * const formatP, unsigned int * const depthP, sample * const maxvalP, @@ -306,16 +468,12 @@ getCommonInfo(const char * const inputFilePattern, among all input images and the output image. I.e. everything except width and height. -----------------------------------------------------------------------------*/ - const char * fileName; - /* Name of top left input image */ FILE * ifP; /* Top left input image stream */ struct pam inpam00; /* Description of top left input image */ - computeInputFileName(inputFilePattern, 0, 0, &fileName); - - ifP = pm_openr(fileName); + ifP = pm_openr(inputFileName(inputFiles, 0, 0)); pnm_readpaminit(ifP, &inpam00, PAM_STRUCT_SIZE(tuple_type)); @@ -325,40 +483,19 @@ getCommonInfo(const char * const inputFilePattern, strcpy(tupleType, inpam00.tuple_type); pm_close(ifP); - - pm_strfree(fileName); } -static FILE * -openInputImage(const char * const inputFilePattern, - unsigned int const rank, - unsigned int const file) { - - FILE * retval; - const char * fileName; - - computeInputFileName(inputFilePattern, rank, file, &fileName); - - retval = pm_openr(fileName); - - pm_strfree(fileName); - - return retval; -} - - - static void -getImageInfo(const char * const inputFilePattern, +getImageInfo(InputFiles const inputFiles, unsigned int const rank, unsigned int const file, struct pam * const pamP) { FILE * ifP; - ifP = openInputImage(inputFilePattern, rank, file); + ifP = pm_openr(inputFileName(inputFiles, rank, file)); pnm_readpaminit(ifP, pamP, PAM_STRUCT_SIZE(tuple_type)); @@ -369,76 +506,70 @@ getImageInfo(const char * const inputFilePattern, static void -getOutputWidth(const char * const inputFilePattern, - unsigned int const nFile, - unsigned int const hoverlap, - int * const widthP) { +getOutputWidth(InputFiles const inputFiles, + int * const widthP) { /*---------------------------------------------------------------------------- - Get the output width by adding up the widths of all 'nFile' images of - the top rank, and allowing for overlap of 'hoverlap' pixels. + Get the output width by adding up the widths of all 'inputFiles.nFile' + images of the top rank, and allowing for overlap of 'inputFiles.hoverlap' + pixels. -----------------------------------------------------------------------------*/ - unsigned int totalWidth; + double totalWidth; unsigned int file; - for (file = 0, totalWidth = 0; file < nFile; ++file) { + for (file = 0, totalWidth = 0; file < inputFiles.nFile; ++file) { struct pam inpam; - getImageInfo(inputFilePattern, 0, file, &inpam); + getImageInfo(inputFiles, 0, file, &inpam); - if (inpam.width < hoverlap) + if (inpam.width < inputFiles.hoverlap) pm_error("Rank 0, file %u image has width %u, " "which is less than the horizontal overlap of %u pixels", - file, inpam.width, hoverlap); + file, inpam.width, inputFiles.hoverlap); else { totalWidth += inpam.width; - if (file < nFile-1) - totalWidth -= hoverlap; + if (file < inputFiles.nFile-1) + totalWidth -= inputFiles.hoverlap; } } - *widthP = totalWidth; + *widthP = (int) totalWidth; } static void -getOutputHeight(const char * const inputFilePattern, - unsigned int const nRank, - unsigned int const voverlap, - int * const heightP) { +getOutputHeight(InputFiles const inputFiles, + int * const heightP) { /*---------------------------------------------------------------------------- - Get the output height by adding up the widths of all 'nRank' images of - the left file, and allowing for overlap of 'voverlap' pixels. + Get the output height by adding up the widths of all 'inputFiles.nRank' + images of the left file, and allowing for overlap of 'inputFiles.voverlap' + pixels. -----------------------------------------------------------------------------*/ - unsigned int totalHeight; + double totalHeight; unsigned int rank; - for (rank = 0, totalHeight = 0; rank < nRank; ++rank) { + for (rank = 0, totalHeight = 0; rank < inputFiles.nRank; ++rank) { struct pam inpam; - getImageInfo(inputFilePattern, rank, 0, &inpam); + getImageInfo(inputFiles, rank, 0, &inpam); - if (inpam.height < voverlap) + if (inpam.height < inputFiles.voverlap) pm_error("Rank %u, file 0 image has height %u, " "which is less than the vertical overlap of %u pixels", - rank, inpam.height, voverlap); - + rank, inpam.height, inputFiles.voverlap); + totalHeight += inpam.height; - - if (rank < nRank-1) - totalHeight -= voverlap; + + if (rank < inputFiles.nRank-1) + totalHeight -= inputFiles.voverlap; } - *heightP = totalHeight; + *heightP = (int) totalHeight; } static void -initOutpam(const char * const inputFilePattern, - unsigned int const nFile, - unsigned int const nRank, - unsigned int const hoverlap, - unsigned int const voverlap, +initOutpam(InputFiles const inputFiles, FILE * const ofP, bool const verbose, struct pam * const outpamP) { @@ -447,10 +578,10 @@ initOutpam(const char * const inputFilePattern, *outpamP. Do this by examining the top rank and left file of the input images, - which are in files named by 'inputFilePattern', 'nFile', and 'nRank'. + which are in 'inputFiles.list'. - In computing dimensions, assume 'hoverlap' pixels of horizontal - overlap and 'voverlap' pixels of vertical overlap. + In computing dimensions, assume 'inputFiles.hoverlap' pixels of horizontal + overlap and 'inputFiles.voverlap' pixels of vertical overlap. We overlook any inconsistencies among the images. E.g. if two images have different depths, we just return one of them. If two images in @@ -459,23 +590,23 @@ initOutpam(const char * const inputFilePattern, Therefore, Caller must check all the input images to make sure they are consistent with the information we return. -----------------------------------------------------------------------------*/ - assert(nFile >= 1); - assert(nRank >= 1); + assert(inputFiles.nFile >= 1); + assert(inputFiles.nRank >= 1); outpamP->size = sizeof(*outpamP); outpamP->len = PAM_STRUCT_SIZE(tuple_type); outpamP->file = ofP; outpamP->plainformat = 0; - - getCommonInfo(inputFilePattern, &outpamP->format, &outpamP->depth, + + getCommonInfo(inputFiles, &outpamP->format, &outpamP->depth, &outpamP->maxval, outpamP->tuple_type); - getOutputWidth(inputFilePattern, nFile, hoverlap, &outpamP->width); + getOutputWidth(inputFiles, &outpamP->width); - getOutputHeight(inputFilePattern, nRank, voverlap, &outpamP->height); + getOutputHeight(inputFiles, &outpamP->height); if (verbose) { - pm_message("Output width = %u pixels", outpamP->width); + pm_message("Output width = %u pixels", outpamP->width); pm_message("Output height = %u pixels", outpamP->height); } } @@ -485,53 +616,50 @@ initOutpam(const char * const inputFilePattern, static void openInStreams(struct pam inpam[], unsigned int const rank, - unsigned int const fileCount, - char const inputFilePattern[]) { + InputFiles const inputFiles) { /*---------------------------------------------------------------------------- Open the input files for a single horizontal slice (there's one file for each vertical slice) and read the Netpbm headers from them. Return the pam structures to describe each as inpam[]. - Open the files for horizontal slice number 'rank', assuming there are - 'fileCount' vertical slices (so open 'fileCount' files). Use - inputFilePattern[] with each rank and file number to compute the name of - each file. + Open the files for horizontal slice number 'rank', as described by + 'inputFiles'. -----------------------------------------------------------------------------*/ unsigned int file; - for (file = 0; file < fileCount; ++file) { - FILE * const ifP = openInputImage(inputFilePattern, rank, file); + for (file = 0; file < inputFiles.nFile; ++file) { + FILE * const ifP = pm_openr(inputFileName(inputFiles, rank, file)); pnm_readpaminit(ifP, &inpam[file], PAM_STRUCT_SIZE(tuple_type)); - } + } } static void closeInFiles(struct pam pam[], - unsigned int const fileCount) { + unsigned int const fileCt) { /*---------------------------------------------------------------------------- - Close the 'fileCount' input file streams represented by pam[]. + Close the 'fileCt' input file streams represented by pam[]. -----------------------------------------------------------------------------*/ - unsigned int file; - - for (file = 0; file < fileCount; ++file) - pm_close(pam[file].file); + unsigned int fileSeq; + + for (fileSeq = 0; fileSeq < fileCt; ++fileSeq) + pm_close(pam[fileSeq].file); } static void -assembleRow(tuple outputRow[], - struct pam inpam[], - unsigned int const fileCount, +assembleRow(tuple outputRow[], + struct pam inpam[], + unsigned int const fileCt, unsigned int const hOverlap) { /*---------------------------------------------------------------------------- - Assemble the row outputRow[] from the 'fileCount' input files + Assemble the row outputRow[] from the 'fileCt' input files described out inpam[]. - 'hOverlap', which is meaningful only when fileCount is greater than 1, + 'hOverlap', which is meaningful only when 'fileCt' is greater than 1, is the amount by which files overlap each other. We assume every input image is at least that wide. @@ -539,19 +667,17 @@ assembleRow(tuple outputRow[], entire assembly. -----------------------------------------------------------------------------*/ tuple * inputRow; - unsigned int file; + unsigned int fileSeq; - for (file = 0, inputRow = &outputRow[0]; - file < fileCount; - ++file) { + for (fileSeq = 0, inputRow = &outputRow[0]; fileSeq < fileCt; ++fileSeq) { - unsigned int const overlap = file == fileCount - 1 ? 0 : hOverlap; + unsigned int const overlap = fileSeq == fileCt - 1 ? 0 : hOverlap; - assert(hOverlap <= inpam[file].width); + assert(hOverlap <= inpam[fileSeq].width); - pnm_readpamrow(&inpam[file], inputRow); + pnm_readpamrow(&inpam[fileSeq], inputRow); - inputRow += inpam[file].width - overlap; + inputRow += inpam[fileSeq].width - overlap; } } @@ -574,6 +700,7 @@ allocInpam(unsigned int const rankCount, + static void verifyRankFileAttributes(struct pam * const inpam, unsigned int const nFile, @@ -622,7 +749,7 @@ verifyRankFileAttributes(struct pam * const inpam, rank, file, inpamP->height, inpam[0].height); else { totalWidth += inpamP->width; - + if (file < nFile-1) totalWidth -= hoverlap; } @@ -637,11 +764,7 @@ verifyRankFileAttributes(struct pam * const inpam, static void assembleTiles(struct pam * const outpamP, - const char * const inputFilePattern, - unsigned int const nFile, - unsigned int const nRank, - unsigned int const hoverlap, - unsigned int const voverlap, + InputFiles const inputFiles, struct pam inpam[], tuple * const tuplerow) { @@ -649,12 +772,17 @@ assembleTiles(struct pam * const outpamP, /* Number of the current rank (horizontal slice). Ranks are numbered sequentially starting at 0. */ - + + unsigned int const nRank = inputFiles.nRank; + unsigned int const nFile = inputFiles.nFile; + unsigned int const hoverlap = inputFiles.hoverlap; + unsigned int const voverlap = inputFiles.voverlap; + for (rank = 0; rank < nRank; ++rank) { unsigned int row; unsigned int rankHeight; - openInStreams(inpam, rank, nFile, inputFilePattern); + openInStreams(inpam, rank, inputFiles); verifyRankFileAttributes(inpam, nFile, outpamP, hoverlap, rank); @@ -672,36 +800,47 @@ assembleTiles(struct pam * const outpamP, int -main(int argc, char ** argv) { +main(int argc, const char ** argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; + InputFiles inputFiles; struct pam outpam; struct pam * inpam; /* malloc'ed. inpam[x] is the pam structure that controls the - current rank of file x. + current rank of file x. */ tuple * tuplerow; - pnm_init(&argc, argv); - + pm_proginit(&argc, argv); + parseCommandLine(argc, argv, &cmdline); - + allocInpam(cmdline.across, &inpam); - initOutpam(cmdline.inputFilePattern, cmdline.across, cmdline.down, - cmdline.hoverlap, cmdline.voverlap, stdout, cmdline.verbose, - &outpam); - + if (cmdline.listfileSpec) + createInFileListFmFile(cmdline.listfile, + cmdline.down, cmdline.across, + &inputFiles.list); + else + createInFileListFmPattern(cmdline.inputFilePattern, + cmdline.down, cmdline.across, + &inputFiles.list); + + inputFiles.nFile = cmdline.across; + inputFiles.nRank = cmdline.down; + inputFiles.hoverlap = cmdline.hoverlap; + inputFiles.voverlap = cmdline.voverlap; + + initOutpam(inputFiles, stdout, cmdline.verbose, &outpam); + tuplerow = pnm_allocpamrow(&outpam); pnm_writepaminit(&outpam); - assembleTiles(&outpam, - cmdline.inputFilePattern, cmdline.across, cmdline.down, - cmdline.hoverlap, cmdline.voverlap, inpam, tuplerow); + assembleTiles(&outpam, inputFiles, inpam, tuplerow); pnm_freepamrow(tuplerow); - + destroyInFileList(inputFiles.list, inputFiles.nRank, inputFiles.nFile); free(inpam); return 0; |