diff --git a/png.c b/png.c index 621e4a4ad3..df7a662699 100644 --- a/png.c +++ b/png.c @@ -669,7 +669,7 @@ png_get_io_ptr(png_const_structrp png_ptr) return png_ptr->io_ptr; } -#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) # ifdef PNG_STDIO_SUPPORTED /* Initialize the default input/output functions for the PNG file. If you * use your own read or write routines, you can call either png_set_read_fn() @@ -677,18 +677,91 @@ png_get_io_ptr(png_const_structrp png_ptr) * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a * function of your own because "FILE *" isn't necessarily available. */ -void PNGAPI -png_init_io(png_structrp png_ptr, png_FILE_p fp) +void PNGAPI ( +png_init_io)(png_structrp png_ptr, FILE *fp, + size_t (*fread)(void *ptr, size_t size, size_t nmemb, FILE*), + size_t (*fwrite)(const void *ptr, size_t size, size_t nmemb, FILE*), + int (*fflush)(FILE*)) { png_debug(1, "in png_init_io"); if (png_ptr == NULL) return; - png_ptr->io_ptr = (png_voidp)fp; -} + /* If SEQUENTIAL_READ is not supported and this IS a read png_struct + * png_init_io cannot be used (or, rather, it will not work). Detect this + * early. + */ +# ifndef PNG_SEQUENTIAL_READ_SUPPORTED + /* Read must be done using the progressive reader therefore: */ + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + { + png_app_error(png_ptr, "API: IO cannot be set with progressive read"); + return; + } +# endif /* !SEQUENTIAL_READ */ + + /* First clear out any read/write functionality set by the caller. */ + png_ptr->io_ptr = NULL; +# ifdef PNG_READ_SUPPORTED + png_ptr->read_data_fn = NULL; +# endif /* READ */ +# ifdef PNG_WRITE_SUPPORTED + png_ptr->write_data_fn = NULL; +# endif /* WRITE */ +# ifdef PNG_WRITE_FLUSH_SUPPPORTED + png_ptr->output_flush_fn = NULL; # endif + /* Set up the stdio based read or write functions as appropriate. */ +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + { + png_ptr->fread = fread; + png_ptr->read_data_fn = png_stdio_read; + } + else + { + png_ptr->fread = NULL; + } +# else /* !SEQUENTIAL_READ */ + PNG_UNUSED(fread) +# endif /* !SEQUENTIAL_READ */ + +# ifdef PNG_WRITE_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + { + png_ptr->fwrite = fwrite; + png_ptr->write_data_fn = png_stdio_write; + } + else + { + png_ptr->fwrite = NULL; + } +# else /* !WRITE */ + PNG_UNUSED(fwrite) +# endif /* !WRITE */ + +# ifdef PNG_WRITE_FLUSH_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + { + png_ptr->fflush = fflush; + png_ptr->output_flush_fn = png_stdio_flush; + } + else + { + png_ptr->fflush = NULL; + } +# else /* !WRITE_FLUSH */ + PNG_UNUSED(fflush) +# endif /* !WRITE_FLUSH */ + + png_ptr->stdio_ptr = fp; +} +# endif /* STDIO */ +#endif /* SEQUENTIAL_READ || WRITE */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) # ifdef PNG_SAVE_INT_32_SUPPORTED /* PNG signed integers are saved in 32-bit 2's complement format. ANSI C-90 * defines a cast of a signed integer to an unsigned integer either to preserve @@ -4556,15 +4629,14 @@ png_image_free_function(png_voidp argument) /* First free any data held in the control structure. */ # ifdef PNG_STDIO_SUPPORTED - if (cp->owned_file != 0) + if (cp->io_file != NULL) { - FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); - cp->owned_file = 0; + FILE *fp = cp->io_file; /* Ignore errors here. */ if (fp != NULL) { - cp->png_ptr->io_ptr = NULL; + cp->io_file = NULL; (void)fclose(fp); } } diff --git a/png.h b/png.h index a30fb99e02..eedc1c25e6 100644 --- a/png.h +++ b/png.h @@ -1541,9 +1541,17 @@ PNG_REMOVED(209, void, png_set_filter_heuristics_fixed, */ #ifdef PNG_STDIO_SUPPORTED -/* Initialize the input/output for the PNG file to the default functions. */ -PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); -#endif +# if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Initialize the input/output for the PNG file to use stdio. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp, + size_t (*fread)(void *ptr, size_t size, size_t nmemb, FILE*), + size_t (*fwrite)(const void *ptr, size_t size, size_t nmemb, FILE*), + int (*fflush)(FILE*))); + +#define png_init_io(png_ptr, fp)\ + ((png_init_io)(png_ptr, fp, fread, fwrite, fflush)) +#endif /* SEQUENTIAL_READ || WRITE */ +#endif /* STDIO */ /* Replace the (error and abort), and warning functions with user * supplied functions. If no messages are to be printed you must still @@ -2977,7 +2985,10 @@ PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, */ PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, - FILE* file)); + FILE* file, size_t (*fread)(void *ptr, size_t size, size_t nmemb, FILE*))); +#define png_image_begin_read_from_stdio(image, file)\ + ((png_image_begin_read_from_stdio)(image, file, fread)) + /* The PNG header is read from the stdio FILE object. */ #endif /* STDIO */ @@ -3051,8 +3062,14 @@ PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, int convert_to_8_bit, const void *buffer, png_int_32 row_stride, - const void *colormap)); + const void *colormap, + size_t (*fwrite)(const void *ptr, size_t size, size_t nmemb, FILE*), + int (*fflush)(FILE*))); /* Write the image to the given (FILE*). */ + +#define png_image_write_to_stdio(png_ptr, fp, cmap, cvt_to_8, buffer, stride)\ + ((png_image_write_to_stdio)(png_ptr, fp, cmap, cvt_to_8, buffer, stride,\ + fwrite, fflush)) #endif /* SIMPLIFIED_WRITE_STDIO */ /* With all write APIs if image is in one of the linear formats with 16-bit diff --git a/pngpriv.h b/pngpriv.h index 102ccbae2c..cd4d50f4e3 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -849,23 +849,27 @@ PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); * PNGCBAPI at 1.5.0 */ -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, - png_bytep data, size_t length),PNG_EMPTY); - #ifdef PNG_PROGRESSIVE_READ_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, png_bytep buffer, size_t length),PNG_EMPTY); -#endif +#endif /* PROGRESSIVE_READ */ -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, +#ifdef PNG_STDIO_SUPPORTED +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_stdio_read,(png_structp png_ptr, + png_bytep data, size_t length),PNG_EMPTY); +# endif /* SEQUENTIAL_READ */ + +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_stdio_write,(png_structp png_ptr, png_bytep data, size_t length),PNG_EMPTY); +#endif /* WRITE */ #ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_STDIO_SUPPORTED -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr), +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_stdio_flush,(png_structp png_ptr), PNG_EMPTY); -# endif -#endif +#endif /* WRITE_FLUSH */ +#endif /* STDIO */ /* Reset the CRC variable */ PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); @@ -1815,8 +1819,11 @@ typedef struct png_control png_const_bytep memory; /* Memory buffer. */ size_t size; /* Size of the memory buffer. */ +# ifdef PNG_STDIO_SUPPORTED + FILE *io_file; /* FILE* opened by us, not user/app */ +# endif + unsigned int for_write :1; /* Otherwise it is a read structure */ - unsigned int owned_file :1; /* We own the file in io_ptr */ } png_control; /* Return the pointer to the jmp_buf from a png_control: necessary because C diff --git a/pngread.c b/pngread.c index 3ec5623a00..3bec07639b 100644 --- a/pngread.c +++ b/pngread.c @@ -75,12 +75,6 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, if (png_ptr->target_state != 0U) png_set_option(png_ptr, PNG_TARGET_SPECIFIC_CODE, 1); # endif - - /* TODO: delay this, it can be done in png_init_io (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_read_fn(png_ptr, NULL, NULL); } return png_ptr; @@ -1486,21 +1480,38 @@ png_image_read_header(png_voidp argument) } #ifdef PNG_STDIO_SUPPORTED -int PNGAPI -png_image_begin_read_from_stdio(png_imagep image, FILE* file) +typedef struct +{ + png_structrp png_ptr; + FILE *file; + size_t (*fread)(void *ptr, size_t size, size_t nmemb, FILE*); +} stdio_read_setup; + +static int +setup_stdio_for_read(png_voidp parm) +{ + stdio_read_setup *read = png_voidcast(stdio_read_setup*, parm); + (png_init_io)(read->png_ptr, read->file, read->fread, NULL, NULL); + return 1; +} + +int PNGAPI ( +png_image_begin_read_from_stdio)(png_imagep image, FILE* file, + size_t (*fread)(void *ptr, size_t size, size_t nmemb, FILE*)) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { if (file != NULL) { - if (png_image_read_init(image) != 0) + if (png_image_read_init(image)) { - /* This is slightly evil, but png_init_io doesn't do anything other - * than this and we haven't changed the standard IO functions so - * this saves a 'safe' function. - */ - image->opaque->png_ptr->io_ptr = file; - return png_safe_execute(image, png_image_read_header, image); + stdio_read_setup parm; + parm.png_ptr = image->opaque->png_ptr; + parm.file = file; + parm.fread = fread; + + return png_safe_execute(image, setup_stdio_for_read, &parm) && + png_safe_execute(image, png_image_read_header, image); } } @@ -1523,20 +1534,13 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name) { if (file_name != NULL) { - FILE *fp = fopen(file_name, "rb"); + /* The file is stored in png_control::io_file and this means that it + * will passed to fclose on error: + */ + FILE* fp = image->opaque->io_file = fopen(file_name, "rb"); if (fp != NULL) - { - if (png_image_read_init(image) != 0) - { - image->opaque->png_ptr->io_ptr = fp; - image->opaque->owned_file = 1; - return png_safe_execute(image, png_image_read_header, image); - } - - /* Clean up: just the opened file. */ - (void)fclose(fp); - } + return png_image_begin_read_from_stdio(image, fp); else return png_image_error(image, strerror(errno)); diff --git a/pngrio.c b/pngrio.c index 3b137f275f..be7abac328 100644 --- a/pngrio.c +++ b/pngrio.c @@ -27,93 +27,107 @@ * buffering if you are using unbuffered reads. This should never be asked * to read more than 64K on a 16-bit machine. */ +static int +invalid(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return 1; + + if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0) + { + png_app_error(png_ptr, "API: invalid in write"); + return 1; + } + + return 0; +} + void /* PRIVATE */ png_read_data(png_structrp png_ptr, png_bytep data, size_t length) { - png_debug1(4, "reading %d bytes", (int)length); + if (invalid(png_ptr)) + return; if (png_ptr->read_data_fn != NULL) - (*(png_ptr->read_data_fn))(png_ptr, data, length); + png_ptr->read_data_fn(png_ptr, data, length); else - png_error(png_ptr, "Call to NULL read function"); + png_app_error(png_ptr, "API: no read function"); } -#ifdef PNG_STDIO_SUPPORTED +#if defined(PNG_STDIO_SUPPORTED) && defined (PNG_SEQUENTIAL_READ_SUPPORTED) /* This is the function that 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. */ -void PNGCBAPI -png_default_read_data(png_structp png_ptr, png_bytep data, size_t length) +static int +invalid_stdio(png_const_structrp png_ptr) { - size_t check; + if (invalid(png_ptr)) + return 1; - if (png_ptr == NULL) - return; + if (png_ptr->stdio_ptr == NULL) + { + png_app_error(png_ptr, "API: C stdio: no (FILE*)"); + return 1; + } - /* fread() returns 0 on error, so it is OK to store this in a size_t - * instead of an int, which is what fread() actually returns. - */ - check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); + return 0; +} - if (check != length) - png_error(png_ptr, "Read Error"); +void PNGCBAPI +png_stdio_read(png_structp png_ptr, png_bytep data, size_t length) +{ + if (!invalid_stdio(png_ptr)) + { + if (png_ptr->fread != NULL) + { + size_t read = + (png_ptr->fread)(data, 1U, length, png_ptr->stdio_ptr); + + if (read != length) + png_error(png_ptr, "C stdio: read error"); + } + else /* This should be impossible: */ + png_error(png_ptr, "API(internal): missing fread"); + } } -#endif +#endif /* STDIO && SEQUENTIAL_READ */ -/* This function allows the application to supply a new input function - * for libpng if standard C streams aren't being used. - * - * This function takes as its arguments: - * - * png_ptr - pointer to a png input data structure - * - * io_ptr - pointer to user supplied structure containing info about - * the input functions. May be NULL. - * - * read_data_fn - pointer to a new input function that takes as its - * arguments a pointer to a png_struct, a pointer to - * a location where input data can be stored, and a 32-bit - * unsigned int that is the number of bytes to be read. - * To exit and output any fatal error messages the new write - * function should call png_error(png_ptr, "Error msg"). - * May be NULL, in which case libpng's default function will - * be used. +/* This API is an alternative to png_init_io (see png.c) which must be used if + * the caller of libpng is using something other than FILE* as the input device + * when reading a PNG. */ void PNGAPI -png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, - png_rw_ptr read_data_fn) +png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn) { - if (png_ptr == NULL) + if (invalid(png_ptr)) return; - png_ptr->io_ptr = io_ptr; +# ifdef PNG_STDIO_SUPPORTED + /* Ensure none of the stdio settings remain set: */ + png_ptr->stdio_ptr = NULL; -#ifdef PNG_STDIO_SUPPORTED - if (read_data_fn != NULL) - png_ptr->read_data_fn = read_data_fn; +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + png_ptr->fread = NULL; +# endif /* SEQUENTIAL_READ */ +# ifdef PNG_WRITE_SUPPORTED + png_ptr->fwrite = NULL; +# endif /* WRITE */ +# ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->fflush = NULL; +# endif /* WRITE_FLUSH */ +# endif /* STDIO */ - else - png_ptr->read_data_fn = png_default_read_data; -#else + png_ptr->io_ptr = io_ptr; png_ptr->read_data_fn = read_data_fn; -#endif -#ifdef PNG_WRITE_SUPPORTED - /* It is an error to write to a read device */ - if (png_ptr->write_data_fn != NULL) - { +# ifdef PNG_WRITE_SUPPORTED png_ptr->write_data_fn = NULL; - png_warning(png_ptr, - "Can't set both read_data_fn and write_data_fn in the" - " same structure"); - } -#endif - -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_ptr->output_flush_fn = NULL; -#endif +# endif /* WRITE */ +# ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->output_flush_fn = NULL; +# endif /* WRITE_FLUSH */ } #endif /* READ */ diff --git a/pngstruct.h b/pngstruct.h index e77d69bc2b..fe36f5ee5b 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -155,6 +155,18 @@ struct png_struct_def png_rw_ptr write_data_fn; /* function for writing output data */ png_rw_ptr read_data_fn; /* function for reading input data */ png_voidp io_ptr; /* ptr to application struct for I/O functions */ +#ifdef PNG_STDIO_SUPPORTED + FILE *stdio_ptr; +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + size_t (*fread)(void *ptr, size_t size, size_t nmemb, FILE*); +# endif /* SEQUENTIAL_READ */ +# ifdef PNG_WRITE_SUPPORTED + size_t (*fwrite)(const void *ptr, size_t size, size_t nmemb, FILE*); +# ifdef PNG_WRITE_FLUSH_SUPPORTED + int (*fflush)(FILE*); +# endif /* WRITE_FLUSH */ +# endif /* WRITE */ +#endif /* STDIO */ #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED png_user_transform_ptr read_user_transform_fn; /* user read transform */ diff --git a/pngwio.c b/pngwio.c index 38c9c006cb..c965df412c 100644 --- a/pngwio.c +++ b/pngwio.c @@ -27,141 +27,140 @@ * buffering if you are using unbuffered writes. This should never be asked * to write more than 64K on a 16-bit machine. */ +static int +invalid(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return 1; + + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0) + { + png_app_error(png_ptr, "API: invalid in read"); + return 1; + } + + return 0; +} void /* PRIVATE */ png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length) { + if (invalid(png_ptr)) + return; + /* NOTE: write_data_fn must not change the buffer! */ if (png_ptr->write_data_fn != NULL ) - (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), - length); + png_ptr->write_data_fn(png_ptr, png_constcast(png_bytep, data), length); else - png_error(png_ptr, "Call to NULL write function"); + png_app_error(png_ptr, "API: no write function"); } #ifdef PNG_STDIO_SUPPORTED -/* This is the function that 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. - */ -void PNGCBAPI -png_default_write_data(png_structp png_ptr, png_bytep data, size_t length) +static int +invalid_stdio(png_const_structrp png_ptr) { - size_t check; + if (invalid(png_ptr)) + return 1; - if (png_ptr == NULL) - return; + if (png_ptr->stdio_ptr == NULL) + { + png_app_error(png_ptr, "API: C stdio: no (FILE*)"); + return 1; + } - check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + return 0; +} - if (check != length) - png_error(png_ptr, "Write Error"); +/* A C stream (FILE*) implementation of png_write_data. */ +void PNGCBAPI +png_stdio_write(png_structp png_ptr, png_bytep data, size_t length) +{ + if (!invalid_stdio(png_ptr)) + { + if (png_ptr->fwrite != NULL) + { + size_t written = + (png_ptr->fwrite)(data, 1U, length, png_ptr->stdio_ptr); + + if (written != length) + png_error(png_ptr, "C stdio: write error"); + } + else /* This should be impossible: */ + png_error(png_ptr, "API(internal): missing fwrite"); + } } -#endif +#endif /* STDIO */ -/* This function is called to output any data pending writing (normally - * to disk). After png_flush is called, there should be no data pending - * writing in any buffers. - */ #ifdef PNG_WRITE_FLUSH_SUPPORTED +/* If 'output_flush_fn' has been set it is called here to flush any pending + * data. If it is not set this does nothing apart from validating that this is + * a write png_struct. + */ void /* PRIVATE */ png_flush(png_structrp png_ptr) { - if (png_ptr->output_flush_fn != NULL) + if (!invalid(png_ptr) && png_ptr->output_flush_fn != NULL) (*(png_ptr->output_flush_fn))(png_ptr); } # ifdef PNG_STDIO_SUPPORTED void PNGCBAPI -png_default_flush(png_structp png_ptr) +png_stdio_flush(png_structp png_ptr) { - png_FILE_p io_ptr; - - if (png_ptr == NULL) - return; - - io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); - fflush(io_ptr); + if (!invalid_stdio(png_ptr)) + { + if (png_ptr->fflush != NULL) + { + if ((png_ptr->fflush)(png_ptr->stdio_ptr)) + png_error(png_ptr, "C stdio: write error"); + } + else /* This should be impossible */ + png_error(png_ptr, "API(internal): missing fflush"); + } } -# endif -#endif +# endif /* STDIO */ +#endif /* WRITE_FLUSH */ -/* This function allows the application to supply new output functions for - * libpng if standard C streams aren't being used. +/* This API is an alternative to png_init_io (see png.c) which must be used if + * the caller of libpng is using something other than FILE* as the output device + * when writing a PNG. * - * This function takes as its arguments: - * png_ptr - pointer to a png output data structure - * io_ptr - pointer to user supplied structure containing info about - * the output functions. May be NULL. - * write_data_fn - pointer to a new output function that takes as its - * arguments a pointer to a png_struct, a pointer to - * data to be written, and a 32-bit unsigned int that is - * the number of bytes to be written. The new write - * function should call png_error(png_ptr, "Error msg") - * to exit and output any fatal error messages. May be - * NULL, in which case libpng's default function will - * be used. - * flush_data_fn - pointer to a new flush function that takes as its - * arguments a pointer to a png_struct. After a call to - * the flush function, there should be no data in any buffers - * or pending transmission. If the output method doesn't do - * any buffering of output, a function prototype must still be - * supplied although it doesn't have to do anything. If - * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile - * time, output_flush_fn will be ignored, although it must be - * supplied for compatibility. May be NULL, in which case - * libpng's default function will be used, if - * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not - * a good idea if io_ptr does not point to a standard - * *FILE structure. + * If 'output_flush_fn' is NULL and WRITE_FLUSH is supported no ffflush + * operations will be done. The caller must ensure any buffers used by the + * caller's 'write_data_fn' are flushed appropriately. */ void PNGAPI png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) { - if (png_ptr == NULL) + if (invalid(png_ptr)) return; - png_ptr->io_ptr = io_ptr; - -#ifdef PNG_STDIO_SUPPORTED - if (write_data_fn != NULL) - png_ptr->write_data_fn = write_data_fn; +# ifdef PNG_STDIO_SUPPORTED + /* Ensure none of the stdio settings remain set: */ + png_ptr->stdio_ptr = NULL; +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + png_ptr->fread = NULL; +# endif + png_ptr->fwrite = NULL; +# ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->fflush = NULL; +# endif +# endif /* STDIO */ + +# ifdef PNG_READ_SUPPORTED + png_ptr->read_data_fn = NULL; +# endif - else - png_ptr->write_data_fn = png_default_write_data; -#else png_ptr->write_data_fn = write_data_fn; -#endif -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_STDIO_SUPPORTED - - if (output_flush_fn != NULL) +# ifdef PNG_WRITE_FLUSH_SUPPORTED 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 -#else - PNG_UNUSED(output_flush_fn) -#endif /* WRITE_FLUSH */ - -#ifdef PNG_READ_SUPPORTED - /* It is an error to read while writing a png file */ - if (png_ptr->read_data_fn != NULL) - { - png_ptr->read_data_fn = NULL; + PNG_UNUSED(output_flush_fn) +# endif /* WRITE_FLUSH */ - png_warning(png_ptr, - "Can't set both read_data_fn and write_data_fn in the" - " same structure"); - } -#endif + png_ptr->io_ptr = io_ptr; } #endif /* WRITE */ diff --git a/pngwrite.c b/pngwrite.c index 18b29e38a4..d660230fcd 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -582,12 +582,6 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, #if PNG_RELEASE_BUILD png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; #endif - - /* TODO: delay this, it can be done in png_init_io() (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_write_fn(png_ptr, NULL, NULL, NULL); } return png_ptr; @@ -2276,9 +2270,28 @@ png_image_write_to_memory(png_imagep image, void *memory, } #ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED -int PNGAPI -png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, - const void *buffer, png_int_32 row_stride, const void *colormap) +typedef struct +{ + png_structrp png_ptr; + FILE *file; + size_t (*fwrite)(const void *ptr, size_t size, size_t nmemb, FILE*); + int (*fflush)(FILE*); +} stdio_write_setup; + +static int +setup_stdio_for_write(png_voidp parm) +{ + stdio_write_setup *write = png_voidcast(stdio_write_setup*, parm); + (png_init_io)( + write->png_ptr, write->file, NULL, write->fwrite, write->fflush); + return 1; +} + +int PNGAPI ( +png_image_write_to_stdio)(png_imagep image, FILE *file, int convert_to_8bit, + const void *buffer, png_int_32 row_stride, const void *colormap, + size_t (*fwrite)(const void *ptr, size_t size, size_t nmemb, FILE*), + int (*fflush)(FILE*)) { /* Write the image to the given (FILE*). */ if (image != NULL && image->version == PNG_IMAGE_VERSION) @@ -2288,13 +2301,13 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, if (png_image_write_init(image) != 0) { png_image_write_control display; + stdio_write_setup write; int result; - /* This is slightly evil, but png_init_io doesn't do anything other - * than this and we haven't changed the standard IO functions so - * this saves a 'safe' function. - */ - image->opaque->png_ptr->io_ptr = file; + write.png_ptr = image->opaque->png_ptr; + write.file = file; + write.fwrite = fwrite; + write.fflush = fflush; memset(&display, 0, (sizeof display)); display.image = image; @@ -2303,7 +2316,8 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, display.colormap = colormap; display.convert_to_8bit = convert_to_8bit; - result = png_safe_execute(image, png_image_write_main, &display); + result = png_safe_execute(image, setup_stdio_for_write, &write) && + png_safe_execute(image, png_image_write_main, &display); png_image_free(image); return result; } @@ -2335,14 +2349,15 @@ png_image_write_to_file(png_imagep image, const char *file_name, { if (file_name != NULL && buffer != NULL) { - FILE *fp = fopen(file_name, "wb"); + FILE *fp = image->opaque->io_file = fopen(file_name, "wb"); if (fp != NULL) { if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, - row_stride, colormap) != 0) + row_stride, colormap)) { int error; /* from fflush/fclose */ + image->opaque->io_file = NULL; /* Make sure the file is flushed correctly. */ if (fflush(fp) == 0 && ferror(fp) == 0) @@ -2368,8 +2383,7 @@ png_image_write_to_file(png_imagep image, const char *file_name, else { - /* Clean up: just the opened file. */ - (void)fclose(fp); + /* Clean up: just remove the file that was opened. */ (void)remove(file_name); return 0; }