/***************************************************************************** pamdice ****************************************************************************** Slice a Netpbm image vertically and/or horizontally into multiple images. By Bryan Henderson, San Jose CA 2001.01.31 Contributed to the public domain. ******************************************************************************/ #include #include "pam.h" #include "shhopt.h" #include "nstring.h" #define MAXFILENAMELEN 80 /* Maximum number of characters we accept in filenames */ struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char *inputFilespec; /* '-' if stdin */ char *outstem; /* null-terminated string, max MAXFILENAMELEN-10 characters */ unsigned int sliceVertically; /* boolean */ unsigned int sliceHorizontally; /* boolean */ unsigned int width; unsigned int height; unsigned int verbose; }; static void parseCommandLine ( int argc, 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 = malloc( 100*sizeof( optEntry ) ); /* Instructions to optParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int outstemSpec; unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, &cmdlineP->sliceVertically, 0 ); OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, &cmdlineP->sliceHorizontally, 0 ); OPTENT3(0, "outstem", OPT_STRING, &cmdlineP->outstem, &outstemSpec, 0 ); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0 ); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ /* set defaults */ optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); /* Uses and sets argc, argv, and some of *cmdline_p and others. */ if (!outstemSpec) pm_error("You must specify the -outstem option to indicate where to " "put the output images."); else if (strlen(cmdlineP->outstem) > MAXFILENAMELEN - 10) pm_error("-outstem value is too long. That would exceed the maximum " "allowable filename length of %u", MAXFILENAMELEN); if (argc-1 < 1) cmdlineP->inputFilespec = "-"; else if (argc-1 == 1) cmdlineP->inputFilespec = argv[1]; else pm_error("Progam takes at most 1 parameters: the file specification. " "You specified %d", argc-1); } static void computeSliceGeometry(struct cmdlineInfo const cmdline, struct pam const inpam, bool const verbose, unsigned int * const nHorizSliceP, unsigned int * const sliceWidthP, unsigned int * const nVertSliceP, unsigned int * const sliceHeightP) { /*---------------------------------------------------------------------------- Compute the geometry of the common slices. Edge slices may be smaller than this. -----------------------------------------------------------------------------*/ if (cmdline.sliceHorizontally) { *nHorizSliceP = ((inpam.height-1) / cmdline.height) + 1; *sliceHeightP = cmdline.height; } else { *nHorizSliceP = 1; *sliceHeightP = inpam.height; } if (cmdline.sliceVertically) { *nVertSliceP = ((inpam.width-1) / cmdline.width) + 1; *sliceWidthP = cmdline.width; } else { *nVertSliceP = 1; *sliceWidthP = inpam.width; } if (*nVertSliceP > 100) pm_error("You have %d vertical slices. The maximum allowed is 100", *nVertSliceP); if (*nHorizSliceP > 100) pm_error("You have %d horizontal slices. The maximum allowed is 100", *nHorizSliceP); if (verbose) { pm_message("Creating %u images, %u across by %u down", *nVertSliceP * *nHorizSliceP, *nVertSliceP, *nHorizSliceP); } } static void openOutStreams(struct pam const inpam, struct pam outpam[], unsigned int const horizSlice, unsigned int const nVertSlice, unsigned int const sliceHeight, unsigned int sliceWidth, const char outstem[]) { /*---------------------------------------------------------------------------- Open the output files for a single horizontal slice (there's one file for each vertical slice) and write the Netpbm headers to them. Also compute the pam structures to control each. Assume 'horizSlice' is less than 100 and 'nVertSlice' is less than or equal to 100. Assume the output filename (controlled by 'outstem') fits in MAXFILENAMELEN characters. -----------------------------------------------------------------------------*/ unsigned int vertSlice; for (vertSlice = 0; vertSlice < nVertSlice; ++vertSlice) { char filename[MAXFILENAMELEN+1]; const char * filenameSuffix; int rc; switch(PNM_FORMAT_TYPE(inpam.format)) { case PPM_TYPE: filenameSuffix = "ppm"; break; case PGM_TYPE: filenameSuffix = "pgm"; break; case PBM_TYPE: filenameSuffix = "pbm"; break; case PAM_TYPE: filenameSuffix = "pam"; break; default: filenameSuffix = ""; break; } if (vertSlice > 99 || horizSlice > 99) pm_error("Internal error: slice number doesn't fit in filename"); rc = snprintfN(filename, sizeof(filename), "%s_%02d_%02d.%s", outstem, horizSlice, vertSlice, filenameSuffix); if (rc > sizeof(filename)) pm_error("Internal error: generated filename is too long."); outpam[vertSlice] = inpam; outpam[vertSlice].file = pm_openw(filename); outpam[vertSlice].width = MIN(sliceWidth, inpam.width - vertSlice*sliceWidth); outpam[vertSlice].height = sliceHeight; pnm_writepaminit(&outpam[vertSlice]); } } static void closeOutFiles(struct pam pam[], unsigned int const nVertSlice) { unsigned int vertSlice; for (vertSlice = 0; vertSlice < nVertSlice; ++vertSlice) pm_close(pam[vertSlice].file); } static void sliceRow(tuple inputRow[], struct pam outpam[], unsigned int const nVertSlice) { /*---------------------------------------------------------------------------- Distribute the row inputRow[] across the output 'nVerticalSlice' files described by outpam[]. Each outpam[x] tells how many columns of inputRow[] to take and what their composition is. -----------------------------------------------------------------------------*/ tuple * outputRow; unsigned int vertSlice; for (vertSlice = 0, outputRow = inputRow; vertSlice < nVertSlice; outputRow += outpam[vertSlice].width, ++vertSlice) { pnm_writepamrow(&outpam[vertSlice], outputRow); } } int main(int argc, char ** argv) { struct cmdlineInfo cmdline; FILE *ifP; struct pam inpam; tuple* inputRow; /* Row from input image */ unsigned int rowsRead; unsigned int horizSlice; /* Number of the current horizontal slice. Slices are numbered sequentially starting at 0. */ unsigned int sliceWidth; /* Width in pam columns of each vertical slice, except that the rightmost slice may be narrower. If we aren't slicing vertically, that means one slice, i.e. the slice width is the image width. */ unsigned int sliceHeight; /* Height in pam rows of each horizontal slice, except that the bottom slice may be shorter. If we aren't slicing horizontally, that means one slice, i.e. the slice height is the image height. */ unsigned int nHorizSlice; unsigned int nVertSlice; struct pam outpam[100]; /* outpam[x] is the pam structure that controls the current horizontal slice of vertical slice x. */ pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, sizeof(inpam)); computeSliceGeometry(cmdline, inpam, !!cmdline.verbose, &nHorizSlice, &sliceWidth, &nVertSlice, &sliceHeight); inputRow = pnm_allocpamrow(&inpam); rowsRead = 0; /* initial value */ for (horizSlice = 0; horizSlice < nHorizSlice; ++horizSlice) { unsigned int row; unsigned int thisSliceHeight = MIN(sliceHeight, inpam.height-rowsRead); openOutStreams(inpam, outpam, horizSlice, nVertSlice, thisSliceHeight, sliceWidth, cmdline.outstem); for (row = 0; row < thisSliceHeight; ++row) { pnm_readpamrow(&inpam, inputRow); ++rowsRead; sliceRow(inputRow, outpam, nVertSlice); } closeOutFiles(outpam, nVertSlice); } pnm_freepamrow(inputRow); pm_close(ifP); exit(0); }