diff options
Diffstat (limited to 'converter/other/pngtopnm.c')
-rw-r--r-- | converter/other/pngtopnm.c | 464 |
1 files changed, 247 insertions, 217 deletions
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c index c7a39df1..8ffff617 100644 --- a/converter/other/pngtopnm.c +++ b/converter/other/pngtopnm.c @@ -16,20 +16,6 @@ ** 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) */ @@ -39,33 +25,15 @@ #include <png.h> /* includes zlib.h and setjmp.h */ #define VERSION "2.37.4 (5 December 1999) +netpbm" -#include "pnm.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" +#include "pnm.h" 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 -#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}; struct cmdlineInfo { @@ -82,7 +50,7 @@ struct cmdlineInfo { }; -typedef struct pngcolor { +typedef struct { /*---------------------------------------------------------------------------- A color in a format compatible with the PNG library. @@ -96,15 +64,14 @@ typedef struct pngcolor { static png_uint_16 maxval; -static int verbose = FALSE; -static int mtime; +static bool verbose; static jmpbuf_wrapper pngtopnm_jmpbuf_struct; 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. @@ -115,7 +82,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; @@ -124,6 +91,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); @@ -144,7 +113,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. */ @@ -180,12 +149,9 @@ parseCommandLine(int argc, - -#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; @@ -241,8 +207,8 @@ 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; } @@ -443,29 +409,108 @@ dump_png_info(png_info *info_ptr) { +static unsigned int +computePngLineSize(png_info * const pngInfoP) { + + unsigned int const bytesPerSample = pngInfoP->bit_depth == 16 ? 2 : 1; + + unsigned int samplesPerPixel; + + switch (pngInfoP->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 < pngInfoP->width) + pm_error("Width %u of PNG is uncomputably large", + (unsigned int)pngInfoP->width); + + return pngInfoP->width * bytesPerSample * samplesPerPixel; +} + + + +static void +allocPngRaster(png_info * const pngInfoP, + png_byte *** const pngImageP) { + + unsigned int const lineSize = computePngLineSize(pngInfoP); + + png_byte ** pngImage; + unsigned int row; + + MALLOCARRAY(pngImage, pngInfoP->height); + + if (pngImage == NULL) + pm_error("couldn't allocate space for %u PNG raster rows", + (unsigned int)pngInfoP->height); + + for (row = 0; row < pngInfoP->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, + png_info * const pngInfoP) { + + unsigned int row; + + for (row = 0; row < pngInfoP->height; ++row) + free(pngRaster[row]); + + free(pngRaster); +} + + + static bool isTransparentColor(pngcolor const color, - png_info * const info_ptr, + png_info * const pngInfoP, 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 (pngInfoP->valid & PNG_INFO_tRNS) { + const png_color_16 * const transColorP = &pngInfoP->trans_values; - /* 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. + + 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. - retval = - color.r == gamma_correct(transColorP->red, totalgamma) && - color.g == gamma_correct(transColorP->green, totalgamma) && - color.b == gamma_correct(transColorP->blue, totalgamma); + We could fix this by not letting libpng gamma-correct the + pixels, and just do it ourselves. + */ + + switch (pngInfoP->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; @@ -752,11 +797,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(png_info * const info_ptr, + 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,21 +854,109 @@ getBackgroundColor(png_info * const info_ptr, +#define GET_PNG_VAL(p) get_png_val(&(p), pngInfoP->bit_depth) + + + +static void +makeXelRow(xel * const xelrow, + xelval const maxval, + int const pnmType, + png_info * const pngInfoP, + 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 < pngInfoP->width; ++col) { + switch (pngInfoP->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, pngInfoP, 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 = pngInfoP->palette[index]; + + pngcolor fgColor; + + fgColor.r = paletteColor.red; + fgColor.g = paletteColor.green; + fgColor.b = paletteColor.blue; + + setXel(&xelrow[col], fgColor, bgColor, alphaHandling, + (pngInfoP->valid & PNG_INFO_tRNS) && + index < pngInfoP->num_trans ? + pngInfoP->trans[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, pngInfoP, 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", pngInfoP->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, + png_info * const pngInfoP, + 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 'pngInfoP' 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. @@ -832,93 +965,23 @@ writePnm(FILE * const ofP, 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; - - 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; + xelrow = pnm_allocrow(pngInfoP->width); - case PNG_COLOR_TYPE_RGB_ALPHA: { - pngcolor fgColor; - png_uint_16 alpha; + pnm_writepnminit(stdout, pngInfoP->width, pngInfoP->height, maxval, + pnmType, FALSE); - 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; + for (row = 0; row < pngInfoP->height; ++row) { + makeXelRow(xelrow, maxval, pnmType, pngInfoP, pngRaster[row], bgColor, + alphaHandling, totalgamma); - 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, pngInfoP->width, maxval, pnmType, FALSE); } pnm_freerow (xelrow); } @@ -931,11 +994,9 @@ convertpng(FILE * const ifp, struct cmdlineInfo const cmdline, int * const errorlevelP) { - png_struct *png_ptr; - png_info *info_ptr; - png_byte **png_image; - int x, y; - int linesize; + png_struct * png_ptr; + png_info * info_ptr; + png_byte ** png_image; int pnm_type; pngcolor bgColor; float totalgamma; @@ -961,38 +1022,7 @@ convertpng(FILE * const ifp, png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE); png_read_info (png_ptr, info_ptr); - 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"); - } - - if (info_ptr->bit_depth == 16) - linesize = 2 * info_ptr->width; - else - linesize = info_ptr->width; - - 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"); - } - } + allocPngRaster(info_ptr, &png_image); if (info_ptr->bit_depth < 8) png_set_packing (png_ptr); @@ -1005,24 +1035,24 @@ convertpng(FILE * const ifp, getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval, &bgColor); - png_read_image (png_ptr, png_image); - png_read_end (png_ptr, info_ptr); + png_read_image(png_ptr, png_image); + png_read_end(png_ptr, info_ptr); 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. - */ + 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 (cmdline.time) + show_time(info_ptr); if (tfp) - save_text (info_ptr, 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; + float const 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'", @@ -1038,41 +1068,41 @@ convertpng(FILE * const ifp, 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(png_image, info_ptr); + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); } int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; - FILE *ifp, *tfp; + 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; |