diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2019-03-30 15:18:58 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2019-03-30 15:18:58 +0000 |
commit | aad47e743f21763ac13b73eb927ad7d001254572 (patch) | |
tree | 2be395fe14e199ac00f002dc8b0ded2f0304c557 /converter | |
parent | b10a418cefe3413a727b89300848dc84e8a6195d (diff) | |
download | netpbm-mirror-aad47e743f21763ac13b73eb927ad7d001254572.tar.gz netpbm-mirror-aad47e743f21763ac13b73eb927ad7d001254572.tar.xz netpbm-mirror-aad47e743f21763ac13b73eb927ad7d001254572.zip |
Promote Development to Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3587 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter')
-rw-r--r-- | converter/other/pamtopng.c | 395 | ||||
-rw-r--r-- | converter/other/pngx.c | 64 | ||||
-rw-r--r-- | converter/other/pngx.h | 13 | ||||
-rw-r--r-- | converter/other/pnmtopng.c | 383 |
4 files changed, 539 insertions, 316 deletions
diff --git a/converter/other/pamtopng.c b/converter/other/pamtopng.c index a3c60f24..a323844f 100644 --- a/converter/other/pamtopng.c +++ b/converter/other/pamtopng.c @@ -1,21 +1,8 @@ -/* -** read a PNM/PAM image and produce a Portable Network Graphics (PNG) file -** -** derived from pnmtorast.c by Jef Poskanzer and pamrgbatopng.c by Bryan -** Henderson <bryanh@giraffe-data.com> and probably some other sources -** -** Copyright (C) 1995-1998 by Alexander Lehmann <alex@hal.rhein-main.de> -** and Willem van Schaik <willem@schaik.com> -** Copyright (C) 1999,2001 by Greg Roelofs <newt@pobox.com> -** Copyright (C) 2015 by Willem van Schaik <willem@schaik.com> -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. -*/ +/*============================================================================= + pamtopng +=============================================================================== + Read a Netpbm image and produce a PNG (Portable Network Graphics) image. +=============================================================================*/ /* This Netpbm program pamtopng was derived in 2015 from the Netpbm program @@ -29,16 +16,16 @@ had become rather complex. This program is roughly 1/3 the size of pnmtopng.c that it replaces. - - In 1995 bandwith was limited and therefore filesize had to be kept + - In 1995, bandwith was limited and therefore filesize had to be kept small. The original program tried to optimize for that by applying - many "clever tricks". Today that isn't an issue anymore, so gone + many "clever tricks". Today that isn't an issue anymore, so gone are filters, palettes, etc. Also, image conversions were removed, because those should be done with other NetPBM tools. - Add ability to create iTXt (international language) chunks. */ - +#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <time.h> @@ -62,6 +49,7 @@ static bool verbose; struct CmdlineInfo { const char * inputFileName; unsigned int verbose; + unsigned int interlace; unsigned int transparencySpec; const char * transparency; unsigned int chromaSpec; @@ -89,7 +77,7 @@ parseChromaOpt(const char * const chromaOpt, struct pngx_chroma * const chromaP) { int count; - + count = sscanf(chromaOpt, "%f %f %f %f %f %f %f %f", &chromaP->wx, &chromaP->wy, &chromaP->rx, &chromaP->ry, @@ -108,7 +96,7 @@ parseChromaOpt(const char * const chromaOpt, static void parseSrgbintentOpt(const char * const srgbintentOpt, pngx_srgbIntent * const srgbintentP) { - + if (streq(srgbintentOpt, "perceptual")) *srgbintentP = PNGX_PERCEPTUAL; else if (streq(srgbintentOpt, "relativecolorimetric")) @@ -146,7 +134,7 @@ parseTimeOpt(const char * const timeOpt, if (count != 6) pm_error("Invalid value for -time '%s'. It should have " "the form [yy]yy-mm-dd hh:mm:ss.", timeOpt); - + if (year < 0) pm_error("Year is negative in -time value '%s'", timeOpt); if (year > 9999) @@ -193,7 +181,7 @@ static void parseCommandLine (int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { - + optEntry * option_def; optStruct3 opt; unsigned int option_def_index = 0; /* incremented by OPTENT3 */ @@ -206,6 +194,8 @@ parseCommandLine (int argc, OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "interlace", OPT_FLAG, NULL, + &cmdlineP->interlace, 0); OPTENT3(0, "transparency", OPT_STRING, &cmdlineP->transparency, &cmdlineP->transparencySpec, 0); OPTENT3(0, "chroma", OPT_STRING, &chroma, @@ -240,7 +230,7 @@ parseCommandLine (int argc, if (cmdlineP->timeSpec) parseTimeOpt(time, &cmdlineP->time); - + /* get the input-file or stdin pipe */ if (argc-1 < 1) cmdlineP->inputFileName = "-"; @@ -257,7 +247,7 @@ parseCommandLine (int argc, static png_byte colorTypeFromInputType(const struct pam * const pamP) { /*---------------------------------------------------------------------------- - Analyse the Netpbm image for color-type and bit-depth + Analyze the Netpbm image for color-type and bit-depth -----------------------------------------------------------------------------*/ png_byte retval; @@ -287,7 +277,7 @@ colorTypeFromInputType(const struct pam * const pamP) { if (pamP->depth == 2) retval = PNG_COLOR_TYPE_GRAY_ALPHA; else - pm_error("Input tupel type is GRAYSCALE_ALPHA, " + pm_error("Input tuple type is GRAYSCALE_ALPHA, " "but number of planes is %u instread of 2", pamP->depth); } else if (strneq(pamP->tuple_type, "GRAYSCALE", 9)) { @@ -386,6 +376,8 @@ sigBitsFmImgType(unsigned int const pnmBitDepth, retval.gray = pnmBitDepth; retval.alpha = pnmBitDepth; break; + default: + assert(false); } } else { /* PNG can (so presumably will) use original bit depth */ @@ -405,7 +397,7 @@ doTrnsChunk(const struct pam * const pamP, struct pngx * const pngxP, const char * const trans) { - if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA || + if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA || pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA) pm_error("Both alpha channel and transparency chunk not allowed."); else { @@ -435,13 +427,13 @@ doTrnsChunk(const struct pam * const pamP, static void doChrmChunk(struct pngx * const pngxP, struct pngx_chroma const chroma) { - + pngx_setChrm(pngxP, chroma); if (verbose) { pm_message("writing cHRM chunk { wx, wy, rx, ry, gx, gy, bx, by } = " "{ %4.2f, %4.2f, %4.2f, %4.2f, " - "%4.2f, %4.2f, %4.2f, %4.2f }", + "%4.2f, %4.2f, %4.2f, %4.2f }", chroma.wx, chroma.wy, chroma.rx, chroma.ry, chroma.gx, chroma.gy, @@ -451,7 +443,7 @@ doChrmChunk(struct pngx * const pngxP, -static void +static void doGamaChunk(struct pngx * const pngxP, float const gamma) { @@ -501,9 +493,9 @@ doTextChunkSet(struct pngx * const pngxP, FILE * tfP; tfP = pm_openr(textFileName); - + pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose); - + pm_close(tfP); } @@ -519,9 +511,9 @@ doZtxtChunkSet(struct pngx * const pngxP, FILE * tfP; tfP = pm_openr(textFileName); - + pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose); - + pm_close(tfP); } @@ -557,11 +549,11 @@ doBkgdChunk (const struct pam * const pamP, pngx_setBkgdRgb(pngxP, pngColor); if (verbose) { - if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY || + if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY || pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA) { pm_message("writing bKGD chunk with gray level = %u", pngColor.gray); - } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB || + } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB || pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA) { pm_message("writing bKGD chunk with color {red, green, blue} = " "{%u, %u, %u}", @@ -592,24 +584,10 @@ doTimeChunk(struct pngx * const pngxP, static void -setShift(struct pngx * const pngxP, - png_color_8 const sigBits) { - - if (sigBits.red + sigBits.green + sigBits.blue + - sigBits.gray + sigBits.alpha > 0) { - - /* Move the 1, 2, 4 bits to most significant bits */ - pngx_setShift(pngxP, sigBits); - } -} - - - -static void -convertRaster(const struct pam * const pamP, - const tuple * const tuplerow, - png_byte * const pngRow, - unsigned int const bitDepth) { +convertRow(const struct pam * const pamP, + const tuple * const tuplerow, + png_byte * const pngRow, + unsigned int const bitDepth) { unsigned int col; @@ -638,9 +616,12 @@ convertRaster(const struct pam * const pamP, static void -writeRaster(const struct pam * const pamP, - struct pngx * const pngxP, - int const bitDepth) { +writeRasterRowByRow(const struct pam * const pamP, + struct pngx * const pngxP, + int const bitDepth) { + + unsigned int const rowSz = + pamP->width * pamP->depth * (MAX(1, bitDepth/8)); tuple * tupleRow; png_byte * pngRow; @@ -650,8 +631,7 @@ writeRaster(const struct pam * const pamP, tupleRow = pnm_allocpamrow(pamP); - MALLOCARRAY(pngRow, pamP->width * 8); - /* sufficient to store a 16-bit RGB+A row */ + MALLOCARRAY(pngRow, rowSz); if (pngRow == NULL) pm_error("Unable to allocate space for PNG pixel row for " @@ -660,9 +640,9 @@ writeRaster(const struct pam * const pamP, for (row = 0; row < pamP->height; ++row) { pnm_readpamrow(pamP, tupleRow); - convertRaster(pamP, tupleRow, pngRow, bitDepth); + convertRow(pamP, tupleRow, pngRow, bitDepth); - png_write_row(pngxP->png_ptr, pngRow); + pngx_writeRow(pngxP, pngRow); } free(pngRow); } @@ -671,40 +651,160 @@ writeRaster(const struct pam * const pamP, +static png_bytep +mallocPngImage(unsigned int const rowSize, + unsigned int const height) { + + png_bytep pngImage; + + if (UINT_MAX / rowSize < height) + pm_error("Image is uncomputably large at %u rows of %u bytes", + height, rowSize); + + MALLOCARRAY(pngImage, height * rowSize); + + if (!pngImage) + pm_error("could not allocate %u bytes for a PNG image buffer", + height * rowSize); + + return pngImage; +} + + + +static unsigned int +pngLineSize(struct pngx * const pngxP) { + + unsigned int const bytesPerSample = pngx_bitDepth(pngxP) == 16 ? 2 : 1; + + unsigned int samplesPerPixel; + + switch (pngx_colorType(pngxP)) { + case PNG_COLOR_TYPE_GRAY: + samplesPerPixel = 1; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + samplesPerPixel = 2; + break; + case PNG_COLOR_TYPE_RGB: + samplesPerPixel = 3; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + samplesPerPixel = 4; + break; + default: + assert(false); + } + + if (UINT_MAX / bytesPerSample / samplesPerPixel < pngx_imageWidth(pngxP)) { + pm_error("pngcopy: width %u of PNG is uncomputably large\n", + pngx_imageWidth(pngxP)); + } + + return pngx_imageWidth(pngxP) * bytesPerSample * samplesPerPixel; +} + + + static void -writePng(const struct pam * const pamP, - FILE * const ofP, - struct CmdlineInfo const cmdline) { +writeRasterWholeImg(struct pam * const pamP, + struct pngx * const pngxP, + unsigned int const bitDepth) { - unsigned int const pnmBitDepth = pm_maxvaltobits(pamP->maxval); - int const pngColorType = colorTypeFromInputType(pamP); + unsigned int const pngRowSize = pngLineSize(pngxP); - struct pngx * pngxP; - unsigned int pngBitDepth; - png_color_8 sBit; + tuple * tupleRow; + png_bytep pngImage; + /* A one-dimensional malloc'ed array of all pixels in image */ + png_bytep * pngRowP; + /* A malloc'ed array of row pointers into pngImage[] */ + unsigned int row; - pngx_create(&pngxP, PNGX_WRITE, NULL); + tupleRow = pnm_allocpamrow(pamP); + + pngImage = mallocPngImage(pngRowSize, pamP->height); + + MALLOCARRAY(pngRowP, pamP->height); + + if (!pngRowP) + pm_error("Failed to allocate an array for %u PNG row pointers", + pamP->height); + for (row = 0; row < pamP->height; ++row) { + png_bytep const thisPngRowP = &pngImage[row * pngRowSize]; + pnm_readpamrow(pamP, tupleRow); + + convertRow(pamP, tupleRow, thisPngRowP, bitDepth); + + pngRowP[row] = thisPngRowP; + } + + pngx_writeImage(pngxP, pngRowP); + + free(pngRowP); + free(pngImage); + pnm_freepamrow(tupleRow); +} + + + +static void +reportInputFormat(const struct pam * const pamP) { + + const char * formatDesc; + + if (pamP->format == PBM_FORMAT || pamP->format == RPBM_FORMAT) + formatDesc = "PBM"; + else if (pamP->format == PGM_FORMAT || pamP->format == RPGM_FORMAT) + formatDesc = "PGM"; + else if (pamP->format == PPM_FORMAT || pamP->format == RPPM_FORMAT) + formatDesc = "PPM"; + else if (pamP->format == PAM_FORMAT) + formatDesc = "PAM"; + else + formatDesc = NULL; + + if (formatDesc) + pm_message("Input format = %s", formatDesc); + else + pm_message("Unrecognized input format, format code = 0x%x", + pamP->format); + + pm_message("Input tuple type = '%s'", pamP->tuple_type); + pm_message("Input depth = %u", pamP->depth); + pm_message("Input maxval = %u", (unsigned int) pamP->maxval); +} + + + +static unsigned int +pngBitDepth(unsigned int const pnmBitDepth, + int const pngColorType) { + + unsigned int retval; if ((pngColorType == PNG_COLOR_TYPE_RGB || pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) && pnmBitDepth < 8) { - pngBitDepth = 8; + retval = 8; } else - pngBitDepth = pnmBitDepth; + retval = pnmBitDepth; - png_init_io(pngxP->png_ptr, ofP); + return retval; +} - pngx_setIhdr(pngxP, pamP->width, pamP->height, - pngBitDepth, pngColorType, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - sBit = sigBitsFmImgType(pnmBitDepth, pngColorType); - /* Where requested, add ancillary chunks */ +static void +addAncillaryChunks(struct pam * const pamP, + struct pngx * const pngxP, + struct CmdlineInfo const cmdline, + png_color_8 const sigBits) { +/*---------------------------------------------------------------------------- + Where requested, add ancillary chunks. +-----------------------------------------------------------------------------*/ if (cmdline.transparencySpec) doTrnsChunk(pamP, pngxP,cmdline.transparency); @@ -716,7 +816,7 @@ writePng(const struct pam * const pamP, /* no iccp */ - doSbitChunk(pamP, pngxP, sBit); + doSbitChunk(pamP, pngxP, sigBits); if (cmdline.srgbintentSpec) doSrgbChunk(pngxP, cmdline.srgbintent); @@ -742,54 +842,96 @@ writePng(const struct pam * const pamP, if (cmdline.timeSpec) doTimeChunk(pngxP, cmdline.time); - setShift(pngxP, sBit); - - /* Write the ancillary chunks to PNG file */ + /* Write the ancillary chunks to PNG image */ pngx_writeInfo(pngxP); +} + + + +static void +setShift(struct pngx * const pngxP, + png_color_8 const sigBits) { + + if (sigBits.red + sigBits.green + sigBits.blue + + sigBits.gray + sigBits.alpha > 0) { - if (pngColorType != PNG_COLOR_TYPE_GRAY && pnmBitDepth < 8) { /* Move the 1, 2, 4 bits to most significant bits */ - pngx_setShift(pngxP, sBit); - } - if ((pngColorType == PNG_COLOR_TYPE_GRAY) && (pnmBitDepth < 8)) { - /* Pack multiple pixels in a byte */ - pngx_setPacking(pngxP); + pngx_setShift(pngxP, sigBits); } +} - writeRaster(pamP, pngxP, pnmBitDepth); - pngx_writeEnd(pngxP); - pngx_destroy(pngxP); -} +static void +doIhdrChunk(struct pngx * const pngxP, + unsigned int const width, + unsigned int const height, + unsigned int const pnmBitDepth, + int const pngColorType, + bool const interlace) { + + int const interlaceMethod = + interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; + + pngx_setIhdr(pngxP, width, height, + pngBitDepth(pnmBitDepth, pngColorType), pngColorType, + interlaceMethod, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); +} static void -reportInputFormat(const struct pam * const pamP) { +pamtopng(FILE * const ifP, + FILE * const ofP, + struct CmdlineInfo const cmdline) { - const char * formatDesc; + unsigned int pnmBitDepth; + int pngColorType; + struct pngx * pngxP; + png_color_8 sigBits; + struct pam pam; - if (pamP->format == PBM_FORMAT || pamP->format == RPBM_FORMAT) - formatDesc = "PBM"; - else if (pamP->format == PGM_FORMAT || pamP->format == RPGM_FORMAT) - formatDesc = "PGM"; - else if (pamP->format == PPM_FORMAT || pamP->format == RPPM_FORMAT) - formatDesc = "PPM"; - else if (pamP->format == PAM_FORMAT) - formatDesc = "PAM"; - else - formatDesc = NULL; + pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); - if (formatDesc) - pm_message("Input format = %s", formatDesc); - else - pm_message("Unrecognized input format, format code = 0x%x", - pamP->format); + if (verbose) + reportInputFormat(&pam); - pm_message("Input tuple type = '%s'", pamP->tuple_type); - pm_message("Input depth = %u", pamP->depth); - pm_message("Input maxval = %u", (unsigned int) pamP->maxval); + pnmBitDepth = pm_maxvaltobits(pam.maxval); + + pngColorType = colorTypeFromInputType(&pam); + + pngx_create(&pngxP, PNGX_WRITE, NULL); + + png_init_io(pngxP->png_ptr, ofP); + + doIhdrChunk(pngxP, pam.width, pam.height, + pnmBitDepth, pngColorType, cmdline.interlace > 0); + + sigBits = sigBitsFmImgType(pnmBitDepth, pngColorType); + + addAncillaryChunks(&pam, pngxP, cmdline, sigBits); + + setShift(pngxP, sigBits); + + if ((pngColorType == PNG_COLOR_TYPE_GRAY) && (pnmBitDepth < 8)) { + /* Pack multiple pixels in a byte */ + pngx_setPacking(pngxP); + } + + if (cmdline.interlace) { + /* Libpng will expect us to provide pixels in interlaced sequence + if we write row-by-row, and that is much to difficult, so we + do whole-image-at-once and let Libpng do the work. + */ + writeRasterWholeImg(&pam, pngxP, pnmBitDepth); + } else { + /* We save memory by going row-by-row */ + writeRasterRowByRow(&pam, pngxP, pnmBitDepth); + } + + pngx_writeEnd(pngxP); + pngx_destroy(pngxP); } @@ -800,7 +942,6 @@ main(int argc, FILE * ifP; struct CmdlineInfo cmdline; - struct pam pam; pm_proginit(&argc, argv); @@ -810,12 +951,7 @@ main(int argc, ifP = pm_openr(cmdline.inputFileName); - pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); - - if (verbose) - reportInputFormat(&pam); - - writePng(&pam, stdout, cmdline); + pamtopng(ifP, stdout, cmdline); pm_close(ifP); @@ -824,3 +960,18 @@ main(int argc, +/* Derived from pnmtorast.c by Jef Poskanzer and pamrgbatopng.c by Bryan +** Henderson <bryanh@giraffe-data.com> and probably some other sources +** +** Copyright (C) 1995-1998 by Alexander Lehmann <alex@hal.rhein-main.de> +** and Willem van Schaik <willem@schaik.com> +** Copyright (C) 1999,2001 by Greg Roelofs <newt@pobox.com> +** Copyright (C) 2015 by Willem van Schaik <willem@schaik.com> +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ diff --git a/converter/other/pngx.c b/converter/other/pngx.c index dfc74485..4bb09421 100644 --- a/converter/other/pngx.c +++ b/converter/other/pngx.c @@ -1,3 +1,4 @@ +#include <stdbool.h> #include <assert.h> #include <png.h> #include "pm_c_util.h" @@ -59,6 +60,7 @@ pngx_create(struct pngx ** const pngxPP, if (!pngxP) pm_error("Failed to allocate memory for PNG object"); else { + pngxP->infoPrepared = false; pngxP->numPassesRequired = 1; switch(rw) { @@ -466,6 +468,37 @@ pngx_setIhdr(struct pngx * const pngxP, void pngx_setInterlaceHandling(struct pngx * const pngxP) { + /* The documentation is vague and contradictory on what this does, but + what it appears from reasoning and experimentation to do is the + following. + + It applies to reading and writing by rows (png_write_row, png_read_row) + as opposed to whole image (png_write_image, png_read_image). It has + no effect on whole image read and write. + + This is not what makes an image interlaced or tells the decompressor + that it is interlaced. All it does is control how you you read and + write the raster when the image is interlaced. It has no effect if the + image is not interlaced. (You make an image interlaced by setting the + IHDR; the decompressor finds out from the IHDR that it is interlaced). + + In the write case, it controls whether you construct the subimages + yourself and feed them to libpng in sequence or you feed libpng the + entire image multiple times and libpng picks out the pixels appropriate + for each subimage in each pass. + + In the read case, it controls whether you get the raw subimages and you + assemble them into the full image or you read the whole image multiple + times into the same buffer, with the pixels that belong to each + subimage being filled in on each pass. + + Note that the only kind of interlacing that exists today is ADAM7 and + consequently, the number of passes is always 1 (for no interlacing) or + 7 (for interlacing). + */ + if (!pngxP->infoPrepared) + pm_error("pngx_setInterlaceHandling must not be called before " + "pngx_writeInfo or pngx_readInfo"); pngxP->numPassesRequired = png_set_interlace_handling(pngxP->png_ptr); } @@ -475,6 +508,10 @@ pngx_setInterlaceHandling(struct pngx * const pngxP) { void pngx_setPacking(struct pngx * const pngxP) { + if (!pngxP->infoPrepared) + pm_error("pngx_setPacking must not be called before " + "pngx_writeInfo or pngx_readInfo"); + png_set_packing(pngxP->png_ptr); } @@ -516,7 +553,19 @@ pngx_setSbit(struct pngx * const pngxP, void pngx_setShift(struct pngx * const pngxP, png_color_8 const sigBitArg) { - +/*---------------------------------------------------------------------------- + Tell the number of significant bits in the row buffers that will be given + to the compressor. Those bits are the least significant of the 8 bits of + space in the row buffer for each sample. For example, if red sample values + are in the range 0-7, only the lower 3 bits of the 8-bit byte for each + red sample will be used, so one would call this with sigBitArg.red == 3. + + The name alludes to the fact that to normalize the sample to 8 bits, one + shifts it left, and this function tells how much shift has to happen. In + the example above, each red sample has to be shifted left 5 bits (so that + the upper 3 bits are significant and the lower 5 bits are always zero) to + create an 8 bit sample out of the 3 bit samples. +-----------------------------------------------------------------------------*/ png_color_8 sigBit; sigBit = sigBitArg; @@ -651,6 +700,8 @@ void pngx_readInfo(struct pngx * const pngxP) { png_read_info(pngxP->png_ptr, pngxP->info_ptr); + + pngxP->infoPrepared = true; } @@ -659,6 +710,8 @@ void pngx_writeInfo(struct pngx * const pngxP) { png_write_info(pngxP->png_ptr, pngxP->info_ptr); + + pngxP->infoPrepared = true; } @@ -733,6 +786,15 @@ pngx_writeRow(struct pngx * const pngxP, void +pngx_writeImage(struct pngx * const pngxP, + png_byte ** const raster) { + + png_write_image(pngxP->png_ptr, (png_byte **)raster); +} + + + +void pngx_readEnd(struct pngx * const pngxP) { /* Note that some of info_ptr is not defined until png_read_end() diff --git a/converter/other/pngx.h b/converter/other/pngx.h index be9b9558..f4701bb2 100644 --- a/converter/other/pngx.h +++ b/converter/other/pngx.h @@ -1,6 +1,7 @@ #ifndef PNGX_H_INCLUDED #define PNGX_H_INCLUDED +#include <stdbool.h> #include <png.h> /* This includes the Zlib interface header file zlib.h because libpng uses libz and some of the Zlib interface, e.g. the Z_DEFLATED constant, @@ -53,10 +54,16 @@ struct pngx { pngx_rw rw; png_uint_16 maxval; unsigned int numPassesRequired; - /* The number of times we have write the complete image to the + /* The number of times we have to write the complete image to the compressor. This is more than one when the compressor is set up to do an interlaced format. */ + bool infoPrepared; + /* png_write_info or png_read_info has been called, so libpng is in a + state in which things such as png_set_interlace_handling will work. + These functions use information in *png_ptr that is set by + png_XXX_info. + */ }; void @@ -270,6 +277,10 @@ pngx_writeRow(struct pngx * const pngxP, const png_byte * const line); void +pngx_writeImage(struct pngx * const pngxP, + png_byte ** const raster); + +void pngx_readEnd(struct pngx * const pngxP); void diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c index 08263f29..80f74adb 100644 --- a/converter/other/pnmtopng.c +++ b/converter/other/pnmtopng.c @@ -26,7 +26,7 @@ it's easy to see that an ordinary fax could deplete your virtual memory and even if it didn't, it might deplete your real memory and iterating through the array would cause thrashing. This program - iterates through the image multiple times. + iterates through the image multiple times. So instead, we read the image into memory one row at a time, into a single row buffer. We use Netpbm's pm_openr_seekable() facility to @@ -38,19 +38,19 @@ in the system's I/O cache (remember that the file is a lot smaller than the xel array you'd get by doing a pnm_readpnm() of it). - However, it does introduce some delay because of all the system calls + However, it does introduce some delay because of all the system calls required to read the file. A future enhancement might read the entire - file into an xel array in some cases, and read one row at a time in + file into an xel array in some cases, and read one row at a time in others, depending on the needs of the particular use. We do still read the entire alpha mask (if there is one) into a - 'gray' array, rather than access it one row at a time. + 'gray' array, rather than access it one row at a time. Before May 2001, we did in fact read the whole image into an xel array, and we got complaints. Before April 2000, it wasn't as big a problem because xels were only 24 bits. Now they're 96. */ - + #ifndef PNMTOPNG_WARNING_LEVEL # define PNMTOPNG_WARNING_LEVEL 0 /* use 0 for backward compatibility, */ #endif /* 2 for warnings (1 == error) */ @@ -62,7 +62,7 @@ /* Because of a design error in png.h, you must not #include <setjmp.h> before <png.h>. If you do, png.h won't compile. */ -#include <setjmp.h> +#include <setjmp.h> #include <zlib.h> #include "pm_c_util.h" @@ -169,7 +169,7 @@ parseSizeOpt(const char * const sizeOpt, struct pngx_phys * const sizeP) { int count; - + count = sscanf(sizeOpt, "%d %d %d", &sizeP->x, &sizeP->y, &sizeP->unit); if (count != 3) @@ -184,7 +184,7 @@ parseRgbOpt(const char * const rgbOpt, struct pngx_chroma * const rgbP) { int count; - + count = sscanf(rgbOpt, "%f %f %f %f %f %f %f %f", &rgbP->wx, &rgbP->wy, &rgbP->rx, &rgbP->ry, @@ -203,7 +203,7 @@ parseRgbOpt(const char * const rgbOpt, static void parseSrgbintentOpt(const char * const srgbintentOpt, pngx_srgbIntent * const srgbintentP) { - + if (streq(srgbintentOpt, "perceptual")) *srgbintentP = PNGX_PERCEPTUAL; else if (streq(srgbintentOpt, "relativecolorimetric")) @@ -241,7 +241,7 @@ parseModtimeOpt(const char * const modtimeOpt, if (count != 6) pm_error("Invalid value for -modtime '%s'. It should have " "the form [yy]yy-mm-dd hh:mm:ss.", modtimeOpt); - + if (year < 0) pm_error("Year is negative in -modtime value '%s'", modtimeOpt); if (year > 9999) @@ -289,7 +289,7 @@ 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. @@ -421,11 +421,11 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->ztxt = NULL; if (!paletteSpec) cmdlineP->palette = NULL; - + if (filterSpec + nofilter + sub + up + avg + paeth > 1) pm_error("You may specify at most one of " "-nofilter, -sub, -up, -avg, -paeth, and -filter"); - + if (filterSpec) { if (filter < 0 || filter > 4) pm_error("-filter is obsolete. Use -nofilter, -sub, -up, -avg, " @@ -452,13 +452,13 @@ parseCommandLine(int argc, const char ** argv, else cmdlineP->filterSet = PNG_FILTER_NONE; } - + if (cmdlineP->sizeSpec) parseSizeOpt(size, &cmdlineP->size); if (cmdlineP->rgbSpec) parseRgbOpt(rgb, &cmdlineP->rgb); - + if (cmdlineP->srgbintentSpec) parseSrgbintentOpt(srgbintent, &cmdlineP->srgbintent); @@ -530,14 +530,14 @@ reportInputType(int const format, static png_color_16 -xelToPngColor_16(xel const input, - xelval const maxval, +xelToPngColor_16(xel const input, + xelval const maxval, xelval const pngMaxval) { png_color_16 retval; xel scaled; - + PPM_DEPTH(scaled, input, maxval, pngMaxval); retval.red = PPM_GETR(scaled); @@ -551,12 +551,12 @@ xelToPngColor_16(xel const input, static void -closestColorInPalette(pixel const targetColor, +closestColorInPalette(pixel const targetColor, pixel palette_pnm[], unsigned int const paletteSize, unsigned int * const bestIndexP, unsigned int * const bestMatchP) { - + unsigned int paletteIndex; unsigned int bestIndex; unsigned int bestMatch; @@ -565,7 +565,7 @@ closestColorInPalette(pixel const targetColor, bestMatch = UINT_MAX; for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) { - unsigned int const dist = + unsigned int const dist = PPM_DISTANCE(palette_pnm[paletteIndex], targetColor); if (dist < bestMatch) { @@ -670,13 +670,13 @@ static colorhist_vector getChv_chv; static void -getChv(FILE * const ifP, +getChv(FILE * const ifP, pm_filepos const rasterPos, - int const cols, - int const rows, + int const cols, + int const rows, xelval const maxval, - int const format, - int const maxColors, + int const format, + int const maxColors, colorhist_vector * const chvP, unsigned int * const colorsP) { /*---------------------------------------------------------------------------- @@ -685,7 +685,7 @@ getChv(FILE * const ifP, raster starts at position 'rasterPos' of the file. The image's properties are 'cols', 'rows', 'maxval', and 'format'. - Return the number of colors as *colorsP. Return the details of the + Return the number of colors as *colorsP. Return the details of the colors in newly malloc'ed storage, and its address as *chvP. If there are more than 'maxColors' colors, though, just return NULL as *chvP and leave *colorsP undefined. @@ -700,13 +700,13 @@ getChv(FILE * const ifP, if (!getChv_computed) { int colorCount; - if (verbose) + if (verbose) pm_message ("Finding colors in input image..."); pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); - getChv_chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, + getChv_chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, maxColors, &colorCount); - + getChv_colors = colorCount; if (verbose) { @@ -737,7 +737,7 @@ static void freeChv(void) { static bool pgmBitsAreRepeated(unsigned int const repeatedSize, FILE * const ifP, - pm_filepos const rasterPos, + pm_filepos const rasterPos, int const cols, int const rows, xelval const maxval, @@ -764,7 +764,7 @@ pgmBitsAreRepeated(unsigned int const repeatedSize, xel * xelrow; xelrow = pnm_allocrow(cols); - + pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); mayscale = TRUE; /* initial assumption */ @@ -789,8 +789,8 @@ pgmBitsAreRepeated(unsigned int const repeatedSize, static void -meaningful_bits_pgm(FILE * const ifP, - pm_filepos const rasterPos, +meaningful_bits_pgm(FILE * const ifP, + pm_filepos const rasterPos, int const cols, int const rows, xelval const maxval, @@ -841,8 +841,8 @@ meaningful_bits_pgm(FILE * const ifP, static void -meaningful_bits_ppm(FILE * const ifp, - pm_filepos const rasterPos, +meaningful_bits_ppm(FILE * const ifp, + pm_filepos const rasterPos, int const cols, int const rows, xelval const maxval, @@ -895,12 +895,12 @@ meaningful_bits_ppm(FILE * const ifp, static void -tryTransparentColor(FILE * const ifp, - pm_filepos const rasterPos, - int const cols, - int const rows, +tryTransparentColor(FILE * const ifp, + pm_filepos const rasterPos, + int const cols, + int const rows, xelval const maxval, - int const format, + int const format, gray ** const alphaMask, gray const alphaMaxval, pixel const transcolor, @@ -918,7 +918,7 @@ tryTransparentColor(FILE * const ifp, pm_seek2(ifp, &rasterPos, sizeof(rasterPos)); singleColorIsTrans = TRUE; /* initial assumption */ - + for (row = 0; row < rows && singleColorIsTrans; ++row) { int col; pnm_readpnmrow(ifp, xelrow, cols, maxval, format); @@ -953,7 +953,7 @@ tryTransparentColor(FILE * const ifp, } } } - } + } pnm_freerow(xelrow); } @@ -961,15 +961,15 @@ tryTransparentColor(FILE * const ifp, static void analyzeAlpha(FILE * const ifP, - pm_filepos const rasterPos, - unsigned int const cols, - unsigned int const rows, + pm_filepos const rasterPos, + unsigned int const cols, + unsigned int const rows, xelval const maxval, - int const format, + int const format, gray ** const alphaMask, gray const alphaMaxval, bool * const allOpaqueP, - bool * const singleColorIsTransP, + bool * const singleColorIsTransP, pixel * const alphaTranscolorP) { /*---------------------------------------------------------------------------- Get information about the alpha mask, in combination with the masked @@ -995,7 +995,7 @@ analyzeAlpha(FILE * const ifP, /* We found a pixel in the image where the alpha mask says it is not fully opaque. */ - + xelrow = pnm_allocrow(cols); { @@ -1075,7 +1075,7 @@ determineTransparency(struct cmdlineInfo const cmdline, or identify a transparent color. We return as *transparentP: - + -1 PNG is not to have single-color transparency 1 PNG is to have single-color transparency as directed by user 2 PNG is to have single-color transparency that effects an alpha @@ -1099,7 +1099,7 @@ determineTransparency(struct cmdlineInfo const cmdline, if (alphaCols != cols || alphaRows != rows) { pm_error("dimensions for image and alpha mask do not agree"); } - analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format, + analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format, alphaMask, alphaMaxval, &allOpaque, &alphaCanBeTransparencyIndex, &alphaTranscolor); @@ -1129,7 +1129,7 @@ determineTransparency(struct cmdlineInfo const cmdline, *alphaMaxvalP = 255; if (cmdline.transparent) { - const char * transstring2; + const char * transstring2; /* The -transparent value, but with possible leading '=' removed */ if (cmdline.transparent[0] == '=') { *transExactP = TRUE; @@ -1137,12 +1137,12 @@ determineTransparency(struct cmdlineInfo const cmdline, } else { *transExactP = FALSE; transstring2 = cmdline.transparent; - } + } /* We do this funny PPM_DEPTH thing instead of just passing 'maxval' to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval scaling, and this is more precise. */ - PPM_DEPTH(*transColorP, + PPM_DEPTH(*transColorP, ppm_parsecolor(transstring2, PNM_OVERALLMAXVAL), PNM_OVERALLMAXVAL, maxval); @@ -1159,9 +1159,9 @@ determineBackground(struct cmdlineInfo const cmdline, xelval const maxval, xel * const backColorP) { - if (cmdline.background) + if (cmdline.background) PPM_DEPTH(*backColorP, - ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL), + ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL), PNM_OVERALLMAXVAL, maxval);; } @@ -1213,8 +1213,8 @@ hasColor(FILE * const ifP, static void -findRedundantBits(FILE * const ifp, - int const rasterPos, +findRedundantBits(FILE * const ifp, + int const rasterPos, int const cols, int const rows, xelval const maxval, @@ -1231,13 +1231,13 @@ findRedundantBits(FILE * const ifp, of bits, starting from the least significant end, that contain original information. -----------------------------------------------------------------------------*/ - if (!alpha && PNM_FORMAT_TYPE(format) == PGM_TYPE && !force) + if (!alpha && PNM_FORMAT_TYPE(format) == PGM_TYPE && !force) meaningful_bits_pgm(ifp, rasterPos, cols, rows, maxval, format, meaningfulBitsP); else if (PNM_FORMAT_TYPE(format) == PPM_TYPE && !force) meaningful_bits_ppm(ifp, rasterPos, cols, rows, maxval, format, meaningfulBitsP); - else + else *meaningfulBitsP = pm_maxvaltobits(maxval); if (verbose && *meaningfulBitsP != pm_maxvaltobits(maxval)) @@ -1249,24 +1249,24 @@ findRedundantBits(FILE * const ifp, static void readOrderedPalette(FILE * const pfp, - xel ordered_palette[], + xel ordered_palette[], unsigned int * const ordered_palette_size_p) { xel ** xels; int cols, rows; xelval maxval; int format; - + if (verbose) pm_message("reading ordered palette (colormap)..."); xels = pnm_readpnm(pfp, &cols, &rows, &maxval, &format); - - if (PNM_FORMAT_TYPE(format) != PPM_TYPE) + + if (PNM_FORMAT_TYPE(format) != PPM_TYPE) pm_error("ordered palette must be a PPM file, not type %d", format); *ordered_palette_size_p = rows * cols; - if (*ordered_palette_size_p > MAXCOLORS) + if (*ordered_palette_size_p > MAXCOLORS) pm_error("ordered-palette image contains %d pixels. Maximum is %d", *ordered_palette_size_p, MAXCOLORS); if (verbose) @@ -1278,12 +1278,12 @@ readOrderedPalette(FILE * const pfp, j = 0; /* initial value */ for (row = 0; row < rows; ++row) { int col; - for (col = 0; col < cols; ++col) + for (col = 0; col < cols; ++col) ordered_palette[j++] = xels[row][col]; } } pnm_freearray(xels, rows); -} +} @@ -1307,31 +1307,31 @@ compute_nonalpha_palette(colorhist_vector const chv, wants the colors in a particular order in the palette. -----------------------------------------------------------------------------*/ unsigned int colorIndex; - + xel ordered_palette[MAXCOLORS]; unsigned int ordered_palette_size; if (pfp) { readOrderedPalette(pfp, ordered_palette, &ordered_palette_size); - if (colors != ordered_palette_size) + if (colors != ordered_palette_size) pm_error("sizes of ordered palette (%d) " "and existing palette (%d) differ", ordered_palette_size, colors); - + /* Make sure the ordered palette contains all the colors in - the image + the image */ for (colorIndex = 0; colorIndex < colors; colorIndex++) { int j; bool found; - + found = FALSE; for (j = 0; j < ordered_palette_size && !found; ++j) { - if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color)) + if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color)) found = TRUE; } - if (!found) + if (!found) pm_error("failed to find color (%d, %d, %d), which is in the " "input image, in the ordered palette", PPM_GETR(chv[colorIndex].color), @@ -1344,7 +1344,7 @@ compute_nonalpha_palette(colorhist_vector const chv, for (colorIndex = 0; colorIndex < colors; ++colorIndex) palette_pnm[colorIndex] = ordered_palette[colorIndex]; } else { - for (colorIndex = 0; colorIndex < colors; ++colorIndex) + for (colorIndex = 0; colorIndex < colors; ++colorIndex) palette_pnm[colorIndex] = chv[colorIndex].color; } *paletteSizeP = colors; @@ -1434,7 +1434,7 @@ computeUnsortedAlphaPalette(FILE * const ifP, described by 'cols', 'rows', 'maxval', and 'format'. Using the alpha mask 'alpha_mask' and color map 'chv' (of size 'colors') - for the image, construct a palette of (color index, alpha) ordered pairs + for the image, construct a palette of (color index, alpha) ordered pairs for the image, as follows. The alpha/color palette is the set of all ordered pairs of @@ -1468,7 +1468,7 @@ computeUnsortedAlphaPalette(FILE * const ifP, alphasOfColor[colorIndex] = NULL; alphasOfColorCnt[colorIndex] = 0; } - + pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); xelrow = pnm_allocrow(cols); @@ -1554,22 +1554,22 @@ sortAlphaPalette(gray * const alphasOfColor[], unsigned int bot_idx; unsigned int top_idx; unsigned int colorIndex; - + /* We start one index at the bottom of the palette index range and another at the top. We run through the unsorted palette, and when we see an opaque entry, we map it to the current top - cursor and bump it down. When we see a non-opaque entry, we map + cursor and bump it down. When we see a non-opaque entry, we map it to the current bottom cursor and bump it up. Because the input and output palettes are the same size, the two cursors should meet right when we process the last entry of the unsorted palette. - */ + */ bot_idx = 0; top_idx = alphasFirstIndex[colors-1] + alphasOfColorCnt[colors-1] - 1; - + for (colorIndex = 0; colorIndex < colors; ++colorIndex) { unsigned int j; for (j = 0; j < alphasOfColorCnt[colorIndex]; ++j) { - unsigned int const paletteIndex = + unsigned int const paletteIndex = alphasFirstIndex[colorIndex] + j; if (alphasOfColor[colorIndex][j] == alphaMaxval) mapping[paletteIndex] = top_idx--; @@ -1590,7 +1590,7 @@ sortAlphaPalette(gray * const alphasOfColor[], static void -compute_alpha_palette(FILE * const ifP, +compute_alpha_palette(FILE * const ifP, int const cols, int const rows, xelval const maxval, @@ -1618,10 +1618,10 @@ compute_alpha_palette(FILE * const ifP, The palette is sorted so that the opaque entries are last, and we return *transSizeP as the number of non-opaque entries. - palette[] and trans[] are allocated by the caller to at least + palette[] and trans[] are allocated by the caller to at least MAXPALETTEENTRIES elements. - If there are more than MAXPALETTEENTRIES color/alpha pairs in the image, + If there are more than MAXPALETTEENTRIES color/alpha pairs in the image, don't return any palette information -- just return *tooBigP == TRUE. -----------------------------------------------------------------------------*/ colorhist_vector chv; @@ -1630,8 +1630,8 @@ compute_alpha_palette(FILE * const ifP, gray * alphas_of_color[MAXPALETTEENTRIES]; unsigned int alphas_first_index[MAXPALETTEENTRIES]; unsigned int alphas_of_color_cnt[MAXPALETTEENTRIES]; - - getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, + + getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, &chv, &colors); assert(colors <= ARRAY_SIZE(alphas_of_color)); @@ -1650,7 +1650,7 @@ compute_alpha_palette(FILE * const ifP, index into the sorted PNG palette of the alpha/color pair whose index is x in the unsorted PNG palette. This mapping sorts the palette so that opaque entries - are last. + are last. */ *paletteSizeP = colors == 0 ? @@ -1669,22 +1669,22 @@ compute_alpha_palette(FILE * const ifP, for (colorIndex = 0; colorIndex < colors; ++colorIndex) { unsigned int j; for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) { - unsigned int const paletteIndex = + unsigned int const paletteIndex = alphas_first_index[colorIndex] + j; palette_pnm[mapping[paletteIndex]] = chv[colorIndex].color; - trans_pnm[mapping[paletteIndex]] = + trans_pnm[mapping[paletteIndex]] = alphas_of_color[colorIndex][j]; } } } freeAlphasOfColor(alphas_of_color, colors); } -} +} static void -makeOneColorTransparentInPalette(xel const transColor, +makeOneColorTransparentInPalette(xel const transColor, bool const exact, pixel palette_pnm[], unsigned int const paletteSize, @@ -1692,12 +1692,12 @@ makeOneColorTransparentInPalette(xel const transColor, unsigned int * const transSizeP) { /*---------------------------------------------------------------------------- Find the color 'transColor' in the color/alpha palette defined by - palette_pnm[], paletteSize, trans_pnm[] and *transSizeP. + palette_pnm[], paletteSize, trans_pnm[] and *transSizeP. Make that entry fully transparent. Rearrange the palette so that that entry is first. (The PNG compressor - can do a better job when the opaque entries are all last in the + can do a better job when the opaque entries are all last in the color/alpha palette). If the specified color is not there and exact == TRUE, return @@ -1712,26 +1712,26 @@ makeOneColorTransparentInPalette(xel const transColor, unsigned int distance; assert(paletteSize > 0); - + if (*transSizeP != 0) pm_error("Internal error: trying to make a color in the palette " "transparent where there already is one."); - closestColorInPalette(transColor, palette_pnm, paletteSize, + closestColorInPalette(transColor, palette_pnm, paletteSize, &transparentIndex, &distance); if (distance != 0 && exact) { pm_message("specified transparent color not present in palette; " "ignoring -transparent"); errorlevel = PNMTOPNG_WARNING_LEVEL; - } else { + } else { /* Swap this with the first entry in the palette */ pixel tmp; - + tmp = palette_pnm[transparentIndex]; palette_pnm[transparentIndex] = palette_pnm[0]; palette_pnm[0] = tmp; - + /* Make it transparent */ trans_pnm[0] = PGM_TRANSPARENT; *transSizeP = 1; @@ -1747,8 +1747,8 @@ makeOneColorTransparentInPalette(xel const transColor, static void -findOrAddBackgroundInPalette(pixel const backColor, - pixel palette_pnm[], +findOrAddBackgroundInPalette(pixel const backColor, + pixel palette_pnm[], unsigned int * const paletteSizeP, unsigned int * const backgroundIndexP) { /*---------------------------------------------------------------------------- @@ -1757,11 +1757,11 @@ findOrAddBackgroundInPalette(pixel const backColor, add it, choose a background color that's already in the palette, as close to 'backColor' as possible. - If we add an entry to the palette, make it opaque. But in searching the + If we add an entry to the palette, make it opaque. But in searching the existing palette, ignore transparency. Note that PNG specs say that transparency of the background is meaningless; - i.e. a viewer must ignore the transparency of the palette entry when + i.e. a viewer must ignore the transparency of the palette entry when using the background color. Return the palette index of the background color as *backgroundIndexP. @@ -1770,9 +1770,9 @@ findOrAddBackgroundInPalette(pixel const backColor, unsigned int paletteIndex; backgroundIndex = -1; - for (paletteIndex = 0; - paletteIndex < *paletteSizeP; - ++paletteIndex) + for (paletteIndex = 0; + paletteIndex < *paletteSizeP; + ++paletteIndex) if (PPM_EQUAL(palette_pnm[paletteIndex], backColor)) backgroundIndex = paletteIndex; @@ -1815,8 +1815,8 @@ findOrAddBackgroundInPalette(pixel const backColor, -static void -buildColorLookup(pixel palette_pnm[], +static void +buildColorLookup(pixel palette_pnm[], unsigned int const paletteSize, colorhash_table * const chtP) { /*---------------------------------------------------------------------------- @@ -1841,14 +1841,14 @@ buildColorLookup(pixel palette_pnm[], -static void -buildColorAlphaLookup(pixel palettePnm[], +static void +buildColorAlphaLookup(pixel palettePnm[], unsigned int const paletteSize, - gray transPnm[], + gray transPnm[], unsigned int const transSize, gray const alphaMaxval, coloralphahash_table * const cahtP) { - + coloralphahash_table const caht = alloccoloralphahash(); unsigned int paletteIndex; @@ -1897,18 +1897,18 @@ tryAlphaPalette(FILE * const ifP, "a PNG with transparency when you specify " "the palette with -palette."); - compute_alpha_palette(ifP, cols, rows, maxval, format, + compute_alpha_palette(ifP, cols, rows, maxval, format, rasterPos, alpha_mask, alphaMaxval, - palette_pnm, trans_pnm, + palette_pnm, trans_pnm, paletteSizeP, transSizeP, &tooBig); if (tooBig) { pm_asprintf(impossibleReasonP, "too many color/transparency pairs " - "(more than the PNG maximum of %u", + "(more than the PNG maximum of %u", MAXPALETTEENTRIES); } else *impossibleReasonP = NULL; -} +} @@ -1922,15 +1922,15 @@ computePixelWidth(bool const colorPng, unsigned int bitsPerSample, bitsPerPixel; if (colorPng || alpha) { - /* PNG allows only depths of 8 and 16 for a truecolor image + /* PNG allows only depths of 8 and 16 for a truecolor image and for a grayscale image with an alpha channel. */ if (pnmMeaningfulBitCt > 8) bitsPerSample = 16; - else + else bitsPerSample = 8; } else { - /* A grayscale, non-colormapped, no-alpha PNG may have any + /* A grayscale, non-colormapped, no-alpha PNG may have any bit depth from 1 to 16 */ if (pnmMeaningfulBitCt > 8) @@ -1969,7 +1969,7 @@ paletteIndexBits(unsigned int const nColors) { Return the number of bits that a palette index in the PNG will occupy given that the palette has 'nColors' colors in it. It is 1, 2, 4, or 8 bits. - + If 'nColors' is not a valid PNG palette size, return 0. -----------------------------------------------------------------------------*/ unsigned int retval; @@ -2048,7 +2048,7 @@ computeColorMap(FILE * const ifP, pm_asprintf(noColormapReasonP, "You requested no color map"); else if (maxval > PALETTEMAXVAL) pm_asprintf(noColormapReasonP, "The maxval of the input image (%u) " - "exceeds the PNG palette maxval (%u)", + "exceeds the PNG palette maxval (%u)", maxval, PALETTEMAXVAL); else { unsigned int bitsPerPixel; @@ -2068,20 +2068,20 @@ computeColorMap(FILE * const ifP, */ colorhist_vector chv; unsigned int colors; - - getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, + + getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, &chv, &colors); if (chv == NULL) { - pm_asprintf(noColormapReasonP, + pm_asprintf(noColormapReasonP, "More than %u colors found -- too many for a " "colormapped PNG", MAXCOLORS); } else { /* There are few enough colors that a palette is possible */ if (bitsPerPixel <= paletteIndexBits(colors) && !pfP) - pm_asprintf(noColormapReasonP, + pm_asprintf(noColormapReasonP, "palette index for %u colors would be " - "no smaller than the indexed value (%u bits)", + "no smaller than the indexed value (%u bits)", colors, bitsPerPixel); else { unsigned int paletteSize; @@ -2090,7 +2090,7 @@ computeColorMap(FILE * const ifP, tryAlphaPalette(ifP, cols, rows, maxval, format, rasterPos, alpha_mask, alphaMaxval, pfP, - palette_pnm, &paletteSize, + palette_pnm, &paletteSize, trans_pnm, &transSize, noColormapReasonP); @@ -2098,13 +2098,13 @@ computeColorMap(FILE * const ifP, *noColormapReasonP = NULL; compute_nonalpha_palette(chv, colors, maxval, pfP, - palette_pnm, &paletteSize, + palette_pnm, &paletteSize, trans_pnm, &transSize); - + if (transparent) makeOneColorTransparentInPalette( - transcolor, transexact, - palette_pnm, paletteSize, trans_pnm, + transcolor, transexact, + palette_pnm, paletteSize, trans_pnm, &transSize); } if (!*noColormapReasonP) { @@ -2137,7 +2137,7 @@ static void computeColorMapLookupTable( /*---------------------------------------------------------------------------- Compute applicable lookup tables for the palette index. If there's no alpha mask, this is just a standard Netpbm colorhash_table. If there's - an alpha mask, it is the slower Pnmtopng-specific + an alpha mask, it is the slower Pnmtopng-specific coloralphahash_table. If a lookup table is not applicable to the image, return NULL as @@ -2145,10 +2145,10 @@ static void computeColorMapLookupTable( -----------------------------------------------------------------------------*/ if (colorMapped) { if (alpha) { - buildColorAlphaLookup(palette_pnm, palette_size, + buildColorAlphaLookup(palette_pnm, palette_size, trans_pnm, trans_size, alpha_maxval, cahtP); *chtP = NULL; - } else { + } else { buildColorLookup(palette_pnm, palette_size, chtP); *cahtP = NULL; } @@ -2195,15 +2195,15 @@ computeRasterWidth(bool const colorMapped, bitsPerSampleP, bitsPerPixelP); if (verbose) - pm_message("Writing %u bits per component per pixel", + pm_message("Writing %u bits per component per pixel", *bitsPerSampleP); } } static void -createPngPalette(pixel palette_pnm[], - unsigned int const paletteSize, +createPngPalette(pixel palette_pnm[], + unsigned int const paletteSize, pixval const maxval, gray trans_pnm[], unsigned int const transSize, @@ -2261,7 +2261,7 @@ setZlibCompression(struct pngx * const pngxP, pngx_setCompressionSize(pngxP, zlibCompression.buffer_size); } } - + static void @@ -2276,7 +2276,7 @@ makePngLine(png_byte * const line, struct pngx * const pngxP, xelval const png_maxval, unsigned int const depth) { - + unsigned int col; png_byte *pp; @@ -2310,7 +2310,7 @@ makePngLine(png_byte * const line, *pp++ = PPM_GETB(p_png) & 0xff; } else pm_error("INTERNAL ERROR: undefined color_type"); - + if (pngx_colorType(pngxP) & PNG_COLOR_MASK_ALPHA) { int const png_alphaval = (int) alpha_mask[col] * (float) png_maxval / maxval + 0.5; @@ -2364,7 +2364,7 @@ writeRaster(struct pngx * const pngxP, pnm_readpnmrow(ifP, xelrow, cols, maxval, format); pnm_promoteformatrow(xelrow, cols, maxval, format, maxval, PPM_TYPE); - + makePngLine(line, xelrow, cols, maxval, alpha, alpha ? alpha_mask[row] : NULL, cht, caht, pngxP, png_maxval, depth); @@ -2393,15 +2393,15 @@ doHistChunk(struct pngx * const pngxP, colorhist_vector chv; unsigned int colorCt; colorhash_table cht; - - getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, + + getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, &chv, &colorCt); cht = ppm_colorhisttocolorhash(chv, colorCt); - - { + + { png_uint_16 * histogram; /* malloc'ed */ - + MALLOCARRAY(histogram, MAXCOLORS); if (!histogram) @@ -2416,7 +2416,7 @@ doHistChunk(struct pngx * const pngxP, else histogram[i] = chv[chvIndex].value; } - + pngx_setHist(pngxP, histogram); if (verbose) @@ -2463,7 +2463,7 @@ doIhdrChunk(struct pngx * const pngxP, static void doGamaChunk(struct cmdlineInfo const cmdline, struct pngx * const pngxP) { - + if (cmdline.gammaSpec) pngx_setGama(pngxP, cmdline.gamma); } @@ -2507,7 +2507,7 @@ reportTrans(struct pngx * const pngxP) { struct pngx_trns const transInfo = pngx_trns(pngxP); pm_message("%u transparency values", transInfo.numTrans); - + pm_message("Transparent color {gray, red, green, blue} = " "{%d, %d, %d, %d}", transInfo.transColor.gray, @@ -2560,21 +2560,21 @@ doBkgdChunk(struct pngx * const pngxP, xelval const maxval, xelval const pngMaxval, bool const verbose) { - + if (bkgdRequested) { if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) pngx_setBkgdPalette(pngxP, backgroundIndex); else { - png_color_16 const pngBackground = + png_color_16 const pngBackground = xelToPngColor_16(backColor, maxval, pngMaxval); pngx_setBkgdRgb(pngxP, pngBackground); if (verbose) pm_message("Writing bKGD chunk with background color " " {gray, red, green, blue} = {%d, %d, %d, %d}", - pngBackground.gray, - pngBackground.red, - pngBackground.green, - pngBackground.blue ); + pngBackground.gray, + pngBackground.red, + pngBackground.green, + pngBackground.blue ); } } } @@ -2591,20 +2591,19 @@ doSbitChunk(struct pngx * const pngxP, if (pngx_colorType(pngxP) != PNG_COLOR_TYPE_PALETTE && (pngMaxval > maxval || (alpha && pngMaxval > alphaMaxval))) { - /* We're writing in a bit depth that doesn't match the maxval - of the input image and the alpha mask. So we write an sBIT - chunk to tell what the original image's maxval was. The - sBit chunk doesn't let us specify any maxval -- only powers - of two minus one. So we pick the power of two minus one - which is greater than or equal to the actual input maxval. - - PNG also doesn't let an sBIT chunk indicate a maxval - _greater_ than the the PNG maxval. The designers probably - did not conceive of the case where that would happen. The - case is this: We detected redundancy in the bits so were - able to store fewer bits than the user provided. But since - PNG doesn't allow it, we don't attempt to create such an - sBIT chunk. + /* We're writing in a bit depth that doesn't match the maxval of the + input image and the alpha mask. So we write an sBIT chunk to tell + what the original image's maxval was. The sBit chunk doesn't let + us specify any maxval -- only powers of two minus one. So we pick + the power of two minus one which is greater than or equal to the + actual input maxval. + + PNG also doesn't let an sBIT chunk indicate a maxval _greater_ than + the PNG maxval. The designers probably did not conceive of the + case where that would happen. The case is this: We detected + redundancy in the bits so were able to store fewer bits than the + user provided. But since PNG doesn't allow it, we don't attempt to + create such an sBIT chunk. */ { @@ -2618,7 +2617,7 @@ doSbitChunk(struct pngx * const pngxP, sbit.blue = sbitval; } else sbit.gray = sbitval; - + if (verbose) pm_message("Writing sBIT chunk with bits = %d", sbitval); @@ -2649,7 +2648,7 @@ addSrgbChunk(struct pngx * const pngxP, -static void +static void convertpnm(struct cmdlineInfo const cmdline, FILE * const ifP, FILE * const ofP, @@ -2668,14 +2667,14 @@ convertpnm(struct cmdlineInfo const cmdline, xelval maxval; /* The maxval of the input image */ xelval pngMaxval; - /* The maxval of the samples in the PNG output + /* The maxval of the samples in the PNG output (must be 1, 3, 7, 15, 255, or 65535) */ pixel transcolor; /* The color that is to be transparent, with maxval equal to that of the input image. */ - bool transExact; + bool transExact; /* boolean: the user wants only the exact color he specified to be transparent; not just something close to it. */ @@ -2694,14 +2693,14 @@ convertpnm(struct cmdlineInfo const cmdline, pixel palettePnm[MAXCOLORS]; png_color palette[MAXCOLORS]; /* The color part of the color/alpha palette passed to the PNG - compressor + compressor */ unsigned int paletteSize; gray transPnm[MAXCOLORS]; png_byte trans[MAXCOLORS]; /* The alpha part of the color/alpha palette passed to the PNG - compressor + compressor */ unsigned int transSize; @@ -2717,7 +2716,7 @@ convertpnm(struct cmdlineInfo const cmdline, we should. malloc'ed null-terminated string. */ unsigned int depth; - /* The number of bits per sample in the (uncompressed) png + /* The number of bits per sample in the (uncompressed) png raster -- if the raster contains palette indices, this is the number of bits in the index. */ @@ -2725,7 +2724,7 @@ convertpnm(struct cmdlineInfo const cmdline, /* The total number of bits per pixel in the (uncompressed) png raster, including all channels. */ - pm_filepos rasterPos; + pm_filepos rasterPos; /* file position in input image file of start of image (i.e. after the header) */ @@ -2766,7 +2765,7 @@ convertpnm(struct cmdlineInfo const cmdline, colorPng = (PNM_FORMAT_TYPE(format) == PPM_TYPE); else { if (PNM_FORMAT_TYPE(format) == PPM_TYPE) { - colorPng = hasColor(ifP, cols, rows, maxval, format, rasterPos); + colorPng = hasColor(ifP, cols, rows, maxval, format, rasterPos); } else colorPng = false; } @@ -2781,10 +2780,10 @@ convertpnm(struct cmdlineInfo const cmdline, findRedundantBits(ifP, rasterPos, cols, rows, maxval, format, alpha, cmdline.force, &pnmMeaningfulBitCt); - + computeColorMap(ifP, rasterPos, cols, rows, maxval, colorPng, format, cmdline.force, pfP, - alpha, transparent >= 0, transcolor, transExact, + alpha, transparent >= 0, transcolor, transExact, !!cmdline.background, backColor, alpha_mask, alphaMaxval, pnmMeaningfulBitCt, palettePnm, &paletteSize, transPnm, &transSize, @@ -2801,7 +2800,7 @@ convertpnm(struct cmdlineInfo const cmdline, colorMapped = FALSE; } else colorMapped = TRUE; - + computeColorMapLookupTable(colorMapped, palettePnm, paletteSize, transPnm, transSize, alpha, alphaMaxval, &cht, &caht); @@ -2812,7 +2811,7 @@ convertpnm(struct cmdlineInfo const cmdline, if (verbose) pm_message ("writing a%s %d-bit %s%s file%s", fulldepth == 8 ? "n" : "", fulldepth, - colorMapped ? "palette": + colorMapped ? "palette": colorPng ? "RGB" : "gray", alpha ? (colorMapped ? "+transparency" : "+alpha") : "", cmdline.interlace ? " (interlaced)" : ""); @@ -2839,7 +2838,7 @@ convertpnm(struct cmdlineInfo const cmdline, /* creating PNG palette (Not counting the transparency palette) */ createPngPalette(palettePnm, paletteSize, maxval, - transPnm, transSize, alphaMaxval, + transPnm, transSize, alphaMaxval, palette, trans); pngx_setPlte(pngxP, palette, paletteSize); @@ -2940,7 +2939,7 @@ displayVersion() { -int +int main(int argc, const char * argv[]) { struct cmdlineInfo cmdline; @@ -2950,29 +2949,29 @@ main(int argc, const char * argv[]) { FILE * tfP; int errorlevel; - + pm_proginit(&argc, argv); - + parseCommandLine(argc, argv, &cmdline); - + if (cmdline.libversion) { displayVersion(); return 0; } verbose = cmdline.verbose; - + ifP = pm_openr_seekable(cmdline.inputFileName); - + if (cmdline.alpha) afP = pm_openr(cmdline.alpha); else afP = NULL; - + if (cmdline.palette) pfP = pm_openr(cmdline.palette); else pfP = NULL; - + if (cmdline.text) tfP = pm_openr(cmdline.text); else if (cmdline.ztxt) @@ -2981,7 +2980,7 @@ main(int argc, const char * argv[]) { tfP = NULL; convertpnm(cmdline, ifP, stdout, afP, pfP, tfP, &errorlevel); - + if (afP) pm_close(afP); if (pfP) |