diff -rubN netpbm-10.26.56/converter/other/pnmtopng.c netpbm-10.26.56-monty/converter/other/pnmtopng.c --- netpbm-10.26.56/converter/other/pnmtopng.c 2007-07-03 23:26:21.000000000 -0400 +++ netpbm-10.26.56-monty/converter/other/pnmtopng.c 2008-09-10 08:36:52.349110352 -0400 @@ -3,11 +3,11 @@ ** read a portable anymap and produce a Portable Network Graphics file ** ** derived from pnmtorast.c (c) 1990,1991 by Jef Poskanzer and some -** parts derived from ppmtogif.c by Marcel Wijkstra +** parts derived from ppmtogif.c by Marcel Wijkstra ** -** Copyright (C) 1995-1998 by Alexander Lehmann -** and Willem van Schaik -** Copyright (C) 1999,2001 by Greg Roelofs +** Copyright (C) 1995-1998 by Alexander Lehmann +** and Willem van Schaik +** Copyright (C) 1998-2005 by Greg Roelofs ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided @@ -17,89 +17,117 @@ ** implied warranty. */ -/* A performance note: This program reads one row at a time because - the whole image won't fit in memory always. When you realize that - in a Netpbm xel array a one bit pixel can take 96 bits of memory, - 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. - - 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 - access the file. That facility copies the file into a temporary - file if it isn't seekable, so we always end up with a file that we - can rewind and reread multiple times. - - This shouldn't cause I/O delays because the entire image ought to fit - 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 - 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 - 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. - - 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. -*/ - -#define GRR_GRAY_PALETTE_FIX +/* GRR 20051112: applied Cosmin Truta's zlib-strategy patch (new -strategy + * option); applied Sascha Demetrio's significant-bits patch (new -sbit + * option); cleaned up read_text() function and fixed a memleak; adapted + * 2002 Debian/Alan Cox patch to fix several potential allocation-overflow + * bugs (added new grr_overflow2() function as aid). */ + +/* GRR 20051102: ifdef'd out some impossible code (maxval = unsigned short + * at most, so testing for > 65535 is pointless). */ + +/* GRR 20051023: fixed buffer overrun in RGBA-palette calculations. */ + +/* GRR 20020616: fixed assumption in read_text() that malloc() always + * returns successfully; applied TenThumbs' enhancement patch of 20010808: + * + * The problem is the keyword on a line by itself. The read_text + * function sees the keyword and replaces the first non-text character + * with '\0'. In this case that's a newline. The function then tries + * to copy the rest of the line as text but it only looks for space, + * tab, and newline which don't exist so it keeps looking and copying + * until it finds something. Buffer overrun time! + * + * When I fixed that I found that pnmtopng treats blank lines as a + * text chunk and libpng complains about zero length keywords. That + * seems silly. + * + * Here's a patch to fix all of this. */ + +/* GRR 20020408: plugged some memory leaks; improved documentation of + * palette-remapping code; added support for custom (ordered) palette + * (new -palette option). */ + +/* GRR 20010816: fixed bug reported by Eric Fu : + * stdin/stdout must be set to binary mode for DOS-like environments. (Bug + * report was specifically about NetPBM 9.x version, not this code, but same + * issue applies here.) Borrowed tried-and-true funzip.c code for maximal + * portability, but note dependency on macros that may need to be defined + * explicitly in makefiles (e.g., HAVE_SETMODE, FLEXOS and RISCOS). */ + +/* GRR 20010721: fixed bug reported by Werner Lemberg : + * bKGD value not scaled properly in sub-8-bit grayscale images (should + * be scaled identically to tRNS value). Also renamed version.h to + * less generic pnmtopng_version.h and replaced one last (deprecated) + * png_write_destroy() call. */ + +/* GRR 20000930: fixed bug reported by Steven Grady : + * if -transparent option given but exact color does not exist (e.g., when + * doing batch conversion of a web site), pnmtopng would pick an approximate + * color instead of ignoring the transparency. Also added optional errorlevel + * and return-code 2 (== warning; see PNMTOPNG_WARNING_LEVEL below) for such + * cases. (1 already used by pm_error().) */ + +/* GRR 20000315: ifdef'd out a never-reached (for non-PGM_BIGGRAYS) case + * that causes a gcc warning. */ + +/* GRR 19991203: incorporated fix by Rafal Rzeczkowski : + * gray images with exactly 16 shades were being promoted to 8-bit grayscale + * rather than 4-bit palette due to misuse of the pm_maxvaltobits() function. + * Also moved VERSION to new version.h header file. */ + +/* GRR 19990713: fixed redundant freeing of png_ptr and info_ptr in setjmp() + * blocks and added png_destroy_write_struct() and pm_close(ifp) in each + * pm_error() block. */ + +/* GRR 19990308: declared "clobberable" automatic variables in convertpnm() + * 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). */ + +/* GRR 19980621: moved some if-tests out of full-image loops; added fix for + * following problem discovered by Magnus Holmgren and debugged by Glenn: + * + * The pnm has three colors: 0 0 0, 204 204 204, and 255 255 255. + * These were apparently scaled to 0 0 0, 12 12 12, and 15 15 15. + * That would have been OK if the file had been written as color-type + * 0 (grayscale), but it was written as an indexed-color file, in + * which the colors from the pnm file should have been used rather + * than the scaled colors. What appears in the PLTE chunk is + * 0 0 0, 12 12 12, and 15 15 15, which of course results in a + * very dark image. + */ #ifndef PNMTOPNG_WARNING_LEVEL # define PNMTOPNG_WARNING_LEVEL 0 /* use 0 for backward compatibility, */ #endif /* 2 for warnings (1 == error) */ #include /* strcat() */ -#include #include /* includes zlib.h and setjmp.h */ -#define VERSION "2.37.6 (21 July 2001) +netpbm" -#include "pnm.h" -#include "pngtxt.h" -#include "mallocvar.h" -#include "nstring.h" - -struct zlib_compression { - /* These are parameters that describe a form of zlib compression. - In each of them, -1 means "don't care," and a nonnegative value is - as defined by zlib. - */ - int level; - int mem_level; - int strategy; - int window_bits; - int method; - int buffer_size; -}; - -struct chroma { - float wx; - float wy; - float rx; - float ry; - float gx; - float gy; - float bx; - float by; -}; - -typedef struct cahitem { - xel color; - gray alpha; - int value; - struct cahitem * next; -} cahitem; +#include "pnmtopng_version.h" /* VERSION macro */ +#include +#ifndef PPM_STDSORT /* defined in newer ppmcmap.h, which newer pnm.h includes */ +# include /* NOT installed by default with older netpbm */ +#endif + +#include "pnmtopng_overflow.h" /* grr_overflow2() function (clone) */ -typedef cahitem ** coloralphahash_table; +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; 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 @@ -110,164 +138,221 @@ # define NONE 0 #endif #define MAXCOLORS 256 -#define MAXPALETTEENTRIES 256 - -/* PALETTEMAXVAL is the maxval used in a PNG palette */ -#define PALETTEMAXVAL 255 - -#define PALETTEOPAQUE 255 -#define PALETTETRANSPARENT 0 +#define MAXCOMMENTS 256 /* function prototypes */ #ifdef __STDC__ +static int closestcolor (pixel color, colorhist_vector chv, int colors, + xelval maxval); +static void read_text (png_info *info_ptr, FILE *tfp); static void pnmtopng_error_handler (png_structp png_ptr, png_const_charp msg); +static int convertpnm (FILE *ifp, FILE *afp, FILE *pfp, FILE *tfp, FILE *ofp); int main (int argc, char *argv[]); #endif static int verbose = FALSE; - +static int interlace = FALSE; +static int downscale = FALSE; +static int transparent = -1; +static char *transstring; +static int alpha = FALSE; +static char *alpha_file; +static int background = -1; +static char *backstring; +static float gamma = -1.0; +static int hist = FALSE; +static float chroma_wx = -1.0; +static float chroma_wy = -1.0; +static float chroma_rx = -1.0; +static float chroma_ry = -1.0; +static float chroma_gx = -1.0; +static float chroma_gy = -1.0; +static float chroma_bx = -1.0; +static float chroma_by = -1.0; +static int phys_x = -1.0; +static int phys_y = -1.0; +static int phys_unit = -1.0; +static int have_ordered_palette = FALSE; +static char *palette_file; +static int text = FALSE; +static int ztxt = FALSE; +static char *text_file; +static int mtime = FALSE; +static char *date_string; +static char *time_string; +static struct tm time_struct; +static int sbit_n = 0; +static int sbit[4] = { 0, 0, 0, 0 }; +static int filter = -1; +static int compression = -1; +static int strategy = -1; +static int force = FALSE; static jmpbuf_wrapper pnmtopng_jmpbuf_struct; -static int errorlevel; - - - -static png_color_16 -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); - retval.green = PPM_GETG(scaled); - retval.blue = PPM_GETB(scaled); - retval.gray = PNM_GET1(scaled); - return retval; -} +/******************/ +/* CLOSESTCOLOR */ +/******************/ +#ifdef __STDC__ +static int closestcolor (pixel color, colorhist_vector chv, int colors, + xelval maxval) +#else +static int closestcolor (color, chv, colors, maxval) + pixel color; + colorhist_vector chv; + int colors; + xelval maxval; +#endif +{ + int i, r, g, b, d; + int imin, dmin; + r = (int)PPM_GETR (color) * 255 / maxval; + g = (int)PPM_GETG (color) * 255 / maxval; + b = (int)PPM_GETB (color) * 255 / maxval; -static void -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; - - bestMatch = UINT_MAX; - for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) { - unsigned int const dist = - PPM_DISTANCE(palette_pnm[paletteIndex], targetColor); - - if (dist < bestMatch) { - bestMatch = dist; - bestIndex = paletteIndex; - } - } - if (bestIndexP != NULL) - *bestIndexP = bestIndex; - if (bestMatchP != NULL) - *bestMatchP = bestMatch; + imin = 0; + dmin = 1000000; + for (i = 0 ; i < colors ; i++) { + d = (r - PPM_GETR (chv[i].color)) * (r - PPM_GETR (chv[i].color))+ + (g - PPM_GETG (chv[i].color)) * (g - PPM_GETG (chv[i].color))+ + (b - PPM_GETB (chv[i].color)) * (b - PPM_GETB (chv[i].color)); + if (d < dmin) { + dmin = d; + imin = i; + } + } + return imin; } -/* We really ought to make this hash function actually depend upon - the "a" argument; we just don't know a decent prime number off-hand. -*/ -#define HASH_SIZE 20023 -#define hashpixelalpha(p,a) ((((long) PPM_GETR(p) * 33023 + \ - (long) PPM_GETG(p) * 30013 + \ - (long) PPM_GETB(p) * 27011 ) \ - & 0x7fffffff ) % HASH_SIZE ) - -static coloralphahash_table -alloccoloralphahash(void) { - coloralphahash_table caht; - int i; - - MALLOCARRAY(caht,HASH_SIZE); - if (caht == NULL) - pm_error( "out of memory allocating hash table" ); - - for (i = 0; i < HASH_SIZE; ++i) - caht[i] = NULL; - return caht; -} +/***************/ +/* READ_TEXT */ +/***************/ - -static void -freecoloralphahash(coloralphahash_table const caht) { - int i; - - for (i = 0; i < HASH_SIZE; ++i) { - cahitem * p; - cahitem * next; - for (p = caht[i]; p; p = next) { - next = p->next; - free(p); +#ifdef __STDC__ +static void read_text (png_info *info_ptr, FILE *tfp) +#else +static void read_text (info_ptr, tfp) + png_info *info_ptr; + FILE *tfp; +#endif +{ +# define TEXTLINE_MAX 1024 /* some URLs are > 256 chars long */ + char textline[TEXTLINE_MAX]; + int textpos; + int i, j; + int ch; + char *cp, texterr[] = "pnmtopng: unable to allocate memory for text chunks\n"; + + grr_overflow2 (MAXCOMMENTS, (int)sizeof(png_text)); + info_ptr->text = (png_text *)malloc (MAXCOMMENTS * sizeof (png_text)); + if (!info_ptr->text) { + fprintf(stderr, texterr); + fflush(stderr); + return; /* not much else can do */ } + j = 0; + textpos = 0; + /* loop over bytes in text file until hit EOF */ + do { + /* any line >= TEXTLINE_MAX bytes long: truncate and treat as EOF */ + ch = (textpos < TEXTLINE_MAX)? getc(tfp) : EOF; + if (ch != '\n' && ch != EOF) { + textline[textpos++] = ch; + } else { + textline[textpos++] = '\0'; + if (textline[0] == '\0') { /* check for keyword-on-own-line case */ + textpos = 0; + continue; + } + if ((textline[0] != ' ') && (textline[0] != '\t')) { + /* the following is an inaccurate check for Author or Title */ + if ((!ztxt) || (textline[0] == 'A') || (textline[0] == 'T')) + info_ptr->text[j].compression = -1; + else + info_ptr->text[j].compression = 0; + info_ptr->text[j].key = cp = malloc (textpos); + if (!cp) { + fprintf(stderr, texterr); + fflush(stderr); + return; /* not much else can do */ } - free(caht); -} - - - -static void -addtocoloralphahash(coloralphahash_table const caht, - pixel * const colorP, - gray * const alphaP, - int const value) { - - int hash; - cahitem * itemP; - - MALLOCVAR(itemP); - if (itemP == NULL) - pm_error("Out of memory building hash table"); - hash = hashpixelalpha(*colorP, *alphaP); - itemP->color = *colorP; - itemP->alpha = *alphaP; - itemP->value = value; - itemP->next = caht[hash]; - caht[hash] = itemP; + i = 0; + if (textline[0] == '"') { + i++; + while (textline[i] != '"' && textline[i] != '\n' && + textline[i] != '\0') + *(cp++) = textline[i++]; + i++; + } else { + while (textline[i] != ' ' && textline[i] != '\t' && + textline[i] != '\n' && textline[i] != '\0') + *(cp++) = textline[i++]; + } + *cp = '\0'; /* end of info_ptr->text[j].key */ + info_ptr->text[j].text = cp = malloc (textpos); + if (!cp) { + fprintf(stderr, texterr); + fflush(stderr); + return; /* not much else can do */ + } + while (textline[i] == ' ' || textline[i] == '\t') + i++; + strcpy (cp, &textline[i]); + info_ptr->text[j].text_length = strlen (cp); + j++; + } else { + --j; + if (info_ptr->text[j].text_length + textpos <= 0) { + /* malloc() would overflow: terminate now; lose comment */ + fprintf(stderr, texterr); + fflush(stderr); + ch = EOF; + break; + } + /* FIXME: should just use realloc() here */ + cp = malloc (info_ptr->text[j].text_length + textpos); + if (!cp) { + fprintf(stderr, texterr); + fflush(stderr); + return; /* not much else can do */ + } + strcpy (cp, info_ptr->text[j].text); + cp[ info_ptr->text[j].text_length ] = '\n'; + i = 0; + while (textline[i] == ' ' || textline[i] == '\t') + ++i; + strcpy (cp + info_ptr->text[j].text_length + 1, &textline[i]); + free (info_ptr->text[j].text); /* FIXME: see realloc() comment above */ + info_ptr->text[j].text = cp; + info_ptr->text[j].text_length = strlen (cp); + ++j; + } + textpos = 0; + } + } while (ch != EOF); + info_ptr->num_text = j; } -static int -lookupColorAlpha(coloralphahash_table const caht, - pixel const * const colorP, - gray * const alphaP) { - - int hash; - cahitem * p; - - hash = hashpixelalpha(*colorP, *alphaP); - for (p = caht[hash]; p; p = p->next) - if (PPM_EQUAL(p->color, *colorP) && p->alpha == *alphaP) - return p->value; - - return -1; -} - +/****************************/ +/* PNMTOPNG_ERROR_HANDLER */ +/****************************/ #ifdef __STDC__ static void pnmtopng_error_handler (png_structp png_ptr, png_const_charp msg) #else static void pnmtopng_error_handler (png_ptr, msg) -png_structp png_ptr; -png_const_charp msg; + png_structp png_ptr; + png_const_charp msg; #endif { jmpbuf_wrapper *jmpbuf_ptr; @@ -296,1523 +381,681 @@ } -/* The following variables belong to getChv() and freeChv() */ -static bool getChv_computed = FALSE; -static colorhist_vector getChv_chv; -static int getChv_colors; +/****************/ +/* CONVERTPNM */ +/****************/ -static void -getChv(FILE * const ifP, - pm_filepos const imagepos, - int const cols, - int const rows, - xelval const maxval, - int const format, - int const maxColors, - colorhist_vector * const chvP, - unsigned int * const colorsP) { -/*---------------------------------------------------------------------------- - Return a list of all the colors in a libpnm image and the number of - times they occur. The image is in the seekable file 'ifP', whose - raster starts at position 'imagepos' 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 - 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. - - Don't spend the time to read the file if this subroutine has been called - before. In that case, just assume the inputs are all the same and return - the previously computed information. - - *chvP is in static program storage. ------------------------------------------------------------------------------*/ - if (!getChv_computed) { - if (verbose) - pm_message ("Finding colors in input image..."); - - pm_seek2(ifP, &imagepos, sizeof(imagepos)); - getChv_chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, - MAXCOLORS, &getChv_colors); - - if (getChv_chv) - pm_message ("%d colors found", getChv_colors); - - getChv_computed = TRUE; - } - *chvP = getChv_chv; - *colorsP = getChv_colors; -} - +#ifdef __STDC__ + static int convertpnm (FILE *ifp, FILE *afp, FILE *pfp, FILE *tfp, FILE *ofp) +#else + static int convertpnm (ifp, afp, pfp, tfp, ofp) + FILE *ifp, *afp, *pfp, *tfp, *ofp; +#endif +{ + xel **xels; /* GRR: a xel is always a pixel; pixel may be ulg or struct */ + xel p; /* (pnm.h) (ppm.h) */ + int rows, cols, format; + xelval maxval; + xelval scaleval = 255; + xelval value; + pixel transcolor; + int mayscale; + pixel backcolor; + png_struct *png_ptr; + png_info *info_ptr; -static void freeChv(void) { + png_color palette[MAXCOLORS]; + png_color ordered_palette[MAXCOLORS]; + png_byte trans[MAXCOLORS]; + png_uint_16 histogram[MAXCOLORS]; + png_byte *line; + png_byte *pp; + int pass; + int color; + gray alpha_maxval; + int alpha_rows; + int alpha_cols; + int alpha_trans; + gray *alphas_of_color[MAXCOLORS+1]; + int alphas_of_color_cnt[MAXCOLORS+1]; + int alphas_first_index[MAXCOLORS+1]; + int mapping[MAXCOLORS]; /* mapping[old_index] = new_index */ + int colors; + int fulldepth; + int x, y; + int i, j; - if (getChv_computed) - if (getChv_chv) - ppm_freecolorhist(getChv_chv); + /* these variables are declared static because gcc wasn't kidding + * about "variable XXX might be clobbered by `longjmp' or `vfork'" + * (stack corruption observed on Solaris 2.6 with gcc 2.8.1, even + * in the absence of any other error condition) */ + static int pnm_type; + static xelval maxmaxval; + static int sbitval; + static gray **alpha_mask; + static int alpha_sbitval; + static int num_alphas_of_color; + static int palette_size; + static int ordered_palette_size; + static colorhist_vector chv; + static colorhash_table cht; + static int depth; + static int errorlevel; - getChv_computed = FALSE; -} + /* these guys are initialized to quiet compiler warnings: */ + maxmaxval = 255; + num_alphas_of_color = 0; + alpha_mask = NULL; + alpha_sbitval = 0; + palette_size = 0; + chv = NULL; + cht = NULL; + depth = 0; + errorlevel = 0; + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, + &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL); + if (png_ptr == NULL) { + pm_closer (ifp); + pm_error ("cannot allocate main libpng structure (png_ptr)"); + } -static void -meaningful_bits_pgm(FILE * const ifp, - pm_filepos const imagepos, - int const cols, - int const rows, - xelval const maxval, - int const format, - unsigned int * const retvalP) { -/*---------------------------------------------------------------------------- - In the PGM raster with maxval 'maxval' at file offset 'imagepos' - in file 'ifp', the samples may be composed of groups of 1, 2, 4, or 8 - bits repeated. This would be the case if the image were converted - at some point from a 2 bits-per-pixel image to an 8-bits-per-pixel - image, for example. - - If this is the case, we find out and find out how small these repeated - groups of bits are and return the number of bits. ------------------------------------------------------------------------------*/ - int mayscale; - int x, y; - xel * xelrow; + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) { + png_destroy_write_struct (&png_ptr, (png_infopp)NULL); + pm_closer (ifp); + pm_error ("cannot allocate libpng info structure (info_ptr)"); + } - xelrow = pnm_allocrow(cols); + if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error ("setjmp returns error condition (1)"); + } - *retvalP = pm_maxvaltobits(maxval); + /* handle ordered palette first so we can reuse common variables: */ + if (have_ordered_palette) { + if (verbose) + pm_message ("reading ordered palette (colormap)..."); + xels = pnm_readpnm (pfp, &cols, &rows, &maxval, &format); - if (maxval == 65535) { - mayscale = TRUE; /* initial assumption */ - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (y = 0 ; y < rows && mayscale ; y++) { - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (x = 0 ; x < cols && mayscale ; x++) { - xel const p = xelrow[x]; - if ( (PNM_GET1 (p)&0xff)*0x101 != PNM_GET1 (p) ) - mayscale = FALSE; - } - } - if (mayscale) - *retvalP = 8; - } - if (maxval == 255 || *retvalP == 8) { - mayscale = TRUE; /* initial assumption */ - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (y = 0 ; y < rows && mayscale ; y++) { - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (x = 0 ; x < cols && mayscale ; x++) { - if ((PNM_GET1 (xelrow[x]) & 0xf) * 0x11 - != (PNM_GET1 (xelrow[x]) & 0xff)) - mayscale = FALSE; - } - } - if (mayscale) - *retvalP = 4; + pnm_type = PNM_FORMAT_TYPE (format); + if (pnm_type != PPM_TYPE) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_closer (pfp); + pnm_freearray (xels, rows); + xels = NULL; + pm_error ("ordered palette must be a PPM file (P3 or P6)"); } - if (maxval == 15 || *retvalP == 4) { - mayscale = TRUE; /* initial assumption */ - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (y = 0 ; y < rows && mayscale ; y++) { - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (x = 0 ; x < cols && mayscale ; x++) { - if ((PNM_GET1 (xelrow[x])&3) * 0x5 != - (PNM_GET1 (xelrow[x]) & 0xf)) - mayscale = FALSE; - } - } - if (mayscale) { - *retvalP = 2; - } + ordered_palette_size = rows * cols; + if (ordered_palette_size > MAXCOLORS) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_closer (pfp); + pnm_freearray (xels, rows); + xels = NULL; + pm_error("ordered-palette image can contain no more than 256 RGB pixels"); } + if (verbose) + pm_message ("%d colors found", ordered_palette_size); - if (maxval == 3 || *retvalP == 2) { - mayscale = TRUE; /* initial assumption */ - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (y = 0 ; y < rows && mayscale ; y++) { - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (x = 0 ; x < cols && mayscale ; x++) { - if ((PNM_GET1 (xelrow[x])&1) * 0x3 != - (PNM_GET1 (xelrow[x]) & 3)) - mayscale = FALSE; + j = 0; + for (y = 0 ; y < rows ; y++) { + for (x = 0 ; x < cols ; x++) { + p = xels[y][x]; + ordered_palette[j].red = PPM_GETR (p); + ordered_palette[j].green = PPM_GETG (p); + ordered_palette[j].blue = PPM_GETB (p); + ++j; } } - if (mayscale) { - *retvalP = 1; - } + pnm_freearray (xels, rows); + xels = NULL; + /* now ordered_palette[] and ordered_palette_size are valid */ } - pnm_freerow(xelrow); -} - -static void -meaningful_bits_ppm(FILE * const ifp, - pm_filepos const imagepos, - int const cols, - int const rows, - xelval const maxval, - int const format, - unsigned int * const retvalP) { -/*---------------------------------------------------------------------------- - In the PPM raster with maxval 'maxval' at file offset 'imagepos' - in file 'ifp', the samples may be composed of groups of 8 - bits repeated twice. This would be the case if the image were converted - at some point from a 8 bits-per-pixel image to an 16-bits-per-pixel - image, for example. - - We return the smallest number of bits we can take from the right of - a sample without losing information (8 or all). ------------------------------------------------------------------------------*/ - int mayscale; - int x, y; - xel * xelrow; + xels = pnm_readpnm (ifp, &cols, &rows, &maxval, &format); + pnm_type = PNM_FORMAT_TYPE (format); - xelrow = pnm_allocrow(cols); + if (verbose) { + if (pnm_type == PBM_TYPE) + pm_message ("reading a PBM file (maxval=%u)", maxval); + else if (pnm_type == PGM_TYPE) + pm_message ("reading a PGM file (maxval=%u)", maxval); + else if (pnm_type == PPM_TYPE) + pm_message ("reading a PPM file (maxval=%u)", maxval); + } - *retvalP = pm_maxvaltobits(maxval); + if (pnm_type == PGM_TYPE) + maxmaxval = PGM_MAXMAXVAL; + else if (pnm_type == PPM_TYPE) + maxmaxval = PPM_MAXMAXVAL; - if (maxval == 65535) { - mayscale = TRUE; /* initial assumption */ - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (y = 0 ; y < rows && mayscale ; y++) { - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (x = 0 ; x < cols && mayscale ; x++) { - xel const p = xelrow[x]; - if ( (PPM_GETR (p)&0xff)*0x101 != PPM_GETR (p) || - (PPM_GETG (p)&0xff)*0x101 != PPM_GETG (p) || - (PPM_GETB (p)&0xff)*0x101 != PPM_GETB (p) ) - mayscale = FALSE; - } - } - if (mayscale) - *retvalP = 8; - } - pnm_freerow(xelrow); -} + if (transparent > 0) /* -1 or 1 are the only possibilities so far */ + transcolor = ppm_parsecolor (transstring, maxmaxval); + if (alpha) { + if (verbose) + pm_message ("reading alpha-channel image..."); + alpha_mask = pgm_readpgm (afp, &alpha_cols, &alpha_rows, &alpha_maxval); + if (alpha_cols != cols || alpha_rows != rows) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error ("dimensions for image and alpha mask do not agree"); + } -static void -alpha_trans(FILE * const ifp, - pm_filepos const imagepos, - int const cols, - int const rows, - xelval const maxval, int const format, - gray ** const alpha_mask, gray alpha_maxval, - bool * const alpha_can_be_transparency_indexP, - pixel* const alpha_transcolorP) { -/*---------------------------------------------------------------------------- - Check if the alpha mask can be represented by a single transparency + /* check if the alpha mask can be represented by a single transparency value (i.e. all colors fully opaque except one fully transparent; the transparent color may not also occur as fully opaque. we have to do this before any scaling occurs, since alpha is only - possible with 8 and 16-bit. ------------------------------------------------------------------------------*/ - xel * xelrow; - bool retval; - /* Our eventual return value -- alpha mask can be represented by - a simple transparency index. - */ - bool found_transparent_pixel; - /* We found a pixel in the image where the alpha mask says it is - transparent. - */ - pixel transcolor; - /* Color of the transparent pixel mentioned above. */ - int const pnm_type = PNM_FORMAT_TYPE(format); - - xelrow = pnm_allocrow(cols); - - { - int row; - /* Find a candidate transparent color -- the color of any pixel in the - image that the alpha mask says should be transparent. - */ - found_transparent_pixel = FALSE; /* initial assumption */ - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (row = 0 ; row < rows && !found_transparent_pixel ; ++row) { - int col; - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (col = 0 ; col < cols && !found_transparent_pixel; ++col) { - if (alpha_mask[row][col] == 0) { - found_transparent_pixel = TRUE; - transcolor = pnm_xeltopixel(xelrow[col], format); - } + possible with 8 and 16-bit */ + /* first find the possible candidate */ + alpha_trans = FALSE; + for (y = 0 ; y < rows && !alpha_trans ; y++) + for (x = 0 ; x < cols && !alpha_trans ; x++) { + if (alpha_mask[y][x] == 0) { + if (transparent < 0) { + alpha_trans = TRUE; + transparent = 2; + transcolor = xels[y][x]; } } } - - if (found_transparent_pixel) { - int row; - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - retval = TRUE; /* initial assumption */ - - for (row = 0 ; row < rows && retval == TRUE; ++row) { - int col; - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (col = 0 ; col < cols && retval == TRUE; ++col) { - if (alpha_mask[row][col] == 0) { /* transparent */ - /* If we have a second transparent color, we're - disqualified - */ + /* if alpha_trans is TRUE check the whole image */ + for (y = 0 ; y < rows && alpha_trans ; y++) + for (x = 0 ; x < cols && alpha_trans ; x++) { + if (alpha_mask[y][x] == 0) { /* transparent one */ if (pnm_type == PPM_TYPE) { - if (!PPM_EQUAL (xelrow[col], transcolor)) - retval = FALSE; + if (!PPM_EQUAL (xels[y][x], transcolor)) + alpha_trans = FALSE; } else { - if (PNM_GET1 (xelrow[col]) != PNM_GET1 (transcolor)) - retval = FALSE; + if (PNM_GET1 (xels[y][x]) != PNM_GET1 (transcolor)) + alpha_trans = FALSE; } - } else if (alpha_mask[row][col] != alpha_maxval) { - /* Here's an area of the mask that is translucent. That - disqualified us. - */ - retval = FALSE; - } else - /* Here's an area of the mask that is opaque. If it's - the same color as our candidate transparent color, - that disqualifies us. - */ + } else /* is it fully opaque ? */ + if (alpha_mask[y][x] != alpha_maxval) { + alpha_trans = FALSE; + } else /* does the transparent color also exists fully opaque */ if (pnm_type == PPM_TYPE) { - if (PPM_EQUAL (xelrow[col], transcolor)) - retval = FALSE; + if (PPM_EQUAL (xels[y][x], transcolor)) + alpha_trans = FALSE; } else { - if (PNM_GET1 (xelrow[col]) == PNM_GET1 (transcolor)) - retval = FALSE; - } + if (PNM_GET1 (xels[y][x]) == PNM_GET1 (transcolor)) + alpha_trans = FALSE; } } - } else - retval = FALSE; - - pnm_freerow(xelrow); - - *alpha_can_be_transparency_indexP = retval; - *alpha_transcolorP = transcolor; -} - - - -static void -findRedundantBits(FILE * const ifp, - int const imagepos, - int const cols, - int const rows, - xelval const maxval, - int const format, - bool const alpha, - bool const force, - unsigned int * const meaningfulBitsP) { -/*---------------------------------------------------------------------------- - Find out if we can use just a subset of the bits from each input - sample. Often, people create an image with e.g. 8 bit samples from - one that has e.g. only 4 bit samples by scaling by 256/16, which is - the same as repeating the bits. E.g. 1011 becomes 10111011. We - detect this case. We return as *meaningfulBitsP the minimum number - of bits, starting from the least significant end, that contain - original information. ------------------------------------------------------------------------------*/ - if (!alpha && PNM_FORMAT_TYPE(format) == PGM_TYPE && !force) - meaningful_bits_pgm(ifp, imagepos, cols, rows, maxval, format, - meaningfulBitsP); - else if (PNM_FORMAT_TYPE(format) == PPM_TYPE && !force) - meaningful_bits_ppm(ifp, imagepos, cols, rows, maxval, format, - meaningfulBitsP); - else - *meaningfulBitsP = pm_maxvaltobits(maxval); - - if (verbose && *meaningfulBitsP != pm_maxvaltobits(maxval)) - pm_message("Using only %d rightmost bits of input samples. The " - "rest are redundant.", *meaningfulBitsP); -} - - - -static void -readOrderedPalette(FILE * const pfp, - xel ordered_palette[], - unsigned int * const ordered_palette_size_p) { - - xel ** xels; - int cols, rows; - xelval maxval; - int format; - + if (alpha_trans && !force) { if (verbose) - pm_message ("reading ordered palette (colormap)..."); - - xels = pnm_readpnm (pfp, &cols, &rows, &maxval, &format); - - 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) - pm_error("ordered-palette image contains %d pixels. Maximum is %d", - *ordered_palette_size_p, MAXCOLORS); - if (verbose) - pm_message ("%d colors found", *ordered_palette_size_p); - - { - int j; - int row; - j = 0; /* initial value */ - for (row = 0; row < rows; ++row) { - int col; - for (col = 0; col < cols; ++col) - ordered_palette[j++] = xels[row][col]; + pm_message ("converting alpha mask to transparency index"); + alpha = FALSE; + } else { + transparent = -1; } } - pnm_freearray(xels, rows); -} - + /* gcc 2.7.0 -fomit-frame-pointer causes stack corruption here */ + if (background > -1) /* scale to maxval later: */ + backcolor = ppm_parsecolor (backstring, maxmaxval); -static void -compute_nonalpha_palette(colorhist_vector const chv, - int const colors, - pixval const maxval, - FILE * const pfp, - pixel palette_pnm[], - unsigned int * const paletteSizeP, - gray trans_pnm[], - unsigned int * const transSizeP) { -/*---------------------------------------------------------------------------- - Compute the palette corresponding to the color set 'chv' - (consisting of 'colors' distinct colors) assuming a pure-color (no - transparency) palette. - - If 'pfp' is non-null, assume it's a PPM file and read the palette - from that. Make sure it contains the same colors as the palette - we computed ourself would have. Caller supplied the file because he - 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) - pm_error("sizes of ordered palette (%d) " - "and existing palette (%d) differ", - ordered_palette_size, colors); + /* first of all, check if we have a grayscale image written as PPM */ - /* Make sure the ordered palette contains all the colors in - the image - */ - for (colorIndex = 0; colorIndex < colors; colorIndex++) { - int j; - bool found; + if (pnm_type == PPM_TYPE && !force) { + int isgray = TRUE; - found = FALSE; - for (j = 0; j < ordered_palette_size && !found; ++j) { - if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color)) - found = TRUE; - } - 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), - PPM_GETG(chv[colorIndex].color), - PPM_GETB(chv[colorIndex].color)); + for (y = 0 ; y < rows && isgray ; y++) + for (x = 0 ; x < cols && isgray ; x++) { + p = xels[y][x]; + if (PPM_GETR (p) != PPM_GETG (p) || PPM_GETG (p) != PPM_GETB (p)) + isgray = FALSE; } - /* OK, the ordered palette passes muster as a palette; go ahead - and return it as the palette. - */ - for (colorIndex = 0; colorIndex < colors; ++colorIndex) - palette_pnm[colorIndex] = ordered_palette[colorIndex]; - } else { - for (colorIndex = 0; colorIndex < colors; ++colorIndex) - palette_pnm[colorIndex] = chv[colorIndex].color; + if (isgray) + pnm_type = PGM_TYPE; } - *paletteSizeP = colors; - *transSizeP = 0; -} + /* handle `odd' maxvalues */ - -static void -computeUnsortedAlphaPalette(FILE * const ifP, - int const cols, - int const rows, - xelval const maxval, - int const format, - pm_filepos const imagepos, - gray ** const alpha_mask, - unsigned int const maxPaletteEntries, - colorhist_vector const chv, - int const colors, - gray * alphas_of_color[], - unsigned int alphas_first_index[], - unsigned int alphas_of_color_cnt[]) { -/*---------------------------------------------------------------------------- - Read the image at position 'imagepos' in file *ifP, which is a PNM - 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, as follows. - - The alpha/color palette is the set of all ordered pairs of - (color,alpha) in the PNG, including the background color. The - actual palette is an array with up to 'maxPaletteEntries elements. Each - array element contains a color index from the color palette and - an alpha value. All the elements with the same color index are - contiguous. alphas_first_index[x] is the index in the - alpha/color palette of the first element that has color index x. - alphas_of_color_cnt[x] is the number of elements that have color - index x. alphas_of_color[x][y] is the yth alpha value that - appears with color index x (in order of appearance). - alpha_color_pair_count is the total number of elements, i.e. the - total number of combinations color and alpha. ------------------------------------------------------------------------------*/ - colorhash_table cht; - int color_index; - int row; - xel * xelrow; - - cht = ppm_colorhisttocolorhash (chv, colors); - - for (color_index = 0 ; color_index < colors ; ++color_index) { - /* TODO: It sure would be nice if we didn't have to allocate - 256 words here for what is normally only 0 or 1 different - alpha values! Maybe we should do some sophisticated reallocation. - */ - MALLOCARRAY(alphas_of_color[color_index], maxPaletteEntries); - if (alphas_of_color[color_index] == NULL) - pm_error ("out of memory allocating alpha/palette entries"); - alphas_of_color_cnt[color_index] = 0; + sbitval = 0; + if (pnm_type != PBM_TYPE || alpha) { +#if 0 /* GRR 20051102: pointless since maxval is unsigned char or short */ + if (maxval > 65535 && !downscale) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error ("can handle files only up to 16-bit (use -downscale to override"); } - pm_seek2(ifP, &imagepos, sizeof(imagepos)); - - xelrow = pnm_allocrow(cols); - - for (row = 0 ; row < rows ; ++row) { - int col; - pnm_readpnmrow(ifP, xelrow, cols, maxval, format); - pnm_promoteformatrow(xelrow, cols, maxval, format, maxval, PPM_TYPE); - for (col = 0 ; col < cols ; ++col) { - int i; - int const color = ppm_lookupcolor(cht, &xelrow[col]); - for (i = 0 ; i < alphas_of_color_cnt[color] ; ++i) { - if (alpha_mask[row][col] == alphas_of_color[color][i]) - break; - } - if (i == alphas_of_color_cnt[color]) { - alphas_of_color[color][i] = alpha_mask[row][col]; - alphas_of_color_cnt[color]++; - } - } - } + if (maxval < 65536) +#endif /* 0 */ { - int i; - alphas_first_index[0] = 0; - for (i = 1 ; i < colors ; i++) - alphas_first_index[i] = alphas_first_index[i-1] + - alphas_of_color_cnt[i-1]; + sbitval = pm_maxvaltobits (maxval); + if (maxval != pm_bitstomaxval (sbitval)) + sbitval = 0; } - pnm_freerow(xelrow); - ppm_freecolorhash(cht); -} - - -static void -sortAlphaPalette(gray *alphas_of_color[], - unsigned int alphas_first_index[], - unsigned int alphas_of_color_cnt[], - unsigned int const colors, - gray const alphaMaxval, - unsigned int mapping[], - unsigned int * const transSizeP) { -/*---------------------------------------------------------------------------- - Remap the palette indices so opaque entries are last. - - alphas_of_color[], alphas_first_index[], and alphas_of_color_cnt[] - describe an unsorted PNG (alpha/color) palette. We generate - mapping[] such that mapping[x] is the 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. ------------------------------------------------------------------------------*/ - 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 - 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 = alphas_first_index[colors-1] + alphas_of_color_cnt[colors-1] - 1; - - for (colorIndex = 0; colorIndex < colors; ++colorIndex) { - unsigned int j; - for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) { - unsigned int const paletteIndex = - alphas_first_index[colorIndex] + j; - if (alphas_of_color[colorIndex][j] == alphaMaxval) - mapping[paletteIndex] = top_idx--; - else - mapping[paletteIndex] = bot_idx++; - } - } - /* indices should have just crossed paths */ - if (bot_idx != top_idx + 1) { - pm_error ("internal inconsistency: " - "remapped bot_idx = %u, top_idx = %u", - bot_idx, top_idx); + if (maxval != 255 && maxval != 65535 && + (alpha || pnm_type != PGM_TYPE || + (maxval != 1 && maxval != 3 && maxval != 15))) { + if (!alpha && maxval == 7 && pnm_type == PGM_TYPE) { + if (verbose) + pm_message ("rescaling to 4-bit"); + scaleval = 15; + } else + if (maxval < 255) { + if (verbose) + pm_message ("rescaling to 8-bit"); + scaleval = 255; + } else { + /* to do: this can fail; xels aren't big enough - larger pixels & + * maxvals are needed */ + if (sizeof(scaleval) < 2) { /* unsigned char by default */ + pm_message ("cannot rescale to 16-bit; rescaling to 8-bit instead (maxval = %d)", + maxval); + scaleval = 255; +#ifdef PGM_BIGGRAYS /* GRR 20000315: this avoids a gcc warning */ + } else { + if (verbose) + pm_message ("rescaling to 16-bit"); + scaleval = 65535; +#endif } - *transSizeP = bot_idx; -} - - - -static void -compute_alpha_palette(FILE * const ifP, - int const cols, - int const rows, - xelval const maxval, - int const format, - pm_filepos const imagepos, - gray ** const alpha_mask, - gray const alphaMaxval, - pixel palette_pnm[], - gray trans_pnm[], - unsigned int * const paletteSizeP, - unsigned int * const transSizeP, - bool * const tooBigP) { -/*---------------------------------------------------------------------------- - Return the palette of color/alpha pairs for the image indicated by - 'ifP', 'cols', 'rows', 'maxval', 'format', and 'imagepos'. - alpha_mask[] is the Netpbm-style alpha mask for the image. - - Return the palette as the arrays palette_pnm[] and trans_pnm[]. - The ith entry in the palette is the combination of palette[i], - which defines the color, and trans[i], which defines the - transparency. - - Return the number of entries in the palette as *paletteSizeP. - - 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 - MAXPALETTEENTRIES elements. - - 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; - unsigned int colors; - - gray *alphas_of_color[MAXPALETTEENTRIES]; - unsigned int alphas_first_index[MAXPALETTEENTRIES]; - unsigned int alphas_of_color_cnt[MAXPALETTEENTRIES]; - - getChv(ifP, imagepos, cols, rows, maxval, format, MAXCOLORS, - &chv, &colors); - - computeUnsortedAlphaPalette(ifP, cols, rows, maxval, format, imagepos, - alpha_mask, MAXPALETTEENTRIES, chv, colors, - alphas_of_color, - alphas_first_index, - alphas_of_color_cnt); - - *paletteSizeP = - alphas_first_index[colors-1] + alphas_of_color_cnt[colors-1]; - if (*paletteSizeP > MAXPALETTEENTRIES) { - *tooBigP = TRUE; - } else { - unsigned int mapping[MAXPALETTEENTRIES]; - /* Sorting of the alpha/color palette. mapping[x] is the - 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. - */ - - *tooBigP = FALSE; - - /* Make the opaque palette entries last */ - sortAlphaPalette(alphas_of_color, alphas_first_index, - alphas_of_color_cnt, colors, alphaMaxval, - mapping, transSizeP); - - { - unsigned int colorIndex; - - for (colorIndex = 0; colorIndex < colors; ++colorIndex) { - unsigned int j; - for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) { - unsigned int const paletteIndex = - alphas_first_index[colorIndex] + j; - palette_pnm[mapping[paletteIndex]] = chv[colorIndex].color; - trans_pnm[mapping[paletteIndex]] = - alphas_of_color[colorIndex][j]; } + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + p = xels[y][x]; + PPM_DEPTH (xels[y][x], p, maxval, scaleval); } + if (transparent == 2) { /* "1" case (-transparent) handled below */ + PPM_DEPTH (transcolor, transcolor, maxval, scaleval); } + maxval = scaleval; + } else { + sbitval = 0; /* no scaling happened */ } - { - unsigned int colorIndex; - for (colorIndex = 0; colorIndex < colors; ++colorIndex) - free(alphas_of_color[colorIndex]); } -} - + /* now do a real scaling (better than ppm_parsecolor()) */ + if (maxval != 65535) { + if (background > -1) + PPM_DEPTH (backcolor, backcolor, maxmaxval, maxval); + if (transparent == 1) /* "2" case (-alpha) already done */ + PPM_DEPTH (transcolor, transcolor, maxmaxval, maxval); + } -static void -makeOneColorTransparentInPalette(xel const transColor, - bool const exact, - pixel palette_pnm[], - unsigned int const paletteSize, - gray trans_pnm[], - unsigned int * const transSizeP) { -/*---------------------------------------------------------------------------- - Find the color 'transColor' in the color/alpha palette defined by - 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 - color/alpha palette). - - If the specified color is not there and exact == TRUE, return - without changing anything, but issue a warning message. If it's - not there and exact == FALSE, just find the closest color. - - We assume every entry in the palette is opaque upon entry. ------------------------------------------------------------------------------*/ - unsigned int transparentIndex; - unsigned int distance; - - 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, - &transparentIndex, &distance); - - if (distance != 0 && exact) { - pm_message ("specified transparent color not present in palette; " - "ignoring -transparent"); - errorlevel = PNMTOPNG_WARNING_LEVEL; - } else { - /* Swap this with the first entry in the palette */ - pixel tmp; + /* check for 16-bit entries that are just scaled 8-bit entries, e.g., + when converting an 8-bit palette TIFF to PPM */ - tmp = palette_pnm[transparentIndex]; - palette_pnm[transparentIndex] = palette_pnm[0]; - palette_pnm[0] = tmp; - - /* Make it transparent */ - trans_pnm[0] = PGM_TRANSPARENT; - *transSizeP = 1; - if (verbose) { - pixel const p = palette_pnm[0]; - pm_message("Making all occurences of color (%u, %u, %u) " - "transparent.", - PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); + if (pnm_type != PBM_TYPE && maxval == 65535 && !force) { + mayscale = TRUE; + if (pnm_type == PGM_TYPE) + for (y = 0 ; y < rows && mayscale ; y++) + for (x = 0 ; x < cols && mayscale ; x++) { + p = xels[y][x]; + if ( (PNM_GET1 (p)&0xff)*0x101 != PNM_GET1 (p) ) + mayscale = FALSE; } + else /* PPM_TYPE */ + for (y = 0 ; y < rows && mayscale ; y++) + for (x = 0 ; x < cols && mayscale ; x++) { + p = xels[y][x]; + if ( (PPM_GETR (p)&0xff)*0x101 != PPM_GETR (p) || + (PPM_GETG (p)&0xff)*0x101 != PPM_GETG (p) || + (PPM_GETB (p)&0xff)*0x101 != PPM_GETB (p) ) + mayscale = FALSE; } -} - - - -static void -findOrAddBackgroundInPalette(pixel const backColor, - pixel palette_pnm[], - unsigned int * const paletteSizeP, - unsigned int * const backgroundIndexP) { -/*---------------------------------------------------------------------------- - Add the background color 'backColor' to the palette, unless - it's already in there. If it's not present and there's no room to - 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 - 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 - using the background color. - - Return the palette index of the background color as *backgroundIndexP. ------------------------------------------------------------------------------*/ - int backgroundIndex; /* negative means not found */ - unsigned int paletteIndex; - - backgroundIndex = -1; - for (paletteIndex = 0; - paletteIndex < *paletteSizeP; - ++paletteIndex) - if (PPM_EQUAL(palette_pnm[paletteIndex], backColor)) - backgroundIndex = paletteIndex; - - if (backgroundIndex >= 0) { - /* The background color is already in the palette. */ - *backgroundIndexP = backgroundIndex; - if (verbose) { - pixel const p = palette_pnm[*backgroundIndexP]; - pm_message ("background color (%u, %u, %u) appears in image.", - PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); + if (mayscale) { + if (verbose) + pm_message ("scaling to 8-bit (superfluous 16-bit data)"); + maxval = 255; + if (pnm_type == PGM_TYPE) { + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + p = xels[y][x]; + PNM_ASSIGN1 (xels[y][x], PNM_GET1 (p)&0xff); + } + } else { /* PPM_TYPE */ + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + p = xels[y][x]; + PPM_ASSIGN (xels[y][x], PPM_GETR (p)&0xff, PPM_GETG (p)&0xff, + PPM_GETB (p)&0xff); } - } else { - /* Try to add the background color, opaque, to the palette. */ - if (*paletteSizeP < MAXCOLORS) { - /* There's room, so just add it to the end of the palette */ - - /* Because we're not expanding the transparency palette, this - entry is not in it, and is thus opaque. - */ - *backgroundIndexP = (*paletteSizeP)++; - palette_pnm[*backgroundIndexP] = backColor; - if (verbose) { - pixel const p = palette_pnm[*backgroundIndexP]; - pm_message ("added background color (%u, %u, %u) to palette.", - PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); } + if (transparent > 0) { + p = transcolor; + if (pnm_type == PGM_TYPE) { + PNM_ASSIGN1 (transcolor, PNM_GET1 (p)&0xff); } else { - closestColorInPalette(backColor, palette_pnm, *paletteSizeP, - backgroundIndexP, NULL); - errorlevel = PNMTOPNG_WARNING_LEVEL; - if (verbose) { - pixel const p = palette_pnm[*backgroundIndexP]; - pm_message ("no room in palette for background color; " - "using closest match (%u, %u, %u) instead", - PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); + PPM_ASSIGN (transcolor, PPM_GETR (p)&0xff, PPM_GETG (p)&0xff, + PPM_GETB (p)&0xff); } } - } -} - - -static void -buildColorLookup(pixel palette_pnm[], - unsigned int const paletteSize, - colorhash_table * const chtP) { -/*---------------------------------------------------------------------------- - Create a colorhash_table out of the palette described by - palette_pnm[] (which has 'paletteSize' entries) so one can look up - the palette index of a given color. - - Where the same color appears twice in the palette, the lookup table - finds an arbitrary one of them. We don't consider transparency of - palette entries, so if the same color appears in the palette once - transparent and once opaque, the lookup table finds an arbitrary one - of those two. ------------------------------------------------------------------------------*/ - colorhash_table const cht = ppm_alloccolorhash(); - unsigned int paletteIndex; - - for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) { - ppm_addtocolorhash(cht, &palette_pnm[paletteIndex], paletteIndex); + if (sbitval > 0) sbitval >>= 1; } - *chtP = cht; -} - - -static void -buildColorAlphaLookup(pixel palette_pnm[], - unsigned int const paletteSize, - gray trans_pnm[], - unsigned int const transSize, - gray const alphaMaxval, - coloralphahash_table * const cahtP) { - - coloralphahash_table const caht = alloccoloralphahash(); - - unsigned int paletteIndex; - - for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) { - gray paletteTrans; - - if (paletteIndex < transSize) - paletteTrans = alphaMaxval; - else - paletteTrans = trans_pnm[paletteIndex]; - - - addtocoloralphahash(caht, &palette_pnm[paletteIndex], - &trans_pnm[paletteIndex], paletteIndex); } - *cahtP = caht; -} - - - -static void -tryAlphaPalette(FILE * const ifP, - int const cols, - int const rows, - xelval const maxval, - int const format, - pm_filepos const imagepos, - gray ** const alpha_mask, - gray const alphaMaxval, - FILE * const pfP, - pixel * const palette_pnm, - unsigned int * const paletteSizeP, - gray * const trans_pnm, - unsigned int * const transSizeP, - const char ** const impossibleReasonP) { - - bool tooBig; - if (pfP) - pm_error("This program is not capable of generating " - "a PNG with transparency when you specify " - "the palette with -palette."); - - compute_alpha_palette(ifP, cols, rows, maxval, format, - imagepos, alpha_mask, alphaMaxval, - palette_pnm, trans_pnm, - paletteSizeP, transSizeP, &tooBig); - if (tooBig) { - asprintfN(impossibleReasonP, - "too many color/transparency pairs " - "(more than the PNG maximum of %u", - MAXPALETTEENTRIES); - } else - *impossibleReasonP = NULL; -} + /* scale alpha mask to match bit depth of image */ - -static void -computePixelWidth(int const pnm_type, - unsigned int const pnm_meaningful_bits, - bool const alpha, - unsigned int * const bitsPerSampleP, - unsigned int * const bitsPerPixelP) { - - unsigned int bitsPerSample, bitsPerPixel; - - if (pnm_type == PPM_TYPE || alpha) { - /* PNG allows only depths of 8 and 16 for a truecolor image - and for a grayscale image with an alpha channel. - */ - if (pnm_meaningful_bits > 8) - bitsPerSample = 16; - else - bitsPerSample = 8; - } else { - /* A grayscale, non-colormapped, no-alpha PNG may have any - bit depth from 1 to 16 - */ - if (pnm_meaningful_bits > 8) - bitsPerSample = 16; - else if (pnm_meaningful_bits > 4) - bitsPerSample = 8; - else if (pnm_meaningful_bits > 2) - bitsPerSample = 4; - else if (pnm_meaningful_bits > 1) - bitsPerSample = 2; - else - bitsPerSample = 1; - } if (alpha) { - if (pnm_type == PPM_TYPE) - bitsPerPixel = 4 * bitsPerSample; +#if 0 /* GRR: pointless since alpha_maxval is unsigned char or short */ + if (alpha_maxval > 65535) + alpha_sbitval = 0; else - bitsPerPixel = 2 * bitsPerSample; - } else { - if (pnm_type == PPM_TYPE) - bitsPerPixel = 3 * bitsPerSample; - else - bitsPerPixel = bitsPerSample; +#endif + { + alpha_sbitval = pm_maxvaltobits (alpha_maxval); + if (alpha_maxval != pm_bitstomaxval (alpha_sbitval)) + alpha_sbitval = 0; } - if (bitsPerPixelP) - *bitsPerPixelP = bitsPerPixel; - if (bitsPerSampleP) - *bitsPerSampleP = bitsPerSample; -} - - -static unsigned int -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; - - if (nColors < 1) - retval = 0; - else if (nColors <= 2) - retval = 1; - else if (nColors <= 4) - retval = 2; - else if (nColors <= 16) - retval = 4; - else if (nColors <= 256) - retval = 8; - else - retval = 0; - - return retval; -} - - - -static void -computeColorMap(FILE * const ifP, - pm_filepos const imagepos, - int const cols, - int const rows, - xelval const maxval, - int const format, - bool const force, - FILE * const pfP, - bool const alpha, - int const transparent, - pixel const transcolor, - bool const transexact, - int const background, - pixel const backcolor, - gray ** const alpha_mask, - gray const alphaMaxval, - unsigned int const pnm_meaningful_bits, - /* Outputs */ - pixel * const palette_pnm, - unsigned int * const paletteSizeP, - gray * const trans_pnm, - unsigned int * const transSizeP, - unsigned int * const backgroundIndexP, - const char ** const noColormapReasonP) { -/*--------------------------------------------------------------------------- - Determine whether to do a colormapped or truecolor PNG and if - colormapped, compute the full PNG palette -- both color and - transparency. - - If we decide to do truecolor, we return as *noColormapReasonP a text - description of why, in newly malloced memory. If we decide to go - with colormapped, we return *noColormapReasonP == NULL. - - In the colormapped case, we return the palette as arrays - palette_pnm[] and trans_pnm[], allocated by Caller, with sizes - *paletteSizeP and *transSizeP. - - If the image is to have a background color, we return the palette index - of that color as *backgroundIndexP. --------------------------------------------------------------------------- */ - if (force) - asprintfN(noColormapReasonP, "You requested no color map"); - else if (maxval > PALETTEMAXVAL) - asprintfN(noColormapReasonP, "The maxval of the input image (%u) " - "exceeds the PNG palette maxval (%u)", - maxval, PALETTEMAXVAL); - else { - unsigned int bitsPerPixel; - computePixelWidth(PNM_FORMAT_TYPE(format), pnm_meaningful_bits, alpha, - NULL, &bitsPerPixel); - - if (!pfP && bitsPerPixel == 1) - /* No palette can beat 1 bit per pixel -- no need to waste time - counting the colors. - */ - asprintfN(noColormapReasonP, "pixel is already only 1 bit"); - else { - /* We'll have to count the colors ('colors') to know if a - palette is possible and desirable. Along the way, we'll - compute the actual set of colors (chv) too, and then create - the palette itself if we decide we want one. - */ - colorhist_vector chv; - unsigned int colors; - - getChv(ifP, imagepos, cols, rows, maxval, format, MAXCOLORS, - &chv, &colors); - - if (chv == NULL) { - asprintfN(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) - asprintfN(noColormapReasonP, - "palette index for %u colors would be " - "no smaller than the indexed value (%u bits)", - colors, bitsPerPixel); - else { - unsigned int paletteSize; - unsigned int transSize; - if (alpha) - tryAlphaPalette(ifP, cols, rows, maxval, format, - imagepos, alpha_mask, alphaMaxval, - pfP, - palette_pnm, &paletteSize, - trans_pnm, &transSize, - noColormapReasonP); - - else { - *noColormapReasonP = NULL; - - compute_nonalpha_palette(chv, colors, maxval, pfP, - palette_pnm, &paletteSize, - trans_pnm, &transSize); - - if (transparent) - makeOneColorTransparentInPalette( - transcolor, transexact, - palette_pnm, paletteSize, trans_pnm, - &transSize); - } - if (!*noColormapReasonP) { - if (background > -1) - findOrAddBackgroundInPalette( - backcolor, palette_pnm, &paletteSize, - backgroundIndexP); - *paletteSizeP = paletteSize; - *transSizeP = transSize; - } - } - } - freeChv(); + if (alpha_maxval != maxval) { + if (verbose) + pm_message ("rescaling alpha mask to match image bit depth"); + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) + alpha_mask[y][x] = (alpha_mask[y][x] * maxval + alpha_maxval / 2) / + alpha_maxval; + alpha_maxval = maxval; + } else { + alpha_sbitval = 0; /* no scaling happened */ } } -} + /* now do scaling for grayscale pics of bit-depth 4, 2 and 1, but only + when we don't have an alpha channel */ - -static void computeColorMapLookupTable( - bool const colorMapped, - pixel palette_pnm[], - unsigned int const palette_size, - gray trans_pnm[], - unsigned int const trans_size, - bool const alpha, - xelval const alpha_maxval, - colorhash_table * const chtP, - coloralphahash_table * const cahtP) { -/*---------------------------------------------------------------------------- - 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 - coloralphahash_table. - - If a lookup table is not applicable to the image, return NULL as - its address. (If the image is not colormapped, both will be NULL). ------------------------------------------------------------------------------*/ - if (colorMapped) { - if (alpha) { - buildColorAlphaLookup(palette_pnm, palette_size, - trans_pnm, trans_size, alpha_maxval, cahtP); - *chtP = NULL; - } else { - buildColorLookup(palette_pnm, palette_size, chtP); - *cahtP = NULL; + if (!alpha && pnm_type == PGM_TYPE && !force) { + if (maxval == 255) { + mayscale = TRUE; + for (y = 0 ; y < rows && mayscale ; y++) + for (x = 0 ; x < cols && mayscale ; x++) { + if ((PNM_GET1 (xels[y][x]) & 0xf) * 0x11 != PNM_GET1 (xels[y][x])) + mayscale = FALSE; } + if (mayscale) { + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + PNM_ASSIGN1 (xels[y][x], PNM_GET1 (xels[y][x])&0xf); + } + if (transparent > 0) + PNM_ASSIGN1 (transcolor, PNM_GET1 (transcolor) & 0xf); + if (background > 0) + PNM_ASSIGN1 (backcolor, PNM_GET1 (backcolor) & 0xf); + maxval = 15; if (verbose) - pm_message("PNG palette has %u entries, %u of them non-opaque", - palette_size, trans_size); - } else { - *chtP = NULL; - *cahtP = NULL; + pm_message ("scaling to 4-bit (grayscale superfluous data)"); + if (sbitval > 0) sbitval >>= 1; } -} - - - -static void -computeRasterWidth(bool const colorMapped, - unsigned int const palette_size, - int const pnm_type, - unsigned int const pnm_meaningful_bits, - bool const alpha, - unsigned int * const bitsPerSampleP, - unsigned int * const bitsPerPixelP) { -/*---------------------------------------------------------------------------- - Compute the number of bits per raster sample and per raster pixel: - *bitsPerSampleP and *bitsPerPixelP. Note that a raster element may be a - palette index, or a gray value or color with or without alpha mask. ------------------------------------------------------------------------------*/ - if (colorMapped) { - /* The raster element is a palette index */ - if (palette_size <= 2) - *bitsPerSampleP = 1; - else if (palette_size <= 4) - *bitsPerSampleP = 2; - else if (palette_size <= 16) - *bitsPerSampleP = 4; - else - *bitsPerSampleP = 8; - *bitsPerPixelP = *bitsPerSampleP; - if (verbose) - pm_message("Writing %d-bit color indexes", *bitsPerSampleP); - } else { - /* The raster element is an explicit pixel -- color and transparency */ - computePixelWidth(pnm_type, pnm_meaningful_bits, alpha, - bitsPerSampleP, bitsPerPixelP); - - if (verbose) - pm_message("Writing %d bits per component per pixel", - *bitsPerSampleP); } -} - -static void -createPngPalette(pixel palette_pnm[], - unsigned int const paletteSize, - pixval const maxval, - gray trans_pnm[], - unsigned int const transSize, - gray alpha_maxval, - png_color palette[], - png_byte trans[]) { -/*---------------------------------------------------------------------------- - Create the data structure to be passed to the PNG compressor to represent - the palette -- the whole palette, color + transparency. - - This is basically just a maxval conversion from the Netpbm-format - equivalents we get as input. ------------------------------------------------------------------------------*/ - unsigned int i; - - for (i = 0; i < paletteSize; ++i) { - pixel p; - PPM_DEPTH(p, palette_pnm[i], maxval, PALETTEMAXVAL); - palette[i].red = PPM_GETR(p); - palette[i].green = PPM_GETG(p); - palette[i].blue = PPM_GETB(p); -#ifdef DEBUG - pm_message("palette[%u] = (%d, %d, %d)", - i, palette[i].red, palette[i].green, palette[i].blue); -#endif + if (maxval == 15) { + mayscale = TRUE; + for (y = 0 ; y < rows && mayscale ; y++) + for (x = 0 ; x < cols && mayscale ; x++) { + if ((PNM_GET1 (xels[y][x])&3) * 5 != PNM_GET1 (xels[y][x])) + mayscale = FALSE; } - - for (i = 0; i < transSize; ++i) { - unsigned int const newmv = PALETTEMAXVAL; - unsigned int const oldmv = alpha_maxval; - trans[i] = (trans_pnm[i] * newmv + (oldmv/2)) / oldmv; -#ifdef DEBUG - pm_message("trans[%u] = %d", i, trans[i]); -#endif + if (mayscale) { + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + PNM_ASSIGN1 (xels[y][x], PNM_GET1 (xels[y][x]) & 3); + } + if (transparent > 0) + PNM_ASSIGN1 (transcolor, PNM_GET1 (transcolor) & 3); + if (background > 0) + PNM_ASSIGN1 (backcolor, PNM_GET1 (backcolor) & 3); + maxval = 3; + if (verbose) + pm_message ("scaling to 2-bit (grayscale superfluous data)"); + if (sbitval > 0) sbitval >>= 1; } -} - - -static int -convertpnm(FILE * const ifp, - FILE * const afp, - FILE * const pfp, - FILE * const tfp, - float const gamma, - int const interlace, - int const downscale, - bool const transparent_opt, - char * const transstring, - bool const alpha_opt, - char * const alpha_file, - int const background, - char * const backstring, - int const hist, - struct chroma const chroma, - int const phys_x, - int const phys_y, - int const phys_unit, - int const text, - int const ztxt, - char * const text_file, - int const mtime, - char * const date_string, - char * const time_string, - int const filterSet, - int const force, - struct zlib_compression zlib_compression - ) { - - xel p; - int rows, cols, format; - xelval maxval; - /* The maxval of the input image */ - xelval png_maxval; - /* 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. - */ - int transexact; - /* boolean: the user wants only the exact color he specified to be - transparent; not just something close to it. - */ - int transparent; - bool alpha; - /* There will be an alpha mask */ - unsigned int pnm_meaningful_bits; - pixel backcolor; - /* The background color, with maxval equal to that of the input - image. - */ - png_struct *png_ptr; - png_info *info_ptr; - - bool colorMapped; - pixel palette_pnm[MAXCOLORS]; - png_color palette[MAXCOLORS]; - /* The color part of the color/alpha palette passed to the PNG - compressor - */ - unsigned int palette_size; - - gray trans_pnm[MAXCOLORS]; - png_byte trans[MAXCOLORS]; - /* The alpha part of the color/alpha palette passed to the PNG - compressor - */ - unsigned int trans_size; - - colorhash_table cht; - coloralphahash_table caht; - - unsigned int background_index; - /* Index into palette[] of the background color. */ - - png_uint_16 histogram[MAXCOLORS]; - png_byte *line; - png_byte *pp; - int pass; - gray alpha_maxval; - int alpha_rows; - int alpha_cols; - int alpha_can_be_transparency_index; - const char * noColormapReason; - /* The reason that we shouldn't make a colormapped PNG, or NULL if - we should. malloc'ed null-terminated string. - */ - unsigned int depth; - /* 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. - */ - unsigned int fulldepth; - /* The total number of bits per pixel in the (uncompressed) png - raster, including all channels - */ - int x, y; - pm_filepos imagepos; - /* file position in input image file of start of image (i.e. after - the header) - */ - xel *xelrow; /* malloc'ed */ - /* The row of the input image currently being processed */ - - /* these variables are declared static because gcc wasn't kidding - * about "variable XXX might be clobbered by `longjmp' or `vfork'" - * (stack corruption observed on Solaris 2.6 with gcc 2.8.1, even - * in the absence of any other error condition) */ - static int pnm_type; - static xelval maxmaxval; - static gray **alpha_mask; - - - /* these guys are initialized to quiet compiler warnings: */ - maxmaxval = 255; - alpha_mask = NULL; - depth = 0; - errorlevel = 0; - - png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, - &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL); - if (png_ptr == NULL) { - pm_closer (ifp); - pm_error ("cannot allocate main libpng structure (png_ptr)"); } - info_ptr = png_create_info_struct (png_ptr); - if (info_ptr == NULL) { - png_destroy_write_struct (&png_ptr, (png_infopp)NULL); - pm_closer (ifp); - pm_error ("cannot allocate libpng info structure (info_ptr)"); + if (maxval == 3) { + mayscale = TRUE; + for (y = 0 ; y < rows && mayscale ; y++) + for (x = 0 ; x < cols && mayscale ; x++) { + if ((PNM_GET1 (xels[y][x])&1) * 3 != PNM_GET1 (xels[y][x])) + mayscale = FALSE; + } + if (mayscale) { + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + PNM_ASSIGN1 (xels[y][x], PNM_GET1 (xels[y][x])&1); + } + if (transparent > 0) + PNM_ASSIGN1 (transcolor, PNM_GET1 (transcolor) & 1); + if (background > 0) + PNM_ASSIGN1 (backcolor, PNM_GET1 (backcolor) & 1); + maxval = 1; + if (verbose) + pm_message ("scaling to 1-bit (grayscale superfluous data)"); + sbitval = 0; + } } - - if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { - png_destroy_write_struct (&png_ptr, &info_ptr); - pm_closer (ifp); - pm_error ("setjmp returns error condition (1)"); } - pnm_readpnminit (ifp, &cols, &rows, &maxval, &format); - pm_tell2(ifp, &imagepos, sizeof(imagepos)); - pnm_type = PNM_FORMAT_TYPE (format); - - xelrow = pnm_allocrow(cols); + /* + We can write a palette file if maxval <= 255 and one of the following is + true: + - for ppm files if we have <= 256 colors + - for alpha if we have <= 256 color/transparency pairs + - for pgm files (with or without alpha) if the number of bits needed for + the gray-transparency pairs is smaller than the number of bits needed + for maxval + When maxval > 255, we never write a paletted image. + */ + if ((!force) && (pnm_type != PBM_TYPE) && (maxval <= 255)) { if (verbose) { - if (pnm_type == PBM_TYPE) - pm_message ("reading a PBM file (maxval=%d)", maxval); - else if (pnm_type == PGM_TYPE) - pm_message ("reading a PGM file (maxval=%d)", maxval); - else if (pnm_type == PPM_TYPE) - pm_message ("reading a PPM file (maxval=%d)", maxval); + pm_message ("maxval is %d", maxval); + pm_message ("computing colormap..."); + } + /* for the palette check, we have to copy the grayscale values to the RGB + channels if we want to apply ppm-functions to grayscale images */ + if (pnm_type == PGM_TYPE) { + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + value = PNM_GET1 (xels[y][x]); + PPM_ASSIGN (xels[y][x], value, value, value); + } + } + chv = ppm_computecolorhist (xels, cols, rows, MAXCOLORS, &colors); + if (verbose) { + pm_message ("%d colors found", colors); + } + /* GRR: note that if colors == 16, maxval == 15 (i.e., range == 0-15) */ + if ((chv == (colorhist_vector) NULL) || /* GRR: v-- was `>=' here */ + (pnm_type == PGM_TYPE && pm_maxvaltobits(colors-1) > + (pm_maxvaltobits(maxval) / 2)) || + (pnm_type == PPM_TYPE && maxval > 255)) { + if (chv) { + ppm_freecolorhist (chv); + chv = NULL; + } + if (verbose) { + pm_message ("too many colors for writing a colormapped image"); } - - if (pnm_type == PGM_TYPE) - maxmaxval = PGM_OVERALLMAXVAL; - else if (pnm_type == PPM_TYPE) - maxmaxval = PPM_OVERALLMAXVAL; - - if (transparent_opt) { - char * transstring2; - /* Same as transstring, but with possible leading '=' removed */ - if (transstring[0] == '=') { - transexact = 1; - transstring2 = transstring+1; - } else { - transexact = 0; - transstring2 = transstring; - } - /* 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 (transcolor, ppm_parsecolor(transstring2, maxmaxval), - maxmaxval, maxval); } - if (alpha_opt) { - pixel alpha_transcolor; - if (verbose) - pm_message ("reading alpha-channel image..."); - alpha_mask = pgm_readpgm (afp, &alpha_cols, &alpha_rows, &alpha_maxval); + if (chv != (colorhist_vector) NULL) { /* palette image not yet ruled out */ - if (alpha_cols != cols || alpha_rows != rows) { + if (alpha) { + /* now check if there are different alpha values for the same color + and if all pairs still fit into 256 (MAXCOLORS) entries; malloc + one extra for possible background color */ + /* GRR: BUG? this doesn't check whether cht is NULL before using it: */ + cht = ppm_colorhisttocolorhash (chv, colors); + for (i = 0 ; i < colors + 1 ; i++) { + grr_overflow2 (MAXCOLORS, (int)sizeof(int)); + if ((alphas_of_color[i] = (gray *)malloc (MAXCOLORS * sizeof (int))) + == NULL) + { png_destroy_write_struct (&png_ptr, &info_ptr); pm_closer (ifp); - pm_error ("dimensions for image and alpha mask do not agree"); + pm_error ("out of memory allocating alpha/palette entries"); + } + alphas_of_color_cnt[i] = 0; + } + num_alphas_of_color = colors + 1; + for (y = 0 ; y < rows ; y++) { + for (x = 0 ; x < cols ; x++) { + color = ppm_lookupcolor (cht, &xels[y][x]); + for (i = 0 ; i < alphas_of_color_cnt[color] ; i++) { + if (alpha_mask[y][x] == alphas_of_color[color][i]) + break; + } + if (i == alphas_of_color_cnt[color]) { + alphas_of_color[color][i] = alpha_mask[y][x]; + alphas_of_color_cnt[color]++; + } + } + } + ppm_freecolorhash (cht); + cht = NULL; + alphas_first_index[0] = 0; + for (i = 0 ; i < colors ; i++) + alphas_first_index[i+1] = alphas_first_index[i] + + alphas_of_color_cnt[i]; + if (verbose) + pm_message ("number of color/transparency pairs is %d", + alphas_first_index[colors]); + if (alphas_first_index[colors] > MAXCOLORS) { + ppm_freecolorhist (chv); + chv = NULL; /* NOT a palette image */ + if (verbose) + pm_message ( + "too many color/transparency pairs, writing a non-mapped file"); } - alpha_trans(ifp, imagepos, cols, rows, maxval, format, - alpha_mask, alpha_maxval, - &alpha_can_be_transparency_index, &alpha_transcolor); + } /* if alpha */ - if (alpha_can_be_transparency_index && !force) { + /* add possible background color to palette */ + /* GRR: BUG: this doesn't check whether chv is NULL (previous block): */ + if (background > -1) { + cht = ppm_colorhisttocolorhash (chv, colors); + background = ppm_lookupcolor (cht, &backcolor); + if (background == -1) { + if ((!alpha && colors < MAXCOLORS) || + (alpha && alphas_first_index[colors] < MAXCOLORS)) + { + background = colors; + ppm_addtocolorhist (chv, &colors, MAXCOLORS, &backcolor, colors, + colors); + if (alpha) { + alphas_of_color[background][0] = 255; /* opaque */ + alphas_of_color_cnt[background] = 1; /* unique */ + alphas_first_index[colors] = alphas_first_index[background] + 1; + } if (verbose) - pm_message ("converting alpha mask to transparency index"); - alpha = FALSE; - transparent = 2; - transcolor = alpha_transcolor; + pm_message ("added background color to palette"); } else { - alpha = TRUE; - transparent = -1; + background = closestcolor (backcolor, chv, colors, maxval); + errorlevel = PNMTOPNG_WARNING_LEVEL; + if (verbose) + pm_message ("no room in palette for background color; using closest match instead"); } - } else { - /* Though there's no alpha_mask, we still need an alpha_maxval for - use with trans[], which can have stuff in it if the user specified - a transparent color. - */ - alpha = FALSE; - alpha_maxval = 255; - transparent = transparent_opt ? 1 : -1; } - /* gcc 2.7.0 -fomit-frame-pointer causes stack corruption here */ - if (background > -1) - PPM_DEPTH(backcolor, ppm_parsecolor (backstring, maxmaxval), - maxmaxval, maxval);; + ppm_freecolorhash (cht); /* built again about 110 lines below */ + cht = NULL; + } - /* first of all, check if we have a grayscale image written as PPM */ + } + } else { + chv = NULL; /* NOT a palette image */ + } - if (pnm_type == PPM_TYPE && !force) { - int isgray = TRUE; + if (chv) { + if (alpha) + palette_size = alphas_first_index[colors]; + else + palette_size = colors; - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (y = 0 ; y < rows && isgray ; y++) { - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - for (x = 0 ; x < cols && isgray ; x++) { - p = xelrow[x]; - if (PPM_GETR (p) != PPM_GETG (p) || PPM_GETG (p) != PPM_GETB (p)) - isgray = FALSE; - } + if (maxval < 255) { + if (verbose) + pm_message ("rescaling palette values to 8-bit"); + for (i = 0 ; i < colors ; i++) { + p = chv[i].color; + PPM_DEPTH (chv[i].color, p, maxval, 255); + } + /* ...also image pixels so they still match the palette [inefficient!] */ + for (y = 0 ; y < rows ; y++) + for (x = 0 ; x < cols ; x++) { + p = xels[y][x]; + PPM_DEPTH (xels[y][x], p, maxval, 255); } - if (isgray) - pnm_type = PGM_TYPE; + maxval = 255; /* necessary for transparent case, at least */ } - /* handle `odd' maxvalues */ + if (palette_size <= 2) + depth = 1; + else if (palette_size <= 4) + depth = 2; + else if (palette_size <= 16) + depth = 4; + else + depth = 8; + fulldepth = depth; + } else { + /* non-mapped color or grayscale */ - if (maxval > 65535 && !downscale) { + if (maxval == 65535) + depth = 16; + else if (maxval == 255) + depth = 8; + else if (maxval == 15) + depth = 4; + else if (maxval == 3) + depth = 2; + else if (maxval == 1) + depth = 1; + else { png_destroy_write_struct (&png_ptr, &info_ptr); pm_closer (ifp); - pm_error ("can only handle files up to 16-bit " - "(use -downscale to override"); + pm_error (" (can't happen) undefined maxval"); } - findRedundantBits(ifp, imagepos, cols, rows, maxval, format, alpha, - force, &pnm_meaningful_bits); - - computeColorMap(ifp, imagepos, cols, rows, maxval, format, - force, pfp, alpha, transparent >= 0, transcolor, transexact, - background, backcolor, - alpha_mask, alpha_maxval, pnm_meaningful_bits, - palette_pnm, &palette_size, trans_pnm, &trans_size, - &background_index, &noColormapReason); - - if (noColormapReason) { - if (pfp) - pm_error("You specified a particular palette, but this image " - "cannot be represented by any palette. %s", - noColormapReason); - if (verbose) - pm_message("Not using color map. %s", noColormapReason); - strfree(noColormapReason); - colorMapped = FALSE; - } else - colorMapped = TRUE; + if (alpha) { + if (pnm_type == PPM_TYPE) + fulldepth = 4 * depth; + else + fulldepth = 2 * depth; + } else { + if (pnm_type == PPM_TYPE) + fulldepth = 3 * depth; + else + fulldepth = depth; + } + } - computeColorMapLookupTable(colorMapped, palette_pnm, palette_size, - trans_pnm, trans_size, alpha, alpha_maxval, - &cht, &caht); - - computeRasterWidth(colorMapped, palette_size, pnm_type, - pnm_meaningful_bits, alpha, - &depth, &fulldepth); if (verbose) pm_message ("writing a%s %d-bit %s%s file%s", fulldepth == 8 ? "n" : "", fulldepth, - colorMapped ? "palette": - (pnm_type == PPM_TYPE ? "RGB" : "gray"), - alpha ? (colorMapped ? "+transparency" : "+alpha") : "", + chv ? "palette": (pnm_type == PPM_TYPE ? "RGB" : "gray"), + alpha ? (chv ? "+transparency" : "+alpha") : "", interlace? " (interlaced)" : ""); /* now write the file */ - png_maxval = pm_bitstomaxval(depth); - if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { png_destroy_write_struct (&png_ptr, &info_ptr); pm_closer (ifp); pm_error ("setjmp returns error condition (2)"); } - png_init_io (png_ptr, stdout); + /* GRR: all of this needs to be rewritten to avoid direct struct access */ + png_init_io (png_ptr, ofp); info_ptr->width = cols; info_ptr->height = rows; info_ptr->bit_depth = depth; - if (colorMapped) + if (chv != NULL) info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; else if (pnm_type == PPM_TYPE) info_ptr->color_type = PNG_COLOR_TYPE_RGB; @@ -1831,16 +1074,16 @@ } /* cHRM chunk */ - if (chroma.wx != -1.0) { + if (chroma_wx != -1.0) { info_ptr->valid |= PNG_INFO_cHRM; - info_ptr->x_white = chroma.wx; - info_ptr->y_white = chroma.wy; - info_ptr->x_red = chroma.rx; - info_ptr->y_red = chroma.ry; - info_ptr->x_green = chroma.gx; - info_ptr->y_green = chroma.gy; - info_ptr->x_blue = chroma.bx; - info_ptr->y_blue = chroma.by; + info_ptr->x_white = chroma_wx; + info_ptr->y_white = chroma_wy; + info_ptr->x_red = chroma_rx; + info_ptr->y_red = chroma_ry; + info_ptr->x_green = chroma_gx; + info_ptr->y_green = chroma_gy; + info_ptr->x_blue = chroma_bx; + info_ptr->y_blue = chroma_by; } /* pHYS chunk */ @@ -1851,73 +1094,173 @@ info_ptr->phys_unit_type = phys_unit; } - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - - /* creating PNG palette (PLTE and tRNS chunks) */ - - createPngPalette(palette_pnm, palette_size, maxval, - trans_pnm, trans_size, alpha_maxval, - palette, trans); - info_ptr->valid |= PNG_INFO_PLTE; - info_ptr->palette = palette; - info_ptr->num_palette = palette_size; - if (trans_size > 0) { + /* PLTE chunk */ + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { /* i.e., chv != NULL) */ + cht = ppm_colorhisttocolorhash (chv, colors); + /* before creating palette figure out the transparent color */ + if (transparent > 0) { + transparent = ppm_lookupcolor (cht, &transcolor); + if (transparent == -1) { + if (verbose) + pm_message ("specified transparent color not present in palette; ignoring -transparent"); + errorlevel = PNMTOPNG_WARNING_LEVEL; + /* BUG: don't pick an approximate color unless requested to do so + (allow with -force option? -approx option?) + [reported by Steven Grady, grady|xcf.berkeley.edu, 20000507] + transparent = closestcolor (transcolor, chv, colors, maxval); + transcolor = chv[transparent].color; + */ + } else { + /* now put transparent color in entry 0 by swapping */ + chv[transparent].color = chv[0].color; + chv[0].color = transcolor; + /* check if background color was (by bad luck) part of swap */ + if (background == transparent) + background = 0; + else if (background == 0) + background = transparent; + /* rebuild hashtable */ + ppm_freecolorhash (cht); + cht = ppm_colorhisttocolorhash (chv, colors); + transparent = 0; + trans[0] = 0; /* fully transparent */ info_ptr->valid |= PNG_INFO_tRNS; info_ptr->trans = trans; - info_ptr->num_trans = trans_size; /* omit opaque values */ + info_ptr->num_trans = 1; + } } - /* creating hIST chunk */ - if (hist) { - colorhist_vector chv; - unsigned int colors; - colorhash_table cht; - getChv(ifp, imagepos, cols, rows, maxval, format, MAXCOLORS, - &chv, &colors); + /* creating PNG palette (tRNS *not* yet valid) */ + if (alpha) { + int bot_idx = 0; + int top_idx = alphas_first_index[colors] - 1; - cht = ppm_colorhisttocolorhash (chv, colors); + /* + * same color same color (c2), + * (c0), four five different +-- single color/alpha entry + * alpha vals. alpha values | (c3, alphas_of_color[3][0]) + * =========== ============== | + * 0 1 2 3 4 5 6 7 8 9 10 ... + * c0 c0 c0 c0 c1 c2 c2 c2 c2 c2 c3 ... + * | | | | + * | | | +-- alphas_first_index[3] = 10 + * | | +----------------- alphas_first_index[2] = 5 + * | +-------------------- alphas_first_index[1] = 4 + * +-------------------------------- alphas_first_index[0] = 0 + * + * remap palette indices so opaque entries are last (omittable): for + * each color (i), loop over all the alpha values for that color (j) and + * check for opaque ones, moving them to the end (counting down from + * above since we don't yet know the relative numbers of opaque and + * partially transparent entries). mapping[old_index] = new_index. + */ + for (i = 0; i < colors; ++i) { + for (j = alphas_first_index[i]; j < alphas_first_index[i+1]; ++j) { + if (alphas_of_color[i][j-alphas_first_index[i]] == 255) + mapping[j] = top_idx--; + else + mapping[j] = bot_idx++; + } + } + /* indices should have just crossed paths */ + if (bot_idx != top_idx + 1) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error ("internal inconsistency: remapped bot_idx = %d, top_idx = %d", + bot_idx, top_idx); + } + for (i = 0 ; i < colors ; i++) { + for (j = alphas_first_index[i] ; j < alphas_first_index[i+1] ; j++) { + palette[mapping[j]].red = PPM_GETR (chv[i].color); + palette[mapping[j]].green = PPM_GETG (chv[i].color); + palette[mapping[j]].blue = PPM_GETB (chv[i].color); + trans[mapping[j]] = alphas_of_color[i][j-alphas_first_index[i]]; + } + } + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->trans = trans; + info_ptr->num_trans = bot_idx; /* omit opaque values */ + pm_message ("writing %d non-opaque transparency values", bot_idx); + } else if (have_ordered_palette) { + /* GRR BUG: this is explicitly NOT compatible with RGBA palettes */ + if (colors != ordered_palette_size) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error ( + "sizes of ordered palette (%d) and existing palette (%d) differ", + ordered_palette_size, colors); + } + for (i = 0 ; i < colors ; i++) { + int red = PPM_GETR (chv[i].color); + int green = PPM_GETG (chv[i].color); + int blue = PPM_GETB (chv[i].color); + + mapping[i] = -1; + for (j = 0 ; j < ordered_palette_size ; j++) { + if (ordered_palette[j].red == red && + ordered_palette[j].green == green && + ordered_palette[j].blue == blue) { - unsigned int i; - for (i = 0 ; i < MAXCOLORS; ++i) { - int const chvIndex = ppm_lookupcolor(cht, &palette_pnm[i]); - if (chvIndex == -1) - histogram[i] = 0; - else - histogram[i] = chv[chvIndex].value; + /* mapping[old_index] = new_index */ + mapping[i] = j; + break; + } + } + if (mapping[i] == -1) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error ("failed to find ordered-palette match for existing color " + "(%d,%d,%d)", red, green, blue); } + palette[mapping[i]].red = red; + palette[mapping[i]].green = green; + palette[mapping[i]].blue = blue; } + palette_size = ordered_palette_size; - ppm_freecolorhash(cht); + } else { + for (i = 0 ; i < MAXCOLORS ; i++) { + palette[i].red = PPM_GETR (chv[i].color); + palette[i].green = PPM_GETG (chv[i].color); + palette[i].blue = PPM_GETB (chv[i].color); + } + } + info_ptr->valid |= PNG_INFO_PLTE; + info_ptr->palette = palette; + info_ptr->num_palette = palette_size; + /* creating hIST chunk */ + if (hist) { + for (i = 0 ; i < MAXCOLORS ; i++) + histogram[i] = chv[i].value; info_ptr->valid |= PNG_INFO_hIST; info_ptr->hist = histogram; if (verbose) pm_message ("histogram created"); } - } else { /* color_type != PNG_COLOR_TYPE_PALETTE */ - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == PNG_COLOR_TYPE_RGB) { + ppm_freecolorhist (chv); + + } else /* color_type != PNG_COLOR_TYPE_PALETTE */ { + if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY) { + if (transparent > 0) { + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->trans_values.gray = PNM_GET1 (transcolor); + } + } else + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB) { if (transparent > 0) { info_ptr->valid |= PNG_INFO_tRNS; - info_ptr->trans_values = - xelToPngColor_16(transcolor, maxval, png_maxval); + info_ptr->trans_values.red = PPM_GETR (transcolor); + info_ptr->trans_values.green = PPM_GETG (transcolor); + info_ptr->trans_values.blue = PPM_GETB (transcolor); } } else { - /* This is PNG_COLOR_MASK_ALPHA. Transparency will be handled - by the alpha channel, not a transparency color. - */ + if (transparent > 0) { + png_destroy_write_struct (&png_ptr, &info_ptr); + pm_closer (ifp); + pm_error (" (can't happen) transparency AND alpha"); } - if (verbose) { - if (info_ptr->valid && PNG_INFO_tRNS) - pm_message("Transparent color {gray, red, green, blue} = " - "{%d, %d, %d, %d}", - info_ptr->trans_values.gray, - info_ptr->trans_values.red, - info_ptr->trans_values.green, - info_ptr->trans_values.blue); - else - pm_message("No transparent color"); } } @@ -1925,82 +1268,84 @@ if (background > -1) { info_ptr->valid |= PNG_INFO_bKGD; if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - info_ptr->background.index = background_index; - } else { - info_ptr->background = - xelToPngColor_16(backcolor, maxval, png_maxval); - if (verbose) - pm_message("Writing bKGD chunk with background color " - " {gray, red, green, blue} = {%d, %d, %d, %d}", - info_ptr->background.gray, - info_ptr->background.red, - info_ptr->background.green, - info_ptr->background.blue ); + if (alpha) + info_ptr->background.index = mapping[alphas_first_index[background]]; + else if (have_ordered_palette) + info_ptr->background.index = mapping[background]; + else + info_ptr->background.index = background; + } else if (info_ptr->color_type == PNG_COLOR_TYPE_RGB || + info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + info_ptr->background.red = PPM_GETR (backcolor); + info_ptr->background.green = PPM_GETG (backcolor); + info_ptr->background.blue = PPM_GETB (backcolor); + } else if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || + info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + info_ptr->background.gray = PNM_GET1 (backcolor); } } - if (!colorMapped && (png_maxval != maxval || - (alpha && png_maxval != alpha_maxval))) { - /* 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. - */ /* sBIT chunk */ - int sbitval; - + if ((sbitval != 0) || (alpha && (alpha_sbitval != 0)) || sbit_n > 0) { info_ptr->valid |= PNG_INFO_sBIT; - sbitval = pm_maxvaltobits(maxval); + if (sbitval == 0) + sbitval = pm_maxvaltobits (maxval); + if (alpha_sbitval == 0) + alpha_sbitval = pm_maxvaltobits (maxval); if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) { - info_ptr->sig_bit.red = sbitval; - info_ptr->sig_bit.green = sbitval; - info_ptr->sig_bit.blue = sbitval; - } else { - info_ptr->sig_bit.gray = sbitval; + info_ptr->sig_bit.red = (sbit_n > 0)? sbit[0] : sbitval; + info_ptr->sig_bit.green = (sbit_n > 1)? sbit[1] : sbitval; + info_ptr->sig_bit.blue = (sbit_n > 2)? sbit[2] : sbitval; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) { + info_ptr->sig_bit.alpha = (sbit_n > 3)? sbit[3] : alpha_sbitval; } - if (verbose) - pm_message("Writing sBIT chunk with sbitval = %d", sbitval); - + } else /* grayscale */ { + info_ptr->sig_bit.gray = (sbit_n > 0)? sbit[0] : sbitval; if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) { - info_ptr->sig_bit.alpha = pm_maxvaltobits(alpha_maxval); + info_ptr->sig_bit.alpha = (sbit_n > 1)? sbit[1] : alpha_sbitval; + } } } /* tEXT and zTXT chunks */ if ((text) || (ztxt)) { - pnmpng_read_text (info_ptr, tfp, ztxt, verbose); + read_text (info_ptr, tfp); } /* tIME chunk */ if (mtime) { - struct tm time_struct; info_ptr->valid |= PNG_INFO_tIME; sscanf (date_string, "%d-%d-%d", &time_struct.tm_year, &time_struct.tm_mon, &time_struct.tm_mday); if (time_struct.tm_year > 1900) time_struct.tm_year -= 1900; - time_struct.tm_mon--; /* tm has monthes 0..11 */ + time_struct.tm_mon--; /* tm has months 0..11 */ sscanf (time_string, "%d:%d:%d", &time_struct.tm_hour, &time_struct.tm_min, &time_struct.tm_sec); png_convert_from_struct_tm (&info_ptr->mod_time, &time_struct); } - /* explicit filter-type (or none) required */ - if (filterSet != 0) + /* explicit filter-type (or none) */ + if ((filter >= 0) && (filter <= 4)) + { + png_set_filter (png_ptr, 0, filter); + } + + /* zlib compression-level (or none) */ + if ((compression >= 0) && (compression <= 9)) { - png_set_filter (png_ptr, 0, filterSet); + png_set_compression_level (png_ptr, compression); } - /* zlib compression-level (or none) required */ - if (zlib_compression.level >= 0) + /* zlib compression-strategy (or none) */ + if ((strategy >= 0) && (strategy <= 3)) { - png_set_compression_level (png_ptr, zlib_compression.level); + png_set_compression_strategy(png_ptr, strategy); } /* write the png-info struct */ @@ -2017,11 +1362,9 @@ /* let libpng take care of, e.g., bit-depth conversions */ png_set_packing (png_ptr); - /* Write the raster */ - /* max: 3 color channels, one alpha channel, 16-bit */ - MALLOCARRAY(line, cols * 8); - if (line == NULL) + grr_overflow2 (cols, 8); + if ((line = (png_byte *) malloc (cols*8)) == NULL) { png_destroy_write_struct (&png_ptr, &info_ptr); pm_closer (ifp); @@ -2029,165 +1372,110 @@ } for (pass = 0 ; pass < png_set_interlace_handling (png_ptr) ; pass++) { - unsigned int row; - pm_seek2(ifp, &imagepos, sizeof(imagepos)); - for (row = 0; row < rows; ++row) { - unsigned int col; - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); - pnm_promoteformatrow(xelrow, cols, maxval, format, maxval, PPM_TYPE); + for (y = 0 ; y < rows ; y++) { pp = line; - for (col = 0 ; col < cols ; ++col) { - xel p_png; - xel const p = xelrow[col]; - PPM_DEPTH(p_png, p, maxval, png_maxval); + for (x = 0 ; x < cols ; x++) { + p = xels[y][x]; if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { if (depth == 16) - *pp++ = PNM_GET1 (p_png) >> 8; - *pp++ = PNM_GET1 (p_png)&0xff; + *pp++ = PNM_GET1 (p) >> 8; + *pp++ = PNM_GET1 (p)&0xff; } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - unsigned int paletteIndex; - if (alpha) - paletteIndex = lookupColorAlpha(caht, &p, - &alpha_mask[row][col]); - else - paletteIndex = ppm_lookupcolor(cht, &p); - *pp++ = paletteIndex; + color = ppm_lookupcolor (cht, &p); + if (alpha) { + for (i = alphas_first_index[color] ; + i < alphas_first_index[color+1] ; i++) + if (alpha_mask[y][x] == + alphas_of_color[color][i - alphas_first_index[color]]) + { + color = mapping[i]; + break; + } + } else if (have_ordered_palette) + color = mapping[color]; + *pp++ = color; } else if (info_ptr->color_type == PNG_COLOR_TYPE_RGB || info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { if (depth == 16) - *pp++ = PPM_GETR (p_png) >> 8; - *pp++ = PPM_GETR (p_png)&0xff; + *pp++ = PPM_GETR (p) >> 8; + *pp++ = PPM_GETR (p)&0xff; if (depth == 16) - *pp++ = PPM_GETG (p_png) >> 8; - *pp++ = PPM_GETG (p_png)&0xff; + *pp++ = PPM_GETG (p) >> 8; + *pp++ = PPM_GETG (p)&0xff; if (depth == 16) - *pp++ = PPM_GETB (p_png) >> 8; - *pp++ = PPM_GETB (p_png)&0xff; + *pp++ = PPM_GETB (p) >> 8; + *pp++ = PPM_GETB (p)&0xff; } else { png_destroy_write_struct (&png_ptr, &info_ptr); pm_closer (ifp); pm_error (" (can't happen) undefined color_type"); } if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) { - int const png_alphaval = (int) - alpha_mask[row][col] * (float) png_maxval / maxval +0.5; if (depth == 16) - *pp++ = png_alphaval >> 8; - *pp++ = png_alphaval & 0xff; + *pp++ = alpha_mask[y][x] >> 8; + *pp++ = alpha_mask[y][x]&0xff; } } png_write_row (png_ptr, line); } } png_write_end (png_ptr, info_ptr); - - -#if 0 - /* The following code may be intended to solve some segfault problem - that arises with png_destroy_write_struct(). The latter is the - method recommended in the libpng documentation and this program - will not compile under Cygwin because the Windows DLL for libpng - does not contain png_write_destroy() at all. Since the author's - comment below does not make it clear what the segfault issue is, - we cannot consider it. -Bryan 00.09.15 -*/ - - png_write_destroy (png_ptr); - /* flush first because free(png_ptr) can segfault due to jmpbuf problems - in png_write_destroy */ - fflush (stdout); - free (png_ptr); - free (info_ptr); -#else + /* flush first in case freeing png_ptr segfaults due to jmpbuf problems */ + fflush (ofp); png_destroy_write_struct(&png_ptr, &info_ptr); -#endif - - pnm_freerow(xelrow); - if (cht) - ppm_freecolorhash(cht); - if (caht) - freecoloralphahash(caht); + /* num_alphas_of_color is non-zero iff alphas_of_color[] were successfully + * allocated, so no need to test for NULL */ + for (i = 0 ; i < num_alphas_of_color ; i++) { + free(alphas_of_color[i]); + alphas_of_color[i] = NULL; + } + /* note that chv was already freed at end of PNG_COLOR_TYPE_PALETTE block */ + if (cht) { + ppm_freecolorhash (cht); + cht = NULL; + } + if (alpha_mask) { + pgm_freearray (alpha_mask, alpha_rows); + alpha_mask = NULL; + } + if (xels) { + pnm_freearray (xels, rows); + xels = NULL; + } return errorlevel; } -static void -displayVersion() { - - fprintf(stderr,"pnmtopng version %s.\n", VERSION); - - /* We'd like to display the version of libpng with which we're - linked, as we do for zlib, but it isn't practical. - While libpng is capable of telling you what it's level - is, different versions of it do it two different ways: with - png_libpng_ver or with png_get_header_ver. So we have to be - compiled for a particular version just to find out what - version it is! It's not worth having a link failure, much - less a compile failure, if we choose wrong. - png_get_header_ver is not in anything older than libpng 1.0.2a - (Dec 1998). png_libpng_ver is not there in libraries built - without USE_GLOBAL_ARRAYS. Cygwin versions are normally built - without USE_GLOBAL_ARRAYS. -bjh 2002.06.17. - */ - fprintf(stderr, " Compiled with libpng %s.\n", - PNG_LIBPNG_VER_STRING); - fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", - ZLIB_VERSION, zlib_version); - fprintf(stderr, - " Compiled with %d-bit netpbm support " - "(PPM_OVERALLMAXVAL = %d).\n", - pm_maxvaltobits (PPM_OVERALLMAXVAL), PPM_OVERALLMAXVAL); - fprintf(stderr, "\n"); -} - +/**********/ +/* MAIN */ +/**********/ -int -main(int argc, char *argv[]) { +#ifdef __STDC__ + int main (int argc, char *argv[]) +#else + int main (argc, argv) + int argc; + char *argv[]; +#endif +{ + FILE *ifp, *afp, *pfp, *tfp, *ofp; + int argn, errorlevel; - float gamma = -1.0; - int interlace = FALSE; - int downscale = FALSE; - bool transparent = FALSE; - char *transstring; - bool alpha = FALSE; - char *alpha_file; - int background = -1; - char *backstring; - int hist = FALSE; - struct chroma chroma = {-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0}; - int phys_x = -1.0; - int phys_y = -1.0; - int phys_unit = -1.0; - int text = FALSE; - int ztxt = FALSE; - char *text_file; - int mtime = FALSE; - char *date_string; - char *time_string; - int force = FALSE; - int filterSet = 0; - struct zlib_compression zlib_compression; - - FILE *ifp, *tfp, *afp, *pfp; - char *palette_file; - /* Name of user-supplied palette, file; NULL if none */ - int argn, errorlevel=0; + char usage[] = "[-verbose] [-downscale] [-interlace] [-force] [-alpha file] ...\n" +" ... [-transparent color] [-background color] [-gamma value] ...\n" +" ... [-hist] [-chroma wx wy rx ry gx gy bx by] [-phys x y unit] ...\n" +" ... [-text file] [-ztxt file] [-time [yy]yy-mm-dd hh:mm:ss] ...\n" +" ... [-filter 0..4] [-compression 0..9] [-strategy 0..3] ...\n" +" ... [-palette file] [-sbit s1[,s2[,s3[,s4]]]] [pnmfile]"; pnm_init (&argc, argv); argn = 1; - zlib_compression.level = -1; /* initial value */ - /* TODO: switch this to shhopt, and make options for the rest of - zlib_compression. - */ - - palette_file = NULL; /* Initial value */ - while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch (argv[argn], "-verbose", 2)) { verbose = TRUE; @@ -2199,100 +1487,104 @@ interlace = TRUE; } else if (pm_keymatch (argv[argn], "-alpha", 2)) { - if (transparent) + if (transparent > 0) pm_error ("-alpha and -transparent are mutually exclusive"); + if (have_ordered_palette) + pm_error ("-alpha and -palette are mutually exclusive"); alpha = TRUE; if (++argn < argc) alpha_file = argv[argn]; else - pm_error("-alpha requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-transparent", 3)) { if (alpha) pm_error ("-alpha and -transparent are mutually exclusive"); - transparent = TRUE; + if (have_ordered_palette) + pm_error ("-transparent and -palette are (currently) mutually exclusive"); + transparent = 1; if (++argn < argc) transstring = argv[argn]; else - pm_error("-transparent requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-background", 2)) { background = 1; if (++argn < argc) backstring = argv[argn]; else - pm_error("-background requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-gamma", 2)) { if (++argn < argc) sscanf (argv[argn], "%f", &gamma); else - pm_error("-gamma requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-hist", 2)) { hist = TRUE; } else if (pm_keymatch (argv[argn], "-chroma", 3)) { if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.wx); + sscanf (argv[argn], "%f", &chroma_wx); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.wy); + sscanf (argv[argn], "%f", &chroma_wy); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.rx); + sscanf (argv[argn], "%f", &chroma_rx); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.ry); + sscanf (argv[argn], "%f", &chroma_ry); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.gx); + sscanf (argv[argn], "%f", &chroma_gx); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.gy); + sscanf (argv[argn], "%f", &chroma_gy); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.bx); + sscanf (argv[argn], "%f", &chroma_bx); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); if (++argn < argc) - sscanf (argv[argn], "%f", &chroma.by); + sscanf (argv[argn], "%f", &chroma_by); else - pm_error("-chroma requires 6 values"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-phys", 3)) { if (++argn < argc) sscanf (argv[argn], "%d", &phys_x); else - pm_error("-phys requires 3 values"); + pm_usage (usage); if (++argn < argc) sscanf (argv[argn], "%d", &phys_y); else - pm_error("-phys requires 3 values"); + pm_usage (usage); if (++argn < argc) sscanf (argv[argn], "%d", &phys_unit); else - pm_error("-phys requires 3 values"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-text", 3)) { text = TRUE; if (++argn < argc) text_file = argv[argn]; else - pm_error("-text requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-ztxt", 2)) { ztxt = TRUE; if (++argn < argc) text_file = argv[argn]; else - pm_error("-ztxt requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-time", 3)) { mtime = TRUE; @@ -2301,91 +1593,135 @@ if (++argn < argc) time_string = argv[argn]; else - pm_error("-time requires 2 values"); + pm_usage (usage); } else { - pm_error("-time requires 2 values"); + pm_usage (usage); } } else if (pm_keymatch (argv[argn], "-palette", 3)) { + if (alpha) + pm_error ("-alpha and -palette are mutually exclusive"); + if (transparent > 0) + pm_error ("-transparent and -palette are (currently) mutually exclusive"); + have_ordered_palette = TRUE; if (++argn < argc) palette_file = argv[argn]; else - pm_error("-palette requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-filter", 3)) { - if (++argn < argc) - { - int filter; + if (++argn < argc) { sscanf (argv[argn], "%d", &filter); - if ((filter < 0) || (filter > 4)) - pm_error("-filter is obsolete. Use -nofilter, -sub, -up, -avg, " - "and -paeth options instead."); + if ((filter < 0) || (filter > 4)) { + pm_message + ("filter must be 0 (none), 1 (sub), 2 (up), 3 (avg), or 4 (paeth)"); + pm_usage (usage); + } + } else - switch (filter) { - case 0: filterSet = PNG_FILTER_NONE; break; - case 1: filterSet = PNG_FILTER_SUB; break; - case 2: filterSet = PNG_FILTER_UP; break; - case 3: filterSet = PNG_FILTER_AVG; break; - case 4: filterSet = PNG_FILTER_PAETH; break; + pm_usage (usage); + } else + if (pm_keymatch (argv[argn], "-compression", 3)) { + if (++argn < argc) { + sscanf (argv[argn], "%d", &compression); + if ((compression < 0) || (compression > 9)) { + pm_message ("zlib compression must be between 0 (none) and 9 (max)"); + pm_usage (usage); } } else - pm_error("-filter is obsolete. Use -nofilter, -sub, -up, -avg, " - "and -paeth options instead."); + pm_usage (usage); } else - if (pm_keymatch (argv[argn], "-nofilter", 4)) - filterSet |= PNG_FILTER_NONE; - else if (pm_keymatch (argv[argn], "-sub", 3)) - filterSet |= PNG_FILTER_SUB; - else if (pm_keymatch (argv[argn], "-up", 3)) - filterSet |= PNG_FILTER_UP; - else if (pm_keymatch (argv[argn], "-avg", 3)) - filterSet |= PNG_FILTER_AVG; - else if (pm_keymatch (argv[argn], "-paeth", 3)) - filterSet |= PNG_FILTER_PAETH; + if (pm_keymatch (argv[argn], "-strategy", 3)) { + if (++argn < argc) { + sscanf (argv[argn], "%d", &strategy); + if ((strategy < 0) || (strategy > 3)) { + pm_message ("zlib strategy must be 0 (default), 1 (filtered), " + "2 (Huffman), or 3 (RLE)"); + pm_usage (usage); + } + } else - if (pm_keymatch (argv[argn], "-compression", 3)) { + pm_usage (usage); + } else + if (pm_keymatch (argv[argn], "-sbit", 2)) { if (++argn < argc) { - sscanf (argv[argn], "%d", &zlib_compression.level); - if ((zlib_compression.level < 0) || (zlib_compression.level > 9)) - { - pm_error("zlib compression must be between 0 (none) and 9 (max)"); + sbit_n = sscanf (argv[argn], "%i,%i,%i,%i", &sbit[0], &sbit[1], + &sbit[2], &sbit[3]); + if (sbit_n == 0 || sbit_n == EOF) + pm_usage (usage); + else { + int j; + for (j = 0; j < sbit_n; ++j) { + if (sbit[j] < 1 || sbit[j] > 16) { + pm_message + ("number of significant bits must be between 1 and 16"); + pm_usage (usage); + break; + } + } } } else - pm_error("-compression requires a value"); + pm_usage (usage); } else if (pm_keymatch (argv[argn], "-force", 3)) { force = TRUE; - } else - if (pm_keymatch (argv[argn], "-libversion", 3)) - displayVersion(); /* exits program */ - else - pm_error("Unrecognized option: '%s'", argv[argn]); - argn++; + } else { + fprintf(stderr,"pnmtopng version %s.\n", VERSION); + fprintf(stderr, " Compiled with libpng %s; using libpng %s.\n", + PNG_LIBPNG_VER_STRING, png_libpng_ver); + fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", + ZLIB_VERSION, zlib_version); +#ifdef PGM_BIGGRAYS + fprintf(stderr, + " Compiled with 16-bit netpbm support (PGM_BIGGRAYS defined).\n"); +#else + fprintf(stderr, + " Compiled with %d-bit netpbm support (PPM_MAXMAXVAL = %d).\n", + pm_maxvaltobits (PPM_MAXMAXVAL), PPM_MAXMAXVAL); +#endif + fprintf(stderr, "\n"); + pm_usage (usage); } - - { - const char *input_file; - if (argn == argc) - input_file = "-"; - else { - input_file = argv[argn]; argn++; } - ifp = pm_openr_seekable(input_file); + + if (argn != argc) { + ifp = pm_openr (argv[argn]); + ++argn; + } else { + + /* setmode/fdopen code borrowed from Info-ZIP's funzip.c (Mark Adler) */ + /* [HAVE_SETMODE is same macro used by NetPBM 9.x for Cygwin, etc.] */ + +#ifdef HAVE_SETMODE /* DOS, FLEXOS, Human68k, NetWare, OS/2, Win32 */ +# if (defined(__HIGHC__) && !defined(FLEXOS)) + setmode(stdin, _BINARY); +# else + setmode(0, O_BINARY); /* some buggy C libraries require BOTH setmode() */ +# endif /* call AND the fdopen() in binary mode :-( */ +#endif + +#ifdef RISCOS + ifp = stdin; +#else + if ((ifp = fdopen(0, "rb")) == (FILE *)NULL) + pm_error ("cannot find stdin"); +#endif + } if (argn != argc) - pm_error("Too many arguments."); + pm_usage (usage); if (alpha) afp = pm_openr (alpha_file); else afp = NULL; - if (palette_file) + if (have_ordered_palette) pfp = pm_openr (palette_file); else pfp = NULL; @@ -2395,23 +1731,34 @@ else tfp = NULL; - errorlevel = convertpnm(ifp, afp, pfp, tfp, - gamma, interlace, downscale, transparent, - transstring, alpha, alpha_file, background, - backstring, hist, chroma, phys_x, phys_y, - phys_unit, text, ztxt, text_file, mtime, - date_string, time_string, filterSet, - force, zlib_compression); + /* output always goes to stdout */ - if (afp) +#ifdef HAVE_SETMODE /* DOS, FLEXOS, Human68k, NetWare, OS/2, Win32 */ +# if (defined(__HIGHC__) && !defined(FLEXOS)) + setmode(stdout, _BINARY); +# else + setmode(1, O_BINARY); +# endif +#endif + +#ifdef RISCOS + ofp = stdout; +#else + if ((ofp = fdopen(1, "wb")) == (FILE *)NULL) + pm_error ("cannot write to stdout"); +#endif + + errorlevel = convertpnm (ifp, afp, pfp, tfp, ofp); + + if (alpha) pm_closer (afp); - if (pfp) + if (have_ordered_palette) pm_closer (pfp); - if (tfp) + if ((text) || (ztxt)) pm_closer (tfp); pm_closer (ifp); - pm_closew (stdout); + pm_closew (ofp); return errorlevel; } diff -rubN netpbm-10.26.56/converter/other/pnmtopng_overflow.h netpbm-10.26.56-monty/converter/other/pnmtopng_overflow.h --- netpbm-10.26.56/converter/other/pnmtopng_overflow.h 1969-12-31 19:00:00.000000000 -0500 +++ netpbm-10.26.56-monty/converter/other/pnmtopng_overflow.h 2008-09-10 08:38:50.399164016 -0400 @@ -0,0 +1,12 @@ +/* Wild guess/reimplementation/adaptation of Alan Cox's (presumed) overflow- + * protection function (seen used in http://security.debian.org/pool/updates/ + * main/n/netpbm-free/netpbm-free_9.20-8.4.diff.gz). Greg Roelofs, 20051112 */ + +static void grr_overflow2(int mult1, int mult2) +{ + int product = mult1 * mult2; + + if (mult1 <= 0 || mult2 <= 0 || product/mult1 < mult2) { + pm_error ("dimensions would overflow memory allocation"); + } +} diff -rubN netpbm-10.26.56/converter/other/pnmtopng_version.h netpbm-10.26.56-monty/converter/other/pnmtopng_version.h --- netpbm-10.26.56/converter/other/pnmtopng_version.h 1969-12-31 19:00:00.000000000 -0500 +++ netpbm-10.26.56-monty/converter/other/pnmtopng_version.h 2008-09-10 08:38:59.299810912 -0400 @@ -0,0 +1 @@ +#define VERSION "2.39 (12 November 2005)" diff -rubN netpbm-10.26.56/editor/Makefile netpbm-10.26.56-monty/editor/Makefile --- netpbm-10.26.56/editor/Makefile 2004-09-26 17:54:21.000000000 -0400 +++ netpbm-10.26.56-monty/editor/Makefile 2008-09-10 00:52:14.771146384 -0400 @@ -28,7 +28,7 @@ pnmscale pnmscalefixed pnmshear pnmsmooth pnmstitch pnmtile \ ppm3d ppmbrighten ppmchange ppmcolormask \ ppmdim ppmdist ppmdither ppmflash ppmglobe ppmlabel ppmmix \ - ppmntsc ppmrelief ppmshift ppmspread ppmtv + ppmntsc ppmrelief ppmshift ppmspread ppmtv ppmfilter # We don't include programs that have special library dependencies in the # merge scheme, because we don't want those dependencies to prevent us diff -rubN netpbm-10.26.56/editor/ppmdither.c netpbm-10.26.56-monty/editor/ppmdither.c --- netpbm-10.26.56/editor/ppmdither.c 2003-07-06 15:54:02.000000000 -0400 +++ netpbm-10.26.56-monty/editor/ppmdither.c 2008-09-10 01:33:41.194152632 -0400 @@ -223,6 +223,79 @@ } } +static void flood_fill(const unsigned int width, const unsigned int height, + pixel ** const input, pixel ** const output, + int *xfifo, int *yfifo, unsigned char **flags, + int row, int col) { + int head=1; + int tail=0; + pixel color=input[row][col]; + + if(flags[row][col])return; + + xfifo[0]=col; + yfifo[0]=row; + flags[row][col]=1; + + while(head>tail){ + col = xfifo[tail]; + row = yfifo[tail]; + output[row][col]=color; + + if(col>0 && !flags[row][col-1] && PPM_EQUAL(input[row][col-1],color)){ + xfifo[head]=col-1; + yfifo[head++]=row; + flags[row][col-1]=1; + } + if(col+10 && !flags[row-1][col] && PPM_EQUAL(input[row-1][col],color)){ + xfifo[head]=col; + yfifo[head++]=row-1; + flags[row-1][col]=1; + } + if(row+1] [-red ] [-green ] [-blue ] [ppmfile]"; + "[-dim ] [-red ] [-green ] [-blue ] \\\n" + "[-borderpreserve] [-maxpreserve] [ppmfile]"; unsigned int dith_nr; /* number of red shades in output */ unsigned int dith_ng; /* number of green shades in output */ unsigned int dith_nb; /* number of blue shades in output */ - + int borderpreserve=0; + int maxpreserve=0; ppm_init( &argc, argv ); @@ -276,6 +351,13 @@ if (sscanf(argv[argn], "%u", &dith_nb) != 1) pm_usage( usage ); } + else if ( pm_keymatch( argv[argn], "-borderpreserve", 3)) { + borderpreserve=1; + maxpreserve=1; + } + else if ( pm_keymatch( argv[argn], "-maxpreserve", 2)) { + maxpreserve=1; + } else if ( pm_keymatch( argv[argn], "-debug", 6 )) { debug = 1; } @@ -298,12 +380,19 @@ ipixels = ppm_readppm( ifp, &cols, &rows, &maxval ); pm_close( ifp ); opixels = ppm_allocarray(cols, rows); + if(maxpreserve){ + output_maxval = maxval; + }else{ output_maxval = pm_lcm(dith_nr-1, dith_ng-1, dith_nb-1, PPM_MAXMAXVAL); + } dith_setup(dith_power, dith_nr, dith_ng, dith_nb, output_maxval, &colormap); dith_dither(cols, rows, maxval, colormap, ipixels, opixels, dith_nr, dith_ng, dith_nb, output_maxval); + if(borderpreserve) + dith_border(cols, rows, ipixels, opixels); ppm_writeppm(stdout, opixels, cols, rows, output_maxval, 0); pm_close(stdout); + exit(0); } diff -rubN netpbm-10.26.56/editor/ppmfilter.c netpbm-10.26.56-monty/editor/ppmfilter.c --- netpbm-10.26.56/editor/ppmfilter.c 1969-12-31 19:00:00.000000000 -0500 +++ netpbm-10.26.56-monty/editor/ppmfilter.c 2008-09-10 03:11:11.813723264 -0400 @@ -0,0 +1,961 @@ +/* ppmfilter.c - Dual-tree double-density wavelet noise filter; +** use to remove noise or selectively smooth texture. +** +** Copyright (C) 2008 Monty, wavelet code derived from Matlab code by +** Crystal Wagner and Prof. Ivan Selesnik, Polytechnic University, Brooklyn, NY +** See: http://taco.poly.edu/selesi/DoubleSoftware/ +** +** 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. +*/ + +#include +#include +#include "ppm.h" +#include "mallocvar.h" + +/* COLOR(): + * returns the index in the colormap for the + * r, g, b values specified. + */ +#define COLOR(r,g,b) (((r) * dith_ng + (g)) * dith_nb + (b)) + + +#define FSZ 14 + +static double analysis_fs[2][3][FSZ]={ + { + { + +0.00000000000000, + +0.00069616789827, + -0.02692519074183, + -0.04145457368920, + +0.19056483888763, + +0.58422553883167, + +0.58422553883167, + +0.19056483888763, + -0.04145457368920, + -0.02692519074183, + +0.00069616789827, + +0.00000000000000, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + -0.00014203017443, + +0.00549320005590, + +0.01098019299363, + -0.13644909765612, + -0.21696226276259, + +0.33707999754362, + +0.33707999754362, + -0.21696226276259, + -0.13644909765612, + +0.01098019299363, + +0.00549320005590, + -0.00014203017443, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00014203017443, + -0.00549320005590, + -0.00927404236573, + +0.07046152309968, + +0.13542356651691, + -0.64578354990472, + +0.64578354990472, + -0.13542356651691, + -0.07046152309968, + +0.00927404236573, + +0.00549320005590, + -0.00014203017443, + +0.00000000000000, + } + }, + { + { + +0.00000000000000, + +0.00000000000000, + +0.00069616789827, + -0.02692519074183, + -0.04145457368920, + +0.19056483888763, + +0.58422553883167, + +0.58422553883167, + +0.19056483888763, + -0.04145457368920, + -0.02692519074183, + +0.00069616789827, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + -0.00014203017443, + +0.00549320005590, + +0.01098019299363, + -0.13644909765612, + -0.21696226276259, + +0.33707999754362, + +0.33707999754362, + -0.21696226276259, + -0.13644909765612, + +0.01098019299363, + +0.00549320005590, + -0.00014203017443, + }, + { + +0.00000000000000, + +0.00000000000000, + +0.00014203017443, + -0.00549320005590, + -0.00927404236573, + +0.07046152309968, + +0.13542356651691, + -0.64578354990472, + +0.64578354990472, + -0.13542356651691, + -0.07046152309968, + +0.00927404236573, + +0.00549320005590, + -0.00014203017443, + } + } +}; + +static double analysis[2][3][FSZ]={ + { + { + +0.00000000000000, + +0.00000000000000, + +0.00017870679071, + -0.02488304194507, + +0.00737700819766, + +0.29533805776119, + +0.59529279993637, + +0.45630440337458, + +0.11239376309619, + -0.01971220693439, + -0.00813549683439, + +0.00005956893024, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + -0.00012344587034, + +0.01718853971559, + -0.00675291099550, + +0.02671809818132, + -0.64763513288874, + +0.47089724990858, + +0.16040017815754, + -0.01484700537727, + -0.00588868840296, + +0.00004311757177, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + +0.00001437252392, + -0.00200122286479, + +0.00027261232228, + +0.06840460220387, + +0.01936710587994, + -0.68031992557818, + +0.42976785708978, + +0.11428688385011, + +0.05057805218407, + -0.00037033761102, + +0.00000000000000, + +0.00000000000000, + } + }, + { + { + +0.00000000000000, + +0.00000000000000, + +0.00005956893024, + -0.00813549683439, + -0.01971220693439, + +0.11239376309619, + +0.45630440337458, + +0.59529279993637, + +0.29533805776119, + +0.00737700819766, + -0.02488304194507, + +0.00017870679071, + +0.00000000000000, + +0.00000000000000, + }, + + { + +0.00000000000000, + +0.00000000000000, + -0.00037033761102, + +0.05057805218407, + +0.11428688385011, + +0.42976785708978, + -0.68031992557818, + +0.01936710587994, + +0.06840460220387, + +0.00027261232228, + -0.00200122286479, + +0.00001437252392, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + +0.00004311757177, + -0.00588868840296, + -0.01484700537727, + +0.16040017815754, + +0.47089724990858, + -0.64763513288874, + +0.02671809818132, + -0.00675291099550, + +0.01718853971559, + -0.00012344587034, + +0.00000000000000, + +0.00000000000000, + } + } +}; + +static double synthesis_fs[2][3][FSZ]={ + { + { + +0.00000000000000, + +0.00000000000000, + +0.00000000000000, + +0.00069616789827, + -0.02692519074183, + -0.04145457368920, + +0.19056483888763, + +0.58422553883167, + +0.58422553883167, + +0.19056483888763, + -0.04145457368920, + -0.02692519074183, + +0.00069616789827, + +0.00000000000000, + }, + { + +0.00000000000000, + -0.00014203017443, + +0.00549320005590, + +0.01098019299363, + -0.13644909765612, + -0.21696226276259, + +0.33707999754362, + +0.33707999754362, + -0.21696226276259, + -0.13644909765612, + +0.01098019299363, + +0.00549320005590, + -0.00014203017443, + +0.00000000000000, + }, + { + +0.00000000000000, + -0.00014203017443, + +0.00549320005590, + +0.00927404236573, + -0.07046152309968, + -0.13542356651691, + +0.64578354990472, + -0.64578354990472, + +0.13542356651691, + +0.07046152309968, + -0.00927404236573, + -0.00549320005590, + +0.00014203017443, + +0.00000000000000, + } + }, + { + { + +0.00000000000000, + +0.00000000000000, + +0.00069616789827, + -0.02692519074183, + -0.04145457368920, + +0.19056483888763, + +0.58422553883167, + +0.58422553883167, + +0.19056483888763, + -0.04145457368920, + -0.02692519074183, + +0.00069616789827, + +0.00000000000000, + +0.00000000000000, + }, + { + -0.00014203017443, + +0.00549320005590, + +0.01098019299363, + -0.13644909765612, + -0.21696226276259, + +0.33707999754362, + +0.33707999754362, + -0.21696226276259, + -0.13644909765612, + +0.01098019299363, + +0.00549320005590, + -0.00014203017443, + +0.00000000000000, + +0.00000000000000, + }, + { + -0.00014203017443, + +0.00549320005590, + +0.00927404236573, + -0.07046152309968, + -0.13542356651691, + +0.64578354990472, + -0.64578354990472, + +0.13542356651691, + +0.07046152309968, + -0.00927404236573, + -0.00549320005590, + +0.00014203017443, + +0.00000000000000, + +0.00000000000000, + } + } +}; + +static double synthesis[2][3][FSZ]={ + { + { + +0.00000000000000, + +0.00000000000000, + +0.00005956893024, + -0.00813549683439, + -0.01971220693439, + +0.11239376309619, + +0.45630440337458, + +0.59529279993637, + +0.29533805776119, + +0.00737700819766, + -0.02488304194507, + +0.00017870679071, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + +0.00004311757177, + -0.00588868840296, + -0.01484700537727, + +0.16040017815754, + +0.47089724990858, + -0.64763513288874, + +0.02671809818132, + -0.00675291099550, + +0.01718853971559, + -0.00012344587034, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + -0.00037033761102, + +0.05057805218407, + +0.11428688385011, + +0.42976785708978, + -0.68031992557818, + +0.01936710587994, + +0.06840460220387, + +0.00027261232228, + -0.00200122286479, + +0.00001437252392, + +0.00000000000000, + +0.00000000000000, + } + }, + { + { + +0.00000000000000, + +0.00000000000000, + +0.00017870679071, + -0.02488304194507, + +0.00737700819766, + +0.29533805776119, + +0.59529279993637, + +0.45630440337458, + +0.11239376309619, + -0.01971220693439, + -0.00813549683439, + +0.00005956893024, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + +0.00001437252392, + -0.00200122286479, + +0.00027261232228, + +0.06840460220387, + +0.01936710587994, + -0.68031992557818, + +0.42976785708978, + +0.11428688385011, + +0.05057805218407, + -0.00037033761102, + +0.00000000000000, + +0.00000000000000, + }, + { + +0.00000000000000, + +0.00000000000000, + -0.00012344587034, + +0.01718853971559, + -0.00675291099550, + +0.02671809818132, + -0.64763513288874, + +0.47089724990858, + +0.16040017815754, + -0.01484700537727, + -0.00588868840296, + +0.00004311757177, + +0.00000000000000, + +0.00000000000000, + } + } +}; + +/* This is a fairly straight translation of the Matlab code into C + with minimal refactoring. I'm certain performance can be greatly + improved, but I was after solid immediate results. --Monty */ + +typedef struct { + double **x; + int rows; + int cols; +} m2D; + +static m2D alloc_m2D(int m, int n){ + int i; + m2D ret; + ret.rows = m; + ret.cols = n; + ret.x = calloc(m,sizeof(*ret.x)); + for(i=0;i=N)p-=N; + a+=x.x[i][p]*f[FSZ-k]; + } + y.x[i][j>>1]=a; + } + + return y; +} + +static m2D convolveS(m2D x, double *f){ + int i,M = x.rows; + int j,N = x.cols*2; + int k; + m2D y=alloc_m2D(M,N); + + for(i=0;i=N)p-=N; + a+=x.x[i][p>>1]*f[FSZ-k]; + } + } + y.x[i][j]=a; + } + + return y; +} + +static void afb3_2d_A(m2D x, m2D *L, m2D *H1, m2D *H2, + double f[3][FSZ], int d){ + m2D xt=x; + m2D Lt; + m2D H1t; + m2D H2t; + + if(d==0){ + xt = transpose(x); + } + + shift2D(xt,-(FSZ/2)); + *L = Lt = convolveA(xt, f[0]); + *H1 = H1t = convolveA(xt, f[1]); + *H2 = H2t = convolveA(xt, f[2]); + + if(d==0){ + *L=transpose(Lt); + *H1=transpose(H1t); + *H2=transpose(H2t); + free_m2D(xt); + free_m2D(Lt); + free_m2D(H1t); + free_m2D(H2t); + } +} + +static m2D sfb3_2d_A(m2D L, m2D H1, m2D H2, + double f[3][FSZ], int d){ + + int N,i,j; + m2D Lt=L; + m2D H1t=H1; + m2D H2t=H2; + m2D y,y2; + + if(d==0){ + Lt=transpose(L); + H1t=transpose(H1); + H2t=transpose(H2); + } + + N=L.cols*2; + y=convolveS(Lt,f[0]); + y2=convolveS(H1t,f[1]); + for(i=0;imax)v=max;if(v<0)v=0; + rgb[p]=v; + PPM_ASSIGN(y[i][j],rgb[0],rgb[1],rgb[2]); + } + + free_m2D(yc); + free_m2D(lo[0][0]); + free_m2D(lo[0][1]); + free_m2D(lo[1][0]); + free_m2D(lo[1][1]); + for(j=0;jtail){ + col = xfifo[tail]; + row = yfifo[tail]; + output[row][col]=color; + + if(col>0 && !flags[row][col-1] && PPM_EQUAL(input[row][col-1],color)){ + xfifo[head]=col-1; + yfifo[head++]=row; + flags[row][col-1]=1; + } + if(col+10 && !flags[row-1][col] && PPM_EQUAL(input[row-1][col],color)){ + xfifo[head]=col; + yfifo[head++]=row-1; + flags[row-1][col]=1; + } + if(row+1] [-f1 ] [-f2 ] [-f3 ] [-f4 ] \\\n" + "[-borderpreserve] [ppmfile]"; + int borderpreserve=0; + double T[4]={1,2,4,4}; + + ppm_init( &argc, argv ); + argn = 1; + + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) + { + if ( pm_keymatch( argv[argn], "-borderpreserve", 7)) { + borderpreserve=1; + } + else if ( pm_keymatch( argv[argn], "-f1", 3) && argn+1 < argc) { + argn++; + if (sscanf(argv[argn], "%lf", &T[0]) != 1) + pm_usage( usage ); + } + else if ( pm_keymatch( argv[argn], "-f2", 3) && argn+1 < argc) { + argn++; + if (sscanf(argv[argn], "%lf", &T[1]) != 1) + pm_usage( usage ); + } + else if ( pm_keymatch( argv[argn], "-f3", 3) && argn+1 < argc) { + argn++; + if (sscanf(argv[argn], "%lf", &T[2]) != 1) + pm_usage( usage ); + } + else if ( pm_keymatch( argv[argn], "-f4", 3) && argn+1 < argc) { + argn++; + if (sscanf(argv[argn], "%lf", &T[3]) != 1) + pm_usage( usage ); + } + else if ( pm_keymatch( argv[argn], "-filter", 4 )) { + argn++; + if (sscanf(argv[argn], "%lf", &T[0]) != 1) + pm_usage( usage ); + T[1]=T[0]; + T[2]=T[0]; + T[3]=T[0]; + } + else + pm_usage( usage ); + ++argn; + } + + if ( argn != argc ) + { + ifp = pm_openr( argv[argn] ); + ++argn; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + ipixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + pm_close( ifp ); + opixels = ppm_allocarray(cols, rows); + wfilter(ipixels,opixels,cols,rows,T,maxval); + if(borderpreserve) + filter_border(cols, rows, ipixels, opixels); + ppm_writeppm(stdout, opixels, cols, rows, maxval, 0); + pm_close(stdout); + + exit(0); +}