Backed out changeset cfe0bbc666b8 (bug 1784757) in order to wait some more for a...
[gecko.git] / media / libpng / apng.patch
blobab4ca59567cdd4cbaabb81eb8117f5693b4f3d4e
1 diff --git a/png.h b/png.h
2 --- a/png.h
3 +++ b/png.h
4 @@ -329,8 +329,12 @@
5 */
6 # include "pnglibconf.h"
7 #endif
9 +#define PNG_APNG_SUPPORTED
10 +#define PNG_READ_APNG_SUPPORTED
11 +#define PNG_WRITE_APNG_SUPPORTED
13 #ifndef PNG_VERSION_INFO_ONLY
14 /* Machine specific configuration. */
15 # include "pngconf.h"
16 #endif
17 @@ -424,8 +428,19 @@ extern "C" {
18 * constants.
19 * See pngconf.h for base types that vary by machine/system
22 +#ifdef PNG_APNG_SUPPORTED
23 +/* dispose_op flags from inside fcTL */
24 +#define PNG_DISPOSE_OP_NONE 0x00
25 +#define PNG_DISPOSE_OP_BACKGROUND 0x01
26 +#define PNG_DISPOSE_OP_PREVIOUS 0x02
28 +/* blend_op flags from inside fcTL */
29 +#define PNG_BLEND_OP_SOURCE 0x00
30 +#define PNG_BLEND_OP_OVER 0x01
31 +#endif /* APNG */
33 /* This triggers a compiler error in png.c, if png.c and png.h
34 * do not agree upon the version number.
36 typedef char* png_libpng_version_1_6_37;
37 @@ -745,8 +760,12 @@ typedef png_unknown_chunk * * png_unknow
38 #define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */
39 #define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */
40 #define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */
41 #define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */
42 +#ifdef PNG_APNG_SUPPORTED
43 +#define PNG_INFO_acTL 0x20000U
44 +#define PNG_INFO_fcTL 0x40000U
45 +#endif
47 /* This is used for the transformation routines, as some of them
48 * change these values for the row. It also should enable using
49 * the routines for other purposes.
50 @@ -782,8 +801,12 @@ typedef PNG_CALLBACK(void, *png_write_st
52 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
53 typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
54 typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
55 +#ifdef PNG_APNG_SUPPORTED
56 +typedef PNG_CALLBACK(void, *png_progressive_frame_ptr, (png_structp,
57 + png_uint_32));
58 +#endif
60 /* The following callback receives png_uint_32 row_number, int pass for the
61 * png_bytep data of the row. When transforming an interlaced image the
62 * row number is the row number within the sub-image of the interlace pass, so
63 @@ -3226,17 +3249,90 @@ PNG_EXPORT(244, int, png_set_option, (pn
64 /*******************************************************************************
65 * END OF HARDWARE AND SOFTWARE OPTIONS
66 ******************************************************************************/
68 +#ifdef PNG_APNG_SUPPORTED
69 +PNG_EXPORT(248, png_uint_32, png_get_acTL, (png_structp png_ptr,
70 + png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays));
72 +PNG_EXPORT(249, png_uint_32, png_set_acTL, (png_structp png_ptr,
73 + png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays));
75 +PNG_EXPORT(250, png_uint_32, png_get_num_frames, (png_structp png_ptr,
76 + png_infop info_ptr));
78 +PNG_EXPORT(251, png_uint_32, png_get_num_plays, (png_structp png_ptr,
79 + png_infop info_ptr));
81 +PNG_EXPORT(252, png_uint_32, png_get_next_frame_fcTL,
82 + (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width,
83 + png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset,
84 + png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op,
85 + png_byte *blend_op));
87 +PNG_EXPORT(253, png_uint_32, png_set_next_frame_fcTL,
88 + (png_structp png_ptr, png_infop info_ptr, png_uint_32 width,
89 + png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset,
90 + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
91 + png_byte blend_op));
93 +PNG_EXPORT(254, png_uint_32, png_get_next_frame_width,
94 + (png_structp png_ptr, png_infop info_ptr));
95 +PNG_EXPORT(255, png_uint_32, png_get_next_frame_height,
96 + (png_structp png_ptr, png_infop info_ptr));
97 +PNG_EXPORT(256, png_uint_32, png_get_next_frame_x_offset,
98 + (png_structp png_ptr, png_infop info_ptr));
99 +PNG_EXPORT(257, png_uint_32, png_get_next_frame_y_offset,
100 + (png_structp png_ptr, png_infop info_ptr));
101 +PNG_EXPORT(258, png_uint_16, png_get_next_frame_delay_num,
102 + (png_structp png_ptr, png_infop info_ptr));
103 +PNG_EXPORT(259, png_uint_16, png_get_next_frame_delay_den,
104 + (png_structp png_ptr, png_infop info_ptr));
105 +PNG_EXPORT(260, png_byte, png_get_next_frame_dispose_op,
106 + (png_structp png_ptr, png_infop info_ptr));
107 +PNG_EXPORT(261, png_byte, png_get_next_frame_blend_op,
108 + (png_structp png_ptr, png_infop info_ptr));
109 +PNG_EXPORT(262, png_byte, png_get_first_frame_is_hidden,
110 + (png_structp png_ptr, png_infop info_ptr));
111 +PNG_EXPORT(263, png_uint_32, png_set_first_frame_is_hidden,
112 + (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden));
114 +#ifdef PNG_READ_APNG_SUPPORTED
115 +PNG_EXPORT(264, void, png_read_frame_head, (png_structp png_ptr,
116 + png_infop info_ptr));
117 +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
118 +PNG_EXPORT(265, void, png_set_progressive_frame_fn, (png_structp png_ptr,
119 + png_progressive_frame_ptr frame_info_fn,
120 + png_progressive_frame_ptr frame_end_fn));
121 +#endif /* PROGRESSIVE_READ */
122 +#endif /* READ_APNG */
124 +#ifdef PNG_WRITE_APNG_SUPPORTED
125 +PNG_EXPORT(266, void, png_write_frame_head, (png_structp png_ptr,
126 + png_infop info_ptr, png_bytepp row_pointers,
127 + png_uint_32 width, png_uint_32 height,
128 + png_uint_32 x_offset, png_uint_32 y_offset,
129 + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
130 + png_byte blend_op));
132 +PNG_EXPORT(267, void, png_write_frame_tail, (png_structp png_ptr,
133 + png_infop info_ptr));
134 +#endif /* WRITE_APNG */
135 +#endif /* APNG */
137 /* Maintainer: Put new public prototypes here ^, in libpng.3, in project
138 * defs, and in scripts/symbols.def.
141 /* The last ordinal number (this is the *last* one already used; the next
142 * one to use is one more than this.)
144 #ifdef PNG_EXPORT_LAST_ORDINAL
145 +#ifdef PNG_APNG_SUPPORTED
146 + PNG_EXPORT_LAST_ORDINAL(269);
147 +#else
148 PNG_EXPORT_LAST_ORDINAL(249);
149 +#endif /* APNG */
150 #endif
152 #ifdef __cplusplus
154 diff --git a/pngget.c b/pngget.c
155 --- a/pngget.c
156 +++ b/pngget.c
157 @@ -1245,5 +1245,167 @@ png_get_palette_max(png_const_structp pn
159 # endif
160 #endif
162 +#ifdef PNG_APNG_SUPPORTED
163 +png_uint_32 PNGAPI
164 +png_get_acTL(png_structp png_ptr, png_infop info_ptr,
165 + png_uint_32 *num_frames, png_uint_32 *num_plays)
167 + png_debug1(1, "in %s retrieval function", "acTL");
169 + if (png_ptr != NULL && info_ptr != NULL &&
170 + (info_ptr->valid & PNG_INFO_acTL) != 0 &&
171 + num_frames != NULL && num_plays != NULL)
173 + *num_frames = info_ptr->num_frames;
174 + *num_plays = info_ptr->num_plays;
175 + return (1);
178 + return (0);
181 +png_uint_32 PNGAPI
182 +png_get_num_frames(png_structp png_ptr, png_infop info_ptr)
184 + png_debug(1, "in png_get_num_frames()");
186 + if (png_ptr != NULL && info_ptr != NULL)
187 + return (info_ptr->num_frames);
188 + return (0);
191 +png_uint_32 PNGAPI
192 +png_get_num_plays(png_structp png_ptr, png_infop info_ptr)
194 + png_debug(1, "in png_get_num_plays()");
196 + if (png_ptr != NULL && info_ptr != NULL)
197 + return (info_ptr->num_plays);
198 + return (0);
201 +png_uint_32 PNGAPI
202 +png_get_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr,
203 + png_uint_32 *width, png_uint_32 *height,
204 + png_uint_32 *x_offset, png_uint_32 *y_offset,
205 + png_uint_16 *delay_num, png_uint_16 *delay_den,
206 + png_byte *dispose_op, png_byte *blend_op)
208 + png_debug1(1, "in %s retrieval function", "fcTL");
210 + if (png_ptr != NULL && info_ptr != NULL &&
211 + (info_ptr->valid & PNG_INFO_fcTL) != 0 &&
212 + width != NULL && height != NULL &&
213 + x_offset != NULL && y_offset != NULL &&
214 + delay_num != NULL && delay_den != NULL &&
215 + dispose_op != NULL && blend_op != NULL)
217 + *width = info_ptr->next_frame_width;
218 + *height = info_ptr->next_frame_height;
219 + *x_offset = info_ptr->next_frame_x_offset;
220 + *y_offset = info_ptr->next_frame_y_offset;
221 + *delay_num = info_ptr->next_frame_delay_num;
222 + *delay_den = info_ptr->next_frame_delay_den;
223 + *dispose_op = info_ptr->next_frame_dispose_op;
224 + *blend_op = info_ptr->next_frame_blend_op;
225 + return (1);
228 + return (0);
231 +png_uint_32 PNGAPI
232 +png_get_next_frame_width(png_structp png_ptr, png_infop info_ptr)
234 + png_debug(1, "in png_get_next_frame_width()");
236 + if (png_ptr != NULL && info_ptr != NULL)
237 + return (info_ptr->next_frame_width);
238 + return (0);
241 +png_uint_32 PNGAPI
242 +png_get_next_frame_height(png_structp png_ptr, png_infop info_ptr)
244 + png_debug(1, "in png_get_next_frame_height()");
246 + if (png_ptr != NULL && info_ptr != NULL)
247 + return (info_ptr->next_frame_height);
248 + return (0);
251 +png_uint_32 PNGAPI
252 +png_get_next_frame_x_offset(png_structp png_ptr, png_infop info_ptr)
254 + png_debug(1, "in png_get_next_frame_x_offset()");
256 + if (png_ptr != NULL && info_ptr != NULL)
257 + return (info_ptr->next_frame_x_offset);
258 + return (0);
261 +png_uint_32 PNGAPI
262 +png_get_next_frame_y_offset(png_structp png_ptr, png_infop info_ptr)
264 + png_debug(1, "in png_get_next_frame_y_offset()");
266 + if (png_ptr != NULL && info_ptr != NULL)
267 + return (info_ptr->next_frame_y_offset);
268 + return (0);
271 +png_uint_16 PNGAPI
272 +png_get_next_frame_delay_num(png_structp png_ptr, png_infop info_ptr)
274 + png_debug(1, "in png_get_next_frame_delay_num()");
276 + if (png_ptr != NULL && info_ptr != NULL)
277 + return (info_ptr->next_frame_delay_num);
278 + return (0);
281 +png_uint_16 PNGAPI
282 +png_get_next_frame_delay_den(png_structp png_ptr, png_infop info_ptr)
284 + png_debug(1, "in png_get_next_frame_delay_den()");
286 + if (png_ptr != NULL && info_ptr != NULL)
287 + return (info_ptr->next_frame_delay_den);
288 + return (0);
291 +png_byte PNGAPI
292 +png_get_next_frame_dispose_op(png_structp png_ptr, png_infop info_ptr)
294 + png_debug(1, "in png_get_next_frame_dispose_op()");
296 + if (png_ptr != NULL && info_ptr != NULL)
297 + return (info_ptr->next_frame_dispose_op);
298 + return (0);
301 +png_byte PNGAPI
302 +png_get_next_frame_blend_op(png_structp png_ptr, png_infop info_ptr)
304 + png_debug(1, "in png_get_next_frame_blend_op()");
306 + if (png_ptr != NULL && info_ptr != NULL)
307 + return (info_ptr->next_frame_blend_op);
308 + return (0);
311 +png_byte PNGAPI
312 +png_get_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr)
314 + png_debug(1, "in png_first_frame_is_hidden()");
316 + if (png_ptr != NULL)
317 + return (png_byte)(png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN);
319 + PNG_UNUSED(info_ptr)
321 + return 0;
323 +#endif /* APNG */
324 #endif /* READ || WRITE */
325 diff --git a/pnginfo.h b/pnginfo.h
326 --- a/pnginfo.h
327 +++ b/pnginfo.h
328 @@ -262,6 +262,19 @@ defined(PNG_READ_BACKGROUND_SUPPORTED)
329 /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
330 png_bytepp row_pointers; /* the image bits */
331 #endif
333 +#ifdef PNG_APNG_SUPPORTED
334 + png_uint_32 num_frames; /* including default image */
335 + png_uint_32 num_plays;
336 + png_uint_32 next_frame_width;
337 + png_uint_32 next_frame_height;
338 + png_uint_32 next_frame_x_offset;
339 + png_uint_32 next_frame_y_offset;
340 + png_uint_16 next_frame_delay_num;
341 + png_uint_16 next_frame_delay_den;
342 + png_byte next_frame_dispose_op;
343 + png_byte next_frame_blend_op;
344 +#endif
347 #endif /* PNGINFO_H */
348 diff --git a/pngpread.c b/pngpread.c
349 --- a/pngpread.c
350 +++ b/pngpread.c
351 @@ -194,8 +194,91 @@ png_push_read_chunk(png_structrp png_ptr
354 chunk_name = png_ptr->chunk_name;
356 +#ifdef PNG_READ_APNG_SUPPORTED
357 + if (png_ptr->num_frames_read > 0 &&
358 + png_ptr->num_frames_read < info_ptr->num_frames)
360 + if (chunk_name == png_IDAT)
362 + /* Discard trailing IDATs for the first frame */
363 + if ((png_ptr->mode & PNG_HAVE_fcTL) != 0 ||
364 + png_ptr->num_frames_read > 1)
365 + png_error(png_ptr, "out of place IDAT");
367 + PNG_PUSH_SAVE_BUFFER_IF_FULL
368 + png_crc_finish(png_ptr, png_ptr->push_length);
369 + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
372 + else if (chunk_name == png_fdAT)
374 + PNG_PUSH_SAVE_BUFFER_IF_LT(4)
375 + png_ensure_sequence_number(png_ptr, 4);
377 + if ((png_ptr->mode & PNG_HAVE_fcTL) == 0)
379 + /* Discard trailing fdATs for frames other than the first */
380 + if (png_ptr->num_frames_read < 2)
381 + png_error(png_ptr, "out of place fdAT");
383 + PNG_PUSH_SAVE_BUFFER_IF_FULL
384 + png_crc_finish(png_ptr, png_ptr->push_length);
385 + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
388 + else
390 + /* frame data follows */
391 + png_ptr->idat_size = png_ptr->push_length - 4;
392 + png_ptr->mode |= PNG_HAVE_IDAT;
393 + png_ptr->process_mode = PNG_READ_IDAT_MODE;
397 + else if (chunk_name == png_fcTL)
399 + PNG_PUSH_SAVE_BUFFER_IF_FULL
400 + png_read_reset(png_ptr);
401 + png_ptr->mode &= ~PNG_HAVE_fcTL;
403 + png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length);
405 + if ((png_ptr->mode & PNG_HAVE_fcTL) == 0)
406 + png_error(png_ptr, "missing required fcTL chunk");
408 + png_read_reinit(png_ptr, info_ptr);
409 + png_progressive_read_reset(png_ptr);
411 + if (png_ptr->frame_info_fn != NULL)
412 + (*(png_ptr->frame_info_fn))(png_ptr, png_ptr->num_frames_read);
414 + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
417 + else if (chunk_name == png_IEND)
419 + PNG_PUSH_SAVE_BUFFER_IF_FULL
420 + png_warning(png_ptr, "Number of actual frames fewer than expected");
421 + png_crc_finish(png_ptr, png_ptr->push_length);
422 + png_ptr->process_mode = PNG_READ_DONE_MODE;
423 + png_push_have_end(png_ptr, info_ptr);
426 + else
428 + PNG_PUSH_SAVE_BUFFER_IF_FULL
429 + png_warning(png_ptr, "Skipped (ignored) a chunk "
430 + "between APNG chunks");
431 + png_crc_finish(png_ptr, png_ptr->push_length);
432 + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
435 + return;
437 +#endif /* READ_APNG */
439 if (chunk_name == png_IDAT)
441 if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
442 png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
443 @@ -260,8 +343,11 @@ png_push_read_chunk(png_structrp png_ptr
446 else if (chunk_name == png_IDAT)
448 +#ifdef PNG_READ_APNG_SUPPORTED
449 + png_have_info(png_ptr, info_ptr);
450 +#endif
451 png_ptr->idat_size = png_ptr->push_length;
452 png_ptr->process_mode = PNG_READ_IDAT_MODE;
453 png_push_have_info(png_ptr, info_ptr);
454 png_ptr->zstream.avail_out =
455 @@ -406,8 +492,22 @@ png_push_read_chunk(png_structrp png_ptr
456 png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
458 #endif
460 +#ifdef PNG_READ_APNG_SUPPORTED
461 + else if (chunk_name == png_acTL)
463 + PNG_PUSH_SAVE_BUFFER_IF_FULL
464 + png_handle_acTL(png_ptr, info_ptr, png_ptr->push_length);
467 + else if (chunk_name == png_fcTL)
469 + PNG_PUSH_SAVE_BUFFER_IF_FULL
470 + png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length);
473 +#endif /* READ_APNG */
474 else
476 PNG_PUSH_SAVE_BUFFER_IF_FULL
477 png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,
478 @@ -538,27 +638,74 @@ png_push_read_IDAT(png_structrp png_ptr)
479 png_byte chunk_length[4];
480 png_byte chunk_tag[4];
482 /* TODO: this code can be commoned up with the same code in push_read */
483 +#ifdef PNG_READ_APNG_SUPPORTED
484 + PNG_PUSH_SAVE_BUFFER_IF_LT(12)
485 +#else
486 PNG_PUSH_SAVE_BUFFER_IF_LT(8)
487 +#endif
488 png_push_fill_buffer(png_ptr, chunk_length, 4);
489 png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
490 png_reset_crc(png_ptr);
491 png_crc_read(png_ptr, chunk_tag, 4);
492 png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
493 png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
495 +#ifdef PNG_READ_APNG_SUPPORTED
496 + if (png_ptr->chunk_name != png_fdAT && png_ptr->num_frames_read > 0)
498 + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) != 0)
500 + png_ptr->process_mode = PNG_READ_CHUNK_MODE;
501 + if (png_ptr->frame_end_fn != NULL)
502 + (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read);
503 + png_ptr->num_frames_read++;
504 + return;
506 + else
508 + if (png_ptr->chunk_name == png_IEND)
509 + png_error(png_ptr, "Not enough image data");
510 + PNG_PUSH_SAVE_BUFFER_IF_FULL
511 + png_warning(png_ptr, "Skipping (ignoring) a chunk between "
512 + "APNG chunks");
513 + png_crc_finish(png_ptr, png_ptr->push_length);
514 + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
515 + return;
518 + else
519 +#endif
520 +#ifdef PNG_READ_APNG_SUPPORTED
521 + if (png_ptr->chunk_name != png_IDAT && png_ptr->num_frames_read == 0)
522 +#else
523 if (png_ptr->chunk_name != png_IDAT)
524 +#endif
526 png_ptr->process_mode = PNG_READ_CHUNK_MODE;
528 if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
529 png_error(png_ptr, "Not enough compressed data");
531 +#ifdef PNG_READ_APNG_SUPPORTED
532 + if (png_ptr->frame_end_fn != NULL)
533 + (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read);
534 + png_ptr->num_frames_read++;
535 +#endif
537 return;
540 png_ptr->idat_size = png_ptr->push_length;
542 +#ifdef PNG_READ_APNG_SUPPORTED
543 + if (png_ptr->num_frames_read > 0)
545 + png_ensure_sequence_number(png_ptr, 4);
546 + png_ptr->idat_size -= 4;
548 +#endif
551 if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0)
553 @@ -630,8 +777,18 @@ png_process_IDAT_data(png_structrp png_p
554 /* The caller checks for a non-zero buffer length. */
555 if (!(buffer_length > 0) || buffer == NULL)
556 png_error(png_ptr, "No IDAT data (internal error)");
558 +#ifdef PNG_READ_APNG_SUPPORTED
559 + /* If the app is not APNG-aware, decode only the first frame */
560 + if ((png_ptr->apng_flags & PNG_APNG_APP) == 0 &&
561 + png_ptr->num_frames_read > 0)
563 + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
564 + return;
566 +#endif
568 /* This routine must process all the data it has been given
569 * before returning, calling the row callback as required to
570 * handle the uncompressed results.
572 @@ -1084,8 +1241,20 @@ png_set_progressive_read_fn(png_structrp
574 png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
577 +#ifdef PNG_READ_APNG_SUPPORTED
578 +void PNGAPI
579 +png_set_progressive_frame_fn(png_structp png_ptr,
580 + png_progressive_frame_ptr frame_info_fn,
581 + png_progressive_frame_ptr frame_end_fn)
583 + png_ptr->frame_info_fn = frame_info_fn;
584 + png_ptr->frame_end_fn = frame_end_fn;
585 + png_ptr->apng_flags |= PNG_APNG_APP;
587 +#endif
589 png_voidp PNGAPI
590 png_get_progressive_ptr(png_const_structrp png_ptr)
592 if (png_ptr == NULL)
593 diff --git a/pngpriv.h b/pngpriv.h
594 --- a/pngpriv.h
595 +++ b/pngpriv.h
596 @@ -636,8 +636,12 @@
597 #define PNG_HAVE_PNG_SIGNATURE 0x1000U
598 #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */
599 /* 0x4000U (unused) */
600 #define PNG_IS_READ_STRUCT 0x8000U /* Else is a write struct */
601 +#ifdef PNG_APNG_SUPPORTED
602 +#define PNG_HAVE_acTL 0x10000U
603 +#define PNG_HAVE_fcTL 0x20000U
604 +#endif
606 /* Flags for the transformations the PNG library does on the image data */
607 #define PNG_BGR 0x0001U
608 #define PNG_INTERLACE 0x0002U
609 @@ -872,8 +876,18 @@
610 #define png_tIME PNG_U32(116, 73, 77, 69)
611 #define png_tRNS PNG_U32(116, 82, 78, 83)
612 #define png_zTXt PNG_U32(122, 84, 88, 116)
614 +#ifdef PNG_APNG_SUPPORTED
615 +#define png_acTL PNG_U32( 97, 99, 84, 76)
616 +#define png_fcTL PNG_U32(102, 99, 84, 76)
617 +#define png_fdAT PNG_U32(102, 100, 65, 84)
619 +/* For png_struct.apng_flags: */
620 +#define PNG_FIRST_FRAME_HIDDEN 0x0001U
621 +#define PNG_APNG_APP 0x0002U
622 +#endif
624 /* The following will work on (signed char*) strings, whereas the get_uint_32
625 * macro will fail on top-bit-set values because of the sign extension.
627 #define PNG_CHUNK_FROM_STRING(s)\
628 @@ -1623,8 +1637,51 @@ PNG_INTERNAL_FUNCTION(void,png_push_read
629 # endif
631 #endif /* PROGRESSIVE_READ */
633 +#ifdef PNG_APNG_SUPPORTED
634 +PNG_INTERNAL_FUNCTION(void,png_ensure_fcTL_is_valid,(png_structp png_ptr,
635 + png_uint_32 width, png_uint_32 height,
636 + png_uint_32 x_offset, png_uint_32 y_offset,
637 + png_uint_16 delay_num, png_uint_16 delay_den,
638 + png_byte dispose_op, png_byte blend_op),PNG_EMPTY);
640 +#ifdef PNG_READ_APNG_SUPPORTED
641 +PNG_INTERNAL_FUNCTION(void,png_handle_acTL,(png_structp png_ptr,
642 + png_infop info_ptr, png_uint_32 length),PNG_EMPTY);
643 +PNG_INTERNAL_FUNCTION(void,png_handle_fcTL,(png_structp png_ptr,
644 + png_infop info_ptr, png_uint_32 length),PNG_EMPTY);
645 +PNG_INTERNAL_FUNCTION(void,png_handle_fdAT,(png_structp png_ptr,
646 + png_infop info_ptr, png_uint_32 length),PNG_EMPTY);
647 +PNG_INTERNAL_FUNCTION(void,png_have_info,(png_structp png_ptr,
648 + png_infop info_ptr),PNG_EMPTY);
649 +PNG_INTERNAL_FUNCTION(void,png_ensure_sequence_number,(png_structp png_ptr,
650 + png_uint_32 length),PNG_EMPTY);
651 +PNG_INTERNAL_FUNCTION(void,png_read_reset,(png_structp png_ptr),PNG_EMPTY);
652 +PNG_INTERNAL_FUNCTION(void,png_read_reinit,(png_structp png_ptr,
653 + png_infop info_ptr),PNG_EMPTY);
654 +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
655 +PNG_INTERNAL_FUNCTION(void,png_progressive_read_reset,(png_structp png_ptr),
656 + PNG_EMPTY);
657 +#endif /* PROGRESSIVE_READ */
658 +#endif /* READ_APNG */
660 +#ifdef PNG_WRITE_APNG_SUPPORTED
661 +PNG_INTERNAL_FUNCTION(void,png_write_acTL,(png_structp png_ptr,
662 + png_uint_32 num_frames, png_uint_32 num_plays),PNG_EMPTY);
663 +PNG_INTERNAL_FUNCTION(void,png_write_fcTL,(png_structp png_ptr,
664 + png_uint_32 width, png_uint_32 height,
665 + png_uint_32 x_offset, png_uint_32 y_offset,
666 + png_uint_16 delay_num, png_uint_16 delay_den,
667 + png_byte dispose_op, png_byte blend_op),PNG_EMPTY);
668 +PNG_INTERNAL_FUNCTION(void,png_write_fdAT,(png_structp png_ptr,
669 + png_const_bytep data, png_size_t length),PNG_EMPTY);
670 +PNG_INTERNAL_FUNCTION(void,png_write_reset,(png_structp png_ptr),PNG_EMPTY);
671 +PNG_INTERNAL_FUNCTION(void,png_write_reinit,(png_structp png_ptr,
672 + png_infop info_ptr, png_uint_32 width, png_uint_32 height),PNG_EMPTY);
673 +#endif /* WRITE_APNG */
674 +#endif /* APNG */
676 /* Added at libpng version 1.6.0 */
677 #ifdef PNG_GAMMA_SUPPORTED
678 PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr,
679 png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY);
680 diff --git a/pngread.c b/pngread.c
681 --- a/pngread.c
682 +++ b/pngread.c
683 @@ -160,8 +160,11 @@ png_read_info(png_structrp png_ptr, png_
684 png_handle_PLTE(png_ptr, info_ptr, length);
686 else if (chunk_name == png_IDAT)
688 +#ifdef PNG_READ_APNG_SUPPORTED
689 + png_have_info(png_ptr, info_ptr);
690 +#endif
691 png_ptr->idat_size = length;
692 break;
695 @@ -254,15 +257,92 @@ png_read_info(png_structrp png_ptr, png_
696 else if (chunk_name == png_iTXt)
697 png_handle_iTXt(png_ptr, info_ptr, length);
698 #endif
700 +#ifdef PNG_READ_APNG_SUPPORTED
701 + else if (chunk_name == png_acTL)
702 + png_handle_acTL(png_ptr, info_ptr, length);
704 + else if (chunk_name == png_fcTL)
705 + png_handle_fcTL(png_ptr, info_ptr, length);
707 + else if (chunk_name == png_fdAT)
708 + png_handle_fdAT(png_ptr, info_ptr, length);
709 +#endif
711 else
712 png_handle_unknown(png_ptr, info_ptr, length,
713 PNG_HANDLE_CHUNK_AS_DEFAULT);
716 #endif /* SEQUENTIAL_READ */
718 +#ifdef PNG_READ_APNG_SUPPORTED
719 +void PNGAPI
720 +png_read_frame_head(png_structp png_ptr, png_infop info_ptr)
722 + png_byte have_chunk_after_DAT; /* after IDAT or after fdAT */
724 + png_debug(0, "Reading frame head");
726 + if ((png_ptr->mode & PNG_HAVE_acTL) == 0)
727 + png_error(png_ptr, "attempt to png_read_frame_head() but "
728 + "no acTL present");
730 + /* do nothing for the main IDAT */
731 + if (png_ptr->num_frames_read == 0)
732 + return;
734 + png_read_reset(png_ptr);
735 + png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
736 + png_ptr->mode &= ~PNG_HAVE_fcTL;
738 + have_chunk_after_DAT = 0;
739 + for (;;)
741 + png_uint_32 length = png_read_chunk_header(png_ptr);
743 + if (png_ptr->chunk_name == png_IDAT)
745 + /* discard trailing IDATs for the first frame */
746 + if (have_chunk_after_DAT != 0 || png_ptr->num_frames_read > 1)
747 + png_error(png_ptr, "png_read_frame_head(): out of place IDAT");
748 + png_crc_finish(png_ptr, length);
751 + else if (png_ptr->chunk_name == png_fcTL)
753 + png_handle_fcTL(png_ptr, info_ptr, length);
754 + have_chunk_after_DAT = 1;
757 + else if (png_ptr->chunk_name == png_fdAT)
759 + png_ensure_sequence_number(png_ptr, length);
761 + /* discard trailing fdATs for frames other than the first */
762 + if (have_chunk_after_DAT == 0 && png_ptr->num_frames_read > 1)
763 + png_crc_finish(png_ptr, length - 4);
764 + else if (png_ptr->mode & PNG_HAVE_fcTL)
766 + png_ptr->idat_size = length - 4;
767 + png_ptr->mode |= PNG_HAVE_IDAT;
769 + break;
771 + else
772 + png_error(png_ptr, "png_read_frame_head(): out of place fdAT");
774 + else
776 + png_warning(png_ptr, "Skipped (ignored) a chunk "
777 + "between APNG chunks");
778 + png_crc_finish(png_ptr, length);
782 +#endif /* READ_APNG */
784 /* Optional call to update the users info_ptr structure */
785 void PNGAPI
786 png_read_update_info(png_structrp png_ptr, png_inforp info_ptr)
788 diff --git a/pngrutil.c b/pngrutil.c
789 --- a/pngrutil.c
790 +++ b/pngrutil.c
791 @@ -864,8 +864,13 @@ png_handle_IHDR(png_structrp png_ptr, pn
792 compression_type = buf[10];
793 filter_type = buf[11];
794 interlace_type = buf[12];
796 +#ifdef PNG_READ_APNG_SUPPORTED
797 + png_ptr->first_frame_width = width;
798 + png_ptr->first_frame_height = height;
799 +#endif
801 /* Set internal variables */
802 png_ptr->width = width;
803 png_ptr->height = height;
804 png_ptr->bit_depth = (png_byte)bit_depth;
805 @@ -2856,8 +2861,182 @@ png_handle_iTXt(png_structrp png_ptr, pn
806 png_chunk_benign_error(png_ptr, errmsg);
808 #endif
810 +#ifdef PNG_READ_APNG_SUPPORTED
811 +void /* PRIVATE */
812 +png_handle_acTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
814 + png_byte data[8];
815 + png_uint_32 num_frames;
816 + png_uint_32 num_plays;
817 + png_uint_32 didSet;
819 + png_debug(1, "in png_handle_acTL");
821 + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
823 + png_error(png_ptr, "Missing IHDR before acTL");
825 + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
827 + png_warning(png_ptr, "Invalid acTL after IDAT skipped");
828 + png_crc_finish(png_ptr, length);
829 + return;
831 + else if ((png_ptr->mode & PNG_HAVE_acTL) != 0)
833 + png_warning(png_ptr, "Duplicate acTL skipped");
834 + png_crc_finish(png_ptr, length);
835 + return;
837 + else if (length != 8)
839 + png_warning(png_ptr, "acTL with invalid length skipped");
840 + png_crc_finish(png_ptr, length);
841 + return;
844 + png_crc_read(png_ptr, data, 8);
845 + png_crc_finish(png_ptr, 0);
847 + num_frames = png_get_uint_31(png_ptr, data);
848 + num_plays = png_get_uint_31(png_ptr, data + 4);
850 + /* the set function will do error checking on num_frames */
851 + didSet = png_set_acTL(png_ptr, info_ptr, num_frames, num_plays);
852 + if (didSet != 0)
853 + png_ptr->mode |= PNG_HAVE_acTL;
856 +void /* PRIVATE */
857 +png_handle_fcTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
859 + png_byte data[22];
860 + png_uint_32 width;
861 + png_uint_32 height;
862 + png_uint_32 x_offset;
863 + png_uint_32 y_offset;
864 + png_uint_16 delay_num;
865 + png_uint_16 delay_den;
866 + png_byte dispose_op;
867 + png_byte blend_op;
869 + png_debug(1, "in png_handle_fcTL");
871 + png_ensure_sequence_number(png_ptr, length);
873 + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
875 + png_error(png_ptr, "Missing IHDR before fcTL");
877 + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
879 + /* for any frames other then the first this message may be misleading,
880 + * but correct. PNG_HAVE_IDAT is unset before the frame head is read
881 + * i can't think of a better message */
882 + png_warning(png_ptr, "Invalid fcTL after IDAT skipped");
883 + png_crc_finish(png_ptr, length-4);
884 + return;
886 + else if ((png_ptr->mode & PNG_HAVE_fcTL) != 0)
888 + png_warning(png_ptr, "Duplicate fcTL within one frame skipped");
889 + png_crc_finish(png_ptr, length-4);
890 + return;
892 + else if (length != 26)
894 + png_warning(png_ptr, "fcTL with invalid length skipped");
895 + png_crc_finish(png_ptr, length-4);
896 + return;
899 + png_crc_read(png_ptr, data, 22);
900 + png_crc_finish(png_ptr, 0);
902 + width = png_get_uint_31(png_ptr, data);
903 + height = png_get_uint_31(png_ptr, data + 4);
904 + x_offset = png_get_uint_31(png_ptr, data + 8);
905 + y_offset = png_get_uint_31(png_ptr, data + 12);
906 + delay_num = png_get_uint_16(data + 16);
907 + delay_den = png_get_uint_16(data + 18);
908 + dispose_op = data[20];
909 + blend_op = data[21];
911 + if (png_ptr->num_frames_read == 0 && (x_offset != 0 || y_offset != 0))
913 + png_warning(png_ptr, "fcTL for the first frame must have zero offset");
914 + return;
917 + if (info_ptr != NULL)
919 + if (png_ptr->num_frames_read == 0 &&
920 + (width != info_ptr->width || height != info_ptr->height))
922 + png_warning(png_ptr, "size in first frame's fcTL must match "
923 + "the size in IHDR");
924 + return;
927 + /* The set function will do more error checking */
928 + png_set_next_frame_fcTL(png_ptr, info_ptr, width, height,
929 + x_offset, y_offset, delay_num, delay_den,
930 + dispose_op, blend_op);
932 + png_read_reinit(png_ptr, info_ptr);
934 + png_ptr->mode |= PNG_HAVE_fcTL;
938 +void /* PRIVATE */
939 +png_have_info(png_structp png_ptr, png_infop info_ptr)
941 + if ((info_ptr->valid & PNG_INFO_acTL) != 0 &&
942 + (info_ptr->valid & PNG_INFO_fcTL) == 0)
944 + png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN;
945 + info_ptr->num_frames++;
949 +void /* PRIVATE */
950 +png_handle_fdAT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
952 + png_ensure_sequence_number(png_ptr, length);
954 + /* This function is only called from png_read_end(), png_read_info(),
955 + * and png_push_read_chunk() which means that:
956 + * - the user doesn't want to read this frame
957 + * - or this is an out-of-place fdAT
958 + * in either case it is safe to ignore the chunk with a warning */
959 + png_warning(png_ptr, "ignoring fdAT chunk");
960 + png_crc_finish(png_ptr, length - 4);
961 + PNG_UNUSED(info_ptr)
964 +void /* PRIVATE */
965 +png_ensure_sequence_number(png_structp png_ptr, png_uint_32 length)
967 + png_byte data[4];
968 + png_uint_32 sequence_number;
970 + if (length < 4)
971 + png_error(png_ptr, "invalid fcTL or fdAT chunk found");
973 + png_crc_read(png_ptr, data, 4);
974 + sequence_number = png_get_uint_31(png_ptr, data);
976 + if (sequence_number != png_ptr->next_seq_num)
977 + png_error(png_ptr, "fcTL or fdAT chunk with out-of-order sequence "
978 + "number found");
980 + png_ptr->next_seq_num++;
982 +#endif /* READ_APNG */
984 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
985 /* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */
986 static int
987 png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
988 @@ -3161,9 +3340,13 @@ png_check_chunk_length(png_const_structr
989 # elif PNG_USER_CHUNK_MALLOC_MAX > 0
990 if (PNG_USER_CHUNK_MALLOC_MAX < limit)
991 limit = PNG_USER_CHUNK_MALLOC_MAX;
992 # endif
993 +#ifdef PNG_READ_APNG_SUPPORTED
994 + if (png_ptr->chunk_name == png_IDAT || png_ptr->chunk_name == png_fdAT)
995 +#else
996 if (png_ptr->chunk_name == png_IDAT)
997 +#endif
999 png_alloc_size_t idat_limit = PNG_UINT_31_MAX;
1000 size_t row_factor =
1001 (size_t)png_ptr->width
1002 @@ -4165,8 +4348,40 @@ png_read_IDAT_data(png_structrp png_ptr,
1004 uInt avail_in;
1005 png_bytep buffer;
1007 +#ifdef PNG_READ_APNG_SUPPORTED
1008 + png_uint_32 bytes_to_skip = 0;
1010 + while (png_ptr->idat_size == 0 || bytes_to_skip != 0)
1012 + png_crc_finish(png_ptr, bytes_to_skip);
1013 + bytes_to_skip = 0;
1015 + png_ptr->idat_size = png_read_chunk_header(png_ptr);
1016 + if (png_ptr->num_frames_read == 0)
1018 + if (png_ptr->chunk_name != png_IDAT)
1019 + png_error(png_ptr, "Not enough image data");
1021 + else
1023 + if (png_ptr->chunk_name == png_IEND)
1024 + png_error(png_ptr, "Not enough image data");
1025 + if (png_ptr->chunk_name != png_fdAT)
1027 + png_warning(png_ptr, "Skipped (ignored) a chunk "
1028 + "between APNG chunks");
1029 + bytes_to_skip = png_ptr->idat_size;
1030 + continue;
1033 + png_ensure_sequence_number(png_ptr, png_ptr->idat_size);
1035 + png_ptr->idat_size -= 4;
1038 +#else
1039 while (png_ptr->idat_size == 0)
1041 png_crc_finish(png_ptr, 0);
1043 @@ -4176,8 +4391,9 @@ png_read_IDAT_data(png_structrp png_ptr,
1045 if (png_ptr->chunk_name != png_IDAT)
1046 png_error(png_ptr, "Not enough image data");
1048 +#endif /* READ_APNG */
1050 avail_in = png_ptr->IDAT_read_size;
1052 if (avail_in > png_ptr->idat_size)
1053 @@ -4239,8 +4455,11 @@ png_read_IDAT_data(png_structrp png_ptr,
1054 png_ptr->zstream.next_out = NULL;
1056 png_ptr->mode |= PNG_AFTER_IDAT;
1057 png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
1058 +#ifdef PNG_READ_APNG_SUPPORTED
1059 + png_ptr->num_frames_read++;
1060 +#endif
1062 if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0)
1063 png_chunk_benign_error(png_ptr, "Extra compressed data");
1064 break;
1065 @@ -4677,5 +4896,81 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED
1066 png_error(png_ptr, png_ptr->zstream.msg);
1068 png_ptr->flags |= PNG_FLAG_ROW_INIT;
1071 +#ifdef PNG_READ_APNG_SUPPORTED
1072 +/* This function is to be called after the main IDAT set has been read and
1073 + * before a new IDAT is read. It resets some parts of png_ptr
1074 + * to make them usable by the read functions again */
1075 +void /* PRIVATE */
1076 +png_read_reset(png_structp png_ptr)
1078 + png_ptr->mode &= ~PNG_HAVE_IDAT;
1079 + png_ptr->mode &= ~PNG_AFTER_IDAT;
1080 + png_ptr->row_number = 0;
1081 + png_ptr->pass = 0;
1084 +void /* PRIVATE */
1085 +png_read_reinit(png_structp png_ptr, png_infop info_ptr)
1087 + png_ptr->width = info_ptr->next_frame_width;
1088 + png_ptr->height = info_ptr->next_frame_height;
1089 + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width);
1090 + png_ptr->info_rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,
1091 + png_ptr->width);
1092 + if (png_ptr->prev_row != NULL)
1093 + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
1096 +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
1097 +/* same as png_read_reset() but for the progressive reader */
1098 +void /* PRIVATE */
1099 +png_progressive_read_reset(png_structp png_ptr)
1101 +#ifdef PNG_READ_INTERLACING_SUPPORTED
1102 + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
1104 + /* Start of interlace block */
1105 + static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
1107 + /* Offset to next interlace block */
1108 + static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
1110 + /* Start of interlace block in the y direction */
1111 + static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
1113 + /* Offset to next interlace block in the y direction */
1114 + static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
1116 + if (png_ptr->interlaced != 0)
1118 + if ((png_ptr->transformations & PNG_INTERLACE) == 0)
1119 + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
1120 + png_pass_ystart[0]) / png_pass_yinc[0];
1121 + else
1122 + png_ptr->num_rows = png_ptr->height;
1124 + png_ptr->iwidth = (png_ptr->width +
1125 + png_pass_inc[png_ptr->pass] - 1 -
1126 + png_pass_start[png_ptr->pass]) /
1127 + png_pass_inc[png_ptr->pass];
1129 + else
1130 +#endif /* READ_INTERLACING */
1132 + png_ptr->num_rows = png_ptr->height;
1133 + png_ptr->iwidth = png_ptr->width;
1135 + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED;
1136 + if (inflateReset(&(png_ptr->zstream)) != Z_OK)
1137 + png_error(png_ptr, "inflateReset failed");
1138 + png_ptr->zstream.avail_in = 0;
1139 + png_ptr->zstream.next_in = 0;
1140 + png_ptr->zstream.next_out = png_ptr->row_buf;
1141 + png_ptr->zstream.avail_out = (uInt)PNG_ROWBYTES(png_ptr->pixel_depth,
1142 + png_ptr->iwidth) + 1;
1144 +#endif /* PROGRESSIVE_READ */
1145 +#endif /* READ_APNG */
1146 #endif /* READ */
1147 diff --git a/pngset.c b/pngset.c
1148 --- a/pngset.c
1149 +++ b/pngset.c
1150 @@ -287,8 +287,13 @@ png_set_IHDR(png_const_structrp png_ptr,
1152 info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
1154 info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);
1156 +#ifdef PNG_APNG_SUPPORTED
1157 + /* for non-animated png. this may be overwritten from an acTL chunk later */
1158 + info_ptr->num_frames = 1;
1159 +#endif
1162 #ifdef PNG_oFFs_SUPPORTED
1163 void PNGAPI
1164 @@ -1157,8 +1162,148 @@ png_set_sPLT(png_const_structrp png_ptr,
1165 png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
1167 #endif /* sPLT */
1169 +#ifdef PNG_APNG_SUPPORTED
1170 +png_uint_32 PNGAPI
1171 +png_set_acTL(png_structp png_ptr, png_infop info_ptr,
1172 + png_uint_32 num_frames, png_uint_32 num_plays)
1174 + png_debug1(1, "in %s storage function", "acTL");
1176 + if (png_ptr == NULL || info_ptr == NULL)
1178 + png_warning(png_ptr,
1179 + "Call to png_set_acTL() with NULL png_ptr "
1180 + "or info_ptr ignored");
1181 + return (0);
1183 + if (num_frames == 0)
1185 + png_warning(png_ptr,
1186 + "Ignoring attempt to set acTL with num_frames zero");
1187 + return (0);
1189 + if (num_frames > PNG_UINT_31_MAX)
1191 + png_warning(png_ptr,
1192 + "Ignoring attempt to set acTL with num_frames > 2^31-1");
1193 + return (0);
1195 + if (num_plays > PNG_UINT_31_MAX)
1197 + png_warning(png_ptr,
1198 + "Ignoring attempt to set acTL with num_plays > 2^31-1");
1199 + return (0);
1202 + info_ptr->num_frames = num_frames;
1203 + info_ptr->num_plays = num_plays;
1205 + info_ptr->valid |= PNG_INFO_acTL;
1207 + return (1);
1210 +/* delay_num and delay_den can hold any 16-bit values including zero */
1211 +png_uint_32 PNGAPI
1212 +png_set_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr,
1213 + png_uint_32 width, png_uint_32 height,
1214 + png_uint_32 x_offset, png_uint_32 y_offset,
1215 + png_uint_16 delay_num, png_uint_16 delay_den,
1216 + png_byte dispose_op, png_byte blend_op)
1218 + png_debug1(1, "in %s storage function", "fcTL");
1220 + if (png_ptr == NULL || info_ptr == NULL)
1222 + png_warning(png_ptr,
1223 + "Call to png_set_fcTL() with NULL png_ptr or info_ptr "
1224 + "ignored");
1225 + return (0);
1228 + png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset,
1229 + delay_num, delay_den, dispose_op, blend_op);
1231 + if (blend_op == PNG_BLEND_OP_OVER)
1233 + if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0 &&
1234 + png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) == 0)
1236 + png_warning(png_ptr, "PNG_BLEND_OP_OVER is meaningless "
1237 + "and wasteful for opaque images, ignored");
1238 + blend_op = PNG_BLEND_OP_SOURCE;
1242 + info_ptr->next_frame_width = width;
1243 + info_ptr->next_frame_height = height;
1244 + info_ptr->next_frame_x_offset = x_offset;
1245 + info_ptr->next_frame_y_offset = y_offset;
1246 + info_ptr->next_frame_delay_num = delay_num;
1247 + info_ptr->next_frame_delay_den = delay_den;
1248 + info_ptr->next_frame_dispose_op = dispose_op;
1249 + info_ptr->next_frame_blend_op = blend_op;
1251 + info_ptr->valid |= PNG_INFO_fcTL;
1253 + return (1);
1256 +void /* PRIVATE */
1257 +png_ensure_fcTL_is_valid(png_structp png_ptr,
1258 + png_uint_32 width, png_uint_32 height,
1259 + png_uint_32 x_offset, png_uint_32 y_offset,
1260 + png_uint_16 delay_num, png_uint_16 delay_den,
1261 + png_byte dispose_op, png_byte blend_op)
1263 + if (width == 0 || width > PNG_UINT_31_MAX)
1264 + png_error(png_ptr, "invalid width in fcTL (0 or > 2^31-1)");
1265 + if (height == 0 || height > PNG_UINT_31_MAX)
1266 + png_error(png_ptr, "invalid height in fcTL (0 or > 2^31-1)");
1267 + if (x_offset > PNG_UINT_31_MAX)
1268 + png_error(png_ptr, "invalid x_offset in fcTL (> 2^31-1)");
1269 + if (y_offset > PNG_UINT_31_MAX)
1270 + png_error(png_ptr, "invalid y_offset in fcTL (> 2^31-1)");
1271 + if (width + x_offset > png_ptr->first_frame_width ||
1272 + height + y_offset > png_ptr->first_frame_height)
1273 + png_error(png_ptr, "dimensions of a frame are greater than "
1274 + "the ones in IHDR");
1276 + if (dispose_op != PNG_DISPOSE_OP_NONE &&
1277 + dispose_op != PNG_DISPOSE_OP_BACKGROUND &&
1278 + dispose_op != PNG_DISPOSE_OP_PREVIOUS)
1279 + png_error(png_ptr, "invalid dispose_op in fcTL");
1281 + if (blend_op != PNG_BLEND_OP_SOURCE &&
1282 + blend_op != PNG_BLEND_OP_OVER)
1283 + png_error(png_ptr, "invalid blend_op in fcTL");
1285 + PNG_UNUSED(delay_num)
1286 + PNG_UNUSED(delay_den)
1289 +png_uint_32 PNGAPI
1290 +png_set_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr,
1291 + png_byte is_hidden)
1293 + png_debug(1, "in png_first_frame_is_hidden()");
1295 + if (png_ptr == NULL)
1296 + return 0;
1298 + if (is_hidden != 0)
1299 + png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN;
1300 + else
1301 + png_ptr->apng_flags &= ~PNG_FIRST_FRAME_HIDDEN;
1303 + PNG_UNUSED(info_ptr)
1305 + return 1;
1307 +#endif /* APNG */
1309 #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
1310 static png_byte
1311 check_location(png_const_structrp png_ptr, int location)
1313 diff --git a/pngstruct.h b/pngstruct.h
1314 --- a/pngstruct.h
1315 +++ b/pngstruct.h
1316 @@ -408,8 +408,29 @@ struct png_struct_def
1317 #ifdef PNG_MNG_FEATURES_SUPPORTED
1318 png_byte filter_type;
1319 #endif
1321 +#ifdef PNG_APNG_SUPPORTED
1322 + png_uint_32 apng_flags;
1323 + png_uint_32 next_seq_num; /* next fcTL/fdAT chunk sequence number */
1324 + png_uint_32 first_frame_width;
1325 + png_uint_32 first_frame_height;
1327 +#ifdef PNG_READ_APNG_SUPPORTED
1328 + png_uint_32 num_frames_read; /* incremented after all image data of */
1329 + /* a frame is read */
1330 +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
1331 + png_progressive_frame_ptr frame_info_fn; /* frame info read callback */
1332 + png_progressive_frame_ptr frame_end_fn; /* frame data read callback */
1333 +#endif
1334 +#endif
1336 +#ifdef PNG_WRITE_APNG_SUPPORTED
1337 + png_uint_32 num_frames_to_write;
1338 + png_uint_32 num_frames_written;
1339 +#endif
1340 +#endif /* APNG */
1342 /* New members added in libpng-1.2.0 */
1344 /* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
1345 #ifdef PNG_USER_MEM_SUPPORTED
1346 diff --git a/pngwrite.c b/pngwrite.c
1347 --- a/pngwrite.c
1348 +++ b/pngwrite.c
1349 @@ -127,8 +127,12 @@ png_write_info_before_PLTE(png_structrp
1350 * an error and calls png_error while the color space is being set, yet
1351 * the application continues writing the PNG. So check the 'invalid'
1352 * flag here too.
1354 +#ifdef PNG_WRITE_APNG_SUPPORTED
1355 + if ((info_ptr->valid & PNG_INFO_acTL) != 0)
1356 + png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays);
1357 +#endif
1358 #ifdef PNG_GAMMA_SUPPORTED
1359 # ifdef PNG_WRITE_gAMA_SUPPORTED
1360 if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
1361 (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
1362 @@ -364,8 +368,13 @@ png_write_end(png_structrp png_ptr, png_
1364 if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
1365 png_error(png_ptr, "No IDATs written into file");
1367 +#ifdef PNG_WRITE_APNG_SUPPORTED
1368 + if (png_ptr->num_frames_written != png_ptr->num_frames_to_write)
1369 + png_error(png_ptr, "Not enough frames written");
1370 +#endif
1372 #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
1373 if (png_ptr->num_palette_max > png_ptr->num_palette)
1374 png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");
1375 #endif
1376 @@ -2391,5 +2400,43 @@ png_image_write_to_file(png_imagep image
1377 return 0;
1379 #endif /* SIMPLIFIED_WRITE_STDIO */
1380 #endif /* SIMPLIFIED_WRITE */
1382 +#ifdef PNG_WRITE_APNG_SUPPORTED
1383 +void PNGAPI
1384 +png_write_frame_head(png_structp png_ptr, png_infop info_ptr,
1385 + png_bytepp row_pointers, png_uint_32 width, png_uint_32 height,
1386 + png_uint_32 x_offset, png_uint_32 y_offset,
1387 + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
1388 + png_byte blend_op)
1390 + png_debug(1, "in png_write_frame_head");
1392 + /* there is a chance this has been set after png_write_info was called,
1393 + * so it would be set but not written. is there a way to be sure? */
1394 + if ((info_ptr->valid & PNG_INFO_acTL) == 0)
1395 + png_error(png_ptr, "png_write_frame_head(): acTL not set");
1397 + png_write_reset(png_ptr);
1399 + png_write_reinit(png_ptr, info_ptr, width, height);
1401 + if ((png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) == 0 ||
1402 + png_ptr->num_frames_written != 0)
1403 + png_write_fcTL(png_ptr, width, height, x_offset, y_offset,
1404 + delay_num, delay_den, dispose_op, blend_op);
1406 + PNG_UNUSED(row_pointers)
1409 +void PNGAPI
1410 +png_write_frame_tail(png_structp png_ptr, png_infop info_ptr)
1412 + png_debug(1, "in png_write_frame_tail");
1414 + png_ptr->num_frames_written++;
1416 + PNG_UNUSED(info_ptr)
1418 +#endif /* WRITE_APNG */
1419 #endif /* WRITE */
1420 diff --git a/pngwutil.c b/pngwutil.c
1421 --- a/pngwutil.c
1422 +++ b/pngwutil.c
1423 @@ -820,8 +820,13 @@ png_write_IHDR(png_structrp png_ptr, png
1425 /* Write the chunk */
1426 png_write_complete_chunk(png_ptr, png_IHDR, buf, 13);
1428 +#ifdef PNG_WRITE_APNG_SUPPORTED
1429 + png_ptr->first_frame_width = width;
1430 + png_ptr->first_frame_height = height;
1431 +#endif
1433 if ((png_ptr->do_filter) == PNG_NO_FILTERS)
1435 if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
1436 png_ptr->bit_depth < 8)
1437 @@ -1002,9 +1007,19 @@ png_compress_IDAT(png_structrp png_ptr,
1438 optimize_cmf(data, png_image_size(png_ptr));
1439 #endif
1441 if (size > 0)
1442 +#ifdef PNG_WRITE_APNG_SUPPORTED
1444 + if (png_ptr->num_frames_written == 0)
1445 +#endif
1446 png_write_complete_chunk(png_ptr, png_IDAT, data, size);
1447 +#ifdef PNG_WRITE_APNG_SUPPORTED
1448 + else
1449 + png_write_fdAT(png_ptr, data, size);
1451 +#endif /* WRITE_APNG */
1453 png_ptr->mode |= PNG_HAVE_IDAT;
1455 png_ptr->zstream.next_out = data;
1456 png_ptr->zstream.avail_out = size;
1457 @@ -1049,9 +1064,19 @@ png_compress_IDAT(png_structrp png_ptr,
1458 optimize_cmf(data, png_image_size(png_ptr));
1459 #endif
1461 if (size > 0)
1462 +#ifdef PNG_WRITE_APNG_SUPPORTED
1464 + if (png_ptr->num_frames_written == 0)
1465 +#endif
1466 png_write_complete_chunk(png_ptr, png_IDAT, data, size);
1467 +#ifdef PNG_WRITE_APNG_SUPPORTED
1468 + else
1469 + png_write_fdAT(png_ptr, data, size);
1471 +#endif /* WRITE_APNG */
1473 png_ptr->zstream.avail_out = 0;
1474 png_ptr->zstream.next_out = NULL;
1475 png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
1477 @@ -1884,8 +1909,84 @@ png_write_tIME(png_structrp png_ptr, png
1478 png_write_complete_chunk(png_ptr, png_tIME, buf, 7);
1480 #endif
1482 +#ifdef PNG_WRITE_APNG_SUPPORTED
1483 +void /* PRIVATE */
1484 +png_write_acTL(png_structp png_ptr,
1485 + png_uint_32 num_frames, png_uint_32 num_plays)
1487 + png_byte buf[8];
1489 + png_debug(1, "in png_write_acTL");
1491 + png_ptr->num_frames_to_write = num_frames;
1493 + if ((png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) != 0)
1494 + num_frames--;
1496 + png_save_uint_32(buf, num_frames);
1497 + png_save_uint_32(buf + 4, num_plays);
1499 + png_write_complete_chunk(png_ptr, png_acTL, buf, (png_size_t)8);
1502 +void /* PRIVATE */
1503 +png_write_fcTL(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
1504 + png_uint_32 x_offset, png_uint_32 y_offset,
1505 + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
1506 + png_byte blend_op)
1508 + png_byte buf[26];
1510 + png_debug(1, "in png_write_fcTL");
1512 + if (png_ptr->num_frames_written == 0 && (x_offset != 0 || y_offset != 0))
1513 + png_error(png_ptr, "x and/or y offset for the first frame aren't 0");
1514 + if (png_ptr->num_frames_written == 0 &&
1515 + (width != png_ptr->first_frame_width ||
1516 + height != png_ptr->first_frame_height))
1517 + png_error(png_ptr, "width and/or height in the first frame's fcTL "
1518 + "don't match the ones in IHDR");
1520 + /* more error checking */
1521 + png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset,
1522 + delay_num, delay_den, dispose_op, blend_op);
1524 + png_save_uint_32(buf, png_ptr->next_seq_num);
1525 + png_save_uint_32(buf + 4, width);
1526 + png_save_uint_32(buf + 8, height);
1527 + png_save_uint_32(buf + 12, x_offset);
1528 + png_save_uint_32(buf + 16, y_offset);
1529 + png_save_uint_16(buf + 20, delay_num);
1530 + png_save_uint_16(buf + 22, delay_den);
1531 + buf[24] = dispose_op;
1532 + buf[25] = blend_op;
1534 + png_write_complete_chunk(png_ptr, png_fcTL, buf, (png_size_t)26);
1536 + png_ptr->next_seq_num++;
1539 +void /* PRIVATE */
1540 +png_write_fdAT(png_structp png_ptr,
1541 + png_const_bytep data, png_size_t length)
1543 + png_byte buf[4];
1545 + png_write_chunk_header(png_ptr, png_fdAT, (png_uint_32)(4 + length));
1547 + png_save_uint_32(buf, png_ptr->next_seq_num);
1548 + png_write_chunk_data(png_ptr, buf, 4);
1550 + png_write_chunk_data(png_ptr, data, length);
1552 + png_write_chunk_end(png_ptr);
1554 + png_ptr->next_seq_num++;
1556 +#endif /* WRITE_APNG */
1558 /* Initializes the row writing capability of libpng */
1559 void /* PRIVATE */
1560 png_write_start_row(png_structrp png_ptr)
1562 @@ -2777,5 +2878,40 @@ png_write_filtered_row(png_structrp png_
1563 png_write_flush(png_ptr);
1565 #endif /* WRITE_FLUSH */
1568 +#ifdef PNG_WRITE_APNG_SUPPORTED
1569 +void /* PRIVATE */
1570 +png_write_reset(png_structp png_ptr)
1572 + png_ptr->row_number = 0;
1573 + png_ptr->pass = 0;
1574 + png_ptr->mode &= ~PNG_HAVE_IDAT;
1577 +void /* PRIVATE */
1578 +png_write_reinit(png_structp png_ptr, png_infop info_ptr,
1579 + png_uint_32 width, png_uint_32 height)
1581 + if (png_ptr->num_frames_written == 0 &&
1582 + (width != png_ptr->first_frame_width ||
1583 + height != png_ptr->first_frame_height))
1584 + png_error(png_ptr, "width and/or height in the first frame's fcTL "
1585 + "don't match the ones in IHDR");
1586 + if (width > png_ptr->first_frame_width ||
1587 + height > png_ptr->first_frame_height)
1588 + png_error(png_ptr, "width and/or height for a frame greater than "
1589 + "the ones in IHDR");
1591 + png_set_IHDR(png_ptr, info_ptr, width, height,
1592 + info_ptr->bit_depth, info_ptr->color_type,
1593 + info_ptr->interlace_type, info_ptr->compression_type,
1594 + info_ptr->filter_type);
1596 + png_ptr->width = width;
1597 + png_ptr->height = height;
1598 + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
1599 + png_ptr->usr_width = png_ptr->width;
1601 +#endif /* WRITE_APNG */
1602 #endif /* WRITE */