This patch to Andreas Dilger's XV PNG patch 1.2d addresses the fol- lowing bugs, buglets and typos: - fixes a core-dump triggered by an unfortunate "feature" in libpng 0.97 and later (i.e., libpng frees text buffers it didn't allo- cate) - fixes an infinite-loop bug in text-comment processing (from TenThumbs, tenthumbs@cybernex.net, 5 August 1998) - forces text comments inherited from GIF or elsewhere to be in proper PNG format (i.e., Unix-style "LF" line-endings); inher- ited PNG comments are still assumed to be in proper format - corrects argument types for png_xv_error() and png_xv_warning() to match what png_create_write_struct() and png_create_read_ struct() expect - changes default ">>" increment on compression dial from 2 to 3 so that maximum compression (level 9) is one click away from default (level 6) - fixes some comment typos and removes overlooked debug code This code has been tested with various versions of libpng, most re- cently version 1.0.2, with no apparent problems. Greg Roelofs, newt@pobox.com 9 August 1998 --- xv-3.10a.orig/xvpng.c Thu Jun 13 16:42:11 1996 +++ xv-3.10a/xvpng.c Sun Aug 9 13:08:43 1998 @@ -49,6 +50,9 @@ #define BUTTH 24 +#define LF 10 /* a.k.a. '\n' on ASCII machines */ +#define CR 13 /* a.k.a. '\r' on ASCII machines */ + /*** local functions ***/ static void drawPD PARM((int, int, int, int)); static void clickPD PARM((int, int)); @@ -57,8 +61,10 @@ static int WritePNG PARM((FILE *, byte *, int, int, int, byte *, byte *, byte *, int)); -static void png_xv_error PARM((png_struct *png_ptr, char *message)); -static void png_xv_warning PARM((png_struct *png_ptr, char *message)); +static void png_xv_error PARM((png_structp png_ptr, + png_const_charp message)); +static void png_xv_warning PARM((png_structp png_ptr, + png_const_charp message)); /*** local variables ***/ static char *filename; @@ -87,7 +93,7 @@ XSelectInput(theDisp, pngW, ExposureMask | ButtonPressMask | KeyPressMask); DCreate(&cDial, pngW, 12, 25, DWIDE, DHIGH, (double)Z_NO_COMPRESSION, - (double)Z_BEST_COMPRESSION, COMPRESSION, 1.0, 2.0, + (double)Z_BEST_COMPRESSION, COMPRESSION, 1.0, 3.0, infofg, infobg, hicol, locol, "Compression", NULL); DCreate(&gDial, pngW, DWIDE+27, 25, DWIDE, DHIGH, 1.0, 3.5,DISPLAY_GAMMA,0.01,0.2, @@ -594,14 +607,13 @@ int j; p = pic; for(j = 0; j < h; j++) { - fflush(stdout); if(info_ptr->color_type == PNG_COLOR_TYPE_GRAY) { int k; for(k = 0; k < w; k++) png_line[k] = ptype==PIC24 ? MONO(p[k*3], p[k*3+1], p[k*3+2]) : remap[p[k]]; png_write_row(png_ptr, png_line); - } else /* rbg or palette */ + } else /* RGB or palette */ png_write_row(png_ptr, p); if((j & 0x1f) == 0) WaitCursor(); p += linesize; @@ -642,6 +654,7 @@ } /* See if it looks like a PNG keyword from LoadPNG */ + /* GRR: should test for strictly < 80, right? (key = 1-79 chars only) */ if(comment && comment[1] == ':' && comment - key <= 80) { *(comment++) = '\0'; *(comment++) = '\0'; @@ -661,9 +674,9 @@ /* We have to find the end of this comment, and the next keyword if there is one */ - do { - key = comment = strchr(comment, ':'); - } while (key && key[1] != ':'); + for (; NULL != (key = comment = strchr(comment, ':')); comment++) + if (key[1] == ':') + break; /* It looks like another keyword, go backward to the beginning */ if (key) { @@ -690,11 +703,25 @@ tp++; } } - /* It is just a generic comment */ + /* Just a generic comment: make sure line-endings are valid for PNG */ else { + char *p=key, *q=key; /* only deleting chars, not adding any */ + + while (*p) { + if (*p == CR) { /* lone CR or CR/LF: EOL either way */ + *q++ = LF; /* LF is the only allowed PNG line-ending */ + if (p[1] == LF) /* get rid of any original LF */ + ++p; + } else if (*p == LF) /* lone LF */ + *q++ = LF; + else + *q++ = *p; + ++p; + } + *q = '\0'; /* unnecessary...but what the heck */ tp->key = "Comment"; tp->text = key; - tp->text_length = strlen(tp->text); + tp->text_length = q - key; tp->compression = tp->text_length > 750 ? 0 : -1; info_ptr->num_text++; key = NULL; @@ -712,15 +739,22 @@ info_ptr->valid |= PNG_INFO_tIME; png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); + fflush(fp); /* just in case we core-dump before finishing... */ if (text) { free(text); + /* must do this or png_destroy_write_struct() 0.97+ will free text again: */ + info_ptr->text = (png_textp)NULL; if (savecmnt) + { free(savecmnt); + savecmnt = (char *)NULL; + } } + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; } @@ -941,8 +1001,8 @@ /*******************************************/ static void png_xv_error(png_ptr, message) - png_struct *png_ptr; - char *message; + png_structp png_ptr; + png_const_charp message; { SetISTR(ISTR_WARNING,"%s: libpng error: %s", fbasename, message); @@ -953,8 +1013,8 @@ /*******************************************/ static void png_xv_warning(png_ptr, message) - png_struct *png_ptr; - char *message; + png_structp png_ptr; + png_const_charp message; { if (!png_ptr) return;