++ png_uint_32 num_frames_read; /* incremented after all image data of */
++ /* a frame is read */
++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
++ png_progressive_frame_ptr frame_info_fn; /* frame info read callback */
++ png_progressive_frame_ptr frame_end_fn; /* frame data read callback */
++#endif
++#endif
++
++#ifdef PNG_WRITE_APNG_SUPPORTED
++ png_uint_32 num_frames_to_write;
++ png_uint_32 num_frames_written;
++#endif
++
++/* For png_struct.apng_flags: */
++#define PNG_FIRST_FRAME_HIDDEN 0x0001
++
++/* dispose_op flags from inside fcTL */
++#define PNG_DISPOSE_OP_NONE 0x00
++#define PNG_DISPOSE_OP_BACKGROUND 0x01
++#define PNG_DISPOSE_OP_PREVIOUS 0x02
++
++/* blend_op flags from inside fcTL */
++#define PNG_BLEND_OP_SOURCE 0x00
++#define PNG_BLEND_OP_OVER 0x01
++#endif /* PNG_APNG_SUPPORTED */
++
+ /* New member added in libpng-1.0.25 and 1.2.17 */
+ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+ /* Storage for unknown chunk that the library doesn't recognize. */
+@@ -1840,6 +1894,18 @@
+ extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr,
+ png_bytepp image));
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++extern PNG_EXPORT (void,png_write_frame_head) PNGARG((png_structp png_ptr,
++ png_infop png_info, png_bytepp row_pointers,
++ png_uint_32 width, png_uint_32 height,
++ png_uint_32 x_offset, png_uint_32 y_offset,
++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++ png_byte blend_op));
++
++extern PNG_EXPORT (void,png_write_frame_tail) PNGARG((png_structp png_ptr,
++ png_infop png_info));
++#endif
++
+ /* Writes the end of the PNG file. */
+ extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+@@ -2093,6 +2159,11 @@
+ png_voidp progressive_ptr,
+ png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+ png_progressive_end_ptr end_fn));
++#ifdef PNG_READ_APNG_SUPPORTED
++extern PNG_EXPORT(void,png_set_progressive_frame_fn) PNGARG((png_structp png_ptr,
++ png_progressive_frame_ptr frame_info_fn,
++ png_progressive_frame_ptr frame_end_fn));
++#endif
+
+ /* Returns the user pointer associated with the push read functions */
+ extern PNG_EXPORT(png_voidp,png_get_progressive_ptr)
+@@ -2533,6 +2604,59 @@
+ #endif
+ #endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */
+
++#ifdef PNG_APNG_SUPPORTED
++extern PNG_EXPORT(png_uint_32,png_get_acTL) PNGARG((png_structp png_ptr,
++ png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays));
++extern PNG_EXPORT(png_uint_32,png_set_acTL) PNGARG((png_structp png_ptr,
++ png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays));
++extern PNG_EXPORT(png_uint_32,png_get_num_frames) PNGARG((png_structp png_ptr,
++ png_infop info_ptr));
++extern PNG_EXPORT(png_uint_32,png_get_num_plays)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++
++extern PNG_EXPORT(png_uint_32,png_get_next_frame_fcTL)
++ PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 *width,
++ png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset,
++ png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op,
++ png_byte *blend_op));
++extern PNG_EXPORT(png_uint_32,png_set_next_frame_fcTL)
++ PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 width,
++ png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset,
++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++ png_byte blend_op));
++extern PNG_EXPORT(void,png_ensure_fcTL_is_valid)
++ PNGARG((png_structp png_ptr,
++ png_uint_32 width, png_uint_32 height,
++ png_uint_32 x_offset, png_uint_32 y_offset,
++ png_uint_16 delay_num, png_uint_16 delay_den,
++ png_byte dispose_op, png_byte blend_op));
++extern PNG_EXPORT(png_uint_32,png_get_next_frame_width)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_uint_32,png_get_next_frame_height)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_uint_32,png_get_next_frame_x_offset)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_uint_32,png_get_next_frame_y_offset)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_uint_16,png_get_next_frame_delay_num)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_uint_16,png_get_next_frame_delay_den)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_byte,png_get_next_frame_dispose_op)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_byte,png_get_next_frame_blend_op)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_byte,png_get_first_frame_is_hidden)
++ PNGARG((png_structp png_ptr, png_infop info_ptr));
++extern PNG_EXPORT(png_uint_32,png_set_first_frame_is_hidden)
++ PNGARG((png_structp png_ptr, png_infop info_ptr, png_byte is_hidden));
++#endif /* PNG_APNG_SUPPORTED */
++
++#ifdef PNG_READ_APNG_SUPPORTED
++extern PNG_EXPORT(void,png_read_frame_head) PNGARG((png_structp png_ptr,
++ png_infop info_ptr));
++#endif
++
+ #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ /* Provide a list of chunks and how they are to be handled, if the built-in
+ handling or default unknown chunk handling is not desired. Any chunks not
+@@ -2897,6 +3021,10 @@
+ #define PNG_BACKGROUND_IS_GRAY 0x800
+ #define PNG_HAVE_PNG_SIGNATURE 0x1000
+ #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
++#ifdef PNG_APNG_SUPPORTED
++#define PNG_HAVE_acTL 0x4000
++#define PNG_HAVE_fcTL 0x8000L
++#endif
+
+ /* Flags for the transformations the PNG library does on the image data */
+ #define PNG_BGR 0x0001
+@@ -3039,6 +3167,11 @@
+ #define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'}
+ #define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'}
+ #define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'}
++#ifdef PNG_APNG_SUPPORTED
++#define PNG_acTL png_byte png_acTL[5] = { 97, 99, 84, 76, '\0'}
++#define PNG_fcTL png_byte png_fcTL[5] = {102, 99, 84, 76, '\0'}
++#define PNG_fdAT png_byte png_fdAT[5] = {102, 100, 65, 84, '\0'}
++#endif
+
+ #ifdef PNG_USE_GLOBAL_ARRAYS
+ PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5];
+@@ -3062,6 +3195,11 @@
+ PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5];
+ PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5];
+ PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5];
++#ifdef PNG_APNG_SUPPORTED
++PNG_EXPORT_VAR (png_byte FARDATA) png_acTL[5];
++PNG_EXPORT_VAR (png_byte FARDATA) png_fcTL[5];
++PNG_EXPORT_VAR (png_byte FARDATA) png_fdAT[5];
++#endif
+ #endif /* PNG_USE_GLOBAL_ARRAYS */
+
+ #if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+@@ -3344,6 +3482,17 @@
+ #endif
+ #endif
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++PNG_EXTERN void png_write_acTL PNGARG((png_structp png_ptr,
++ png_uint_32 num_frames, png_uint_32 num_plays));
++
++PNG_EXTERN void png_write_fcTL PNGARG((png_structp png_ptr,
++ png_uint_32 width, png_uint_32 height,
++ png_uint_32 x_offset, png_uint_32 y_offset,
++ png_uint_16 delay_num, png_uint_16 delay_den,
++ png_byte dispose_op, png_byte blend_op));
++#endif
++
+ /* Called when finished processing a row of data */
+ PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)) PNG_PRIVATE;
+
+@@ -3396,6 +3545,20 @@
+ PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr,
+ png_infop info_ptr)) PNG_PRIVATE;
+
++#ifdef PNG_READ_APNG_SUPPORTED
++/* Private, reset some things to become ready for reading next frame */
++PNG_EXTERN void png_read_reset PNGARG((png_structp png_ptr));
++PNG_EXTERN void png_read_reinit PNGARG((png_structp png_ptr,
++ png_infop info_ptr));
++PNG_EXTERN void png_progressive_read_reset PNGARG((png_structp png_ptr));
++#endif
++#ifdef PNG_WRITE_APNG_SUPPORTED
++/* Private, reset some things to become ready for writing next frame */
++PNG_EXTERN void png_write_reset PNGARG((png_structp png_ptr));
++PNG_EXTERN void png_write_reinit PNGARG((png_structp png_ptr,
++ png_infop info_ptr, png_uint_32 width, png_uint_32 height));
++#endif
++
+ /* These are the functions that do the transformations */
+ #ifdef PNG_READ_FILLER_SUPPORTED
+ PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info,
+@@ -3619,6 +3782,18 @@
+ png_uint_32 length)) PNG_PRIVATE;
+ #endif
+
++#ifdef PNG_READ_APNG_SUPPORTED
++PNG_EXTERN void png_handle_acTL PNGARG((png_structp png_ptr, png_infop info_ptr,
++ png_uint_32 length));
++PNG_EXTERN void png_handle_fcTL PNGARG((png_structp png_ptr, png_infop info_ptr,
++ png_uint_32 length));
++PNG_EXTERN void png_have_info PNGARG((png_structp png_ptr, png_infop info_ptr));
++PNG_EXTERN void png_handle_fdAT PNGARG((png_structp png_ptr, png_infop info_ptr,
++ png_uint_32 length));
++PNG_EXTERN void png_ensure_sequence_number PNGARG((png_structp png_ptr,
++ png_uint_32 length));
++#endif
++
+ PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE;
+
+Index: pngwrite.c
+===================================================================
+--- pngwrite.c
++++ pngwrite.c
+@@ -56,6 +56,10 @@
+ /* The rest of these check to see if the valid field has the appropriate
+ * flag set, and if it does, writes the chunk.
+ */
++#ifdef PNG_WRITE_APNG_SUPPORTED
++ if (info_ptr->valid & PNG_INFO_acTL)
++ png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays);
++#endif
+ #ifdef PNG_WRITE_gAMA_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_gAMA)
+ {
+@@ -318,6 +322,10 @@
+ return;
+ if (!(png_ptr->mode & PNG_HAVE_IDAT))
+ png_error(png_ptr, "No IDATs written into file");
++#ifdef PNG_WRITE_APNG_SUPPORTED
++ if (png_ptr->num_frames_written != png_ptr->num_frames_to_write)
++ png_error(png_ptr, "Not enough frames written");
++#endif
+
+ /* See if user wants us to write information chunks */
+ if (info_ptr != NULL)
+@@ -1582,4 +1590,39 @@
+ params = params;
+ }
+ #endif
++
++#ifdef PNG_WRITE_APNG_SUPPORTED
++void PNGAPI
++png_write_frame_head(png_structp png_ptr, png_infop info_ptr,
++ png_bytepp row_pointers, png_uint_32 width, png_uint_32 height,
++ png_uint_32 x_offset, png_uint_32 y_offset,
++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++ png_byte blend_op)
++{
++ png_debug(1, "in png_write_frame_head");
++
++ /* there is a chance this has been set after png_write_info was called,
++ * so it would be set but not written. is there a way to be sure? */
++ if (!(info_ptr->valid & PNG_INFO_acTL))
++ png_error(png_ptr, "png_write_frame_head(): acTL not set");
++
++ png_write_reset(png_ptr);
++
++ png_write_reinit(png_ptr, info_ptr, width, height);
++
++ if ( !(png_ptr->num_frames_written == 0 &&
++ (png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) ) )
++ png_write_fcTL(png_ptr, width, height, x_offset, y_offset,
++ delay_num, delay_den, dispose_op, blend_op);
++}
++
++void PNGAPI
++png_write_frame_tail(png_structp png_ptr, png_infop png_info)
++{
++ png_debug(1, "in png_write_frame_tail");
++
++ png_ptr->num_frames_written++;
++}
++#endif /* PNG_WRITE_APNG_SUPPORTED */
++
+ #endif /* PNG_WRITE_SUPPORTED */
+Index: pngconf.h
+===================================================================
+--- pngconf.h
++++ pngconf.h
+@@ -944,6 +944,10 @@
+ # define PNG_NO_READ_tEXt
+ # define PNG_NO_READ_zTXt
+ #endif
++#ifndef PNG_NO_READ_APNG
++# define PNG_READ_APNG_SUPPORTED
++# define PNG_APNG_SUPPORTED
++#endif
+ #ifndef PNG_NO_READ_bKGD
+ # define PNG_READ_bKGD_SUPPORTED
+ # define PNG_bKGD_SUPPORTED
+@@ -1170,6 +1174,14 @@
+ # define PNG_TEXT_SUPPORTED
+ # endif
+ #endif
++#ifndef PNG_NO_WRITE_APNG
++# ifndef PNG_WRITE_APNG_SUPPORTED
++# define PNG_WRITE_APNG_SUPPORTED
++# endif
++# ifndef PNG_APNG_SUPPORTED
++# define PNG_APNG_SUPPORTED
++# endif
++#endif
+
+ #ifdef PNG_WRITE_tIME_SUPPORTED
+ # ifndef PNG_NO_CONVERT_tIME
+Index: pngpread.c
+===================================================================
+--- pngpread.c
++++ pngpread.c
+@@ -206,6 +206,11 @@
+ #ifdef PNG_READ_zTXt_SUPPORTED
+ PNG_CONST PNG_zTXt;
+ #endif
++#ifdef PNG_READ_APNG_SUPPORTED
++ PNG_CONST PNG_acTL;
++ PNG_CONST PNG_fcTL;
++ PNG_CONST PNG_fdAT;
++#endif
+ #endif /* PNG_USE_LOCAL_ARRAYS */
+
+ /* First we make sure we have enough data for the 4 byte chunk name
+@@ -232,6 +237,103 @@
+ png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+ }
+
++#ifdef PNG_READ_APNG_SUPPORTED
++ if (png_ptr->num_frames_read > 0 &&
++ png_ptr->num_frames_read < info_ptr->num_frames)
++ {
++ if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
++ {
++ /* Discard trailing IDATs for the first frame */
++ if (png_ptr->mode & PNG_HAVE_fcTL || png_ptr->num_frames_read > 1)
++ png_error(png_ptr, "out of place IDAT");
++
++ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++ {
++ png_push_save_buffer(png_ptr);
++ return;
++ }
++ png_push_crc_skip(png_ptr, png_ptr->push_length);
++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
++ return;
++ }
++ else if (!png_memcmp(png_ptr->chunk_name, png_fdAT, 4))
++ {
++ if (png_ptr->buffer_size < 4)