diff options
Diffstat (limited to 'converter/other/pngtopnm.c')
-rw-r--r-- | converter/other/pngtopnm.c | 965 |
1 files changed, 550 insertions, 415 deletions
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c index 59149acd..a8ea25a7 100644 --- a/converter/other/pngtopnm.c +++ b/converter/other/pngtopnm.c @@ -1,6 +1,6 @@ /* ** pngtopnm.c - -** read a Portable Network Graphics file and produce a portable anymap +** read a Portable Network Graphics file and produce a PNM. ** ** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de> ** and Willem van Schaik <willem@schaik.com> @@ -16,61 +16,29 @@ ** with lots of bits pasted from libpng.txt by Guy Eric Schalnat */ -/* - BJH 20000408: rename PPM_MAXMAXVAL to PPM_OVERALLMAXVAL - BJH 20000303: fix include statement so dependencies work out right. -*/ -/* GRR 19991203: moved VERSION to new version.h header file */ - -/* GRR 19990713: fixed redundant freeing of png_ptr and info_ptr in setjmp() - * blocks and added "pm_close(ifp)" in each. */ - -/* GRR 19990317: declared "clobberable" automatic variables in convertpng() - * static to fix Solaris/gcc stack-corruption bug. Also installed custom - * error-handler to avoid jmp_buf size-related problems (i.e., jmp_buf - * compiled with one size in libpng and another size here). */ - #ifndef PNMTOPNG_WARNING_LEVEL # define PNMTOPNG_WARNING_LEVEL 0 /* use 0 for backward compatibility, */ #endif /* 2 for warnings (1 == error) */ +#include <assert.h> #include <math.h> #include <float.h> #include <png.h> /* includes zlib.h and setjmp.h */ #define VERSION "2.37.4 (5 December 1999) +netpbm" -#include "pnm.h" +#include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" +#include "pnm.h" +/* A hack until we can remove direct access to png_info from the program */ #if PNG_LIBPNG_VER >= 10400 -#error Your PNG library (<png.h>) is incompatible with this Netpbm source code. -#error You need either an older PNG library (older than 1.4) -#error newer Netpbm source code (at least 10.48) -#endif - -typedef struct _jmpbuf_wrapper { - jmp_buf jmpbuf; -} jmpbuf_wrapper; - -/* GRR 19991205: this is used as a test for pre-1999 versions of netpbm and - * pbmplus vs. 1999 or later (in which pm_close was split into two) - */ -#ifdef PBMPLUS_RAWBITS -# define pm_closer pm_close -# define pm_closew pm_close +#define TRANS_ALPHA trans_alpha +#else +#define TRANS_ALPHA trans #endif -#ifndef TRUE -# define TRUE 1 -#endif -#ifndef FALSE -# define FALSE 0 -#endif -#ifndef NONE -# define NONE 0 -#endif enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX}; @@ -88,7 +56,7 @@ struct cmdlineInfo { }; -typedef struct pngcolor { +typedef struct { /*---------------------------------------------------------------------------- A color in a format compatible with the PNG library. @@ -102,15 +70,13 @@ typedef struct pngcolor { static png_uint_16 maxval; -static int verbose = FALSE; -static int mtime; -static jmpbuf_wrapper pngtopnm_jmpbuf_struct; +static bool verbose; static void -parseCommandLine(int argc, - char ** argv, - struct cmdlineInfo *cmdlineP ) { +parseCommandLine(int argc, + const char ** argv, + struct cmdlineInfo * cmdlineP ) { /*---------------------------------------------------------------------------- Parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. @@ -121,7 +87,7 @@ parseCommandLine(int argc, Note that the strings we return are stored in the storage that was passed to us as the argv array. We also trash *argv. -----------------------------------------------------------------------------*/ - optEntry *option_def = malloc(100*sizeof(optEntry)); + optEntry * option_def; /* Instructions to optParseOptions3 on how to parse our options. */ optStruct3 opt; @@ -130,6 +96,8 @@ parseCommandLine(int argc, unsigned int alphaSpec, mixSpec, backgroundSpec, gammaSpec, textSpec; + MALLOCARRAY(option_def, 100); + option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); @@ -150,7 +118,7 @@ parseCommandLine(int argc, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ @@ -186,12 +154,215 @@ parseCommandLine(int argc, +static void +pngtopnmErrorHandler(png_structp const png_ptr, + png_const_charp const msg) { + + jmp_buf * jmpbufP; + + /* this function, aside from the extra step of retrieving the "error + pointer" (below) and the fact that it exists within the application + rather than within libpng, is essentially identical to libpng's + default error handler. The second point is critical: since both + setjmp() and longjmp() are called from the same code, they are + guaranteed to have compatible notions of how big a jmp_buf is, + regardless of whether _BSD_SOURCE or anything else has (or has not) + been defined. + */ + + pm_message("fatal libpng error: %s", msg); + + jmpbufP = png_get_error_ptr(png_ptr); + + if (!jmpbufP) { + /* we are completely hosed now */ + pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating."); + } + + longjmp(*jmpbufP, 1); +} + + + +struct pngx { + png_structp png_ptr; + png_infop info_ptr; +}; + + + +static void +pngx_createRead(struct pngx ** const pngxPP, + jmp_buf * const jmpbufP) { + + struct pngx * pngxP; + + MALLOCVAR(pngxP); + + if (!pngxP) + pm_error("Failed to allocate memory for PNG object"); + else { + pngxP->png_ptr = png_create_read_struct( + PNG_LIBPNG_VER_STRING, + jmpbufP, pngtopnmErrorHandler, NULL); + + if (!pngxP->png_ptr) + pm_error("cannot allocate main libpng structure (png_ptr)"); + else { + pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr); + + if (!pngxP->info_ptr) + pm_error("cannot allocate libpng info structure (info_ptr)"); + else + *pngxPP = pngxP; + } + } +} + + + +static void +pngx_destroy(struct pngx * const pngxP) { + + png_destroy_read_struct(&pngxP->png_ptr, &pngxP->info_ptr, NULL); + + free(pngxP); +} + + + +static bool +pngx_chunkIsPresent(struct pngx * const pngxP, + uint32_t const chunkType) { + + return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType); +} + + + +static void +verifyFileIsPng(FILE * const ifP, + size_t * const consumedByteCtP) { + + unsigned char buffer[4]; + size_t bytesRead; + + bytesRead = fread(buffer, 1, sizeof(buffer), ifP); + if (bytesRead != sizeof(buffer)) + pm_error("input file is empty or too short"); + + if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0) + pm_error("input file is not a PNG file " + "(does not have the PNG signature in its first 4 bytes)"); + else + *consumedByteCtP = bytesRead; +} + + + +static unsigned int +computePngLineSize(struct pngx * const pngxP) { + + unsigned int const bytesPerSample = + pngxP->info_ptr->bit_depth == 16 ? 2 : 1; + + unsigned int samplesPerPixel; + + switch (pngxP->info_ptr->color_type) { + 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: samplesPerPixel = 1; + } + + if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width) + pm_error("Width %u of PNG is uncomputably large", + (unsigned int)pngxP->info_ptr->width); + + return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel; +} + + + +static void +allocPngRaster(struct pngx * const pngxP, + png_byte *** const pngImageP) { + + unsigned int const lineSize = computePngLineSize(pngxP); + + png_byte ** pngImage; + unsigned int row; + + MALLOCARRAY(pngImage, pngxP->info_ptr->height); + + if (pngImage == NULL) + pm_error("couldn't allocate space for %u PNG raster rows", + (unsigned int)pngxP->info_ptr->height); + + for (row = 0; row < pngxP->info_ptr->height; ++row) { + MALLOCARRAY(pngImage[row], lineSize); + if (pngImage[row] == NULL) + pm_error("couldn't allocate space for %uth row of PNG raster", + row); + } + *pngImageP = pngImage; +} + + + +static void +freePngRaster(png_byte ** const pngRaster, + struct pngx * const pngxP) { + + unsigned int row; + + for (row = 0; row < pngxP->info_ptr->height; ++row) + free(pngRaster[row]); + + free(pngRaster); +} + + + +static void +readPng(struct pngx * const pngxP, + FILE * const ifP, + png_byte *** const pngRasterP) { + + size_t sigByteCt; + png_byte ** pngRaster; + + verifyFileIsPng(ifP, &sigByteCt); + + /* Declare that we already read the signature bytes */ + png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt); + + png_init_io(pngxP->png_ptr, ifP); + + png_read_info(pngxP->png_ptr, pngxP->info_ptr); + + allocPngRaster(pngxP, &pngRaster); + + if (pngxP->info_ptr->bit_depth < 8) + png_set_packing(pngxP->png_ptr); + + png_read_image(pngxP->png_ptr, pngRaster); + + png_read_end(pngxP->png_ptr, pngxP->info_ptr); + + /* Note that some of info_ptr is not defined until png_read_end() + completes. That's because it comes from chunks that are at the + end of the stream. + */ + + *pngRasterP = pngRaster; +} + -#define get_png_val(p) _get_png_val (&(p), info_ptr->bit_depth) static png_uint_16 -_get_png_val (png_byte ** const pp, - int const bit_depth) { +get_png_val(const png_byte ** const pp, + int const bit_depth) { png_uint_16 c; @@ -247,121 +418,95 @@ gamma_correct(png_uint_16 const v, float const g) { if (g != -1.0) - return (png_uint_16) (pow ((double) v / maxval, - (1.0 / g)) * maxval + 0.5); + return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) * + maxval); else return v; } -#ifdef __STDC__ -static int iscolor (png_color c) -#else -static int iscolor (c) -png_color c; -#endif -{ - return c.red != c.green || c.green != c.blue; +static bool +iscolor(png_color const c) { + + return c.red != c.green || c.green != c.blue; } -#ifdef __STDC__ -static void save_text (png_info *info_ptr, FILE *tfp) -#else -static void save_text (info_ptr, tfp) -png_info *info_ptr; -FILE *tfp; -#endif -{ - int i, j, k; - - for (i = 0 ; i < info_ptr->num_text ; i++) { - j = 0; - while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ') - j++; - if (info_ptr->text[i].key[j] != ' ') { - fprintf (tfp, "%s", info_ptr->text[i].key); - for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++) - putc (' ', tfp); - } else { - fprintf (tfp, "\"%s\"", info_ptr->text[i].key); - for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++) - putc (' ', tfp); - } - putc (' ', tfp); /* at least one space between key and text */ + + +static void +saveText(struct pngx * const pngxP, + FILE * const tfP) { + + png_info * const info_ptr = pngxP->info_ptr; + + unsigned int i; + + for (i = 0 ; i < info_ptr->num_text; ++i) { + unsigned int j; + j = 0; + + while (info_ptr->text[i].key[j] != '\0' && + info_ptr->text[i].key[j] != ' ') + ++j; + + if (info_ptr->text[i].key[j] != ' ') { + fprintf(tfP, "%s", info_ptr->text[i].key); + for (j = strlen (info_ptr->text[i].key); j < 15; ++j) + putc(' ', tfP); + } else { + fprintf(tfP, "\"%s\"", info_ptr->text[i].key); + for (j = strlen (info_ptr->text[i].key); j < 13; ++j) + putc(' ', tfP); + } + putc(' ', tfP); /* at least one space between key and text */ - for (j = 0 ; j < info_ptr->text[i].text_length ; j++) { - putc (info_ptr->text[i].text[j], tfp); - if (info_ptr->text[i].text[j] == '\n') - for (k = 0 ; k < 16 ; k++) - putc ((int)' ', tfp); + for (j = 0; j < info_ptr->text[i].text_length; ++j) { + putc(info_ptr->text[i].text[j], tfP); + if (info_ptr->text[i].text[j] == '\n') { + unsigned int k; + for (k = 0; k < 16; ++k) + putc(' ', tfP); + } + } + putc('\n', tfP); } - putc ((int)'\n', tfp); - } } -#ifdef __STDC__ -static void show_time (png_info *info_ptr) -#else -static void show_time (info_ptr) -png_info *info_ptr; -#endif -{ + + +static void +showTime(struct pngx * const pngxP) { + static const char * const month[] = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - if (info_ptr->valid & PNG_INFO_tIME) { - if (info_ptr->mod_time.month < 1 || - info_ptr->mod_time.month >= ARRAY_SIZE(month)) { - pm_message("tIME chunk in PNG input is invalid; " - "modification time of image is unknown. " - "The month value, which should be in the range " - "1-12, is %u", info_ptr->mod_time.month); - } else - pm_message ("modification time: %02d %s %d %02d:%02d:%02d", - info_ptr->mod_time.day, month[info_ptr->mod_time.month], - info_ptr->mod_time.year, info_ptr->mod_time.hour, - info_ptr->mod_time.minute, info_ptr->mod_time.second); - } -} - -#ifdef __STDC__ -static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg) -#else -static void pngtopnm_error_handler (png_ptr, msg) -png_structp png_ptr; -png_const_charp msg; -#endif -{ - jmpbuf_wrapper *jmpbuf_ptr; - - /* this function, aside from the extra step of retrieving the "error - * pointer" (below) and the fact that it exists within the application - * rather than within libpng, is essentially identical to libpng's - * default error handler. The second point is critical: since both - * setjmp() and longjmp() are called from the same code, they are - * guaranteed to have compatible notions of how big a jmp_buf is, - * regardless of whether _BSD_SOURCE or anything else has (or has not) - * been defined. */ - - pm_message("fatal libpng error: %s", msg); - - jmpbuf_ptr = png_get_error_ptr(png_ptr); - if (jmpbuf_ptr == NULL) { - /* we are completely hosed now */ - pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating."); - } - - longjmp(jmpbuf_ptr->jmpbuf, 1); + if (pngxP->info_ptr->valid & PNG_INFO_tIME) { + if (pngxP->info_ptr->mod_time.month < 1 || + pngxP->info_ptr->mod_time.month >= ARRAY_SIZE(month)) { + pm_message("tIME chunk in PNG input is invalid; " + "modification time of image is unknown. " + "The month value, which should be in the range " + "1-12, is %u", pngxP->info_ptr->mod_time.month); + } else + pm_message("modification time: %02d %s %d %02d:%02d:%02d", + pngxP->info_ptr->mod_time.day, + month[pngxP->info_ptr->mod_time.month], + pngxP->info_ptr->mod_time.year, + pngxP->info_ptr->mod_time.hour, + pngxP->info_ptr->mod_time.minute, + pngxP->info_ptr->mod_time.second); + } } static void -dump_png_info(png_info *info_ptr) { +dumpPngInfo(struct pngx * const pngxP) { + png_info * const info_ptr = pngxP->info_ptr; const char *type_string; const char *filter_string; @@ -445,7 +590,7 @@ dump_png_info(png_info *info_ptr) { else pm_message("bKGD chunk: not present"); - if (info_ptr->valid & PNG_INFO_hIST) + if (info_ptr->valid & PNG_INFO_PLTE) pm_message("hIST chunk: present"); else pm_message("hIST chunk: not present"); @@ -478,29 +623,62 @@ dump_png_info(png_info *info_ptr) { +static const png_color_16 * +transColor(struct pngx * const pngxP) { + + png_bytep trans; + int numTrans; + png_color_16 * transColor; + + assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)); + + png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr, + &trans, &numTrans, &transColor); + + return transColor; +} + + + static bool -isTransparentColor(pngcolor const color, - png_info * const info_ptr, - double const totalgamma) { +isTransparentColor(pngcolor const color, + struct pngx * const pngxP, + double const totalgamma) { /*---------------------------------------------------------------------------- Return TRUE iff pixels of color 'color' are supposed to be transparent everywhere they occur. Assume it's an RGB image. + + 'color' has been gamma-corrected. -----------------------------------------------------------------------------*/ bool retval; - if (info_ptr->valid & PNG_INFO_tRNS) { - const png_color_16 * const transColorP = &info_ptr->trans_values; - + if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { + const png_color_16 * const transColorP = transColor(pngxP); - /* There seems to be a problem here: you can't compare real - numbers for equality. Also, I'm not sure the gamma - corrected/uncorrected color spaces are right here. - */ + /* It seems odd that libpng lets you get gamma-corrected pixel + values, but not gamma-corrected transparency or background + values. But as that is the case, we have to gamma-correct + the transparency values. - retval = - color.r == gamma_correct(transColorP->red, totalgamma) && - color.g == gamma_correct(transColorP->green, totalgamma) && - color.b == gamma_correct(transColorP->blue, totalgamma); + Note that because we compare the gamma-corrected values and + there may be many-to-one mapping of uncorrected to corrected + values, more pixels may be transparent than what the user + intended. + + We could fix this by not letting libpng gamma-correct the + pixels, and just do it ourselves. + */ + + switch (pngxP->info_ptr->color_type) { + case PNG_COLOR_TYPE_GRAY: + retval = color.r == gamma_correct(transColorP->gray, totalgamma); + break; + default: + retval = + color.r == gamma_correct(transColorP->red, totalgamma) && + color.g == gamma_correct(transColorP->green, totalgamma) && + color.b == gamma_correct(transColorP->blue, totalgamma); + } } else retval = FALSE; @@ -509,37 +687,17 @@ isTransparentColor(pngcolor const color, -#define SIG_CHECK_SIZE 4 - static void -read_sig_buf(FILE * const ifP) { - - unsigned char sig_buf[SIG_CHECK_SIZE]; - size_t bytesRead; - - bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP); - if (bytesRead != SIG_CHECK_SIZE) - pm_error ("input file is empty or too short"); - - if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) - != 0) - pm_error ("input file is not a PNG file"); -} - - - -static void -setupGammaCorrection(png_struct * const png_ptr, - png_info * const info_ptr, - float const displaygamma, - float * const totalgammaP) { +setupGammaCorrection(struct pngx * const pngxP, + float const displaygamma, + float * const totalgammaP) { if (displaygamma == -1.0) *totalgammaP = -1.0; else { float imageGamma; - if (info_ptr->valid & PNG_INFO_gAMA) - imageGamma = info_ptr->gamma; + if (pngxP->info_ptr->valid & PNG_INFO_gAMA) + imageGamma = pngxP->info_ptr->gamma; else { if (verbose) pm_message("PNG doesn't specify image gamma. Assuming 1.0"); @@ -553,12 +711,12 @@ setupGammaCorrection(png_struct * const png_ptr, "display gamma %4.2f. No conversion.", imageGamma, displaygamma); } else { - png_set_gamma(png_ptr, displaygamma, imageGamma); + png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma); *totalgammaP = imageGamma * displaygamma; /* in case of gamma-corrections, sBIT's as in the PNG-file are not valid anymore */ - info_ptr->valid &= ~PNG_INFO_sBIT; + pngxP->info_ptr->valid &= ~PNG_INFO_sBIT; if (verbose) pm_message("image gamma is %4.2f, " "converted for display gamma of %4.2f", @@ -582,8 +740,8 @@ paletteHasPartialTransparency(png_info * const info_ptr) { for (i = 0, foundGray = FALSE; i < info_ptr->num_trans && !foundGray; ++i) { - if (info_ptr->trans[i] != 0 && - info_ptr->trans[i] != maxval) { + if (info_ptr->TRANS_ALPHA[i] != 0 && + info_ptr->TRANS_ALPHA[i] != maxval) { foundGray = TRUE; } } @@ -599,18 +757,19 @@ paletteHasPartialTransparency(png_info * const info_ptr) { static void -setupSignificantBits(png_struct * const png_ptr, - png_info * const info_ptr, +setupSignificantBits(struct pngx * const pngxP, enum alpha_handling const alpha, png_uint_16 * const maxvalP, - int * const errorlevelP) { + int * const errorLevelP) { /*---------------------------------------------------------------------------- Figure out what maxval would best express the information in the PNG - described by 'png_ptr' and 'info_ptr', with 'alpha' telling which - information in the PNG we care about (image or alpha mask). + described by *pngxP, with 'alpha' telling which information in the PNG we + care about (image or alpha mask). Return the result as *maxvalP. -----------------------------------------------------------------------------*/ + png_info * const info_ptr = pngxP->info_ptr; + /* Initial assumption of maxval */ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (alpha == ALPHA_ONLY) { @@ -654,7 +813,7 @@ setupSignificantBits(png_struct * const png_ptr, unsigned int i; trans_mix = TRUE; for (i = 0; i < info_ptr->num_trans; ++i) - if (info_ptr->trans[i] != 0 && info_ptr->trans[i] != 255) { + if (info_ptr->TRANS_ALPHA[i] != 0 && info_ptr->TRANS_ALPHA[i] != 255) { trans_mix = FALSE; break; } @@ -675,7 +834,7 @@ setupSignificantBits(png_struct * const png_ptr, "different bit depths for color channels"); pm_message("writing file with %d bit resolution", info_ptr->bit_depth); - *errorlevelP = PNMTOPNG_WARNING_LEVEL; + *errorLevelP = PNMTOPNG_WARNING_LEVEL; } else { if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) && (info_ptr->sig_bit.red < 255)) { @@ -697,7 +856,7 @@ setupSignificantBits(png_struct * const png_ptr, if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB || info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) && (info_ptr->sig_bit.red < info_ptr->bit_depth)) { - png_set_shift (png_ptr, &(info_ptr->sig_bit)); + png_set_shift(pngxP->png_ptr, &(info_ptr->sig_bit)); *maxvalP = (1l << info_ptr->sig_bit.red) - 1; if (verbose) pm_message("image has fewer significant bits, " @@ -709,7 +868,7 @@ setupSignificantBits(png_struct * const png_ptr, info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && (info_ptr->sig_bit.gray < info_ptr->bit_depth)) { - png_set_shift (png_ptr, &(info_ptr->sig_bit)); + png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit); *maxvalP = (1l << info_ptr->sig_bit.gray) - 1; if (verbose) pm_message("image has fewer significant bits, " @@ -723,7 +882,7 @@ setupSignificantBits(png_struct * const png_ptr, if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && (info_ptr->sig_bit.gray < info_ptr->bit_depth)) { - png_set_shift (png_ptr, &(info_ptr->sig_bit)); + png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit); if (verbose) pm_message ("image has fewer significant bits, " "writing file with %d bits", @@ -739,22 +898,22 @@ setupSignificantBits(png_struct * const png_ptr, static bool -imageHasColor(png_info * const info_ptr) { +imageHasColor(struct pngx * const pngxP) { bool retval; - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY || + pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) retval = FALSE; - else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { + else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { bool foundColor; unsigned int i; for (i = 0, foundColor = FALSE; - i < info_ptr->num_palette && !foundColor; + i < pngxP->info_ptr->num_palette && !foundColor; ++i) { - if (iscolor(info_ptr->palette[i])) + if (iscolor(pngxP->info_ptr->palette[i])) foundColor = TRUE; } retval = foundColor; @@ -767,14 +926,14 @@ imageHasColor(png_info * const info_ptr) { static void -determineOutputType(png_info * const info_ptr, +determineOutputType(struct pngx * const pngxP, enum alpha_handling const alphaHandling, pngcolor const bgColor, xelval const maxval, int * const pnmTypeP) { if (alphaHandling != ALPHA_ONLY && - (imageHasColor(info_ptr) || !isGrayscale(bgColor))) + (imageHasColor(pngxP) || !isGrayscale(bgColor))) *pnmTypeP = PPM_TYPE; else { if (maxval > 1) @@ -787,11 +946,11 @@ determineOutputType(png_info * const info_ptr, static void -getBackgroundColor(png_info * const info_ptr, - const char * const requestedColor, - float const totalgamma, - xelval const maxval, - struct pngcolor * const bgColorP) { +getBackgroundColor(struct pngx * const pngxP, + const char * const requestedColor, + float const totalgamma, + xelval const maxval, + pngcolor * const bgColorP) { /*---------------------------------------------------------------------------- Figure out what the background color should be. If the user requested a particular color ('requestedColor' not null), that's the one. @@ -809,19 +968,19 @@ getBackgroundColor(png_info * const info_ptr, bgColorP->g = PPM_GETG(backcolor); bgColorP->b = PPM_GETB(backcolor); - } else if (info_ptr->valid & PNG_INFO_bKGD) { + } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) { /* didn't manage to get libpng to work (bugs?) concerning background processing, therefore we do our own. */ - switch (info_ptr->color_type) { + switch (pngxP->info_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: bgColorP->r = bgColorP->g = bgColorP->b = - gamma_correct(info_ptr->background.gray, totalgamma); + gamma_correct(pngxP->info_ptr->background.gray, totalgamma); break; case PNG_COLOR_TYPE_PALETTE: { png_color const rawBgcolor = - info_ptr->palette[info_ptr->background.index]; + pngxP->info_ptr->palette[pngxP->info_ptr->background.index]; bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma); bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma); bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma); @@ -829,7 +988,7 @@ getBackgroundColor(png_info * const info_ptr, break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: { - png_color_16 const rawBgcolor = info_ptr->background; + png_color_16 const rawBgcolor = pngxP->info_ptr->background; bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma); bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma); @@ -845,115 +1004,159 @@ getBackgroundColor(png_info * const info_ptr, static void +warnNonsquarePixels(struct pngx * const pngxP, + int * const errorLevelP) { + + if (pngxP->info_ptr->valid & PNG_INFO_pHYs) { + float const r = + (float)pngxP->info_ptr->x_pixels_per_unit / + pngxP->info_ptr->y_pixels_per_unit; + + if (r != 1.0) { + pm_message ("warning - non-square pixels; " + "to fix do a 'pamscale -%cscale %g'", + r < 1.0 ? 'x' : 'y', + r < 1.0 ? 1.0 / r : r ); + *errorLevelP = PNMTOPNG_WARNING_LEVEL; + } + } +} + + + +#define GET_PNG_VAL(p) get_png_val(&(p), pngxP->info_ptr->bit_depth) + + + +static void +makeXelRow(xel * const xelrow, + xelval const maxval, + int const pnmType, + struct pngx * const pngxP, + const png_byte * const pngRasterRow, + pngcolor const bgColor, + enum alpha_handling const alphaHandling, + double const totalgamma) { + + const png_byte * pngPixelP; + unsigned int col; + + pngPixelP = &pngRasterRow[0]; /* initial value */ + for (col = 0; col < pngxP->info_ptr->width; ++col) { + switch (pngxP->info_ptr->color_type) { + case PNG_COLOR_TYPE_GRAY: { + pngcolor fgColor; + fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP); + setXel(&xelrow[col], fgColor, bgColor, alphaHandling, + isTransparentColor(fgColor, pngxP, totalgamma) ? + 0 : maxval); + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: { + pngcolor fgColor; + png_uint_16 alpha; + + fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP); + alpha = GET_PNG_VAL(pngPixelP); + setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha); + } + break; + + case PNG_COLOR_TYPE_PALETTE: { + png_uint_16 const index = GET_PNG_VAL(pngPixelP); + png_color const paletteColor = pngxP->info_ptr->palette[index]; + + pngcolor fgColor; + + fgColor.r = paletteColor.red; + fgColor.g = paletteColor.green; + fgColor.b = paletteColor.blue; + + setXel(&xelrow[col], fgColor, bgColor, alphaHandling, + (pngxP->info_ptr->valid & PNG_INFO_tRNS) && + index < pngxP->info_ptr->num_trans ? + pngxP->info_ptr->TRANS_ALPHA[index] : maxval); + } + break; + + case PNG_COLOR_TYPE_RGB: { + pngcolor fgColor; + + fgColor.r = GET_PNG_VAL(pngPixelP); + fgColor.g = GET_PNG_VAL(pngPixelP); + fgColor.b = GET_PNG_VAL(pngPixelP); + setXel(&xelrow[col], fgColor, bgColor, alphaHandling, + isTransparentColor(fgColor, pngxP, totalgamma) ? + 0 : maxval); + } + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: { + pngcolor fgColor; + png_uint_16 alpha; + + fgColor.r = GET_PNG_VAL(pngPixelP); + fgColor.g = GET_PNG_VAL(pngPixelP); + fgColor.b = GET_PNG_VAL(pngPixelP); + alpha = GET_PNG_VAL(pngPixelP); + setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha); + } + break; + + default: + pm_error("unknown PNG color type: %d", + pngxP->info_ptr->color_type); + } + } +} + + + +static void writePnm(FILE * const ofP, xelval const maxval, - int const pnm_type, - png_info * const info_ptr, - png_byte ** const png_image, + int const pnmType, + struct pngx * const pngxP, + png_byte ** const pngRaster, pngcolor const bgColor, - enum alpha_handling const alpha_handling, + enum alpha_handling const alphaHandling, double const totalgamma) { /*---------------------------------------------------------------------------- Write a PNM of either the image or the alpha mask, according to - 'alpha_handling' that is in the PNG image described by 'info_ptr' and - png_image. + 'alphaHandling' that is in the PNG image described by *pngxP and + pngRaster[][]. - 'pnm_type' and 'maxval' are of the output image. + 'pnmType' and 'maxval' are of the output image. Use background color 'bgColor' in the output if the PNG is such that a background color is needed. -----------------------------------------------------------------------------*/ + int const plainFalse = 0; + xel * xelrow; unsigned int row; if (verbose) - pm_message ("writing a %s file (maxval=%u)", - pnm_type == PBM_TYPE ? "PBM" : - pnm_type == PGM_TYPE ? "PGM" : - pnm_type == PPM_TYPE ? "PPM" : - "UNKNOWN!", - maxval); + pm_message("writing a %s file (maxval=%u)", + pnmType == PBM_TYPE ? "PBM" : + pnmType == PGM_TYPE ? "PGM" : + pnmType == PPM_TYPE ? "PPM" : + "UNKNOWN!", + maxval); - xelrow = pnm_allocrow(info_ptr->width); - - pnm_writepnminit(stdout, info_ptr->width, info_ptr->height, maxval, - pnm_type, FALSE); - - for (row = 0; row < info_ptr->height; ++row) { - png_byte * png_pixelP; - int col; - - png_pixelP = &png_image[row][0]; /* initial value */ - for (col = 0; col < info_ptr->width; ++col) { - switch (info_ptr->color_type) { - case PNG_COLOR_TYPE_GRAY: { - pngcolor fgColor; - fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP); - setXel(&xelrow[col], fgColor, bgColor, alpha_handling, - ((info_ptr->valid & PNG_INFO_tRNS) && - (fgColor.r == - gamma_correct(info_ptr->trans_values.gray, - totalgamma))) ? - 0 : maxval); - } - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: { - pngcolor fgColor; - png_uint_16 alpha; - - fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP); - alpha = get_png_val(png_pixelP); - setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha); - } - break; + xelrow = pnm_allocrow(pngxP->info_ptr->width); - case PNG_COLOR_TYPE_PALETTE: { - png_uint_16 const index = get_png_val(png_pixelP); - png_color const paletteColor = info_ptr->palette[index]; - - pngcolor fgColor; - - fgColor.r = paletteColor.red; - fgColor.g = paletteColor.green; - fgColor.b = paletteColor.blue; - - setXel(&xelrow[col], fgColor, bgColor, alpha_handling, - (info_ptr->valid & PNG_INFO_tRNS) && - index < info_ptr->num_trans ? - info_ptr->trans[index] : maxval); - } - break; - - case PNG_COLOR_TYPE_RGB: { - pngcolor fgColor; - - fgColor.r = get_png_val(png_pixelP); - fgColor.g = get_png_val(png_pixelP); - fgColor.b = get_png_val(png_pixelP); - setXel(&xelrow[col], fgColor, bgColor, alpha_handling, - isTransparentColor(fgColor, info_ptr, totalgamma) ? - 0 : maxval); - } - break; + pnm_writepnminit(stdout, + pngxP->info_ptr->width, pngxP->info_ptr->height, maxval, + pnmType, plainFalse); - case PNG_COLOR_TYPE_RGB_ALPHA: { - pngcolor fgColor; - png_uint_16 alpha; + for (row = 0; row < pngxP->info_ptr->height; ++row) { + makeXelRow(xelrow, maxval, pnmType, pngxP, pngRaster[row], bgColor, + alphaHandling, totalgamma); - fgColor.r = get_png_val(png_pixelP); - fgColor.g = get_png_val(png_pixelP); - fgColor.b = get_png_val(png_pixelP); - alpha = get_png_val(png_pixelP); - setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha); - } - break; - - default: - pm_error ("unknown PNG color type: %d", info_ptr->color_type); - } - } - pnm_writepnmrow(ofP, xelrow, info_ptr->width, maxval, pnm_type, FALSE); + pnm_writepnmrow(ofP, xelrow, pngxP->info_ptr->width, maxval, + pnmType, plainFalse); } pnm_freerow (xelrow); } @@ -961,154 +1164,86 @@ writePnm(FILE * const ofP, static void -convertpng(FILE * const ifp, - FILE * const tfp, +convertpng(FILE * const ifP, + FILE * const tfP, struct cmdlineInfo const cmdline, - int * const errorlevelP) { - - png_struct *png_ptr; - png_info *info_ptr; - png_byte **png_image; - int x, y; - int linesize; - int pnm_type; + int * const errorLevelP) { + + png_byte ** pngRaster; + int pnmType; pngcolor bgColor; float totalgamma; + jmp_buf jmpbuf; + struct pngx * pngxP; - *errorlevelP = 0; - - read_sig_buf(ifp); - - png_ptr = png_create_read_struct( - PNG_LIBPNG_VER_STRING, - &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL); - if (png_ptr == NULL) - pm_error("cannot allocate main libpng structure (png_ptr)"); - - info_ptr = png_create_info_struct (png_ptr); - if (info_ptr == NULL) - pm_error("cannot allocate LIBPNG structures"); + *errorLevelP = 0; - if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf)) + if (setjmp(jmpbuf)) pm_error ("setjmp returns error condition"); - png_init_io (png_ptr, ifp); - png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE); - png_read_info (png_ptr, info_ptr); + pngx_createRead(&pngxP, &jmpbuf); - MALLOCARRAY(png_image, info_ptr->height); - if (png_image == NULL) { - png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); - pm_closer (ifp); - pm_error ("couldn't allocate space for image"); - } + readPng(pngxP, ifP, &pngRaster); - if (info_ptr->bit_depth == 16) - linesize = 2 * info_ptr->width; - else - linesize = info_ptr->width; + if (verbose) + dumpPngInfo(pngxP); - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - linesize *= 2; - else - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB) - linesize *= 3; - else - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - linesize *= 4; - - for (y = 0 ; y < info_ptr->height ; y++) { - png_image[y] = malloc (linesize); - if (png_image[y] == NULL) { - for (x = 0 ; x < y ; x++) - free (png_image[x]); - free (png_image); - png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); - pm_closer (ifp); - pm_error ("couldn't allocate space for image"); - } - } + if (cmdline.time) + showTime(pngxP); + if (tfP) + saveText(pngxP, tfP); - if (info_ptr->bit_depth < 8) - png_set_packing (png_ptr); + warnNonsquarePixels(pngxP, errorLevelP); - setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma); + setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma); - setupSignificantBits(png_ptr, info_ptr, cmdline.alpha, - &maxval, errorlevelP); + setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP); - getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval, + getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval, &bgColor); - png_read_image (png_ptr, png_image); - png_read_end (png_ptr, info_ptr); + determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pnmType); - if (verbose) - /* Note that some of info_ptr is not defined until png_read_end() - completes. That's because it comes from chunks that are at the - end of the stream. - */ - dump_png_info(info_ptr); - - if (mtime) - show_time (info_ptr); - if (tfp) - save_text (info_ptr, tfp); - - if (info_ptr->valid & PNG_INFO_pHYs) { - float r; - r = (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit; - if (r != 1.0) { - pm_message ("warning - non-square pixels; " - "to fix do a 'pamscale -%cscale %g'", - r < 1.0 ? 'x' : 'y', - r < 1.0 ? 1.0 / r : r ); - *errorlevelP = PNMTOPNG_WARNING_LEVEL; - } - } - - determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval, &pnm_type); - - writePnm(stdout, maxval, pnm_type, info_ptr, png_image, bgColor, + writePnm(stdout, maxval, pnmType, pngxP, pngRaster, bgColor, cmdline.alpha, totalgamma); fflush(stdout); - for (y = 0 ; y < info_ptr->height ; y++) - free (png_image[y]); - free (png_image); - png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); + + freePngRaster(pngRaster, pngxP); + + pngx_destroy(pngxP); } int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; - FILE *ifp, *tfp; - int errorlevel; + FILE * ifP; + FILE * tfP; + int errorLevel; - pnm_init (&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); verbose = cmdline.verbose; - mtime = cmdline.time; - ifp = pm_openr(cmdline.inputFilespec); + ifP = pm_openr(cmdline.inputFilespec); if (cmdline.text) - tfp = pm_openw(cmdline.text); + tfP = pm_openw(cmdline.text); else - tfp = NULL; + tfP = NULL; - convertpng (ifp, tfp, cmdline, &errorlevel); + convertpng(ifP, tfP, cmdline, &errorLevel); - if (tfp) - pm_close(tfp); + if (tfP) + pm_close(tfP); - pm_close(ifp); + pm_close(ifP); pm_close(stdout); - return errorlevel; + return errorLevel; } |