1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libpng/apng.patch Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1590 @@ 1.4 +Index: pngread.c 1.5 +=================================================================== 1.6 +--- pngread.c 1.7 ++++ pngread.c 1.8 +@@ -158,6 +158,9 @@ 1.9 + 1.10 + else if (chunk_name == png_IDAT) 1.11 + { 1.12 ++#ifdef PNG_READ_APNG_SUPPORTED 1.13 ++ png_have_info(png_ptr, info_ptr); 1.14 ++#endif 1.15 + png_ptr->idat_size = length; 1.16 + break; 1.17 + } 1.18 +@@ -247,6 +250,17 @@ 1.19 + png_handle_iTXt(png_ptr, info_ptr, length); 1.20 + #endif 1.21 + 1.22 ++#ifdef PNG_READ_APNG_SUPPORTED 1.23 ++ else if (chunk_name == png_acTL) 1.24 ++ png_handle_acTL(png_ptr, info_ptr, length); 1.25 ++ 1.26 ++ else if (chunk_name == png_fcTL) 1.27 ++ png_handle_fcTL(png_ptr, info_ptr, length); 1.28 ++ 1.29 ++ else if (chunk_name == png_fdAT) 1.30 ++ png_handle_fdAT(png_ptr, info_ptr, length); 1.31 ++#endif 1.32 ++ 1.33 + else 1.34 + png_handle_unknown(png_ptr, info_ptr, length, 1.35 + PNG_HANDLE_CHUNK_AS_DEFAULT); 1.36 +@@ -254,6 +268,72 @@ 1.37 + } 1.38 + #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ 1.39 + 1.40 ++#ifdef PNG_READ_APNG_SUPPORTED 1.41 ++void PNGAPI 1.42 ++png_read_frame_head(png_structp png_ptr, png_infop info_ptr) 1.43 ++{ 1.44 ++ png_byte have_chunk_after_DAT; /* after IDAT or after fdAT */ 1.45 ++ 1.46 ++ png_debug(0, "Reading frame head"); 1.47 ++ 1.48 ++ if (!(png_ptr->mode & PNG_HAVE_acTL)) 1.49 ++ png_error(png_ptr, "attempt to png_read_frame_head() but " 1.50 ++ "no acTL present"); 1.51 ++ 1.52 ++ /* do nothing for the main IDAT */ 1.53 ++ if (png_ptr->num_frames_read == 0) 1.54 ++ return; 1.55 ++ 1.56 ++ png_read_reset(png_ptr); 1.57 ++ png_ptr->flags &= ~PNG_FLAG_ROW_INIT; 1.58 ++ png_ptr->mode &= ~PNG_HAVE_fcTL; 1.59 ++ 1.60 ++ have_chunk_after_DAT = 0; 1.61 ++ for (;;) 1.62 ++ { 1.63 ++ png_uint_32 length = png_read_chunk_header(png_ptr); 1.64 ++ 1.65 ++ if (png_ptr->chunk_name == png_IDAT) 1.66 ++ { 1.67 ++ /* discard trailing IDATs for the first frame */ 1.68 ++ if (have_chunk_after_DAT || png_ptr->num_frames_read > 1) 1.69 ++ png_error(png_ptr, "png_read_frame_head(): out of place IDAT"); 1.70 ++ png_crc_finish(png_ptr, length); 1.71 ++ } 1.72 ++ 1.73 ++ else if (png_ptr->chunk_name == png_fcTL) 1.74 ++ { 1.75 ++ png_handle_fcTL(png_ptr, info_ptr, length); 1.76 ++ have_chunk_after_DAT = 1; 1.77 ++ } 1.78 ++ 1.79 ++ else if (png_ptr->chunk_name == png_fdAT) 1.80 ++ { 1.81 ++ png_ensure_sequence_number(png_ptr, length); 1.82 ++ 1.83 ++ /* discard trailing fdATs for frames other than the first */ 1.84 ++ if (!have_chunk_after_DAT && png_ptr->num_frames_read > 1) 1.85 ++ png_crc_finish(png_ptr, length - 4); 1.86 ++ else if(png_ptr->mode & PNG_HAVE_fcTL) 1.87 ++ { 1.88 ++ png_ptr->idat_size = length - 4; 1.89 ++ png_ptr->mode |= PNG_HAVE_IDAT; 1.90 ++ 1.91 ++ break; 1.92 ++ } 1.93 ++ else 1.94 ++ png_error(png_ptr, "png_read_frame_head(): out of place fdAT"); 1.95 ++ } 1.96 ++ else 1.97 ++ { 1.98 ++ png_warning(png_ptr, "Skipped (ignored) a chunk " 1.99 ++ "between APNG chunks"); 1.100 ++ png_crc_finish(png_ptr, length); 1.101 ++ } 1.102 ++ } 1.103 ++} 1.104 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.105 ++ 1.106 + /* Optional call to update the users info_ptr structure */ 1.107 + void PNGAPI 1.108 + png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) 1.109 +Index: pngget.c 1.110 +=================================================================== 1.111 +--- pngget.c 1.112 ++++ pngget.c 1.113 +@@ -1174,4 +1174,166 @@ 1.114 + # endif 1.115 + #endif 1.116 + 1.117 ++#ifdef PNG_APNG_SUPPORTED 1.118 ++png_uint_32 PNGAPI 1.119 ++png_get_acTL(png_structp png_ptr, png_infop info_ptr, 1.120 ++ png_uint_32 *num_frames, png_uint_32 *num_plays) 1.121 ++{ 1.122 ++ png_debug1(1, "in %s retrieval function", "acTL"); 1.123 ++ 1.124 ++ if (png_ptr != NULL && info_ptr != NULL && 1.125 ++ (info_ptr->valid & PNG_INFO_acTL) && 1.126 ++ num_frames != NULL && num_plays != NULL) 1.127 ++ { 1.128 ++ *num_frames = info_ptr->num_frames; 1.129 ++ *num_plays = info_ptr->num_plays; 1.130 ++ return (1); 1.131 ++ } 1.132 ++ 1.133 ++ return (0); 1.134 ++} 1.135 ++ 1.136 ++png_uint_32 PNGAPI 1.137 ++png_get_num_frames(png_structp png_ptr, png_infop info_ptr) 1.138 ++{ 1.139 ++ png_debug(1, "in png_get_num_frames()"); 1.140 ++ 1.141 ++ if (png_ptr != NULL && info_ptr != NULL) 1.142 ++ return (info_ptr->num_frames); 1.143 ++ return (0); 1.144 ++} 1.145 ++ 1.146 ++png_uint_32 PNGAPI 1.147 ++png_get_num_plays(png_structp png_ptr, png_infop info_ptr) 1.148 ++{ 1.149 ++ png_debug(1, "in png_get_num_plays()"); 1.150 ++ 1.151 ++ if (png_ptr != NULL && info_ptr != NULL) 1.152 ++ return (info_ptr->num_plays); 1.153 ++ return (0); 1.154 ++} 1.155 ++ 1.156 ++png_uint_32 PNGAPI 1.157 ++png_get_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, 1.158 ++ png_uint_32 *width, png_uint_32 *height, 1.159 ++ png_uint_32 *x_offset, png_uint_32 *y_offset, 1.160 ++ png_uint_16 *delay_num, png_uint_16 *delay_den, 1.161 ++ png_byte *dispose_op, png_byte *blend_op) 1.162 ++{ 1.163 ++ png_debug1(1, "in %s retrieval function", "fcTL"); 1.164 ++ 1.165 ++ if (png_ptr != NULL && info_ptr != NULL && 1.166 ++ (info_ptr->valid & PNG_INFO_fcTL) && 1.167 ++ width != NULL && height != NULL && 1.168 ++ x_offset != NULL && y_offset != NULL && 1.169 ++ delay_num != NULL && delay_den != NULL && 1.170 ++ dispose_op != NULL && blend_op != NULL) 1.171 ++ { 1.172 ++ *width = info_ptr->next_frame_width; 1.173 ++ *height = info_ptr->next_frame_height; 1.174 ++ *x_offset = info_ptr->next_frame_x_offset; 1.175 ++ *y_offset = info_ptr->next_frame_y_offset; 1.176 ++ *delay_num = info_ptr->next_frame_delay_num; 1.177 ++ *delay_den = info_ptr->next_frame_delay_den; 1.178 ++ *dispose_op = info_ptr->next_frame_dispose_op; 1.179 ++ *blend_op = info_ptr->next_frame_blend_op; 1.180 ++ return (1); 1.181 ++ } 1.182 ++ 1.183 ++ return (0); 1.184 ++} 1.185 ++ 1.186 ++png_uint_32 PNGAPI 1.187 ++png_get_next_frame_width(png_structp png_ptr, png_infop info_ptr) 1.188 ++{ 1.189 ++ png_debug(1, "in png_get_next_frame_width()"); 1.190 ++ 1.191 ++ if (png_ptr != NULL && info_ptr != NULL) 1.192 ++ return (info_ptr->next_frame_width); 1.193 ++ return (0); 1.194 ++} 1.195 ++ 1.196 ++png_uint_32 PNGAPI 1.197 ++png_get_next_frame_height(png_structp png_ptr, png_infop info_ptr) 1.198 ++{ 1.199 ++ png_debug(1, "in png_get_next_frame_height()"); 1.200 ++ 1.201 ++ if (png_ptr != NULL && info_ptr != NULL) 1.202 ++ return (info_ptr->next_frame_height); 1.203 ++ return (0); 1.204 ++} 1.205 ++ 1.206 ++png_uint_32 PNGAPI 1.207 ++png_get_next_frame_x_offset(png_structp png_ptr, png_infop info_ptr) 1.208 ++{ 1.209 ++ png_debug(1, "in png_get_next_frame_x_offset()"); 1.210 ++ 1.211 ++ if (png_ptr != NULL && info_ptr != NULL) 1.212 ++ return (info_ptr->next_frame_x_offset); 1.213 ++ return (0); 1.214 ++} 1.215 ++ 1.216 ++png_uint_32 PNGAPI 1.217 ++png_get_next_frame_y_offset(png_structp png_ptr, png_infop info_ptr) 1.218 ++{ 1.219 ++ png_debug(1, "in png_get_next_frame_y_offset()"); 1.220 ++ 1.221 ++ if (png_ptr != NULL && info_ptr != NULL) 1.222 ++ return (info_ptr->next_frame_y_offset); 1.223 ++ return (0); 1.224 ++} 1.225 ++ 1.226 ++png_uint_16 PNGAPI 1.227 ++png_get_next_frame_delay_num(png_structp png_ptr, png_infop info_ptr) 1.228 ++{ 1.229 ++ png_debug(1, "in png_get_next_frame_delay_num()"); 1.230 ++ 1.231 ++ if (png_ptr != NULL && info_ptr != NULL) 1.232 ++ return (info_ptr->next_frame_delay_num); 1.233 ++ return (0); 1.234 ++} 1.235 ++ 1.236 ++png_uint_16 PNGAPI 1.237 ++png_get_next_frame_delay_den(png_structp png_ptr, png_infop info_ptr) 1.238 ++{ 1.239 ++ png_debug(1, "in png_get_next_frame_delay_den()"); 1.240 ++ 1.241 ++ if (png_ptr != NULL && info_ptr != NULL) 1.242 ++ return (info_ptr->next_frame_delay_den); 1.243 ++ return (0); 1.244 ++} 1.245 ++ 1.246 ++png_byte PNGAPI 1.247 ++png_get_next_frame_dispose_op(png_structp png_ptr, png_infop info_ptr) 1.248 ++{ 1.249 ++ png_debug(1, "in png_get_next_frame_dispose_op()"); 1.250 ++ 1.251 ++ if (png_ptr != NULL && info_ptr != NULL) 1.252 ++ return (info_ptr->next_frame_dispose_op); 1.253 ++ return (0); 1.254 ++} 1.255 ++ 1.256 ++png_byte PNGAPI 1.257 ++png_get_next_frame_blend_op(png_structp png_ptr, png_infop info_ptr) 1.258 ++{ 1.259 ++ png_debug(1, "in png_get_next_frame_blend_op()"); 1.260 ++ 1.261 ++ if (png_ptr != NULL && info_ptr != NULL) 1.262 ++ return (info_ptr->next_frame_blend_op); 1.263 ++ return (0); 1.264 ++} 1.265 ++ 1.266 ++png_byte PNGAPI 1.267 ++png_get_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr) 1.268 ++{ 1.269 ++ png_debug(1, "in png_first_frame_is_hidden()"); 1.270 ++ 1.271 ++ if (png_ptr != NULL) 1.272 ++ return (png_byte)(png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN); 1.273 ++ 1.274 ++ PNG_UNUSED(info_ptr) 1.275 ++ 1.276 ++ return 0; 1.277 ++} 1.278 ++#endif /* PNG_APNG_SUPPORTED */ 1.279 + #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ 1.280 +Index: png.h 1.281 +=================================================================== 1.282 +--- png.h 1.283 ++++ png.h 1.284 +@@ -457,6 +457,10 @@ 1.285 + # include "pnglibconf.h" 1.286 + #endif 1.287 + 1.288 ++#define PNG_APNG_SUPPORTED 1.289 ++#define PNG_READ_APNG_SUPPORTED 1.290 ++#define PNG_WRITE_APNG_SUPPORTED 1.291 ++ 1.292 + #ifndef PNG_VERSION_INFO_ONLY 1.293 + /* Machine specific configuration. */ 1.294 + # include "pngconf.h" 1.295 +@@ -547,6 +551,17 @@ 1.296 + * See pngconf.h for base types that vary by machine/system 1.297 + */ 1.298 + 1.299 ++#ifdef PNG_APNG_SUPPORTED 1.300 ++/* dispose_op flags from inside fcTL */ 1.301 ++#define PNG_DISPOSE_OP_NONE 0x00 1.302 ++#define PNG_DISPOSE_OP_BACKGROUND 0x01 1.303 ++#define PNG_DISPOSE_OP_PREVIOUS 0x02 1.304 ++ 1.305 ++/* blend_op flags from inside fcTL */ 1.306 ++#define PNG_BLEND_OP_SOURCE 0x00 1.307 ++#define PNG_BLEND_OP_OVER 0x01 1.308 ++#endif /* PNG_APNG_SUPPORTED */ 1.309 ++ 1.310 + /* This triggers a compiler error in png.c, if png.c and png.h 1.311 + * do not agree upon the version number. 1.312 + */ 1.313 +@@ -867,6 +882,10 @@ 1.314 + #define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ 1.315 + #define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ 1.316 + #define PNG_INFO_IDAT 0x8000 /* ESR, 1.0.6 */ 1.317 ++#ifdef PNG_APNG_SUPPORTED 1.318 ++#define PNG_INFO_acTL 0x10000 1.319 ++#define PNG_INFO_fcTL 0x20000 1.320 ++#endif 1.321 + 1.322 + /* This is used for the transformation routines, as some of them 1.323 + * change these values for the row. It also should enable using 1.324 +@@ -904,6 +923,10 @@ 1.325 + #ifdef PNG_PROGRESSIVE_READ_SUPPORTED 1.326 + typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); 1.327 + typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); 1.328 ++#ifdef PNG_APNG_SUPPORTED 1.329 ++typedef PNG_CALLBACK(void, *png_progressive_frame_ptr, (png_structp, 1.330 ++ png_uint_32)); 1.331 ++#endif 1.332 + 1.333 + /* The following callback receives png_uint_32 row_number, int pass for the 1.334 + * png_bytep data of the row. When transforming an interlaced image the 1.335 +@@ -3241,6 +3264,75 @@ 1.336 + * END OF HARDWARE OPTIONS 1.337 + ******************************************************************************/ 1.338 + 1.339 ++#ifdef PNG_APNG_SUPPORTED 1.340 ++PNG_EXPORT(245, png_uint_32, png_get_acTL, (png_structp png_ptr, 1.341 ++ png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays)); 1.342 ++ 1.343 ++PNG_EXPORT(246, png_uint_32, png_set_acTL, (png_structp png_ptr, 1.344 ++ png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays)); 1.345 ++ 1.346 ++PNG_EXPORT(247, png_uint_32, png_get_num_frames, (png_structp png_ptr, 1.347 ++ png_infop info_ptr)); 1.348 ++ 1.349 ++PNG_EXPORT(248, png_uint_32, png_get_num_plays, (png_structp png_ptr, 1.350 ++ png_infop info_ptr)); 1.351 ++ 1.352 ++PNG_EXPORT(249, png_uint_32, png_get_next_frame_fcTL, 1.353 ++ (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, 1.354 ++ png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset, 1.355 ++ png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op, 1.356 ++ png_byte *blend_op)); 1.357 ++ 1.358 ++PNG_EXPORT(250, png_uint_32, png_set_next_frame_fcTL, 1.359 ++ (png_structp png_ptr, png_infop info_ptr, png_uint_32 width, 1.360 ++ png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, 1.361 ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, 1.362 ++ png_byte blend_op)); 1.363 ++ 1.364 ++PNG_EXPORT(251, png_uint_32, png_get_next_frame_width, 1.365 ++ (png_structp png_ptr, png_infop info_ptr)); 1.366 ++PNG_EXPORT(252, png_uint_32, png_get_next_frame_height, 1.367 ++ (png_structp png_ptr, png_infop info_ptr)); 1.368 ++PNG_EXPORT(253, png_uint_32, png_get_next_frame_x_offset, 1.369 ++ (png_structp png_ptr, png_infop info_ptr)); 1.370 ++PNG_EXPORT(254, png_uint_32, png_get_next_frame_y_offset, 1.371 ++ (png_structp png_ptr, png_infop info_ptr)); 1.372 ++PNG_EXPORT(255, png_uint_16, png_get_next_frame_delay_num, 1.373 ++ (png_structp png_ptr, png_infop info_ptr)); 1.374 ++PNG_EXPORT(256, png_uint_16, png_get_next_frame_delay_den, 1.375 ++ (png_structp png_ptr, png_infop info_ptr)); 1.376 ++PNG_EXPORT(257, png_byte, png_get_next_frame_dispose_op, 1.377 ++ (png_structp png_ptr, png_infop info_ptr)); 1.378 ++PNG_EXPORT(258, png_byte, png_get_next_frame_blend_op, 1.379 ++ (png_structp png_ptr, png_infop info_ptr)); 1.380 ++PNG_EXPORT(259, png_byte, png_get_first_frame_is_hidden, 1.381 ++ (png_structp png_ptr, png_infop info_ptr)); 1.382 ++PNG_EXPORT(260, png_uint_32, png_set_first_frame_is_hidden, 1.383 ++ (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden)); 1.384 ++ 1.385 ++#ifdef PNG_READ_APNG_SUPPORTED 1.386 ++PNG_EXPORT(261, void, png_read_frame_head, (png_structp png_ptr, 1.387 ++ png_infop info_ptr)); 1.388 ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED 1.389 ++PNG_EXPORT(262, void, png_set_progressive_frame_fn, (png_structp png_ptr, 1.390 ++ png_progressive_frame_ptr frame_info_fn, 1.391 ++ png_progressive_frame_ptr frame_end_fn)); 1.392 ++#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ 1.393 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.394 ++ 1.395 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.396 ++PNG_EXPORT(263, void, png_write_frame_head, (png_structp png_ptr, 1.397 ++ png_infop info_ptr, png_bytepp row_pointers, 1.398 ++ png_uint_32 width, png_uint_32 height, 1.399 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.400 ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, 1.401 ++ png_byte blend_op)); 1.402 ++ 1.403 ++PNG_EXPORT(264, void, png_write_frame_tail, (png_structp png_ptr, 1.404 ++ png_infop info_ptr)); 1.405 ++#endif /* PNG_WRITE_APNG_SUPPORTED */ 1.406 ++#endif /* PNG_APNG_SUPPORTED */ 1.407 ++ 1.408 + /* Maintainer: Put new public prototypes here ^, in libpng.3, and project 1.409 + * defs, scripts/pnglibconf.h, and scripts/pnglibconf.h.prebuilt 1.410 + */ 1.411 +@@ -3250,7 +3342,11 @@ 1.412 + * scripts/symbols.def as well. 1.413 + */ 1.414 + #ifdef PNG_EXPORT_LAST_ORDINAL 1.415 ++#ifdef PNG_APNG_SUPPORTED 1.416 ++ PNG_EXPORT_LAST_ORDINAL(264); 1.417 ++#else 1.418 + PNG_EXPORT_LAST_ORDINAL(244); 1.419 ++#endif /* PNG_APNG_SUPPORTED */ 1.420 + #endif 1.421 + 1.422 + #ifdef __cplusplus 1.423 +Index: pngpriv.h 1.424 +=================================================================== 1.425 +--- pngpriv.h 1.426 ++++ pngpriv.h 1.427 +@@ -551,6 +551,10 @@ 1.428 + #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ 1.429 + /* 0x4000 (unused) */ 1.430 + #define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */ 1.431 ++#ifdef PNG_APNG_SUPPORTED 1.432 ++#define PNG_HAVE_acTL 0x10000 1.433 ++#define PNG_HAVE_fcTL 0x20000 1.434 ++#endif 1.435 + 1.436 + /* Flags for the transformations the PNG library does on the image data */ 1.437 + #define PNG_BGR 0x0001 1.438 +@@ -772,6 +776,16 @@ 1.439 + #define png_tRNS PNG_U32(116, 82, 78, 83) 1.440 + #define png_zTXt PNG_U32(122, 84, 88, 116) 1.441 + 1.442 ++#ifdef PNG_APNG_SUPPORTED 1.443 ++#define png_acTL PNG_U32( 97, 99, 84, 76) 1.444 ++#define png_fcTL PNG_U32(102, 99, 84, 76) 1.445 ++#define png_fdAT PNG_U32(102, 100, 65, 84) 1.446 ++ 1.447 ++/* For png_struct.apng_flags: */ 1.448 ++#define PNG_FIRST_FRAME_HIDDEN 0x0001 1.449 ++#define PNG_APNG_APP 0x0002 1.450 ++#endif 1.451 ++ 1.452 + /* The following will work on (signed char*) strings, whereas the get_uint_32 1.453 + * macro will fail on top-bit-set values because of the sign extension. 1.454 + */ 1.455 +@@ -1452,6 +1466,49 @@ 1.456 + 1.457 + #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ 1.458 + 1.459 ++#ifdef PNG_APNG_SUPPORTED 1.460 ++PNG_INTERNAL_FUNCTION(void,png_ensure_fcTL_is_valid,(png_structp png_ptr, 1.461 ++ png_uint_32 width, png_uint_32 height, 1.462 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.463 ++ png_uint_16 delay_num, png_uint_16 delay_den, 1.464 ++ png_byte dispose_op, png_byte blend_op),PNG_EMPTY); 1.465 ++ 1.466 ++#ifdef PNG_READ_APNG_SUPPORTED 1.467 ++PNG_INTERNAL_FUNCTION(void,png_handle_acTL,(png_structp png_ptr, 1.468 ++ png_infop info_ptr, png_uint_32 length),PNG_EMPTY); 1.469 ++PNG_INTERNAL_FUNCTION(void,png_handle_fcTL,(png_structp png_ptr, 1.470 ++ png_infop info_ptr, png_uint_32 length),PNG_EMPTY); 1.471 ++PNG_INTERNAL_FUNCTION(void,png_handle_fdAT,(png_structp png_ptr, 1.472 ++ png_infop info_ptr, png_uint_32 length),PNG_EMPTY); 1.473 ++PNG_INTERNAL_FUNCTION(void,png_have_info,(png_structp png_ptr, 1.474 ++ png_infop info_ptr),PNG_EMPTY); 1.475 ++PNG_INTERNAL_FUNCTION(void,png_ensure_sequence_number,(png_structp png_ptr, 1.476 ++ png_uint_32 length),PNG_EMPTY); 1.477 ++PNG_INTERNAL_FUNCTION(void,png_read_reset,(png_structp png_ptr),PNG_EMPTY); 1.478 ++PNG_INTERNAL_FUNCTION(void,png_read_reinit,(png_structp png_ptr, 1.479 ++ png_infop info_ptr),PNG_EMPTY); 1.480 ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED 1.481 ++PNG_INTERNAL_FUNCTION(void,png_progressive_read_reset,(png_structp png_ptr), 1.482 ++ PNG_EMPTY); 1.483 ++#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ 1.484 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.485 ++ 1.486 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.487 ++PNG_INTERNAL_FUNCTION(void,png_write_acTL,(png_structp png_ptr, 1.488 ++ png_uint_32 num_frames, png_uint_32 num_plays),PNG_EMPTY); 1.489 ++PNG_INTERNAL_FUNCTION(void,png_write_fcTL,(png_structp png_ptr, 1.490 ++ png_uint_32 width, png_uint_32 height, 1.491 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.492 ++ png_uint_16 delay_num, png_uint_16 delay_den, 1.493 ++ png_byte dispose_op, png_byte blend_op),PNG_EMPTY); 1.494 ++PNG_INTERNAL_FUNCTION(void,png_write_fdAT,(png_structp png_ptr, 1.495 ++ png_const_bytep data, png_size_t length),PNG_EMPTY); 1.496 ++PNG_INTERNAL_FUNCTION(void,png_write_reset,(png_structp png_ptr),PNG_EMPTY); 1.497 ++PNG_INTERNAL_FUNCTION(void,png_write_reinit,(png_structp png_ptr, 1.498 ++ png_infop info_ptr, png_uint_32 width, png_uint_32 height),PNG_EMPTY); 1.499 ++#endif /* PNG_WRITE_APNG_SUPPORTED */ 1.500 ++#endif /* PNG_APNG_SUPPORTED */ 1.501 ++ 1.502 + /* Added at libpng version 1.6.0 */ 1.503 + #ifdef PNG_GAMMA_SUPPORTED 1.504 + PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, 1.505 +Index: pnginfo.h 1.506 +=================================================================== 1.507 +--- pnginfo.h 1.508 ++++ pnginfo.h 1.509 +@@ -256,5 +256,18 @@ 1.510 + png_bytepp row_pointers; /* the image bits */ 1.511 + #endif 1.512 + 1.513 ++#ifdef PNG_APNG_SUPPORTED 1.514 ++ png_uint_32 num_frames; /* including default image */ 1.515 ++ png_uint_32 num_plays; 1.516 ++ png_uint_32 next_frame_width; 1.517 ++ png_uint_32 next_frame_height; 1.518 ++ png_uint_32 next_frame_x_offset; 1.519 ++ png_uint_32 next_frame_y_offset; 1.520 ++ png_uint_16 next_frame_delay_num; 1.521 ++ png_uint_16 next_frame_delay_den; 1.522 ++ png_byte next_frame_dispose_op; 1.523 ++ png_byte next_frame_blend_op; 1.524 ++#endif 1.525 ++ 1.526 + }; 1.527 + #endif /* PNGINFO_H */ 1.528 +Index: pngstruct.h 1.529 +=================================================================== 1.530 +--- pngstruct.h 1.531 ++++ pngstruct.h 1.532 +@@ -409,6 +409,27 @@ 1.533 + png_byte filter_type; 1.534 + #endif 1.535 + 1.536 ++#ifdef PNG_APNG_SUPPORTED 1.537 ++ png_uint_32 apng_flags; 1.538 ++ png_uint_32 next_seq_num; /* next fcTL/fdAT chunk sequence number */ 1.539 ++ png_uint_32 first_frame_width; 1.540 ++ png_uint_32 first_frame_height; 1.541 ++ 1.542 ++#ifdef PNG_READ_APNG_SUPPORTED 1.543 ++ png_uint_32 num_frames_read; /* incremented after all image data of */ 1.544 ++ /* a frame is read */ 1.545 ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED 1.546 ++ png_progressive_frame_ptr frame_info_fn; /* frame info read callback */ 1.547 ++ png_progressive_frame_ptr frame_end_fn; /* frame data read callback */ 1.548 ++#endif 1.549 ++#endif 1.550 ++ 1.551 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.552 ++ png_uint_32 num_frames_to_write; 1.553 ++ png_uint_32 num_frames_written; 1.554 ++#endif 1.555 ++#endif /* PNG_APNG_SUPPORTED */ 1.556 ++ 1.557 + /* New members added in libpng-1.2.0 */ 1.558 + 1.559 + /* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ 1.560 +Index: pngwrite.c 1.561 +=================================================================== 1.562 +--- pngwrite.c 1.563 ++++ pngwrite.c 1.564 +@@ -127,6 +127,10 @@ 1.565 + * application continues writing the PNG. So check the 'invalid' flag here 1.566 + * too. 1.567 + */ 1.568 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.569 ++ if (info_ptr->valid & PNG_INFO_acTL) 1.570 ++ png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays); 1.571 ++#endif 1.572 + #ifdef PNG_GAMMA_SUPPORTED 1.573 + # ifdef PNG_WRITE_gAMA_SUPPORTED 1.574 + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && 1.575 +@@ -352,6 +356,11 @@ 1.576 + if (!(png_ptr->mode & PNG_HAVE_IDAT)) 1.577 + png_error(png_ptr, "No IDATs written into file"); 1.578 + 1.579 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.580 ++ if (png_ptr->num_frames_written != png_ptr->num_frames_to_write) 1.581 ++ png_error(png_ptr, "Not enough frames written"); 1.582 ++#endif 1.583 ++ 1.584 + #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED 1.585 + if (png_ptr->num_palette_max > png_ptr->num_palette) 1.586 + png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); 1.587 +@@ -2433,4 +2442,42 @@ 1.588 + } 1.589 + #endif /* PNG_STDIO_SUPPORTED */ 1.590 + #endif /* SIMPLIFIED_WRITE */ 1.591 ++ 1.592 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.593 ++void PNGAPI 1.594 ++png_write_frame_head(png_structp png_ptr, png_infop info_ptr, 1.595 ++ png_bytepp row_pointers, png_uint_32 width, png_uint_32 height, 1.596 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.597 ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, 1.598 ++ png_byte blend_op) 1.599 ++{ 1.600 ++ png_debug(1, "in png_write_frame_head"); 1.601 ++ 1.602 ++ /* there is a chance this has been set after png_write_info was called, 1.603 ++ * so it would be set but not written. is there a way to be sure? */ 1.604 ++ if (!(info_ptr->valid & PNG_INFO_acTL)) 1.605 ++ png_error(png_ptr, "png_write_frame_head(): acTL not set"); 1.606 ++ 1.607 ++ png_write_reset(png_ptr); 1.608 ++ 1.609 ++ png_write_reinit(png_ptr, info_ptr, width, height); 1.610 ++ 1.611 ++ if ( !(png_ptr->num_frames_written == 0 && 1.612 ++ (png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) ) ) 1.613 ++ png_write_fcTL(png_ptr, width, height, x_offset, y_offset, 1.614 ++ delay_num, delay_den, dispose_op, blend_op); 1.615 ++ 1.616 ++ PNG_UNUSED(row_pointers) 1.617 ++} 1.618 ++ 1.619 ++void PNGAPI 1.620 ++png_write_frame_tail(png_structp png_ptr, png_infop info_ptr) 1.621 ++{ 1.622 ++ png_debug(1, "in png_write_frame_tail"); 1.623 ++ 1.624 ++ png_ptr->num_frames_written++; 1.625 ++ 1.626 ++ PNG_UNUSED(info_ptr) 1.627 ++} 1.628 ++#endif /* PNG_WRITE_APNG_SUPPORTED */ 1.629 + #endif /* PNG_WRITE_SUPPORTED */ 1.630 +Index: pngpread.c 1.631 +=================================================================== 1.632 +--- pngpread.c 1.633 ++++ pngpread.c 1.634 +@@ -217,6 +217,109 @@ 1.635 + 1.636 + chunk_name = png_ptr->chunk_name; 1.637 + 1.638 ++#ifdef PNG_READ_APNG_SUPPORTED 1.639 ++ if (png_ptr->num_frames_read > 0 && 1.640 ++ png_ptr->num_frames_read < info_ptr->num_frames) 1.641 ++ { 1.642 ++ if (chunk_name == png_IDAT) 1.643 ++ { 1.644 ++ /* Discard trailing IDATs for the first frame */ 1.645 ++ if (png_ptr->mode & PNG_HAVE_fcTL || png_ptr->num_frames_read > 1) 1.646 ++ png_error(png_ptr, "out of place IDAT"); 1.647 ++ 1.648 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.649 ++ { 1.650 ++ png_push_save_buffer(png_ptr); 1.651 ++ return; 1.652 ++ } 1.653 ++ 1.654 ++ png_push_crc_skip(png_ptr, png_ptr->push_length); 1.655 ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; 1.656 ++ return; 1.657 ++ } 1.658 ++ else if (chunk_name == png_fdAT) 1.659 ++ { 1.660 ++ if (png_ptr->buffer_size < 4) 1.661 ++ { 1.662 ++ png_push_save_buffer(png_ptr); 1.663 ++ return; 1.664 ++ } 1.665 ++ 1.666 ++ png_ensure_sequence_number(png_ptr, 4); 1.667 ++ 1.668 ++ if (!(png_ptr->mode & PNG_HAVE_fcTL)) 1.669 ++ { 1.670 ++ /* Discard trailing fdATs for frames other than the first */ 1.671 ++ if (png_ptr->num_frames_read < 2) 1.672 ++ png_error(png_ptr, "out of place fdAT"); 1.673 ++ 1.674 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.675 ++ { 1.676 ++ png_push_save_buffer(png_ptr); 1.677 ++ return; 1.678 ++ } 1.679 ++ 1.680 ++ png_push_crc_skip(png_ptr, png_ptr->push_length); 1.681 ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; 1.682 ++ return; 1.683 ++ } 1.684 ++ 1.685 ++ else 1.686 ++ { 1.687 ++ /* frame data follows */ 1.688 ++ png_ptr->idat_size = png_ptr->push_length - 4; 1.689 ++ png_ptr->mode |= PNG_HAVE_IDAT; 1.690 ++ png_ptr->process_mode = PNG_READ_IDAT_MODE; 1.691 ++ 1.692 ++ return; 1.693 ++ } 1.694 ++ } 1.695 ++ 1.696 ++ else if (chunk_name == png_fcTL) 1.697 ++ { 1.698 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.699 ++ { 1.700 ++ png_push_save_buffer(png_ptr); 1.701 ++ return; 1.702 ++ } 1.703 ++ 1.704 ++ png_read_reset(png_ptr); 1.705 ++ png_ptr->mode &= ~PNG_HAVE_fcTL; 1.706 ++ 1.707 ++ png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length); 1.708 ++ 1.709 ++ if (!(png_ptr->mode & PNG_HAVE_fcTL)) 1.710 ++ png_error(png_ptr, "missing required fcTL chunk"); 1.711 ++ 1.712 ++ png_read_reinit(png_ptr, info_ptr); 1.713 ++ png_progressive_read_reset(png_ptr); 1.714 ++ 1.715 ++ if (png_ptr->frame_info_fn != NULL) 1.716 ++ (*(png_ptr->frame_info_fn))(png_ptr, png_ptr->num_frames_read); 1.717 ++ 1.718 ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; 1.719 ++ 1.720 ++ return; 1.721 ++ } 1.722 ++ 1.723 ++ else 1.724 ++ { 1.725 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.726 ++ { 1.727 ++ png_push_save_buffer(png_ptr); 1.728 ++ return; 1.729 ++ } 1.730 ++ png_warning(png_ptr, "Skipped (ignored) a chunk " 1.731 ++ "between APNG chunks"); 1.732 ++ png_push_crc_skip(png_ptr, png_ptr->push_length); 1.733 ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; 1.734 ++ return; 1.735 ++ } 1.736 ++ 1.737 ++ return; 1.738 ++ } 1.739 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.740 ++ 1.741 + if (chunk_name == png_IDAT) 1.742 + { 1.743 + if (png_ptr->mode & PNG_AFTER_IDAT) 1.744 +@@ -300,6 +403,9 @@ 1.745 + 1.746 + else if (chunk_name == png_IDAT) 1.747 + { 1.748 ++#ifdef PNG_READ_APNG_SUPPORTED 1.749 ++ png_have_info(png_ptr, info_ptr); 1.750 ++#endif 1.751 + png_ptr->idat_size = png_ptr->push_length; 1.752 + png_ptr->process_mode = PNG_READ_IDAT_MODE; 1.753 + png_push_have_info(png_ptr, info_ptr); 1.754 +@@ -531,6 +637,30 @@ 1.755 + } 1.756 + #endif 1.757 + 1.758 ++#ifdef PNG_READ_APNG_SUPPORTED 1.759 ++ else if (chunk_name == png_acTL) 1.760 ++ { 1.761 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.762 ++ { 1.763 ++ png_push_save_buffer(png_ptr); 1.764 ++ return; 1.765 ++ } 1.766 ++ 1.767 ++ png_handle_acTL(png_ptr, info_ptr, png_ptr->push_length); 1.768 ++ } 1.769 ++ 1.770 ++ else if (chunk_name == png_fcTL) 1.771 ++ { 1.772 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.773 ++ { 1.774 ++ png_push_save_buffer(png_ptr); 1.775 ++ return; 1.776 ++ } 1.777 ++ 1.778 ++ png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length); 1.779 ++ } 1.780 ++ 1.781 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.782 + else 1.783 + { 1.784 + if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.785 +@@ -732,7 +862,11 @@ 1.786 + png_byte chunk_tag[4]; 1.787 + 1.788 + /* TODO: this code can be commoned up with the same code in push_read */ 1.789 ++#ifdef PNG_READ_APNG_SUPPORTED 1.790 ++ if (png_ptr->buffer_size < 12) 1.791 ++#else 1.792 + if (png_ptr->buffer_size < 8) 1.793 ++#endif 1.794 + { 1.795 + png_push_save_buffer(png_ptr); 1.796 + return; 1.797 +@@ -745,17 +879,64 @@ 1.798 + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); 1.799 + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; 1.800 + 1.801 ++#ifdef PNG_READ_APNG_SUPPORTED 1.802 ++ if (png_ptr->chunk_name != png_fdAT && png_ptr->num_frames_read > 0) 1.803 ++ { 1.804 ++ if (png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) 1.805 ++ { 1.806 ++ png_ptr->process_mode = PNG_READ_CHUNK_MODE; 1.807 ++ if (png_ptr->frame_end_fn != NULL) 1.808 ++ (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read); 1.809 ++ png_ptr->num_frames_read++; 1.810 ++ return; 1.811 ++ } 1.812 ++ else 1.813 ++ { 1.814 ++ if (png_ptr->chunk_name == png_IEND) 1.815 ++ png_error(png_ptr, "Not enough image data"); 1.816 ++ if (png_ptr->push_length + 4 > png_ptr->buffer_size) 1.817 ++ { 1.818 ++ png_push_save_buffer(png_ptr); 1.819 ++ return; 1.820 ++ } 1.821 ++ png_warning(png_ptr, "Skipping (ignoring) a chunk between " 1.822 ++ "APNG chunks"); 1.823 ++ png_crc_finish(png_ptr, png_ptr->push_length); 1.824 ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; 1.825 ++ return; 1.826 ++ } 1.827 ++ } 1.828 ++ else 1.829 ++#endif 1.830 ++#ifdef PNG_READ_APNG_SUPPORTED 1.831 ++ if (png_ptr->chunk_name != png_IDAT && png_ptr->num_frames_read == 0) 1.832 ++#else 1.833 + if (png_ptr->chunk_name != png_IDAT) 1.834 ++#endif 1.835 + { 1.836 + png_ptr->process_mode = PNG_READ_CHUNK_MODE; 1.837 + 1.838 + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) 1.839 + png_error(png_ptr, "Not enough compressed data"); 1.840 + 1.841 ++#ifdef PNG_READ_APNG_SUPPORTED 1.842 ++ if (png_ptr->frame_end_fn != NULL) 1.843 ++ (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read); 1.844 ++ png_ptr->num_frames_read++; 1.845 ++#endif 1.846 ++ 1.847 + return; 1.848 + } 1.849 + 1.850 + png_ptr->idat_size = png_ptr->push_length; 1.851 ++ 1.852 ++#ifdef PNG_READ_APNG_SUPPORTED 1.853 ++ if (png_ptr->num_frames_read > 0) 1.854 ++ { 1.855 ++ png_ensure_sequence_number(png_ptr, 4); 1.856 ++ png_ptr->idat_size -= 4; 1.857 ++ } 1.858 ++#endif 1.859 + } 1.860 + 1.861 + if (png_ptr->idat_size && png_ptr->save_buffer_size) 1.862 +@@ -833,6 +1014,15 @@ 1.863 + if (!(buffer_length > 0) || buffer == NULL) 1.864 + png_error(png_ptr, "No IDAT data (internal error)"); 1.865 + 1.866 ++#ifdef PNG_READ_APNG_SUPPORTED 1.867 ++ /* If the app is not APNG-aware, decode only the first frame */ 1.868 ++ if (!(png_ptr->apng_flags & PNG_APNG_APP) && png_ptr->num_frames_read > 0) 1.869 ++ { 1.870 ++ png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; 1.871 ++ return; 1.872 ++ } 1.873 ++#endif 1.874 ++ 1.875 + /* This routine must process all the data it has been given 1.876 + * before returning, calling the row callback as required to 1.877 + * handle the uncompressed results. 1.878 +@@ -1281,6 +1471,18 @@ 1.879 + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); 1.880 + } 1.881 + 1.882 ++#ifdef PNG_READ_APNG_SUPPORTED 1.883 ++void PNGAPI 1.884 ++png_set_progressive_frame_fn(png_structp png_ptr, 1.885 ++ png_progressive_frame_ptr frame_info_fn, 1.886 ++ png_progressive_frame_ptr frame_end_fn) 1.887 ++{ 1.888 ++ png_ptr->frame_info_fn = frame_info_fn; 1.889 ++ png_ptr->frame_end_fn = frame_end_fn; 1.890 ++ png_ptr->apng_flags |= PNG_APNG_APP; 1.891 ++} 1.892 ++#endif 1.893 ++ 1.894 + png_voidp PNGAPI 1.895 + png_get_progressive_ptr(png_const_structrp png_ptr) 1.896 + { 1.897 +Index: pngset.c 1.898 +=================================================================== 1.899 +--- pngset.c 1.900 ++++ pngset.c 1.901 +@@ -239,6 +239,11 @@ 1.902 + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); 1.903 + 1.904 + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); 1.905 ++ 1.906 ++#ifdef PNG_APNG_SUPPORTED 1.907 ++ /* for non-animated png. this may be overwritten from an acTL chunk later */ 1.908 ++ info_ptr->num_frames = 1; 1.909 ++#endif 1.910 + } 1.911 + 1.912 + #ifdef PNG_oFFs_SUPPORTED 1.913 +@@ -1065,6 +1070,147 @@ 1.914 + } 1.915 + #endif /* PNG_sPLT_SUPPORTED */ 1.916 + 1.917 ++#ifdef PNG_APNG_SUPPORTED 1.918 ++png_uint_32 PNGAPI 1.919 ++png_set_acTL(png_structp png_ptr, png_infop info_ptr, 1.920 ++ png_uint_32 num_frames, png_uint_32 num_plays) 1.921 ++{ 1.922 ++ png_debug1(1, "in %s storage function", "acTL"); 1.923 ++ 1.924 ++ if (png_ptr == NULL || info_ptr == NULL) 1.925 ++ { 1.926 ++ png_warning(png_ptr, 1.927 ++ "Call to png_set_acTL() with NULL png_ptr " 1.928 ++ "or info_ptr ignored"); 1.929 ++ return (0); 1.930 ++ } 1.931 ++ if (num_frames == 0) 1.932 ++ { 1.933 ++ png_warning(png_ptr, 1.934 ++ "Ignoring attempt to set acTL with num_frames zero"); 1.935 ++ return (0); 1.936 ++ } 1.937 ++ if (num_frames > PNG_UINT_31_MAX) 1.938 ++ { 1.939 ++ png_warning(png_ptr, 1.940 ++ "Ignoring attempt to set acTL with num_frames > 2^31-1"); 1.941 ++ return (0); 1.942 ++ } 1.943 ++ if (num_plays > PNG_UINT_31_MAX) 1.944 ++ { 1.945 ++ png_warning(png_ptr, 1.946 ++ "Ignoring attempt to set acTL with num_plays " 1.947 ++ "> 2^31-1"); 1.948 ++ return (0); 1.949 ++ } 1.950 ++ 1.951 ++ info_ptr->num_frames = num_frames; 1.952 ++ info_ptr->num_plays = num_plays; 1.953 ++ 1.954 ++ info_ptr->valid |= PNG_INFO_acTL; 1.955 ++ 1.956 ++ return (1); 1.957 ++} 1.958 ++ 1.959 ++/* delay_num and delay_den can hold any 16-bit values including zero */ 1.960 ++png_uint_32 PNGAPI 1.961 ++png_set_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, 1.962 ++ png_uint_32 width, png_uint_32 height, 1.963 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.964 ++ png_uint_16 delay_num, png_uint_16 delay_den, 1.965 ++ png_byte dispose_op, png_byte blend_op) 1.966 ++{ 1.967 ++ png_debug1(1, "in %s storage function", "fcTL"); 1.968 ++ 1.969 ++ if (png_ptr == NULL || info_ptr == NULL) 1.970 ++ { 1.971 ++ png_warning(png_ptr, 1.972 ++ "Call to png_set_fcTL() with NULL png_ptr or info_ptr " 1.973 ++ "ignored"); 1.974 ++ return (0); 1.975 ++ } 1.976 ++ 1.977 ++ png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, 1.978 ++ delay_num, delay_den, dispose_op, blend_op); 1.979 ++ 1.980 ++ if (blend_op == PNG_BLEND_OP_OVER) 1.981 ++ { 1.982 ++ if (!(png_ptr->color_type & PNG_COLOR_MASK_ALPHA) && 1.983 ++ !(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) 1.984 ++ { 1.985 ++ png_warning(png_ptr, "PNG_BLEND_OP_OVER is meaningless " 1.986 ++ "and wasteful for opaque images, ignored"); 1.987 ++ blend_op = PNG_BLEND_OP_SOURCE; 1.988 ++ } 1.989 ++ } 1.990 ++ 1.991 ++ info_ptr->next_frame_width = width; 1.992 ++ info_ptr->next_frame_height = height; 1.993 ++ info_ptr->next_frame_x_offset = x_offset; 1.994 ++ info_ptr->next_frame_y_offset = y_offset; 1.995 ++ info_ptr->next_frame_delay_num = delay_num; 1.996 ++ info_ptr->next_frame_delay_den = delay_den; 1.997 ++ info_ptr->next_frame_dispose_op = dispose_op; 1.998 ++ info_ptr->next_frame_blend_op = blend_op; 1.999 ++ 1.1000 ++ info_ptr->valid |= PNG_INFO_fcTL; 1.1001 ++ 1.1002 ++ return (1); 1.1003 ++} 1.1004 ++ 1.1005 ++void /* PRIVATE */ 1.1006 ++png_ensure_fcTL_is_valid(png_structp png_ptr, 1.1007 ++ png_uint_32 width, png_uint_32 height, 1.1008 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.1009 ++ png_uint_16 delay_num, png_uint_16 delay_den, 1.1010 ++ png_byte dispose_op, png_byte blend_op) 1.1011 ++{ 1.1012 ++ if (width > PNG_UINT_31_MAX) 1.1013 ++ png_error(png_ptr, "invalid width in fcTL (> 2^31-1)"); 1.1014 ++ if (height > PNG_UINT_31_MAX) 1.1015 ++ png_error(png_ptr, "invalid height in fcTL (> 2^31-1)"); 1.1016 ++ if (x_offset > PNG_UINT_31_MAX) 1.1017 ++ png_error(png_ptr, "invalid x_offset in fcTL (> 2^31-1)"); 1.1018 ++ if (y_offset > PNG_UINT_31_MAX) 1.1019 ++ png_error(png_ptr, "invalid y_offset in fcTL (> 2^31-1)"); 1.1020 ++ if (width + x_offset > png_ptr->first_frame_width || 1.1021 ++ height + y_offset > png_ptr->first_frame_height) 1.1022 ++ png_error(png_ptr, "dimensions of a frame are greater than" 1.1023 ++ "the ones in IHDR"); 1.1024 ++ 1.1025 ++ if (dispose_op != PNG_DISPOSE_OP_NONE && 1.1026 ++ dispose_op != PNG_DISPOSE_OP_BACKGROUND && 1.1027 ++ dispose_op != PNG_DISPOSE_OP_PREVIOUS) 1.1028 ++ png_error(png_ptr, "invalid dispose_op in fcTL"); 1.1029 ++ 1.1030 ++ if (blend_op != PNG_BLEND_OP_SOURCE && 1.1031 ++ blend_op != PNG_BLEND_OP_OVER) 1.1032 ++ png_error(png_ptr, "invalid blend_op in fcTL"); 1.1033 ++ 1.1034 ++ PNG_UNUSED(delay_num) 1.1035 ++ PNG_UNUSED(delay_den) 1.1036 ++} 1.1037 ++ 1.1038 ++png_uint_32 PNGAPI 1.1039 ++png_set_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr, 1.1040 ++ png_byte is_hidden) 1.1041 ++{ 1.1042 ++ png_debug(1, "in png_first_frame_is_hidden()"); 1.1043 ++ 1.1044 ++ if (png_ptr == NULL) 1.1045 ++ return 0; 1.1046 ++ 1.1047 ++ if (is_hidden) 1.1048 ++ png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; 1.1049 ++ else 1.1050 ++ png_ptr->apng_flags &= ~PNG_FIRST_FRAME_HIDDEN; 1.1051 ++ 1.1052 ++ PNG_UNUSED(info_ptr) 1.1053 ++ 1.1054 ++ return 1; 1.1055 ++} 1.1056 ++#endif /* PNG_APNG_SUPPORTED */ 1.1057 ++ 1.1058 + #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED 1.1059 + static png_byte 1.1060 + check_location(png_const_structrp png_ptr, int location) 1.1061 +Index: pngrutil.c 1.1062 +=================================================================== 1.1063 +--- pngrutil.c 1.1064 ++++ pngrutil.c 1.1065 +@@ -818,6 +818,11 @@ 1.1066 + filter_type = buf[11]; 1.1067 + interlace_type = buf[12]; 1.1068 + 1.1069 ++#ifdef PNG_READ_APNG_SUPPORTED 1.1070 ++ png_ptr->first_frame_width = width; 1.1071 ++ png_ptr->first_frame_height = height; 1.1072 ++#endif 1.1073 ++ 1.1074 + /* Set internal variables */ 1.1075 + png_ptr->width = width; 1.1076 + png_ptr->height = height; 1.1077 +@@ -2698,6 +2703,179 @@ 1.1078 + } 1.1079 + #endif 1.1080 + 1.1081 ++#ifdef PNG_READ_APNG_SUPPORTED 1.1082 ++void /* PRIVATE */ 1.1083 ++png_handle_acTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 1.1084 ++{ 1.1085 ++ png_byte data[8]; 1.1086 ++ png_uint_32 num_frames; 1.1087 ++ png_uint_32 num_plays; 1.1088 ++ png_uint_32 didSet; 1.1089 ++ 1.1090 ++ png_debug(1, "in png_handle_acTL"); 1.1091 ++ 1.1092 ++ if (!(png_ptr->mode & PNG_HAVE_IHDR)) 1.1093 ++ { 1.1094 ++ png_error(png_ptr, "Missing IHDR before acTL"); 1.1095 ++ } 1.1096 ++ else if (png_ptr->mode & PNG_HAVE_IDAT) 1.1097 ++ { 1.1098 ++ png_warning(png_ptr, "Invalid acTL after IDAT skipped"); 1.1099 ++ png_crc_finish(png_ptr, length); 1.1100 ++ return; 1.1101 ++ } 1.1102 ++ else if (png_ptr->mode & PNG_HAVE_acTL) 1.1103 ++ { 1.1104 ++ png_warning(png_ptr, "Duplicate acTL skipped"); 1.1105 ++ png_crc_finish(png_ptr, length); 1.1106 ++ return; 1.1107 ++ } 1.1108 ++ else if (length != 8) 1.1109 ++ { 1.1110 ++ png_warning(png_ptr, "acTL with invalid length skipped"); 1.1111 ++ png_crc_finish(png_ptr, length); 1.1112 ++ return; 1.1113 ++ } 1.1114 ++ 1.1115 ++ png_crc_read(png_ptr, data, 8); 1.1116 ++ png_crc_finish(png_ptr, 0); 1.1117 ++ 1.1118 ++ num_frames = png_get_uint_31(png_ptr, data); 1.1119 ++ num_plays = png_get_uint_31(png_ptr, data + 4); 1.1120 ++ 1.1121 ++ /* the set function will do error checking on num_frames */ 1.1122 ++ didSet = png_set_acTL(png_ptr, info_ptr, num_frames, num_plays); 1.1123 ++ if(didSet) 1.1124 ++ png_ptr->mode |= PNG_HAVE_acTL; 1.1125 ++} 1.1126 ++ 1.1127 ++void /* PRIVATE */ 1.1128 ++png_handle_fcTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 1.1129 ++{ 1.1130 ++ png_byte data[22]; 1.1131 ++ png_uint_32 width; 1.1132 ++ png_uint_32 height; 1.1133 ++ png_uint_32 x_offset; 1.1134 ++ png_uint_32 y_offset; 1.1135 ++ png_uint_16 delay_num; 1.1136 ++ png_uint_16 delay_den; 1.1137 ++ png_byte dispose_op; 1.1138 ++ png_byte blend_op; 1.1139 ++ 1.1140 ++ png_debug(1, "in png_handle_fcTL"); 1.1141 ++ 1.1142 ++ png_ensure_sequence_number(png_ptr, length); 1.1143 ++ 1.1144 ++ if (!(png_ptr->mode & PNG_HAVE_IHDR)) 1.1145 ++ { 1.1146 ++ png_error(png_ptr, "Missing IHDR before fcTL"); 1.1147 ++ } 1.1148 ++ else if (png_ptr->mode & PNG_HAVE_IDAT) 1.1149 ++ { 1.1150 ++ /* for any frames other then the first this message may be misleading, 1.1151 ++ * but correct. PNG_HAVE_IDAT is unset before the frame head is read 1.1152 ++ * i can't think of a better message */ 1.1153 ++ png_warning(png_ptr, "Invalid fcTL after IDAT skipped"); 1.1154 ++ png_crc_finish(png_ptr, length-4); 1.1155 ++ return; 1.1156 ++ } 1.1157 ++ else if (png_ptr->mode & PNG_HAVE_fcTL) 1.1158 ++ { 1.1159 ++ png_warning(png_ptr, "Duplicate fcTL within one frame skipped"); 1.1160 ++ png_crc_finish(png_ptr, length-4); 1.1161 ++ return; 1.1162 ++ } 1.1163 ++ else if (length != 26) 1.1164 ++ { 1.1165 ++ png_warning(png_ptr, "fcTL with invalid length skipped"); 1.1166 ++ png_crc_finish(png_ptr, length-4); 1.1167 ++ return; 1.1168 ++ } 1.1169 ++ 1.1170 ++ png_crc_read(png_ptr, data, 22); 1.1171 ++ png_crc_finish(png_ptr, 0); 1.1172 ++ 1.1173 ++ width = png_get_uint_31(png_ptr, data); 1.1174 ++ height = png_get_uint_31(png_ptr, data + 4); 1.1175 ++ x_offset = png_get_uint_31(png_ptr, data + 8); 1.1176 ++ y_offset = png_get_uint_31(png_ptr, data + 12); 1.1177 ++ delay_num = png_get_uint_16(data + 16); 1.1178 ++ delay_den = png_get_uint_16(data + 18); 1.1179 ++ dispose_op = data[20]; 1.1180 ++ blend_op = data[21]; 1.1181 ++ 1.1182 ++ if (png_ptr->num_frames_read == 0 && (x_offset != 0 || y_offset != 0)) 1.1183 ++ { 1.1184 ++ png_warning(png_ptr, "fcTL for the first frame must have zero offset"); 1.1185 ++ return; 1.1186 ++ } 1.1187 ++ 1.1188 ++ if (info_ptr != NULL) 1.1189 ++ { 1.1190 ++ if (png_ptr->num_frames_read == 0 && 1.1191 ++ (width != info_ptr->width || height != info_ptr->height)) 1.1192 ++ { 1.1193 ++ png_warning(png_ptr, "size in first frame's fcTL must match " 1.1194 ++ "the size in IHDR"); 1.1195 ++ return; 1.1196 ++ } 1.1197 ++ 1.1198 ++ /* The set function will do more error checking */ 1.1199 ++ png_set_next_frame_fcTL(png_ptr, info_ptr, width, height, 1.1200 ++ x_offset, y_offset, delay_num, delay_den, 1.1201 ++ dispose_op, blend_op); 1.1202 ++ 1.1203 ++ png_read_reinit(png_ptr, info_ptr); 1.1204 ++ 1.1205 ++ png_ptr->mode |= PNG_HAVE_fcTL; 1.1206 ++ } 1.1207 ++} 1.1208 ++ 1.1209 ++void /* PRIVATE */ 1.1210 ++png_have_info(png_structp png_ptr, png_infop info_ptr) 1.1211 ++{ 1.1212 ++ if((info_ptr->valid & PNG_INFO_acTL) && !(info_ptr->valid & PNG_INFO_fcTL)) 1.1213 ++ { 1.1214 ++ png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; 1.1215 ++ info_ptr->num_frames++; 1.1216 ++ } 1.1217 ++} 1.1218 ++ 1.1219 ++void /* PRIVATE */ 1.1220 ++png_handle_fdAT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 1.1221 ++{ 1.1222 ++ png_ensure_sequence_number(png_ptr, length); 1.1223 ++ 1.1224 ++ /* This function is only called from png_read_end(), png_read_info(), 1.1225 ++ * and png_push_read_chunk() which means that: 1.1226 ++ * - the user doesn't want to read this frame 1.1227 ++ * - or this is an out-of-place fdAT 1.1228 ++ * in either case it is safe to ignore the chunk with a warning */ 1.1229 ++ png_warning(png_ptr, "ignoring fdAT chunk"); 1.1230 ++ png_crc_finish(png_ptr, length - 4); 1.1231 ++ PNG_UNUSED(info_ptr) 1.1232 ++} 1.1233 ++ 1.1234 ++void /* PRIVATE */ 1.1235 ++png_ensure_sequence_number(png_structp png_ptr, png_uint_32 length) 1.1236 ++{ 1.1237 ++ png_byte data[4]; 1.1238 ++ png_uint_32 sequence_number; 1.1239 ++ 1.1240 ++ if (length < 4) 1.1241 ++ png_error(png_ptr, "invalid fcTL or fdAT chunk found"); 1.1242 ++ 1.1243 ++ png_crc_read(png_ptr, data, 4); 1.1244 ++ sequence_number = png_get_uint_31(png_ptr, data); 1.1245 ++ 1.1246 ++ if (sequence_number != png_ptr->next_seq_num) 1.1247 ++ png_error(png_ptr, "fcTL or fdAT chunk with out-of-order sequence " 1.1248 ++ "number found"); 1.1249 ++ 1.1250 ++ png_ptr->next_seq_num++; 1.1251 ++} 1.1252 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.1253 ++ 1.1254 + #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 1.1255 + /* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ 1.1256 + static int 1.1257 +@@ -3955,6 +4133,38 @@ 1.1258 + uInt avail_in; 1.1259 + png_bytep buffer; 1.1260 + 1.1261 ++#ifdef PNG_READ_APNG_SUPPORTED 1.1262 ++ png_uint_32 bytes_to_skip = 0; 1.1263 ++ 1.1264 ++ while (png_ptr->idat_size == 0 || bytes_to_skip != 0) 1.1265 ++ { 1.1266 ++ png_crc_finish(png_ptr, bytes_to_skip); 1.1267 ++ bytes_to_skip = 0; 1.1268 ++ 1.1269 ++ png_ptr->idat_size = png_read_chunk_header(png_ptr); 1.1270 ++ if (png_ptr->num_frames_read == 0) 1.1271 ++ { 1.1272 ++ if (png_ptr->chunk_name != png_IDAT) 1.1273 ++ png_error(png_ptr, "Not enough image data"); 1.1274 ++ } 1.1275 ++ else 1.1276 ++ { 1.1277 ++ if (png_ptr->chunk_name == png_IEND) 1.1278 ++ png_error(png_ptr, "Not enough image data"); 1.1279 ++ if (png_ptr->chunk_name != png_fdAT) 1.1280 ++ { 1.1281 ++ png_warning(png_ptr, "Skipped (ignored) a chunk " 1.1282 ++ "between APNG chunks"); 1.1283 ++ bytes_to_skip = png_ptr->idat_size; 1.1284 ++ continue; 1.1285 ++ } 1.1286 ++ 1.1287 ++ png_ensure_sequence_number(png_ptr, png_ptr->idat_size); 1.1288 ++ 1.1289 ++ png_ptr->idat_size -= 4; 1.1290 ++ } 1.1291 ++ } 1.1292 ++#else 1.1293 + while (png_ptr->idat_size == 0) 1.1294 + { 1.1295 + png_crc_finish(png_ptr, 0); 1.1296 +@@ -3966,6 +4176,7 @@ 1.1297 + if (png_ptr->chunk_name != png_IDAT) 1.1298 + png_error(png_ptr, "Not enough image data"); 1.1299 + } 1.1300 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.1301 + 1.1302 + avail_in = png_ptr->IDAT_read_size; 1.1303 + 1.1304 +@@ -4029,6 +4240,9 @@ 1.1305 + 1.1306 + png_ptr->mode |= PNG_AFTER_IDAT; 1.1307 + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; 1.1308 ++#ifdef PNG_READ_APNG_SUPPORTED 1.1309 ++ png_ptr->num_frames_read++; 1.1310 ++#endif 1.1311 + 1.1312 + if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) 1.1313 + png_chunk_benign_error(png_ptr, "Extra compressed data"); 1.1314 +@@ -4474,4 +4688,80 @@ 1.1315 + 1.1316 + png_ptr->flags |= PNG_FLAG_ROW_INIT; 1.1317 + } 1.1318 ++ 1.1319 ++#ifdef PNG_READ_APNG_SUPPORTED 1.1320 ++/* This function is to be called after the main IDAT set has been read and 1.1321 ++ * before a new IDAT is read. It resets some parts of png_ptr 1.1322 ++ * to make them usable by the read functions again */ 1.1323 ++void /* PRIVATE */ 1.1324 ++png_read_reset(png_structp png_ptr) 1.1325 ++{ 1.1326 ++ png_ptr->mode &= ~PNG_HAVE_IDAT; 1.1327 ++ png_ptr->mode &= ~PNG_AFTER_IDAT; 1.1328 ++ png_ptr->row_number = 0; 1.1329 ++ png_ptr->pass = 0; 1.1330 ++} 1.1331 ++ 1.1332 ++void /* PRIVATE */ 1.1333 ++png_read_reinit(png_structp png_ptr, png_infop info_ptr) 1.1334 ++{ 1.1335 ++ png_ptr->width = info_ptr->next_frame_width; 1.1336 ++ png_ptr->height = info_ptr->next_frame_height; 1.1337 ++ png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); 1.1338 ++ png_ptr->info_rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, 1.1339 ++ png_ptr->width); 1.1340 ++ if (png_ptr->prev_row) 1.1341 ++ memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); 1.1342 ++} 1.1343 ++ 1.1344 ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED 1.1345 ++/* same as png_read_reset() but for the progressive reader */ 1.1346 ++void /* PRIVATE */ 1.1347 ++png_progressive_read_reset(png_structp png_ptr) 1.1348 ++{ 1.1349 ++#ifdef PNG_READ_INTERLACING_SUPPORTED 1.1350 ++ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ 1.1351 ++ 1.1352 ++ /* Start of interlace block */ 1.1353 ++ static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; 1.1354 ++ 1.1355 ++ /* Offset to next interlace block */ 1.1356 ++ static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; 1.1357 ++ 1.1358 ++ /* Start of interlace block in the y direction */ 1.1359 ++ static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; 1.1360 ++ 1.1361 ++ /* Offset to next interlace block in the y direction */ 1.1362 ++ static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; 1.1363 ++ 1.1364 ++ if (png_ptr->interlaced) 1.1365 ++ { 1.1366 ++ if (!(png_ptr->transformations & PNG_INTERLACE)) 1.1367 ++ png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - 1.1368 ++ png_pass_ystart[0]) / png_pass_yinc[0]; 1.1369 ++ else 1.1370 ++ png_ptr->num_rows = png_ptr->height; 1.1371 ++ 1.1372 ++ png_ptr->iwidth = (png_ptr->width + 1.1373 ++ png_pass_inc[png_ptr->pass] - 1 - 1.1374 ++ png_pass_start[png_ptr->pass]) / 1.1375 ++ png_pass_inc[png_ptr->pass]; 1.1376 ++ } 1.1377 ++ else 1.1378 ++#endif /* PNG_READ_INTERLACING_SUPPORTED */ 1.1379 ++ { 1.1380 ++ png_ptr->num_rows = png_ptr->height; 1.1381 ++ png_ptr->iwidth = png_ptr->width; 1.1382 ++ } 1.1383 ++ png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED; 1.1384 ++ if (inflateReset(&(png_ptr->zstream)) != Z_OK) 1.1385 ++ png_error(png_ptr, "inflateReset failed"); 1.1386 ++ png_ptr->zstream.avail_in = 0; 1.1387 ++ png_ptr->zstream.next_in = 0; 1.1388 ++ png_ptr->zstream.next_out = png_ptr->row_buf; 1.1389 ++ png_ptr->zstream.avail_out = (uInt)PNG_ROWBYTES(png_ptr->pixel_depth, 1.1390 ++ png_ptr->iwidth) + 1; 1.1391 ++} 1.1392 ++#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ 1.1393 ++#endif /* PNG_READ_APNG_SUPPORTED */ 1.1394 + #endif /* PNG_READ_SUPPORTED */ 1.1395 +Index: pngwutil.c 1.1396 +=================================================================== 1.1397 +--- pngwutil.c 1.1398 ++++ pngwutil.c 1.1399 +@@ -898,6 +898,11 @@ 1.1400 + /* Write the chunk */ 1.1401 + png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); 1.1402 + 1.1403 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.1404 ++ png_ptr->first_frame_width = width; 1.1405 ++ png_ptr->first_frame_height = height; 1.1406 ++#endif 1.1407 ++ 1.1408 + if (!(png_ptr->do_filter)) 1.1409 + { 1.1410 + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || 1.1411 +@@ -1076,7 +1081,15 @@ 1.1412 + optimize_cmf(data, png_image_size(png_ptr)); 1.1413 + # endif 1.1414 + 1.1415 ++# ifdef PNG_WRITE_APNG_SUPPORTED 1.1416 ++ if (png_ptr->num_frames_written == 0) 1.1417 ++# endif 1.1418 + png_write_complete_chunk(png_ptr, png_IDAT, data, size); 1.1419 ++# ifdef PNG_WRITE_APNG_SUPPORTED 1.1420 ++ else 1.1421 ++ png_write_fdAT(png_ptr, data, size); 1.1422 ++# endif /* PNG_WRITE_APNG_SUPPORTED */ 1.1423 ++ 1.1424 + png_ptr->mode |= PNG_HAVE_IDAT; 1.1425 + 1.1426 + png_ptr->zstream.next_out = data; 1.1427 +@@ -1122,7 +1135,15 @@ 1.1428 + optimize_cmf(data, png_image_size(png_ptr)); 1.1429 + # endif 1.1430 + 1.1431 ++# ifdef PNG_WRITE_APNG_SUPPORTED 1.1432 ++ if (png_ptr->num_frames_written == 0) 1.1433 ++# endif 1.1434 + png_write_complete_chunk(png_ptr, png_IDAT, data, size); 1.1435 ++# ifdef PNG_WRITE_APNG_SUPPORTED 1.1436 ++ else 1.1437 ++ png_write_fdAT(png_ptr, data, size); 1.1438 ++# endif /* PNG_WRITE_APNG_SUPPORTED */ 1.1439 ++ 1.1440 + png_ptr->zstream.avail_out = 0; 1.1441 + png_ptr->zstream.next_out = NULL; 1.1442 + png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; 1.1443 +@@ -1934,6 +1955,82 @@ 1.1444 + } 1.1445 + #endif 1.1446 + 1.1447 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.1448 ++void /* PRIVATE */ 1.1449 ++png_write_acTL(png_structp png_ptr, 1.1450 ++ png_uint_32 num_frames, png_uint_32 num_plays) 1.1451 ++{ 1.1452 ++ png_byte buf[8]; 1.1453 ++ 1.1454 ++ png_debug(1, "in png_write_acTL"); 1.1455 ++ 1.1456 ++ png_ptr->num_frames_to_write = num_frames; 1.1457 ++ 1.1458 ++ if (png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) 1.1459 ++ num_frames--; 1.1460 ++ 1.1461 ++ png_save_uint_32(buf, num_frames); 1.1462 ++ png_save_uint_32(buf + 4, num_plays); 1.1463 ++ 1.1464 ++ png_write_complete_chunk(png_ptr, png_acTL, buf, (png_size_t)8); 1.1465 ++} 1.1466 ++ 1.1467 ++void /* PRIVATE */ 1.1468 ++png_write_fcTL(png_structp png_ptr, png_uint_32 width, png_uint_32 height, 1.1469 ++ png_uint_32 x_offset, png_uint_32 y_offset, 1.1470 ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, 1.1471 ++ png_byte blend_op) 1.1472 ++{ 1.1473 ++ png_byte buf[26]; 1.1474 ++ 1.1475 ++ png_debug(1, "in png_write_fcTL"); 1.1476 ++ 1.1477 ++ if (png_ptr->num_frames_written == 0 && (x_offset != 0 || y_offset != 0)) 1.1478 ++ png_error(png_ptr, "x and/or y offset for the first frame aren't 0"); 1.1479 ++ if (png_ptr->num_frames_written == 0 && 1.1480 ++ (width != png_ptr->first_frame_width || 1.1481 ++ height != png_ptr->first_frame_height)) 1.1482 ++ png_error(png_ptr, "width and/or height in the first frame's fcTL " 1.1483 ++ "don't match the ones in IHDR"); 1.1484 ++ 1.1485 ++ /* more error checking */ 1.1486 ++ png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, 1.1487 ++ delay_num, delay_den, dispose_op, blend_op); 1.1488 ++ 1.1489 ++ png_save_uint_32(buf, png_ptr->next_seq_num); 1.1490 ++ png_save_uint_32(buf + 4, width); 1.1491 ++ png_save_uint_32(buf + 8, height); 1.1492 ++ png_save_uint_32(buf + 12, x_offset); 1.1493 ++ png_save_uint_32(buf + 16, y_offset); 1.1494 ++ png_save_uint_16(buf + 20, delay_num); 1.1495 ++ png_save_uint_16(buf + 22, delay_den); 1.1496 ++ buf[24] = dispose_op; 1.1497 ++ buf[25] = blend_op; 1.1498 ++ 1.1499 ++ png_write_complete_chunk(png_ptr, png_fcTL, buf, (png_size_t)26); 1.1500 ++ 1.1501 ++ png_ptr->next_seq_num++; 1.1502 ++} 1.1503 ++ 1.1504 ++void /* PRIVATE */ 1.1505 ++png_write_fdAT(png_structp png_ptr, 1.1506 ++ png_const_bytep data, png_size_t length) 1.1507 ++{ 1.1508 ++ png_byte buf[4]; 1.1509 ++ 1.1510 ++ png_write_chunk_header(png_ptr, png_fdAT, (png_uint_32)(4 + length)); 1.1511 ++ 1.1512 ++ png_save_uint_32(buf, png_ptr->next_seq_num); 1.1513 ++ png_write_chunk_data(png_ptr, buf, 4); 1.1514 ++ 1.1515 ++ png_write_chunk_data(png_ptr, data, length); 1.1516 ++ 1.1517 ++ png_write_chunk_end(png_ptr); 1.1518 ++ 1.1519 ++ png_ptr->next_seq_num++; 1.1520 ++} 1.1521 ++#endif /* PNG_WRITE_APNG_SUPPORTED */ 1.1522 ++ 1.1523 + /* Initializes the row writing capability of libpng */ 1.1524 + void /* PRIVATE */ 1.1525 + png_write_start_row(png_structrp png_ptr) 1.1526 +@@ -3021,4 +3118,39 @@ 1.1527 + } 1.1528 + #endif 1.1529 + } 1.1530 ++ 1.1531 ++#ifdef PNG_WRITE_APNG_SUPPORTED 1.1532 ++void /* PRIVATE */ 1.1533 ++png_write_reset(png_structp png_ptr) 1.1534 ++{ 1.1535 ++ png_ptr->row_number = 0; 1.1536 ++ png_ptr->pass = 0; 1.1537 ++ png_ptr->mode &= ~PNG_HAVE_IDAT; 1.1538 ++} 1.1539 ++ 1.1540 ++void /* PRIVATE */ 1.1541 ++png_write_reinit(png_structp png_ptr, png_infop info_ptr, 1.1542 ++ png_uint_32 width, png_uint_32 height) 1.1543 ++{ 1.1544 ++ if (png_ptr->num_frames_written == 0 && 1.1545 ++ (width != png_ptr->first_frame_width || 1.1546 ++ height != png_ptr->first_frame_height)) 1.1547 ++ png_error(png_ptr, "width and/or height in the first frame's fcTL " 1.1548 ++ "don't match the ones in IHDR"); 1.1549 ++ if (width > png_ptr->first_frame_width || 1.1550 ++ height > png_ptr->first_frame_height) 1.1551 ++ png_error(png_ptr, "width and/or height for a frame greater than" 1.1552 ++ "the ones in IHDR"); 1.1553 ++ 1.1554 ++ png_set_IHDR(png_ptr, info_ptr, width, height, 1.1555 ++ info_ptr->bit_depth, info_ptr->color_type, 1.1556 ++ info_ptr->interlace_type, info_ptr->compression_type, 1.1557 ++ info_ptr->filter_type); 1.1558 ++ 1.1559 ++ png_ptr->width = width; 1.1560 ++ png_ptr->height = height; 1.1561 ++ png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); 1.1562 ++ png_ptr->usr_width = png_ptr->width; 1.1563 ++} 1.1564 ++#endif /* PNG_WRITE_APNG_SUPPORTED */ 1.1565 + #endif /* PNG_WRITE_SUPPORTED */ 1.1566 +Index: scripts/symbols.def 1.1567 +=================================================================== 1.1568 +--- scripts/symbols.def 1.1569 ++++ scripts/symbols.def 1.1570 +@@ -249,3 +249,23 @@ 1.1571 + png_set_check_for_invalid_index @242 1.1572 + png_get_palette_max @243 1.1573 + png_set_option @244 1.1574 ++ png_get_acTL @245 1.1575 ++ png_set_acTL @246 1.1576 ++ png_get_num_frames @247 1.1577 ++ png_get_num_plays @248 1.1578 ++ png_get_next_frame_fcTL @249 1.1579 ++ png_set_next_frame_fcTL @250 1.1580 ++ png_get_next_frame_width @251 1.1581 ++ png_get_next_frame_height @252 1.1582 ++ png_get_next_frame_x_offset @253 1.1583 ++ png_get_next_frame_y_offset @254 1.1584 ++ png_get_next_frame_delay_num @255 1.1585 ++ png_get_next_frame_delay_den @256 1.1586 ++ png_get_next_frame_dispose_op @257 1.1587 ++ png_get_next_frame_blend_op @258 1.1588 ++ png_get_first_frame_is_hidden @259 1.1589 ++ png_set_first_frame_is_hidden @260 1.1590 ++ png_read_frame_head @261 1.1591 ++ png_set_progressive_frame_fn @262 1.1592 ++ png_write_frame_head @263 1.1593 ++ png_write_frame_tail @264