static void determineBackgroundColor(struct pam * const pamP, bool const verbose, tuple * const bgColorP) { /*---------------------------------------------------------------------------- Determine what color is the background color of the image in the file represented by *pamP. Expect the file to be positioned to the start of the raster, and leave it positioned arbitrarily. -----------------------------------------------------------------------------*/ unsigned int row; tuple * tuplerow; tuple ul, ur, ll, lr; /* Color of upper left, upper right, lower left, lower right */ tuplerow = pnm_allocpamrow(pamP); ul = pnm_allocpamtuple(pamP); ur = pnm_allocpamtuple(pamP); ll = pnm_allocpamtuple(pamP); lr = pnm_allocpamtuple(pamP); pnm_readpamrow(pamP, tuplerow); pnm_assigntuple(pamP, ul, tuplerow[0]); pnm_assigntuple(pamP, ur, tuplerow[pamP->width-1]); for (row = 1; row < pamP->height; ++row) pnm_readpamrow(pamP, tuplerow); pnm_assigntuple(pamP, ll, tuplerow[0]); pnm_assigntuple(pamP, lr, tuplerow[pamP->width-1]); selectBackground(pamP, ul, ur, ll, lr, bgColorP); if (verbose) { int const hexokTrue = 1; const char * const colorname = pnm_colorname(pamP, *bgColorP, hexokTrue); pm_message("Background color is %s", colorname); strfree(colorname); } pnm_freepamtuple(lr); pnm_freepamtuple(ll); pnm_freepamtuple(ur); pnm_freepamtuple(ul); pnm_freepamrow(tuplerow); }
static void analyzeDistribution(struct pam * const inpamP, bool const verbose, const unsigned int ** const histogramP, struct range * const rangeP) { /*---------------------------------------------------------------------------- Find the distribution of the sample values -- minimum, maximum, and how many of each value -- in input image *inpamP, whose file is positioned to the raster. Return the minimum and maximum as *rangeP and the frequency distribution as *histogramP, an array such that histogram[i] is the number of pixels that have sample value i. Assume the file is positioned to the raster upon entry and leave it positioned at the same place. -----------------------------------------------------------------------------*/ unsigned int row; tuple * inrow; tuplen * inrown; unsigned int * histogram; /* malloced array */ unsigned int i; pm_filepos rasterPos; /* Position in input file of the raster */ pm_tell2(inpamP->file, &rasterPos, sizeof(rasterPos)); inrow = pnm_allocpamrow(inpamP); inrown = pnm_allocpamrown(inpamP); MALLOCARRAY(histogram, inpamP->maxval+1); if (histogram == NULL) pm_error("Unable to allocate space for %lu-entry histogram", inpamP->maxval+1); /* Initialize histogram -- zero occurrences of everything */ for (i = 0; i <= inpamP->maxval; ++i) histogram[i] = 0; initRange(rangeP); for (row = 0; row < inpamP->height; ++row) { unsigned int col; pnm_readpamrow(inpamP, inrow); pnm_normalizeRow(inpamP, inrow, NULL, inrown); for (col = 0; col < inpamP->width; ++col) { ++histogram[inrow[col][0]]; addToRange(rangeP, inrown[col][0]); } } *histogramP = histogram; pnm_freepamrow(inrow); pnm_freepamrown(inrown); pm_seek2(inpamP->file, &rasterPos, sizeof(rasterPos)); if (verbose) pm_message("Pixel values range from %f to %f", rangeP->min, rangeP->max); }
static void extractOneImage(FILE * const infileP, FILE * const outfileP) { /*---------------------------------------------------------------------------- Copy a complete image from input stream *infileP to output stream *outfileP. But if outfileP == NULL, just read the image and discard it. -----------------------------------------------------------------------------*/ struct pam inpam; struct pam outpam; enum pm_check_code checkRetval; unsigned int row; tuple * tuplerow; pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type)); pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval); outpam = inpam; outpam.file = outfileP; if (outfileP) pnm_writepaminit(&outpam); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); if (outfileP) pnm_writepamrow(&outpam, tuplerow); } pnm_freepamrow(tuplerow); }
static void writePam(struct pam * const outpamP, unsigned int const nfiles, const coord * const coords, const struct pam * const imgs) { tuple *tuplerow; int i; pnm_writepaminit(outpamP); tuplerow = pnm_allocpamrow(outpamP); for (i = 0; i < outpamP->height; ++i) { int j; for (j = 0; j < nfiles; ++j) { if (coords[j].y <= i && i < coords[j].y + imgs[j].height) { pnm_readpamrow(&imgs[j], &tuplerow[coords[j].x]); adjustDepth(tuplerow, &imgs[j], outpamP, coords[j]); adjustMaxval(tuplerow, &imgs[j], outpamP, coords[j]); } } pnm_writepamrow(outpamP, tuplerow); } pnm_freepamrow(tuplerow); }
static void extractOneImage(FILE * const infileP, FILE * const outfileP) { struct pam inpam; struct pam outpam; enum pm_check_code checkRetval; unsigned int row; tuple * tuplerow; pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type)); pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval); outpam = inpam; outpam.file = outfileP; pnm_writepaminit(&outpam); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); pnm_writepamrow(&outpam, tuplerow); } pnm_freepamrow(tuplerow); }
static void wipeoutLr(FILE * const ifP, FILE * const ofP) { /* left-right we can read row-by-row */ struct pam inpam, outpam; tuple ** tuples; tuple * tuplerow; unsigned int row; pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); outpam = inpam; outpam.file = ofP; pnm_writepaminit(&outpam); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); wipeRowByCol(inpam, tuples, tuplerow); pnm_writepamrow(&outpam, tuplerow); } pnm_freepamrow(tuplerow); }
static void initPi(unsigned char ** const pi, const struct pam * const pamP, tuple const backgroundColor) { /*---------------------------------------------------------------------------- Set the initial info about every pixel in pi[][]. Read through the image in the file described by *inpamP and set each pixel which is not of background color, and therefore obviously foreground, to type PT_FG. Set every other pixel to PT_UNKNOWN. -----------------------------------------------------------------------------*/ tuple * tuplerow; unsigned int row; tuplerow = pnm_allocpamrow(pamP); for (row = 0; row < pamP->height; ++row) { unsigned int col; pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { pi[row][col] = pnm_tupleequal(pamP, tuplerow[col], backgroundColor) ? PT_UNKNOWN : PT_FG; } } pnm_freepamrow(tuplerow); }
static void addImageColorsToHash(struct pam * const pamP, tuplehash const tuplehash, unsigned int * const colorCountP) { tuple * tuplerow; unsigned int row; tuplerow = pnm_allocpamrow(pamP); for (row = 0; row < pamP->height; ++row) { unsigned int col; pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { int firstOccurrence; pnm_addtuplefreqoccurrence(pamP, tuplerow[col], tuplehash, &firstOccurrence); if (firstOccurrence) ++(*colorCountP); } } pnm_freepamrow(tuplerow); }
static void computehashrecoverable(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, sample const newMaxval, unsigned int * const sizeP, tuplehash * const tuplefreqhashP, tuple ** const rowbufferP, tuple * const colorP) { /*---------------------------------------------------------------------------- This is computetuplefreqhash(), only it leaves a trail so that if it happens to longjmp out because of a failed memory allocation, the setjmp'er can cleanup whatever it had done so far. -----------------------------------------------------------------------------*/ unsigned int row; struct pam freqPam; bool full; freqPam = *pamP; freqPam.maxval = newMaxval; *tuplefreqhashP = pnm_createtuplehash(); *sizeP = 0; /* initial value */ *rowbufferP = pnm_allocpamrow(pamP); *colorP = pnm_allocpamtuple(&freqPam); full = FALSE; /* initial value */ /* Go through the entire raster, building a hash table of tuple values. */ for (row = 0; row < pamP->height && !full; ++row) { int col; const tuple * tuplerow; /* The row of tuples we are processing */ if (tupleArray) tuplerow = tupleArray[row]; else { pnm_readpamrow(pamP, *rowbufferP); tuplerow = *rowbufferP; } for (col = 0; col < pamP->width && !full; ++col) { pnm_scaletuple(pamP, *colorP, tuplerow[col], freqPam.maxval); addColorOccurrenceToHash( *colorP, *tuplefreqhashP, &freqPam, maxsize, sizeP, &full); } } pnm_freepamtuple(*colorP); *colorP = NULL; pnm_freepamrow(*rowbufferP); *rowbufferP = NULL; if (full) { pnm_destroytuplehash(*tuplefreqhashP); *tuplefreqhashP = NULL; } }
int main(int argc, char *argv[]) { FILE * ifP; tuple * tuplerow; /* Row from input image */ unsigned int row; struct cmdlineInfo cmdline; struct pam inpam; struct pam outpam; pnm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); tuplerow = pnm_allocpamrow(&inpam); outpam = inpam; /* Initial value -- most fields should be same */ outpam.file = stdout; if (inpam.height % 2 == 0) outpam.height = inpam.height / 2; else { if (cmdline.rowsToTake == ODD) outpam.height = inpam.height / 2; else outpam.height = inpam.height / 2 + 1; } pnm_writepaminit(&outpam); { unsigned int modulusToTake; /* The row number mod 2 of the rows that are supposed to go into the output. */ switch (cmdline.rowsToTake) { case EVEN: modulusToTake = 0; break; case ODD: modulusToTake = 1; break; default: pm_error("INTERNAL ERROR: invalid rowsToTake"); } /* Read input and write out rows extracted from it */ for (row = 0; row < inpam.height; row++) { pnm_readpamrow(&inpam, tuplerow); if (row % 2 == modulusToTake) pnm_writepamrow(&outpam, tuplerow); } } pnm_freepamrow(tuplerow); pm_close(inpam.file); pm_close(outpam.file); return 0; }
int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; FILE* ifP; struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PNM image */ pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); validateTupleType(inpam, cmdline.assume); outpam = inpam; outpam.file = stdout; if (inpam.depth < 3) { outpam.depth = 1; if (inpam.maxval == 1) outpam.format = PBM_FORMAT; else outpam.format = PGM_FORMAT; } else { outpam.depth = 3; outpam.format = PPM_FORMAT; } pnm_writepaminit(&outpam); { tuple *tuplerow; tuplerow = pnm_allocpamrow(&inpam); { int row; for (row = 0; row < inpam.height; row++) { pnm_readpamrow(&inpam, tuplerow); pnm_writepamrow(&outpam, tuplerow); } } pnm_freepamrow(tuplerow); } return 0; }
static void makeImageRows(const struct pam * const inPamP, outGenerator * const outputGeneratorP, double const depthOfField, double const eyesep, unsigned int const dpi, bool const crossEyed, bool const makeMask) { tuple * inRow; /* One row of pixels read from the height-map file */ tuple * outRow; /* One row of pixels to write to the height-map file */ int * same; /* Array: same[N] is the column number of a pixel to the right forced to have the same color as the one in column N */ int row; /* Current row in the input and output files */ inRow = pnm_allocpamrow(inPamP); outRow = pnm_allocpamrow(&outputGeneratorP->pam); MALLOCARRAY(same, inPamP->width); if (same == NULL) pm_error("Unable to allocate space for \"same\" array."); /* See the paper cited above for code comments. All I (Scott) did was * transcribe the code and make minimal changes for Netpbm. And some * style changes by Bryan to match Netpbm style. */ for (row = 0; row < inPamP->height; ++row) { pnm_readpamrow(inPamP, inRow); if (crossEyed) /* Invert heights for cross-eyed (as opposed to wall-eyed) people. */ invertHeightRow(inPamP, inRow); /* Determine color constraints. */ makeStereoRow(inPamP, inRow, same, depthOfField, eyesep, dpi); if (makeMask) makeMaskRow(&outputGeneratorP->pam, same, outRow); else makeImageRow(outputGeneratorP, row, same, outRow); /* Write the resulting row. */ pnm_writepamrow(&outputGeneratorP->pam, outRow); } free(same); pnm_freepamrow(outRow); pnm_freepamrow(inRow); }
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 ditherImage(struct pam * const inpamP, const scaler * const scalerP, unsigned int const dithPower, struct colorResolution const colorRes, struct pam * const outpamP, tuple *** const outTuplesP) { unsigned int const dithDim = 1 << dithPower; unsigned int const ditherMatrixArea = SQR(dithDim); unsigned int const modMask = (dithDim - 1); /* And this into N to compute N % dithDim cheaply, since we know (though the compiler doesn't) that dithDim is a power of 2 */ unsigned int ** const ditherMatrix = dithMatrix(dithPower); tuple * inrow; tuple ** outTuples; unsigned int row; struct pam ditherPam; /* Describes the tuples that ditherRow() sees */ assert(dithPower < sizeof(unsigned int) * 8); assert(UINT_MAX / dithDim >= dithDim); validateNoDitherOverflow(ditherMatrixArea, inpamP, colorRes); inrow = pnm_allocpamrow(inpamP); outTuples = pnm_allocpamarray(outpamP); /* We will modify the input to promote it to depth 3 */ ditherPam = *inpamP; ditherPam.depth = 3; for (row = 0; row < inpamP->height; ++row) { pnm_readpamrow(inpamP, inrow); pnm_makerowrgb(inpamP, inrow); ditherRow(&ditherPam, inrow, scalerP, ditherMatrix, ditherMatrixArea, colorRes, row, modMask, outpamP, outTuples[row]); } free(ditherMatrix); pnm_freepamrow(inrow); *outTuplesP = outTuples; }
static void doUnlookup(struct pam * const inpamP, tuplehash const lookupHash, sample const maxIndex, FILE * const ofP) { struct pam outpam; unsigned int row; tuple * inrow; tuple * outrow; inrow = pnm_allocpamrow(inpamP); outpam.size = sizeof(outpam); outpam.len = PAM_STRUCT_SIZE(tuple_type); outpam.file = ofP; outpam.format = PAM_FORMAT; outpam.height = inpamP->height; outpam.width = inpamP->width; outpam.depth = 1; outpam.maxval = maxIndex + 1; /* +1 for missing color */ strcpy(outpam.tuple_type, "INDEX"); pnm_writepaminit(&outpam); outrow = pnm_allocpamrow(&outpam); for (row = 0; row < inpamP->height; ++row) { unsigned int col; pnm_readpamrow(inpamP, inrow); for (col = 0; col < inpamP->width; ++col) { int found; int index; pnm_lookuptuple(inpamP, lookupHash, inrow[col], &found, &index); if (found) { assert(index <= outpam.maxval); outrow[col][0] = index; } else outrow[col][0] = maxIndex + 1; } pnm_writepamrow(&outpam, outrow); } pnm_freepamrow(outrow); pnm_freepamrow(inrow); }
static void load(const struct pam * const pamP, unsigned int const hstep, sample *** const pixelsP, unsigned int * const hsamplesP) { /*---------------------------------------------------------------------------- read file into memory, returning array of rows -----------------------------------------------------------------------------*/ unsigned int const hsamples = 1 + (pamP->width - 1) / hstep; /* use only this many cols */ unsigned int row; tuple * tuplerow; sample ** pixels; tuplerow = pnm_allocpamrow(pamP); MALLOCARRAY(pixels, pamP->height); if (pixels == NULL) pm_error("Unable to allocate array of %u pixel rows", pamP->height); if (pixels == NULL) pm_error("Unable to allocate array of %u rows", pamP->height); for (row = 0; row < pamP->height; ++row) { unsigned int i; unsigned int col; pnm_readpamrow(pamP, tuplerow); MALLOCARRAY(pixels[row], hsamples); if (pixels[row] == NULL) pm_error("Unable to allocate %u-sample array for Row %u", hsamples, row); /* save every hstep'th column */ for (i = col = 0; i < hsamples; ++i, col += hstep) pixels[row][i] = tuplerow[col][0]; } pnm_freepamrow(tuplerow); *hsamplesP = hsamples; *pixelsP = pixels; }
static void doOneImage(const char * const name, unsigned int const imageDoneCount, FILE * const fileP, bool const allimages, bool const justCount, bool const wantComments, bool * const eofP) { struct pam pam; const char * comments; enum pm_check_code checkRetval; pam.comment_p = &comments; pnm_readpaminit(fileP, &pam, PAM_STRUCT_SIZE(comment_p)); if (!justCount) { if (allimages) printf("%s:\tImage %d:\t", name, imageDoneCount); else printf("%s:\t", name); dumpHeader(pam); if (wantComments) dumpComments(comments); } strfree(comments); pnm_checkpam(&pam, PM_CHECK_BASIC, &checkRetval); if (allimages) { tuple * tuplerow; unsigned int row; tuplerow = pnm_allocpamrow(&pam); for (row = 0; row < pam.height; ++row) pnm_readpamrow(&pam, tuplerow); pnm_freepamrow(tuplerow); pnm_nextimage(fileP, eofP); } }
static void writePam(struct pam * const outpamP, unsigned int const imgCt, const Coord * const coords, const struct pam * const imgs) { /*---------------------------------------------------------------------------- Write the entire composite image. There are 'imgCt' source images, described by imgs[]. Their placement in the output is coords[]. Properties of the output image, including where to write it and its dimensions are *outpamP. -----------------------------------------------------------------------------*/ tuple * tuplerow; unsigned int row; /* Row number in the output image */ pnm_writepaminit(outpamP); tuplerow = pnm_allocpamrow(outpamP); for (row = 0; row < outpamP->height; ++row) { unsigned int imgIdx; makeRowBlack(outpamP, tuplerow); /* initial value */ for (imgIdx = 0; imgIdx < imgCt; ++imgIdx) { const Coord * const imgCoordP = &coords[imgIdx]; const struct pam * const imgPamP = &imgs[imgIdx]; if (imgCoordP->y <= row && row < imgCoordP->y + imgPamP->height) { pnm_readpamrow(imgPamP, &tuplerow[imgCoordP->x]); adjustDepth(tuplerow, imgPamP, outpamP, *imgCoordP); adjustMaxval(tuplerow, imgPamP, outpamP, *imgCoordP); } } pnm_writepamrow(outpamP, tuplerow); } pnm_freepamrow(tuplerow); }
static void convertPage(FILE * const ifP, int const turnflag, int const turnokflag, bool const psfilter, bool const rle, bool const flate, bool const ascii85, bool const setpage, bool const showpage, bool const center, float const scale, int const dpiX, int const dpiY, int const pagewid, int const pagehgt, int const imagewidth, int const imageheight, bool const equalpixels, char const name[], bool const dict, bool const vmreclaim, bool const levelIsGiven, bool const levelGiven) { struct pam inpam; tuple* tuplerow; unsigned int padright; /* Number of bits we must add to the right end of each Postscript output line in order to have an integral number of bytes of output. E.g. at 2 bits per sample with 10 columns, this would be 4. */ int row; unsigned int ps_maxval; /* The maxval of the Postscript program */ float scols, srows; float llx, lly; bool turned; bool color; unsigned int postscriptLevel; struct bmepsoe * bmepsoeP; pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); if (!STRSEQ(inpam.tuple_type, PAM_PBM_TUPLETYPE) && !STRSEQ(inpam.tuple_type, PAM_PGM_TUPLETYPE) && !STRSEQ(inpam.tuple_type, PAM_PPM_TUPLETYPE)) pm_error("Unrecognized tuple type %s. This program accepts only " "PBM, PGM, PPM, and equivalent PAM input images", inpam.tuple_type); color = STRSEQ(inpam.tuple_type, PAM_PPM_TUPLETYPE); selectPostscriptLevel(levelIsGiven, levelGiven, color, dict, flate, ascii85, &postscriptLevel); if (color) pm_message("generating color Postscript program."); computeDepth(inpam.maxval, &bitspersample, &ps_maxval); { unsigned int const realBitsPerLine = inpam.width * bitspersample; unsigned int const paddedBitsPerLine = ((realBitsPerLine + 7) / 8) * 8; padright = (paddedBitsPerLine - realBitsPerLine) / bitspersample; } /* In positioning/scaling the image, we treat the input image as if it has a density of 72 pixels per inch. */ computeImagePosition(dpiX, dpiY, inpam.width, inpam.height, turnflag, turnokflag, center, pagewid, pagehgt, scale, imagewidth, imageheight, equalpixels, &scols, &srows, &llx, &lly, &turned); putinit(postscriptLevel, name, inpam.width, inpam.height, scols, srows, llx, lly, padright, bitspersample, pagewid, pagehgt, color, turned, rle, flate, ascii85, setpage, psfilter, dict); createBmepsOutputEncoder(&bmepsoeP, stdout, rle, flate, ascii85); initNativeOutputEncoder(rle, bitspersample); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); if (psfilter) convertRowPsFilter(&inpam, tuplerow, bmepsoeP); else convertRowNative(&inpam, tuplerow, ps_maxval, rle, padright); } pnm_freepamrow(tuplerow); if (psfilter) flushBmepsOutput(bmepsoeP); else flushNativeOutput(rle); destroyBmepsOutputEncoder(bmepsoeP); putEnd(showpage, psfilter, ascii85, dict, vmreclaim); }
int main(int argc, char** args) { int argchar; char* infn = NULL; char* outfn = NULL; unsigned int row; int bits; FILE* fid = stdin; FILE* fout = stdout; int loglvl = LOG_MSG; char* progname = args[0]; int bzero = 0; int outformat; qfits_header* hdr; unsigned int plane; off_t datastart; anbool onepass = FALSE; bl* pixcache = NULL; #if HAVE_NETPBM struct pam img; tuple * tuplerow; #else void* rowbuf; #endif int W, H, depth, maxval; while ((argchar = getopt (argc, args, OPTIONS)) != -1) switch (argchar) { case '?': case 'h': printHelp(progname); exit(0); case 'v': loglvl++; break; case 'q': loglvl--; break; case 'o': outfn = optarg; break; } log_init(loglvl); log_to(stderr); fits_use_error_system(); if (optind == argc) { // ok, stdin to stdout. } else if (optind == argc-1) { infn = args[optind]; } else if (optind == argc-2) { infn = args[optind]; outfn = args[optind+1]; } else { printHelp(progname); exit(-1); } if (infn && !streq(infn, "-")) { fid = fopen(infn, "rb"); if (!fid) { SYSERROR("Failed to open input file %s", infn); exit(-1); } } if (outfn) { fout = fopen(outfn, "wb"); if (!fid) { SYSERROR("Failed to open output file %s", outfn); exit(-1); } } else outfn = "stdout"; #if HAVE_NETPBM pm_init(args[0], 0); pnm_readpaminit(fid, &img, // PAM_STRUCT_SIZE isn't defined until Netpbm 10.23 (July 2004) #if defined(PAM_STRUCT_SIZE) PAM_STRUCT_SIZE(tuple_type) #else sizeof(struct pam) #endif ); W = img.width; H = img.height; depth = img.depth; maxval = img.maxval; tuplerow = pnm_allocpamrow(&img); bits = pm_maxvaltobits(img.maxval); bits = (bits <= 8) ? 8 : 16; #else // No NETPBM if (parse_pnm_header(fid, &W, &H, &depth, &maxval)) { ERROR("Failed to parse PNM header from file: %s\n", infn ? infn : "<stdin>"); exit(-1); } bits = 8 * maxval_to_bytes(maxval); rowbuf = malloc(W * depth * (bits/8)); #endif logmsg("Read file %s: %i x %i pixels x %i color(s); maxval %i\n", infn ? infn : "stdin", W, H, depth, maxval); if (bits == 8) outformat = BPP_8_UNSIGNED; else { outformat = BPP_16_SIGNED; if (maxval >= INT16_MAX) bzero = 0x8000; } logmsg("Using %i-bit output\n", bits); hdr = fits_get_header_for_image3(W, H, outformat, depth, NULL); if (bzero) fits_header_add_int(hdr, "BZERO", bzero, "Number that has been subtracted from pixel values"); if (qfits_header_dump(hdr, fout)) { ERROR("Failed to write FITS header to file %s", outfn); exit(-1); } qfits_header_destroy(hdr); datastart = ftello(fid); // Figure out if we can seek backward in this input file... if ((fid == stdin) || (fseeko(fid, 0, SEEK_SET) || fseeko(fid, datastart, SEEK_SET))) // Nope! onepass = TRUE; if (onepass && depth > 1) { logmsg("Reading in one pass\n"); pixcache = bl_new(16384, bits/8); } for (plane=0; plane<depth; plane++) { if (plane > 0) { if (fseeko(fid, datastart, SEEK_SET)) { SYSERROR("Failed to seek back to start of image data"); exit(-1); } } for (row = 0; row<H; row++) { unsigned int column; #if HAVE_NETPBM pnm_readpamrow(&img, tuplerow); #else read_pnm_row(fid, W, depth, maxval, rowbuf); #endif for (column = 0; column<W; column++) { int rtn; int pixval; #if HAVE_NETPBM pixval = tuplerow[column][plane]; #else pixval = (bits == 8 ? ((uint8_t *)rowbuf)[column*depth + plane] : ((uint16_t*)rowbuf)[column*depth + plane]); #endif if (outformat == BPP_8_UNSIGNED) rtn = fits_write_data_B(fout, pixval); else rtn = fits_write_data_I(fout, pixval-bzero, TRUE); if (rtn) { ERROR("Failed to write FITS pixel"); exit(-1); } } if (onepass && depth > 1) { for (column = 0; column<W; column++) { for (plane=1; plane<depth; plane++) { int pixval; #if HAVE_NETPBM pixval = tuplerow[column][plane]; #else pixval = (bits == 8 ? ((uint8_t *)rowbuf)[column*depth + plane] : ((uint16_t*)rowbuf)[column*depth + plane]); #endif if (outformat == BPP_8_UNSIGNED) { uint8_t pix = pixval; bl_append(pixcache, &pix); } else { int16_t pix = pixval - bzero; bl_append(pixcache, &pix); } } } } } } #if HAVE_NETPBM pnm_freepamrow(tuplerow); #else free(rowbuf); #endif if (pixcache) { int i, j; int step = (depth - 1); logverb("Writing %zu queued pixels\n", bl_size(pixcache)); for (plane=1; plane<depth; plane++) { j = (plane - 1); for (i=0; i<(W * H); i++) { int rtn; if (outformat == BPP_8_UNSIGNED) { uint8_t* pix = bl_access(pixcache, j); rtn = fits_write_data_B(fout, *pix); } else { int16_t* pix = bl_access(pixcache, j); rtn = fits_write_data_I(fout, *pix, TRUE); } if (rtn) { ERROR("Failed to write FITS pixel"); exit(-1); } j += step; } } bl_free(pixcache); } if (fid != stdin) fclose(fid); if (fits_pad_file(fout)) { ERROR("Failed to pad output file \"%s\"", outfn); return -1; } if (fout != stdout) if (fclose(fout)) { SYSERROR("Failed to close output file %s", outfn); exit(-1); } return 0; }
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(leftcol <= rightcol); 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); }
int main(int argc, char *argv[]) { FILE *ifP; struct cmdlineInfo cmdline; struct pam inpam, outpam; unsigned int row; tuple * inrow; tuple * outrow; tuple * prevrow; pnm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); outpam = inpam; outpam.file = stdout; outpam.format = PAM_FORMAT; strcpy(outpam.tuple_type, "hdiff"); pnm_writepaminit(&outpam); inrow = pnm_allocpamrow(&inpam); outrow = pnm_allocpamrow(&outpam); prevrow = pnm_allocpamrow(&inpam); pnm_setpamrow(&inpam, prevrow, 0); /* All arithmetic in this operation and in the reverse operation (to recover the image) is done modulus the maxval+1 (the hdiff PAM and the image have the same maxval) in order to produce legal PAM samples (which must be in the range 0..maxval). This might seem to throw away information, but it doesn't. Example: maxval is 99. Intensity goes from 90 in Row 0 to 10 in Row 1. The difference is -80. -80 mod 100 is 20, so 20 goes in the hdiff output. On reconstructing the image, the interpreter knows the "20" can't be +20, because that would create the sample 90 + 20 = 110, and violate maxval. So it must be -80. Modulus arithmetic by the interpreter effectively makes that decision. */ /* The bias is just to make it easier to look at the output visually. If you display the values as intensities, and your differences are all +/- half of maxval, you can see positive transitions as bright spots and negative transitions as dark spots. */ { unsigned int const bias = outpam.maxval/2; for (row = 0; row < inpam.height; ++row) { unsigned int col; pnm_readpamrow(&inpam, inrow); for (col = 0; col < inpam.width; ++col) { unsigned int plane; for (plane = 0; plane < inpam.depth; ++plane) { sample const sampleValue = inrow[col][plane]; int const difference = sampleValue - prevrow[col][plane]; outrow[col][plane] = (difference + bias) % (outpam.maxval+1); prevrow[col][plane] = sampleValue; } } pnm_writepamrow(&outpam, outrow); } } pnm_freepamrow(prevrow); pnm_freepamrow(outrow); pnm_freepamrow(inrow); exit(0); }
static void composite(int const originleft, int const origintop, struct pam * const underlayPamP, struct pam * const overlayPamP, struct pam * const alphaPamP, bool const invertAlpha, float const masterOpacity, struct pam * const composedPamP, bool const assumeLinear, bool const mixTransparency) { /*---------------------------------------------------------------------------- Overlay the overlay image in the array 'overlayImage', described by *overlayPamP, onto the underlying image from the input image file as described by *underlayPamP, output the composite to the image file as described by *composedPamP. Apply the overlay image with transparency described by the array 'alpha' and *alphaPamP. The underlying image is positioned after its header. 'originleft' and 'origintop' are the coordinates in the underlying image plane where the top left corner of the overlay image is to 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; enum alphaMix const alphaMix = mixTransparency ? AM_OVERLAY : AM_KEEPUNDER; int underlayRow; /* NB may be negative */ int overlayRow; /* NB may be negative */ tuple * composedTuplerow; tuple * underlayTuplerow; struct pam adaptUnderlayPam; tuple * overlayTuplerow; struct pam adaptOverlayPam; tuplen * alphaTuplerown; composedTuplerow = pnm_allocpamrow(composedPamP); underlayTuplerow = pnm_allocpamrow(underlayPamP); overlayTuplerow = pnm_allocpamrow(overlayPamP); if (alphaPamP) alphaTuplerown = pnm_allocpamrown(alphaPamP); else alphaTuplerown = NULL; determineInputAdaptations(underlayPamP, overlayPamP, composedPamP, &adaptUnderlayPam, &adaptOverlayPam); 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); ++underlayRow, ++overlayRow) { if (overlayRow >= 0 && overlayRow < overlayPamP->height) { pnm_readpamrow(overlayPamP, overlayTuplerow); adaptRowFormat(overlayPamP, &adaptOverlayPam, overlayTuplerow); if (alphaPamP) pnm_readpamrown(alphaPamP, alphaTuplerown); } if (underlayRow >= 0 && underlayRow < underlayPamP->height) { pnm_readpamrow(underlayPamP, underlayTuplerow); adaptRowFormat(underlayPamP, &adaptUnderlayPam, underlayTuplerow); if (underlayRow < origintop || underlayRow >= origintop + overlayPamP->height) { /* Overlay image does not touch this underlay row. */ pnm_writepamrow(composedPamP, underlayTuplerow); } else { composeRow(originleft, &adaptUnderlayPam, &adaptOverlayPam, invertAlpha, masterOpacity, composedPamP, sampleScale, alphaMix, underlayTuplerow, overlayTuplerow, alphaTuplerown, composedTuplerow); pnm_writepamrow(composedPamP, composedTuplerow); } } } pnm_freepamrow(composedTuplerow); pnm_freepamrow(underlayTuplerow); pnm_freepamrow(overlayTuplerow); if (alphaPamP) pnm_freepamrown(alphaTuplerown); }
static void composite(int const originleft, int const origintop, struct pam * const underlayPamP, struct pam * const overlayPamP, struct pam * const alphaPamP, bool const invertAlpha, float const masterOpacity, struct pam * const composedPamP, bool const assumeLinear) { /*---------------------------------------------------------------------------- Overlay the overlay image in the array 'overlayImage', described by *overlayPamP, onto the underlying image from the input image file as described by *underlayPamP, output the composite to the image file as described by *composedPamP. Apply the overlay image with transparency described by the array 'alpha' and *alphaPamP. The underlying image is positioned after its header. 'originleft' and 'origintop' are the coordinates in the underlying image plane where the top left corner of the overlay image is to 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. -----------------------------------------------------------------------------*/ enum sampleScale const sampleScale = assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE; int underlayRow; /* NB may be negative */ int overlayRow; /* NB may be negative */ tuple * composedTuplerow; tuple * underlayTuplerow; tuple * overlayTuplerow; tuplen * alphaTuplerown; bool overlayHasOpacity; unsigned int opacityPlane; pnm_getopacity(overlayPamP, &overlayHasOpacity, &opacityPlane); composedTuplerow = pnm_allocpamrow(composedPamP); underlayTuplerow = pnm_allocpamrow(underlayPamP); overlayTuplerow = pnm_allocpamrow(overlayPamP); if (alphaPamP) alphaTuplerown = pnm_allocpamrown(alphaPamP); pnm_writepaminit(composedPamP); for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop); underlayRow < MAX(underlayPamP->height, origintop + overlayPamP->height); ++underlayRow, ++overlayRow) { if (overlayRow >= 0 && overlayRow < overlayPamP->height) { pnm_readpamrow(overlayPamP, overlayTuplerow); adaptRowToOutputFormat(overlayPamP, overlayTuplerow, composedPamP); if (alphaPamP) pnm_readpamrown(alphaPamP, alphaTuplerown); } if (underlayRow >= 0 && underlayRow < underlayPamP->height) { pnm_readpamrow(underlayPamP, underlayTuplerow); adaptRowToOutputFormat(underlayPamP, underlayTuplerow, composedPamP); if (underlayRow < origintop || underlayRow >= origintop + overlayPamP->height) { /* Overlay image does not touch this underlay row. */ 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]); } pnm_writepamrow(composedPamP, composedTuplerow); } } } pnm_freepamrow(composedTuplerow); pnm_freepamrow(underlayTuplerow); pnm_freepamrow(overlayTuplerow); if (alphaPamP) pnm_freepamrown(alphaTuplerown); }
static void writeScanLines(struct pam * const pamP, TIFF * const tif, tuplehash const cht, unsigned short const tiffMaxval, unsigned short const bitspersample, unsigned short const photometric, int const bytesperrow, int const fillorder) { /*---------------------------------------------------------------------------- Write out the raster for the input image described by 'pamP', whose file is positioned to the raster of the image. -----------------------------------------------------------------------------*/ tuple * tuplerow; /* malloc'ed */ unsigned char * buf; /* malloc'ed */ int row; /* The man page for TIFFWriteScanLine doesn't tell the format of it's 'buf' parameter, but here it is: Its format depends on the bits per pixel of the TIFF image. If it's 16, 'buf' is an array of short (16 bit) integers, one per raster column. If it's 8, 'buf' is an array of characters (8 bit integers), one per image column. If it's less than 8, it's an array of characters, each of which represents 1-8 raster columns, packed into it in the order specified by the TIFF image's fill order, with don't-care bits on the right such that each byte contains only whole pixels. In all cases, the array elements are in order left to right going from low array indices to high array indices. */ MALLOCARRAY(buf, bytesperrow); if (buf == NULL) pm_error("can't allocate memory for row buffer"); tuplerow = pnm_allocpamrow(pamP); for (row = 0; row < pamP->height; ++row) { int col; pnm_readpamrow(pamP, tuplerow); if (cht == NULL) { /* It's a direct, non-colormapped raster */ if (bitspersample == 8 || bitspersample == 16) fillRowOfWholeBytePixels(pamP, tuplerow, buf, photometric, tiffMaxval, bitspersample); else fillRowOfSubBytePixels(pamP, tuplerow, buf, tiffMaxval, photometric, fillorder, bitspersample, NULL); } else { /* It's a colormapped raster */ if (bitspersample == 8) { for (col = 0; col < pamP->width; ++col) { int si; int found; pnm_lookuptuple(pamP, cht, tuplerow[col], &found, &si); if (!found) pm_error("INTERNAL ERROR. We made a color map, and a " "color map we need is not in it! " "col=%d r=%lu g=%lu b=%lu", col, tuplerow[col][0], tuplerow[col][1], tuplerow[col][2]); buf[col] = (unsigned char) si; } } else { fillRowOfSubBytePixels(pamP, tuplerow, buf, 0, 0, fillorder, bitspersample, cht); } } if (TIFFWriteScanline(tif, buf, row, 0) < 0) pm_error("failed a scanline write on row %d", row); } pnm_freepamrow(tuplerow); free(buf); }
static void copyRaster(struct pam * const inpamP, struct pam * const outpamP, tupletable const colormap, unsigned int const colormapSize, bool const floyd, bool const randomize, tuple const defaultColor, unsigned int * const missingCountP) { tuplehash const colorhash = pnm_createtuplehash(); tuple * inrow; tuple * outrow; struct pam workpam; /* This is for work space we use for building up the output pixels. To save time and memory, we modify them in place in a buffer, which ultimately holds the output pixels. This pam structure is thus the same as the *outpamP, but with a tuple allocation depth large enough to handle intermediate results. */ depthAdjustment depthAdjustment; struct colormapFinder * colorFinderP; bool usehash; struct fserr fserr; int row; workpam = *outpamP; workpam.allocation_depth = MAX(workpam.depth, inpamP->depth); workpam.size = sizeof(workpam); workpam.len = PAM_STRUCT_SIZE(allocation_depth); inrow = pnm_allocpamrow(inpamP); outrow = pnm_allocpamrow(&workpam); if (outpamP->maxval != inpamP->maxval && defaultColor) pm_error("The maxval of the colormap (%u) is not equal to the " "maxval of the input image (%u). This is allowable only " "if you are doing an approximate mapping (i.e. you don't " "specify -firstisdefault or -missingcolor)", (unsigned int)outpamP->maxval, (unsigned int)inpamP->maxval); selectDepthAdjustment(inpamP, outpamP->depth, &depthAdjustment); usehash = TRUE; createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP); if (floyd) initFserr(inpamP, &fserr, randomize); *missingCountP = 0; /* initial value */ for (row = 0; row < inpamP->height; ++row) { unsigned int missingCount; pnm_readpamrow(inpamP, inrow); convertRow(inpamP, &workpam, inrow, depthAdjustment, colormap, colorFinderP, colorhash, &usehash, floyd, defaultColor, &fserr, outrow, &missingCount); *missingCountP += missingCount; pnm_writepamrow(outpamP, outrow); } destroyColormapFinder(colorFinderP); pnm_freepamrow(inrow); pnm_freepamrow(outrow); pnm_destroytuplehash(colorhash); }
static void createJasperRaster(struct pam * const inpamP, jas_image_t * const jasperP) { jas_matrix_t ** matrix; /* malloc'ed */ /* matrix[X] is the data for Plane X of the current row */ unsigned int plane; unsigned int row; tuple * tuplerow; bool oddMaxval; sample jasperMaxval; matrix = malloc(inpamP->depth * sizeof(jas_matrix_t *)); if (matrix == NULL) pm_error("Out of memory"); for (plane = 0; plane < inpamP->depth; ++plane) { matrix[plane] = jas_matrix_create(1, inpamP->width); if (matrix[plane] == NULL) pm_error("Unable to create matrix for plane %u. " "jas_matrix_create() failed.", plane); } tuplerow = pnm_allocpamrow(inpamP); jasperMaxval = pm_bitstomaxval(pm_maxvaltobits(inpamP->maxval)); oddMaxval = jasperMaxval != inpamP->maxval; for (row = 0; row < inpamP->height; ++row) { unsigned int col; pnm_readpamrow(inpamP, tuplerow); for (col = 0; col < inpamP->width; ++col) { unsigned int plane; for (plane = 0; plane < inpamP->depth; ++plane) { unsigned int jasperSample; if (oddMaxval) jasperSample = tuplerow[col][plane] * jasperMaxval / inpamP->maxval; else jasperSample = tuplerow[col][plane]; jas_matrix_set(matrix[plane], 0, col, jasperSample); } } { unsigned int plane; for (plane = 0; plane < inpamP->depth; ++plane) { int rc; rc = jas_image_writecmpt(jasperP, plane, 0, row, inpamP->width, 1, matrix[plane]); if (rc != 0) pm_error("jas_image_writecmpt() of plane %u failed.", plane); } } } pnm_freepamrow(tuplerow); for (plane = 0; plane < inpamP->depth; ++plane) jas_matrix_destroy(matrix[plane]); free(matrix); }