diff --git a/TODO b/TODO index 605e591c7..e8c95ea62 100644 --- a/TODO +++ b/TODO @@ -3,8 +3,9 @@ pngtodo.txt - list of things to do for libpng add "grayscale->palette" transformation and "palette->grayscale" detection improved dithering multi-lingual error and warning message support +sPLT chunk handling cHRM transformation -sRGB chunk handling +complete sRGB transformation (presently it simply uses gamma=0.5) man pages for function calls high-level API for reading images final bug fixes diff --git a/png.h b/png.h index 3441beb6d..ae7ddf083 100644 --- a/png.h +++ b/png.h @@ -997,8 +997,10 @@ extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, * more information. */ +#if !defined(PNG_NO_STDIO) /* Initialize the input/output for the PNG file to the default functions. */ extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, FILE *fp)); +#endif /* Replace the (error and abort), and warning functions with user * supplied functions. If no messages are to be printed you must still @@ -1066,10 +1068,18 @@ extern void *far_to_near PNGARG((png_structp png_ptr,png_voidp ptr,int check)); extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, png_const_charp error)); +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error)); + /* Non-fatal error in libpng. Can continue, but may have a problem. */ extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, png_const_charp message)); +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp message)); + /* The png_set_ functions are for storing values in the png_info_struct. * Similarly, the png_get_ calls are used to read values from the * png_info_struct, either storing the parameters in the passed variables, or @@ -1202,7 +1212,7 @@ extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, #if defined(PNG_READ_sRGB_SUPPORTED) extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_byte *srgb_intent)); + png_infop info_ptr, png_bytep srgb_intent)); #endif /* PNG_READ_sRGB_SUPPORTED */ #if defined(PNG_READ_sRGB_SUPPORTED) || defined(PNG_WRITE_sRGB_SUPPORTED) diff --git a/pngconf.h b/pngconf.h index 70b77c175..81f21fcc6 100644 --- a/pngconf.h +++ b/pngconf.h @@ -21,7 +21,7 @@ * an IDAT chunk. Make this whatever size you feel is best for your * machine. One of these will be allocated per png_struct. When this * is full, it writes the data to the disk, and does some other - * calculations. Making this an extreamly small size will slow + * calculations. Making this an extremely small size will slow * the library down, but you may want to experiment to determine * where it becomes significant, if you are concerned with memory * usage. Note that zlib allocates at least 32Kb also. For readers, @@ -30,7 +30,9 @@ * it should not make much difference how big this is. */ +#ifndef PNG_ZBUF_SIZE #define PNG_ZBUF_SIZE 8192 +#endif /* If you are running on a machine where you cannot allocate more * than 64K of memory at once, uncomment this. While libpng will not @@ -54,8 +56,9 @@ /* We still need stdio.h for FILE even when PNG_NO_STDIO is defined. */ - +#ifndef PNG_NO_STDIO #include +#endif /* This macro protects us against machines that don't have function * prototypes (ie K&R style headers). If your compiler does not handle @@ -170,9 +173,15 @@ __dont__ include it again * a largish chunk of memory (32K), those who are not as concerned * with dithering quality can decrease some or all of these. */ +#ifndef PNG_DITHER_RED_BITS #define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS #define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS #define PNG_DITHER_BLUE_BITS 5 +#endif /* This controls how fine the gamma correction becomes when you * are only interested in 8 bits anyway. Increasing this value @@ -235,7 +244,6 @@ __dont__ include it again #ifdef PNG_READ_FULLY_SUPPORTED #define PNG_PROGRESSIVE_READ_SUPPORTED #define PNG_READ_OPT_PLTE_SUPPORTED -#define PNG_READ_INTERLACING_SUPPORTED #define PNG_READ_EXPAND_SUPPORTED #define PNG_READ_SHIFT_SUPPORTED #define PNG_READ_PACK_SUPPORTED @@ -252,6 +260,7 @@ __dont__ include it again #define PNG_READ_SWAP_ALPHA_SUPPORTED #define PNG_READ_STRIP_ALPHA_SUPPORTED #endif /* PNG_READ_FULLY_SUPPORTED */ +#define PNG_READ_INTERLACING_SUPPORTED #ifdef PNG_WRITE_FULLY_SUPPORTED #define PNG_WRITE_INTERLACING_SUPPORTED @@ -267,7 +276,9 @@ __dont__ include it again #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #endif /*PNG_WRITE_FULLY_SUPPORTED */ +#if !defined(PNG_NO_STDIO) #define PNG_TIME_RFC1152_SUPPORTED +#endif /* These are currently experimental features */ #undef PNG_READ_16_TO_8_ACCURATE_SHIFT_SUPPORTED /* very little testing */ diff --git a/pngerror.c b/pngerror.c index a800884df..77232d7e3 100644 --- a/pngerror.c +++ b/pngerror.c @@ -51,6 +51,60 @@ png_warning(png_structp png_ptr, png_const_charp message) png_default_warning(png_ptr, message); } +/* These utilities are used internally to build an error message which relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 41 || (c) > 122 || ((c) > 90 && (c) < 97)) +static const char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static void +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp message) +{ + int iout = 0, iin = 0; + + while (iin < 4) { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) { + buffer[iout++] = '['; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0xf]; + buffer[iout++] = ']'; + } else { + buffer[iout++] = c; + } + } + + if (message == NULL) + buffer[iout++] = 0; + else { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + strncpy(buffer+iout, message, 64); + buffer[iout+63] = 0; + } +} + +void +png_chunk_error(png_structp png_ptr, png_const_charp message) +{ + char msg[16+64]; + png_format_buffer(png_ptr, msg, message); + png_error(png_ptr, msg); +} + +void +png_chunk_warning(png_structp png_ptr, png_const_charp message) +{ + char msg[16+64]; + png_format_buffer(png_ptr, msg, message); + png_warning(png_ptr, msg); +} + /* This is the default error handling function. Note that replacements for * this function MUST NOT RETURN, or the program will likely crash. This * function is used by default, or if the program supplies NULL for the diff --git a/pngget.c b/pngget.c index 549c17ab9..f06dde0cb 100644 --- a/pngget.c +++ b/pngget.c @@ -111,13 +111,14 @@ png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) #if defined(PNG_READ_sRGB_SUPPORTED) png_uint_32 -png_get_sRGB(png_structp png_ptr, png_infop info_ptr, png_byte *file_srgb_intent) +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, png_bytep + file_srgb_intent) { if (info_ptr != NULL && info_ptr->valid & PNG_INFO_sRGB && - *file_srgb_intent != NULL) + file_srgb_intent != NULL) { png_debug1(1, "in %s retrieval function\n", "sRGB"); - *file_srgb_intent = (png_byte)info_ptr->srgb_intent; + *file_srgb_intent = info_ptr->srgb_intent; return (PNG_INFO_sRGB); } return (0); diff --git a/pngpread.c b/pngpread.c index a33351389..e892a6207 100644 --- a/pngpread.c +++ b/pngpread.c @@ -1056,10 +1056,7 @@ png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 len if (!(png_ptr->chunk_name[0] & 0x20)) { - char msg[40]; - - sprintf(msg, "Unknown critical chunk %s", png_ptr->chunk_name); - png_error(png_ptr, msg); + png_chunk_error(png_ptr, "unknown critical chunk"); } png_push_crc_skip(png_ptr, length); diff --git a/pngrio.c b/pngrio.c index 119f5826b..1f4865a42 100644 --- a/pngrio.c +++ b/pngrio.c @@ -33,6 +33,7 @@ png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) png_error(png_ptr, "Call to NULL read function"); } +#if !defined(PNG_NO_STDIO) /* This is the function which does the actual reading of data. If you are not reading from a standard C stream, you should create a replacement read_data function and use it at run time with png_set_read_fn(), rather @@ -103,6 +104,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) } } #endif +#endif /* This function allows the application to supply a new input function for libpng if standard C streams aren't being used. @@ -123,10 +125,14 @@ png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, { png_ptr->io_ptr = io_ptr; +#if !defined(PNG_NO_STDIO) if (read_data_fn != NULL) png_ptr->read_data_fn = read_data_fn; else png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif /* It is an error to write to a read device */ png_ptr->write_data_fn = NULL; diff --git a/pngrtran.c b/pngrtran.c index 28b041509..b72f0afec 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -986,11 +986,15 @@ png_do_read_transformations(png_structp png_ptr) #if !defined(PNG_USELESS_TESTS_SUPPORTED) if (png_ptr->row_buf == NULL) { +#if !defined(PNG_NO_STDIO) char msg[50]; sprintf(msg, "NULL row buffer for row %ld, pass %d", png_ptr->row_number, png_ptr->pass); png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif } #endif diff --git a/pngrutil.c b/pngrutil.c index bd225b6ca..4fe7a4d48 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -88,20 +88,16 @@ png_crc_finish(png_structp png_ptr, png_uint_32 skip) if (png_crc_error(png_ptr)) { - char msg[80]; - - sprintf(msg,"CRC error in %s", png_ptr->chunk_name); - if ((png_ptr->chunk_name[0] & 0x20 && /* Ancillary */ !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)) { - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "CRC error"); } else { - png_error(png_ptr, msg); + png_chunk_error(png_ptr, "CRC error"); } return 1; } @@ -307,10 +303,6 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ { - char msg[80]; - - sprintf(msg,"CRC error in %s", png_ptr->chunk_name); - /* If we don't want to use the data from an ancillary chunk, we have two options: an error abort, or a warning and we ignore the data in this chunk (which should be OK, since @@ -319,11 +311,11 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) { - png_error(png_ptr, msg); + png_chunk_error(png_ptr, "CRC error"); } else { - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "CRC error"); png_ptr->flags &= ~PNG_FLAG_FREE_PALETTE; png_free(png_ptr, palette); return; @@ -332,7 +324,7 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) /* Otherwise, we (optionally) emit a warning and use the chunk. */ else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) { - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "CRC error"); } } @@ -406,7 +398,7 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #if defined(PNG_READ_sRGB_SUPPORTED) if ((png_ptr->mode & PNG_HAVE_sRGB)) - if(igamma != 50000) + if(igamma != (png_uint_32)50000L) { png_warning(png_ptr, "Ignoring incorrect gAMA value when sRGB is also present"); @@ -416,7 +408,7 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #if defined(PNG_READ_sRGB_SUPPORTED) if (png_ptr->mode & PNG_HAVE_sRGB) - if(igamma != 50000) + if(igamma != (png_uint_32)50000L) { png_warning(png_ptr, "Ignoring incorrect gAMA value when sRGB is also present"); @@ -585,8 +577,8 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) val = png_get_uint_32(buf); blue_y = (float)val / (float)100000.0; - if (blue_x < 0 || blue_x > 0.8 || blue_y < 0 || blue_y > 0.8 || - blue_x + blue_y > 1.0) + if (blue_x < (float)0 || blue_x > (float)0.8 || blue_y < (float)0 || + blue_y > (float)0.8 || blue_x + blue_y > (float)1.0) { png_warning(png_ptr, "Invalid cHRM blue point"); png_crc_finish(png_ptr, 0); @@ -597,12 +589,20 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) return; #if defined(PNG_READ_sRGB_SUPPORTED) - if ((png_ptr->mode & PNG_HAVE_sRGB)) - if(white_x != .3127 || white_y != .329 || red_x != .64 || red_y != .33 || - green_x != .3 || green_y != .6 || blue_x != .15 || blue_y != .06) + if (png_ptr->mode & PNG_HAVE_sRGB) { - png_warning(png_ptr, - "Ignoring incorrect cHRM value when sRGB is also present"); + if (fabs(white_x - (float).3127) > (float).001 || + fabs(white_y - (float).3290) > (float).001 || + fabs( red_x - (float).6400) > (float).001 || + fabs( red_y - (float).3300) > (float).001 || + fabs(green_x - (float).3000) > (float).001 || + fabs(green_y - (float).6000) > (float).001 || + fabs( blue_x - (float).1500) > (float).001 || + fabs( blue_y - (float).6000) > (float).001) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } return; } #endif /* PNG_READ_sRGB_SUPPORTED */ @@ -664,17 +664,17 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) already defined gamma and chrm with values that are inconsistent with sRGB -- for now, just ignore them */ - file_gamma = 0.45; + file_gamma = (float)45000./(float)100000.; png_set_gAMA(png_ptr, info_ptr, file_gamma); - white_x = 0.3127; - white_y = 0.3290; - red_x = 0.6400; - red_y = 0.3300; - green_x = 0.3000; - green_y = 0.6000; - blue_x = 0.1500; - blue_y = 0.0600; + white_x = (float)31270./(float)100000.; + white_y = (float)32900./(float)100000.; + red_x = (float)64000./(float)100000.; + red_y = (float)33000./(float)100000.; + green_x = (float)30000./(float)100000.; + green_y = (float)60000./(float)100000.; + blue_x = (float)15000./(float)100000.; + blue_y = (float) 6000./(float)100000.; png_set_cHRM(png_ptr, info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); @@ -1342,10 +1342,14 @@ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) else /* if (comp_type >= PNG_TEXT_COMPRESSION_LAST) */ { png_size_t text_size; +#if !defined(PNG_NO_STDIO) char umsg[50]; sprintf(umsg, "Unknown zTXt compression type %d", comp_type); png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif /* Copy what we can of the error message into the text chunk */ text_size = (png_size_t)length - (text - key) - 1; @@ -1380,10 +1384,7 @@ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) if (!(png_ptr->chunk_name[0] & 0x20)) { - char msg[40]; - - sprintf(msg, "Unknown critical chunk %s", png_ptr->chunk_name); - png_error(png_ptr, msg); + png_chunk_error(png_ptr, "unknown critical chunk"); } if (png_ptr->mode & PNG_HAVE_IDAT) @@ -1407,11 +1408,7 @@ png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) { - char msg[45]; - - sprintf(msg, "Invalid chunk type 0x%02X 0x%02X 0x%02X 0x%02X", - chunk_name[0], chunk_name[1], chunk_name[2], chunk_name[3]); - png_error(png_ptr, msg); + png_chunk_error(png_ptr, "invalid chunk type"); } } diff --git a/pngset.c b/pngset.c index 2855207b2..2652b760a 100644 --- a/pngset.c +++ b/pngset.c @@ -238,19 +238,19 @@ png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, png_set_sRGB(png_ptr, info_ptr, intent); #if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED) - file_gamma = 0.45; + file_gamma = (float).45; png_set_gAMA(png_ptr, info_ptr, file_gamma); #endif #if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED) - white_x = 0.3127; - white_y = 0.3290; - red_x = 0.6400; - red_y = 0.3300; - green_x = 0.3000; - green_y = 0.6000; - blue_x = 0.1500; - blue_y = 0.0600; + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; png_set_cHRM(png_ptr, info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); diff --git a/pngtest.c b/pngtest.c index 1348f2d6b..cea4c8eda 100644 --- a/pngtest.c +++ b/pngtest.c @@ -44,19 +44,190 @@ /* #define STDERR stderr */ #define STDERR stdout /* for DOS */ -/* input and output filenames */ -#ifdef RISCOS -PNG_CONST char *inname = "pngtest_png"; -PNG_CONST char *outname = "pngout_png"; +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and */ +/* pngwio.c. They allow "don't include stdio" testing of the library. */ +#if defined(PNG_NO_STDIO) +/* This is the function which does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +static void +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + check = (png_size_t)fread(data, (png_size_t)1, length, + (FILE *)png_ptr->io_ptr); + + if (check != length) + { + png_error(png_ptr, "Read Error"); + } +} #else -PNG_CONST char *inname = "pngtest.png"; -PNG_CONST char *outname = "pngout.png"; +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + FILE *io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { + check = fread(n_data, 1, length, io_ptr); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); + err = fread(buf, (png_size_t)1, read, io_ptr); + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "read Error"); + } +} #endif -char inbuf[256], outbuf[256]; +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +static void +png_default_flush(png_structp png_ptr) +{ + FILE *io_ptr; + io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +} +#endif -int -main(int argc, char *argv[]) +/* This is the function which does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +static void +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr)); + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + FILE *io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { + check = fwrite(near_data, 1, length, io_ptr); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ + err = fwrite(buf, 1, written, io_ptr); + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} + +#endif + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void +png_default_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + if (png_ptr != NULL && png_ptr->error_ptr != NULL) + name = png_ptr->error_ptr; + fprintf(STDERR, "%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void +png_default_error(png_structp png_ptr, png_const_charp message) +{ + png_default_warning(png_ptr, message); + /* We can return because png_error calls the default handler which is + * actually ok in this case. */ +} +#endif +/* END of code to validate stdio-free compilation */ + +/* Test one file */ +int test(PNG_CONST char *inname, PNG_CONST char *outname) { FILE *fpin, *fpout; png_structp read_ptr, write_ptr; @@ -69,30 +240,11 @@ main(int argc, char *argv[]) #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; #endif + + char inbuf[256], outbuf[256]; + row_buf = (png_bytep)NULL; - fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); - - if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) - { - fprintf(STDERR, - "Warning: versions are different between png.h and png.c\n"); - fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); - fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); - } - - if (argc > 1) - inname = argv[1]; - - if (argc > 2) - outname = argv[2]; - - if (argc > 3) - { - fprintf(stderr, "usage: %s [infile.png] [outfile.png]\n", argv[0]); - exit(1); - } - if ((fpin = fopen(inname, "rb")) == NULL) { fprintf(STDERR, "Could not find input file %s\n", inname); @@ -109,8 +261,14 @@ main(int argc, char *argv[]) png_debug(0, "Allocating read and write structures\n"); read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); +#if defined(PNG_NO_STDIO) + png_set_error_fn(read_ptr, (png_voidp)inname, png_default_error, png_default_warning); +#endif write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); +#if defined(PNG_NO_STDIO) + png_set_error_fn(write_ptr, (png_voidp)inname, png_default_error, png_default_warning); +#endif png_debug(0, "Allocating read_info, write_info and end_info structures\n"); read_info_ptr = png_create_info_struct(read_ptr); write_info_ptr = png_create_info_struct(read_ptr); @@ -123,7 +281,7 @@ main(int argc, char *argv[]) if (setjmp(read_ptr->jmpbuf)) #endif { - fprintf(STDERR, "libpng read error\n"); + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); png_destroy_write_struct(&write_ptr, &write_info_ptr); fclose(fpin); @@ -139,7 +297,7 @@ main(int argc, char *argv[]) if (setjmp(write_ptr->jmpbuf)) #endif { - fprintf(STDERR, "libpng write error\n"); + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); png_destroy_write_struct(&write_ptr, &write_info_ptr); fclose(fpin); @@ -151,8 +309,18 @@ main(int argc, char *argv[]) png_memcpy(write_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf)); #endif png_debug(0, "Initializing input and output streams\n"); +#if !defined(PNG_NO_STDIO) png_init_io(read_ptr, fpin); png_init_io(write_ptr, fpout); +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data); + png_set_write_fn(write_ptr, (png_voidp)fpout, png_default_write_data, +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_default_flush); +#else + NULL); +#endif +#endif png_debug(0, "Reading info struct\n"); png_read_info(read_ptr, read_info_ptr); @@ -398,8 +566,70 @@ main(int argc, char *argv[]) fclose(fpin); fclose(fpout); - fprintf(STDERR, "libpng passes test\n"); return 0; } +/* input and output filenames */ +#ifdef RISCOS +PNG_CONST char *inname = "pngtest_png"; +PNG_CONST char *outname = "pngout_png"; +#else +PNG_CONST char *inname = "pngtest.png"; +PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + multiple = 1; + else + inname = argv[1]; + } + + if (!multiple && argc == 3) + outname = argv[2]; + + if (!multiple && argc > 3 || multiple && argc < 2) + { + fprintf(STDERR, "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple) + { + int i; + for (i=2; ioutput_flush_fn))(png_ptr); } +#if !defined(PNG_NO_STDIO) static void png_default_flush(png_structp png_ptr) { @@ -120,6 +123,7 @@ png_default_flush(png_structp png_ptr) fflush(io_ptr); } #endif +#endif /* This function allows the application to supply new output functions for libpng if standard C streams aren't being used. @@ -149,16 +153,24 @@ png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, { png_ptr->io_ptr = io_ptr; +#if !defined(PNG_NO_STDIO) if (write_data_fn != NULL) png_ptr->write_data_fn = write_data_fn; else png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif #if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) if (output_flush_fn != NULL) png_ptr->output_flush_fn = output_flush_fn; else png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif #endif /* PNG_WRITE_FLUSH_SUPPORTED */ /* It is an error to read while writing a png file */ diff --git a/pngwrite.c b/pngwrite.c index 584254dff..6a7b060e6 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -24,7 +24,9 @@ void png_write_info(png_structp png_ptr, png_infop info_ptr) { +#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED) int i; +#endif png_debug(1, "in png_write_info\n"); png_write_sig(png_ptr); /* write PNG signature */ @@ -146,7 +148,9 @@ png_write_end(png_structp png_ptr, png_infop info_ptr) /* see if user wants us to write information chunks */ if (info_ptr != NULL) { +#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED) int i; /* local index variable */ +#endif #if defined(PNG_WRITE_tIME_SUPPORTED) /* check to see if user has supplied a time chunk */ if (info_ptr->valid & PNG_INFO_tIME && @@ -210,9 +214,22 @@ png_convert_to_rfc1152(png_structp png_ptr, png_timep ptime) png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, 29*sizeof(char)); } +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 31, short_months[ptime->month], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*sizeof(char)); + } +#else sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000", - ptime->day % 31, short_months[ptime->month], ptime->year, - ptime->hour % 24, ptime->minute % 60, ptime->second % 61); + ptime->day % 31, short_months[ptime->month], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif return png_ptr->time_buffer; } #endif /* PNG_TIME_RFC1152_SUPPORTED */ @@ -732,7 +749,9 @@ png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, int num_weights, png_doublep filter_weights, png_doublep filter_costs) { +#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED) int i; +#endif png_debug(1, "in png_set_filter_heuristics\n"); if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) diff --git a/pngwutil.c b/pngwutil.c index bad95a912..b4575e4c0 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -593,10 +593,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) if (key == NULL || (key_len = png_strlen(key)) == 0) { - char msg[40]; - - sprintf(msg, "Zero length %s keyword", png_ptr->chunk_name); - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "zero length keyword"); return 0; } @@ -607,13 +604,16 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) /* Replace non-printing characters with a blank and print a warning */ for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) { - if (*kp < 0x20 || (*kp > 0x7E && *kp < 0xA1)) + if (*kp < 0x20 || (*kp > 0x7E && (png_byte)*kp < 0xA1)) { +#if !defined(PNG_NO_STDIO) char msg[40]; - sprintf(msg, "Invalid %s keyword character 0x%02X", - png_ptr->chunk_name, *kp); - png_warning(png_ptr, msg); + sprintf(msg, "invalid keyword character 0x%02X", *kp); + png_chunk_warning(png_ptr, msg); +#else + png_chunk_warning(png_ptr, "invalid character in keyword"); +#endif *dp = ' '; } else @@ -627,11 +627,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) kp = *new_key + key_len - 1; if (*kp == ' ') { - char msg[50]; - sprintf(msg, "Trailing spaces removed from %s keyword", - png_ptr->chunk_name); - - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "trailing spaces removed from keyword"); while (*kp == ' ') { @@ -644,11 +640,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) kp = *new_key; if (*kp == ' ') { - char msg[50]; - sprintf(msg, "Leading spaces removed from %s keyword", - png_ptr->chunk_name); - - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "leading spaces removed from keyword"); while (*kp == ' ') { @@ -681,19 +673,12 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) if (key_len == 0) { - char msg[40]; - - sprintf(msg, "Zero length %s keyword", png_ptr->chunk_name); - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "zero length keyword"); } if (key_len > 79) { - char msg[50]; - - sprintf(msg, "%s keyword length must be 1 - 79 characters", - png_ptr->chunk_name); - png_warning(png_ptr, msg); + png_chunk_warning(png_ptr, "keyword length must be 1 - 79 characters"); new_key[79] = '\0'; key_len = 79; } @@ -714,10 +699,7 @@ png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, png_debug(1, "in png_write_tEXt\n"); if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) { - char msg[40]; - - sprintf(msg, "Empty keyword in %s chunk", "tEXt"); - png_warning(png_ptr, msg); + png_warning(png_ptr, "Empty keyword in tEXt chunk"); return; } @@ -753,10 +735,7 @@ png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) { - char msg[40]; - - sprintf(msg, "Empty keyword in %s chunk", "zTXt"); - png_warning(png_ptr, msg); + png_warning(png_ptr, "Empty keyword in zTXt chunk"); return; } @@ -771,9 +750,13 @@ png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, if (compression >= PNG_TEXT_COMPRESSION_LAST) { +#if !defined(PNG_NO_STDIO) char msg[50]; sprintf(msg, "Unknown zTXt compression type %d", compression); png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif compression = PNG_TEXT_COMPRESSION_zTXt; } @@ -1389,10 +1372,10 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) * been specified by the application, and then writes the row out with the * chosen filter. */ -#define PNG_MAXSUM (~0x0UL >> 1) +#define PNG_MAXSUM (png_uint_32)(~0x0UL >> 1) #define PNG_HISHIFT 10 -#define PNG_LOMASK 0xffffL -#define PNG_HIMASK (~PNG_LOMASK >> PNG_HISHIFT) +#define PNG_LOMASK (png_uint_32)0xffffL +#define PNG_HIMASK (png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT) void png_write_find_filter(png_structp png_ptr, png_row_infop row_info) {