Commit | Line | Data |
---|---|---|
7472392b VM |
1 | Index: LICENSE |
2 | =================================================================== | |
3 | --- LICENSE | |
4 | +++ LICENSE | |
5 | @@ -8,6 +8,12 @@ | |
6 | If you modify libpng you may insert additional notices immediately following | |
7 | this sentence. | |
8 | ||
9 | +This modified version of libpng code adds animated PNG support and is | |
10 | +released under the libpng license described below. The modifications are | |
11 | +Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2017 Max Stepin, | |
12 | +and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives | |
13 | +surrounding them in the modified libpng source files. | |
14 | + | |
15 | This code is released under the libpng license. | |
16 | ||
17 | libpng versions 1.0.7, July 1, 2000 through 1.6.30, June 28, 2017 are | |
18 | Index: pngread.c | |
19 | =================================================================== | |
20 | --- pngread.c | |
21 | +++ pngread.c | |
22 | @@ -161,6 +161,9 @@ | |
23 | ||
24 | else if (chunk_name == png_IDAT) | |
25 | { | |
26 | +#ifdef PNG_READ_APNG_SUPPORTED | |
27 | + png_have_info(png_ptr, info_ptr); | |
28 | +#endif | |
29 | png_ptr->idat_size = length; | |
30 | break; | |
31 | } | |
32 | @@ -250,6 +253,17 @@ | |
33 | png_handle_iTXt(png_ptr, info_ptr, length); | |
34 | #endif | |
35 | ||
36 | +#ifdef PNG_READ_APNG_SUPPORTED | |
37 | + else if (chunk_name == png_acTL) | |
38 | + png_handle_acTL(png_ptr, info_ptr, length); | |
39 | + | |
40 | + else if (chunk_name == png_fcTL) | |
41 | + png_handle_fcTL(png_ptr, info_ptr, length); | |
42 | + | |
43 | + else if (chunk_name == png_fdAT) | |
44 | + png_handle_fdAT(png_ptr, info_ptr, length); | |
45 | +#endif | |
46 | + | |
47 | else | |
48 | png_handle_unknown(png_ptr, info_ptr, length, | |
49 | PNG_HANDLE_CHUNK_AS_DEFAULT); | |
50 | @@ -257,6 +271,72 @@ | |
51 | } | |
52 | #endif /* SEQUENTIAL_READ */ | |
53 | ||
54 | +#ifdef PNG_READ_APNG_SUPPORTED | |
55 | +void PNGAPI | |
56 | +png_read_frame_head(png_structp png_ptr, png_infop info_ptr) | |
57 | +{ | |
58 | + png_byte have_chunk_after_DAT; /* after IDAT or after fdAT */ | |
59 | + | |
60 | + png_debug(0, "Reading frame head"); | |
61 | + | |
62 | + if ((png_ptr->mode & PNG_HAVE_acTL) == 0) | |
63 | + png_error(png_ptr, "attempt to png_read_frame_head() but " | |
64 | + "no acTL present"); | |
65 | + | |
66 | + /* do nothing for the main IDAT */ | |
67 | + if (png_ptr->num_frames_read == 0) | |
68 | + return; | |
69 | + | |
70 | + png_read_reset(png_ptr); | |
71 | + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; | |
72 | + png_ptr->mode &= ~PNG_HAVE_fcTL; | |
73 | + | |
74 | + have_chunk_after_DAT = 0; | |
75 | + for (;;) | |
76 | + { | |
77 | + png_uint_32 length = png_read_chunk_header(png_ptr); | |
78 | + | |
79 | + if (png_ptr->chunk_name == png_IDAT) | |
80 | + { | |
81 | + /* discard trailing IDATs for the first frame */ | |
82 | + if (have_chunk_after_DAT != 0 || png_ptr->num_frames_read > 1) | |
83 | + png_error(png_ptr, "png_read_frame_head(): out of place IDAT"); | |
84 | + png_crc_finish(png_ptr, length); | |
85 | + } | |
86 | + | |
87 | + else if (png_ptr->chunk_name == png_fcTL) | |
88 | + { | |
89 | + png_handle_fcTL(png_ptr, info_ptr, length); | |
90 | + have_chunk_after_DAT = 1; | |
91 | + } | |
92 | + | |
93 | + else if (png_ptr->chunk_name == png_fdAT) | |
94 | + { | |
95 | + png_ensure_sequence_number(png_ptr, length); | |
96 | + | |
97 | + /* discard trailing fdATs for frames other than the first */ | |
98 | + if (have_chunk_after_DAT == 0 && png_ptr->num_frames_read > 1) | |
99 | + png_crc_finish(png_ptr, length - 4); | |
100 | + else if (png_ptr->mode & PNG_HAVE_fcTL) | |
101 | + { | |
102 | + png_ptr->idat_size = length - 4; | |
103 | + png_ptr->mode |= PNG_HAVE_IDAT; | |
104 | + | |
105 | + break; | |
106 | + } | |
107 | + else | |
108 | + png_error(png_ptr, "png_read_frame_head(): out of place fdAT"); | |
109 | + } | |
110 | + else | |
111 | + { | |
112 | + png_warning(png_ptr, "Skipped (ignored) a chunk " | |
113 | + "between APNG chunks"); | |
114 | + png_crc_finish(png_ptr, length); | |
115 | + } | |
116 | + } | |
117 | +} | |
118 | +#endif /* READ_APNG */ | |
119 | + | |
120 | /* Optional call to update the users info_ptr structure */ | |
121 | void PNGAPI | |
122 | png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) | |
123 | Index: pngget.c | |
124 | =================================================================== | |
125 | --- pngget.c | |
126 | +++ pngget.c | |
127 | @@ -1216,4 +1216,166 @@ | |
128 | # endif | |
129 | #endif | |
130 | ||
131 | +#ifdef PNG_APNG_SUPPORTED | |
132 | +png_uint_32 PNGAPI | |
133 | +png_get_acTL(png_structp png_ptr, png_infop info_ptr, | |
134 | + png_uint_32 *num_frames, png_uint_32 *num_plays) | |
135 | +{ | |
136 | + png_debug1(1, "in %s retrieval function", "acTL"); | |
137 | + | |
138 | + if (png_ptr != NULL && info_ptr != NULL && | |
139 | + (info_ptr->valid & PNG_INFO_acTL) != 0 && | |
140 | + num_frames != NULL && num_plays != NULL) | |
141 | + { | |
142 | + *num_frames = info_ptr->num_frames; | |
143 | + *num_plays = info_ptr->num_plays; | |
144 | + return (1); | |
145 | + } | |
146 | + | |
147 | + return (0); | |
148 | +} | |
149 | + | |
150 | +png_uint_32 PNGAPI | |
151 | +png_get_num_frames(png_structp png_ptr, png_infop info_ptr) | |
152 | +{ | |
153 | + png_debug(1, "in png_get_num_frames()"); | |
154 | + | |
155 | + if (png_ptr != NULL && info_ptr != NULL) | |
156 | + return (info_ptr->num_frames); | |
157 | + return (0); | |
158 | +} | |
159 | + | |
160 | +png_uint_32 PNGAPI | |
161 | +png_get_num_plays(png_structp png_ptr, png_infop info_ptr) | |
162 | +{ | |
163 | + png_debug(1, "in png_get_num_plays()"); | |
164 | + | |
165 | + if (png_ptr != NULL && info_ptr != NULL) | |
166 | + return (info_ptr->num_plays); | |
167 | + return (0); | |
168 | +} | |
169 | + | |
170 | +png_uint_32 PNGAPI | |
171 | +png_get_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, | |
172 | + png_uint_32 *width, png_uint_32 *height, | |
173 | + png_uint_32 *x_offset, png_uint_32 *y_offset, | |
174 | + png_uint_16 *delay_num, png_uint_16 *delay_den, | |
175 | + png_byte *dispose_op, png_byte *blend_op) | |
176 | +{ | |
177 | + png_debug1(1, "in %s retrieval function", "fcTL"); | |
178 | + | |
179 | + if (png_ptr != NULL && info_ptr != NULL && | |
180 | + (info_ptr->valid & PNG_INFO_fcTL) != 0 && | |
181 | + width != NULL && height != NULL && | |
182 | + x_offset != NULL && y_offset != NULL && | |
183 | + delay_num != NULL && delay_den != NULL && | |
184 | + dispose_op != NULL && blend_op != NULL) | |
185 | + { | |
186 | + *width = info_ptr->next_frame_width; | |
187 | + *height = info_ptr->next_frame_height; | |
188 | + *x_offset = info_ptr->next_frame_x_offset; | |
189 | + *y_offset = info_ptr->next_frame_y_offset; | |
190 | + *delay_num = info_ptr->next_frame_delay_num; | |
191 | + *delay_den = info_ptr->next_frame_delay_den; | |
192 | + *dispose_op = info_ptr->next_frame_dispose_op; | |
193 | + *blend_op = info_ptr->next_frame_blend_op; | |
194 | + return (1); | |
195 | + } | |
196 | + | |
197 | + return (0); | |
198 | +} | |
199 | + | |
200 | +png_uint_32 PNGAPI | |
201 | +png_get_next_frame_width(png_structp png_ptr, png_infop info_ptr) | |
202 | +{ | |
203 | + png_debug(1, "in png_get_next_frame_width()"); | |
204 | + | |
205 | + if (png_ptr != NULL && info_ptr != NULL) | |
206 | + return (info_ptr->next_frame_width); | |
207 | + return (0); | |
208 | +} | |
209 | + | |
210 | +png_uint_32 PNGAPI | |
211 | +png_get_next_frame_height(png_structp png_ptr, png_infop info_ptr) | |
212 | +{ | |
213 | + png_debug(1, "in png_get_next_frame_height()"); | |
214 | + | |
215 | + if (png_ptr != NULL && info_ptr != NULL) | |
216 | + return (info_ptr->next_frame_height); | |
217 | + return (0); | |
218 | +} | |
219 | + | |
220 | +png_uint_32 PNGAPI | |
221 | +png_get_next_frame_x_offset(png_structp png_ptr, png_infop info_ptr) | |
222 | +{ | |
223 | + png_debug(1, "in png_get_next_frame_x_offset()"); | |
224 | + | |
225 | + if (png_ptr != NULL && info_ptr != NULL) | |
226 | + return (info_ptr->next_frame_x_offset); | |
227 | + return (0); | |
228 | +} | |
229 | + | |
230 | +png_uint_32 PNGAPI | |
231 | +png_get_next_frame_y_offset(png_structp png_ptr, png_infop info_ptr) | |
232 | +{ | |
233 | + png_debug(1, "in png_get_next_frame_y_offset()"); | |
234 | + | |
235 | + if (png_ptr != NULL && info_ptr != NULL) | |
236 | + return (info_ptr->next_frame_y_offset); | |
237 | + return (0); | |
238 | +} | |
239 | + | |
240 | +png_uint_16 PNGAPI | |
241 | +png_get_next_frame_delay_num(png_structp png_ptr, png_infop info_ptr) | |
242 | +{ | |
243 | + png_debug(1, "in png_get_next_frame_delay_num()"); | |
244 | + | |
245 | + if (png_ptr != NULL && info_ptr != NULL) | |
246 | + return (info_ptr->next_frame_delay_num); | |
247 | + return (0); | |
248 | +} | |
249 | + | |
250 | +png_uint_16 PNGAPI | |
251 | +png_get_next_frame_delay_den(png_structp png_ptr, png_infop info_ptr) | |
252 | +{ | |
253 | + png_debug(1, "in png_get_next_frame_delay_den()"); | |
254 | + | |
255 | + if (png_ptr != NULL && info_ptr != NULL) | |
256 | + return (info_ptr->next_frame_delay_den); | |
257 | + return (0); | |
258 | +} | |
259 | + | |
260 | +png_byte PNGAPI | |
261 | +png_get_next_frame_dispose_op(png_structp png_ptr, png_infop info_ptr) | |
262 | +{ | |
263 | + png_debug(1, "in png_get_next_frame_dispose_op()"); | |
264 | + | |
265 | + if (png_ptr != NULL && info_ptr != NULL) | |
266 | + return (info_ptr->next_frame_dispose_op); | |
267 | + return (0); | |
268 | +} | |
269 | + | |
270 | +png_byte PNGAPI | |
271 | +png_get_next_frame_blend_op(png_structp png_ptr, png_infop info_ptr) | |
272 | +{ | |
273 | + png_debug(1, "in png_get_next_frame_blend_op()"); | |
274 | + | |
275 | + if (png_ptr != NULL && info_ptr != NULL) | |
276 | + return (info_ptr->next_frame_blend_op); | |
277 | + return (0); | |
278 | +} | |
279 | + | |
280 | +png_byte PNGAPI | |
281 | +png_get_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr) | |
282 | +{ | |
283 | + png_debug(1, "in png_first_frame_is_hidden()"); | |
284 | + | |
285 | + if (png_ptr != NULL) | |
286 | + return (png_byte)(png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN); | |
287 | + | |
288 | + PNG_UNUSED(info_ptr) | |
289 | + | |
290 | + return 0; | |
291 | +} | |
292 | +#endif /* APNG */ | |
293 | #endif /* READ || WRITE */ | |
294 | Index: png.c | |
295 | =================================================================== | |
296 | --- png.c | |
297 | +++ png.c | |
298 | @@ -776,17 +776,21 @@ | |
299 | #else | |
300 | # ifdef __STDC__ | |
301 | return PNG_STRING_NEWLINE \ | |
302 | - "libpng version 1.6.30 - June 28, 2017" PNG_STRING_NEWLINE \ | |
303 | + "libpng version 1.6.30+apng - June 28, 2017" PNG_STRING_NEWLINE \ | |
304 | "Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson" \ | |
305 | PNG_STRING_NEWLINE \ | |
306 | "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ | |
307 | "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ | |
308 | - PNG_STRING_NEWLINE; | |
309 | + PNG_STRING_NEWLINE \ | |
310 | + "Portions Copyright (c) 2006-2007 Andrew Smith" PNG_STRING_NEWLINE \ | |
311 | + "Portions Copyright (c) 2008-2017 Max Stepin" PNG_STRING_NEWLINE ; | |
312 | # else | |
313 | - return "libpng version 1.6.30 - June 28, 2017\ | |
314 | + return "libpng version 1.6.30+apng - June 28, 2017\ | |
315 | Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson\ | |
316 | Copyright (c) 1996-1997 Andreas Dilger\ | |
317 | - Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; | |
318 | + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\ | |
319 | + Portions Copyright (c) 2006-2007 Andrew Smith\ | |
320 | + Portions Copyright (c) 2008-2017 Max Stepin"; | |
321 | # endif | |
322 | #endif | |
323 | } | |
324 | Index: png.h | |
325 | =================================================================== | |
326 | --- png.h | |
327 | +++ png.h | |
328 | @@ -23,6 +23,12 @@ | |
329 | * If you modify libpng you may insert additional notices immediately following | |
330 | * this sentence. | |
331 | * | |
332 | + * This modified version of libpng code adds animated PNG support and is | |
333 | + * released under the libpng license described below. The modifications are | |
334 | + * Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2017 Max Stepin, | |
335 | + * and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives | |
336 | + * surrounding them in the modified libpng source files. | |
337 | + * | |
338 | * This code is released under the libpng license. | |
339 | * | |
340 | * libpng versions 1.0.7, July 1, 2000 through 1.6.30, June 28, 2017 are | |
341 | @@ -309,8 +315,9 @@ | |
342 | */ | |
343 | ||
344 | /* Version information for png.h - this should match the version in png.c */ | |
345 | -#define PNG_LIBPNG_VER_STRING "1.6.30" | |
346 | -#define PNG_HEADER_VERSION_STRING " libpng version 1.6.30 - June 28, 2017\n" | |
347 | +#define PNG_LIBPNG_VER_STRING "1.6.30+apng" | |
348 | +#define PNG_HEADER_VERSION_STRING \ | |
349 | + " libpng version 1.6.30+apng - June 28, 2017\n" | |
350 | ||
351 | #define PNG_LIBPNG_VER_SONUM 16 | |
352 | #define PNG_LIBPNG_VER_DLLNUM 16 | |
353 | @@ -361,6 +368,10 @@ | |
354 | # include "pnglibconf.h" | |
355 | #endif | |
356 | ||
357 | +#define PNG_APNG_SUPPORTED | |
358 | +#define PNG_READ_APNG_SUPPORTED | |
359 | +#define PNG_WRITE_APNG_SUPPORTED | |
360 | + | |
361 | #ifndef PNG_VERSION_INFO_ONLY | |
362 | /* Machine specific configuration. */ | |
363 | # include "pngconf.h" | |
364 | @@ -456,6 +467,17 @@ | |
365 | * See pngconf.h for base types that vary by machine/system | |
366 | */ | |
367 | ||
368 | +#ifdef PNG_APNG_SUPPORTED | |
369 | +/* dispose_op flags from inside fcTL */ | |
370 | +#define PNG_DISPOSE_OP_NONE 0x00 | |
371 | +#define PNG_DISPOSE_OP_BACKGROUND 0x01 | |
372 | +#define PNG_DISPOSE_OP_PREVIOUS 0x02 | |
373 | + | |
374 | +/* blend_op flags from inside fcTL */ | |
375 | +#define PNG_BLEND_OP_SOURCE 0x00 | |
376 | +#define PNG_BLEND_OP_OVER 0x01 | |
377 | +#endif /* APNG */ | |
378 | + | |
379 | /* This triggers a compiler error in png.c, if png.c and png.h | |
380 | * do not agree upon the version number. | |
381 | */ | |
382 | @@ -776,6 +798,10 @@ | |
383 | #define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ | |
384 | #define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ | |
385 | #define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ | |
386 | +#ifdef PNG_APNG_SUPPORTED | |
387 | +#define PNG_INFO_acTL 0x10000U | |
388 | +#define PNG_INFO_fcTL 0x20000U | |
389 | +#endif | |
390 | ||
391 | /* This is used for the transformation routines, as some of them | |
392 | * change these values for the row. It also should enable using | |
393 | @@ -813,6 +839,10 @@ | |
394 | #ifdef PNG_PROGRESSIVE_READ_SUPPORTED | |
395 | typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); | |
396 | typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); | |
397 | +#ifdef PNG_APNG_SUPPORTED | |
398 | +typedef PNG_CALLBACK(void, *png_progressive_frame_ptr, (png_structp, | |
399 | + png_uint_32)); | |
400 | +#endif | |
401 | ||
402 | /* The following callback receives png_uint_32 row_number, int pass for the | |
403 | * png_bytep data of the row. When transforming an interlaced image the | |
404 | @@ -3245,6 +3275,75 @@ | |
405 | * END OF HARDWARE AND SOFTWARE OPTIONS | |
406 | ******************************************************************************/ | |
407 | ||
408 | +#ifdef PNG_APNG_SUPPORTED | |
409 | +PNG_EXPORT(246, png_uint_32, png_get_acTL, (png_structp png_ptr, | |
410 | + png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays)); | |
411 | + | |
412 | +PNG_EXPORT(247, png_uint_32, png_set_acTL, (png_structp png_ptr, | |
413 | + png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays)); | |
414 | + | |
415 | +PNG_EXPORT(248, png_uint_32, png_get_num_frames, (png_structp png_ptr, | |
416 | + png_infop info_ptr)); | |
417 | + | |
418 | +PNG_EXPORT(249, png_uint_32, png_get_num_plays, (png_structp png_ptr, | |
419 | + png_infop info_ptr)); | |
420 | + | |
421 | +PNG_EXPORT(250, png_uint_32, png_get_next_frame_fcTL, | |
422 | + (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, | |
423 | + png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset, | |
424 | + png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op, | |
425 | + png_byte *blend_op)); | |
426 | + | |
427 | +PNG_EXPORT(251, png_uint_32, png_set_next_frame_fcTL, | |
428 | + (png_structp png_ptr, png_infop info_ptr, png_uint_32 width, | |
429 | + png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, | |
430 | + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, | |
431 | + png_byte blend_op)); | |
432 | + | |
433 | +PNG_EXPORT(252, png_uint_32, png_get_next_frame_width, | |
434 | + (png_structp png_ptr, png_infop info_ptr)); | |
435 | +PNG_EXPORT(253, png_uint_32, png_get_next_frame_height, | |
436 | + (png_structp png_ptr, png_infop info_ptr)); | |
437 | +PNG_EXPORT(254, png_uint_32, png_get_next_frame_x_offset, | |
438 | + (png_structp png_ptr, png_infop info_ptr)); | |
439 | +PNG_EXPORT(255, png_uint_32, png_get_next_frame_y_offset, | |
440 | + (png_structp png_ptr, png_infop info_ptr)); | |
441 | +PNG_EXPORT(256, png_uint_16, png_get_next_frame_delay_num, | |
442 | + (png_structp png_ptr, png_infop info_ptr)); | |
443 | +PNG_EXPORT(257, png_uint_16, png_get_next_frame_delay_den, | |
444 | + (png_structp png_ptr, png_infop info_ptr)); | |
445 | +PNG_EXPORT(258, png_byte, png_get_next_frame_dispose_op, | |
446 | + (png_structp png_ptr, png_infop info_ptr)); | |
447 | +PNG_EXPORT(259, png_byte, png_get_next_frame_blend_op, | |
448 | + (png_structp png_ptr, png_infop info_ptr)); | |
449 | +PNG_EXPORT(260, png_byte, png_get_first_frame_is_hidden, | |
450 | + (png_structp png_ptr, png_infop info_ptr)); | |
451 | +PNG_EXPORT(261, png_uint_32, png_set_first_frame_is_hidden, | |
452 | + (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden)); | |
453 | + | |
454 | +#ifdef PNG_READ_APNG_SUPPORTED | |
455 | +PNG_EXPORT(262, void, png_read_frame_head, (png_structp png_ptr, | |
456 | + png_infop info_ptr)); | |
457 | +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED | |
458 | +PNG_EXPORT(263, void, png_set_progressive_frame_fn, (png_structp png_ptr, | |
459 | + png_progressive_frame_ptr frame_info_fn, | |
460 | + png_progressive_frame_ptr frame_end_fn)); | |
461 | +#endif /* PROGRESSIVE_READ */ | |
462 | +#endif /* READ_APNG */ | |
463 | + | |
464 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
465 | +PNG_EXPORT(264, void, png_write_frame_head, (png_structp png_ptr, | |
466 | + png_infop info_ptr, png_bytepp row_pointers, | |
467 | + png_uint_32 width, png_uint_32 height, | |
468 | + png_uint_32 x_offset, png_uint_32 y_offset, | |
469 | + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, | |
470 | + png_byte blend_op)); | |
471 | + | |
472 | +PNG_EXPORT(265, void, png_write_frame_tail, (png_structp png_ptr, | |
473 | + png_infop info_ptr)); | |
474 | +#endif /* WRITE_APNG */ | |
475 | +#endif /* APNG */ | |
476 | + | |
477 | /* Maintainer: Put new public prototypes here ^, in libpng.3, in project | |
478 | * defs, and in scripts/symbols.def. | |
479 | */ | |
480 | @@ -3253,7 +3352,11 @@ | |
481 | * one to use is one more than this.) | |
482 | */ | |
483 | #ifdef PNG_EXPORT_LAST_ORDINAL | |
484 | +#ifdef PNG_APNG_SUPPORTED | |
485 | + PNG_EXPORT_LAST_ORDINAL(265); | |
486 | +#else | |
487 | PNG_EXPORT_LAST_ORDINAL(245); | |
488 | +#endif /* APNG */ | |
489 | #endif | |
490 | ||
491 | #ifdef __cplusplus | |
492 | Index: pngpriv.h | |
493 | =================================================================== | |
494 | --- pngpriv.h | |
495 | +++ pngpriv.h | |
496 | @@ -625,6 +625,10 @@ | |
497 | #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */ | |
498 | /* 0x4000U (unused) */ | |
499 | #define PNG_IS_READ_STRUCT 0x8000U /* Else is a write struct */ | |
500 | +#ifdef PNG_APNG_SUPPORTED | |
501 | +#define PNG_HAVE_acTL 0x10000U | |
502 | +#define PNG_HAVE_fcTL 0x20000U | |
503 | +#endif | |
504 | ||
505 | /* Flags for the transformations the PNG library does on the image data */ | |
506 | #define PNG_BGR 0x0001U | |
507 | @@ -860,6 +864,16 @@ | |
508 | #define png_tRNS PNG_U32(116, 82, 78, 83) | |
509 | #define png_zTXt PNG_U32(122, 84, 88, 116) | |
510 | ||
511 | +#ifdef PNG_APNG_SUPPORTED | |
512 | +#define png_acTL PNG_U32( 97, 99, 84, 76) | |
513 | +#define png_fcTL PNG_U32(102, 99, 84, 76) | |
514 | +#define png_fdAT PNG_U32(102, 100, 65, 84) | |
515 | + | |
516 | +/* For png_struct.apng_flags: */ | |
517 | +#define PNG_FIRST_FRAME_HIDDEN 0x0001U | |
518 | +#define PNG_APNG_APP 0x0002U | |
519 | +#endif | |
520 | + | |
521 | /* The following will work on (signed char*) strings, whereas the get_uint_32 | |
522 | * macro will fail on top-bit-set values because of the sign extension. | |
523 | */ | |
524 | @@ -1598,6 +1612,49 @@ | |
525 | ||
526 | #endif /* PROGRESSIVE_READ */ | |
527 | ||
528 | +#ifdef PNG_APNG_SUPPORTED | |
529 | +PNG_INTERNAL_FUNCTION(void,png_ensure_fcTL_is_valid,(png_structp png_ptr, | |
530 | + png_uint_32 width, png_uint_32 height, | |
531 | + png_uint_32 x_offset, png_uint_32 y_offset, | |
532 | + png_uint_16 delay_num, png_uint_16 delay_den, | |
533 | + png_byte dispose_op, png_byte blend_op),PNG_EMPTY); | |
534 | + | |
535 | +#ifdef PNG_READ_APNG_SUPPORTED | |
536 | +PNG_INTERNAL_FUNCTION(void,png_handle_acTL,(png_structp png_ptr, | |
537 | + png_infop info_ptr, png_uint_32 length),PNG_EMPTY); | |
538 | +PNG_INTERNAL_FUNCTION(void,png_handle_fcTL,(png_structp png_ptr, | |
539 | + png_infop info_ptr, png_uint_32 length),PNG_EMPTY); | |
540 | +PNG_INTERNAL_FUNCTION(void,png_handle_fdAT,(png_structp png_ptr, | |
541 | + png_infop info_ptr, png_uint_32 length),PNG_EMPTY); | |
542 | +PNG_INTERNAL_FUNCTION(void,png_have_info,(png_structp png_ptr, | |
543 | + png_infop info_ptr),PNG_EMPTY); | |
544 | +PNG_INTERNAL_FUNCTION(void,png_ensure_sequence_number,(png_structp png_ptr, | |
545 | + png_uint_32 length),PNG_EMPTY); | |
546 | +PNG_INTERNAL_FUNCTION(void,png_read_reset,(png_structp png_ptr),PNG_EMPTY); | |
547 | +PNG_INTERNAL_FUNCTION(void,png_read_reinit,(png_structp png_ptr, | |
548 | + png_infop info_ptr),PNG_EMPTY); | |
549 | +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED | |
550 | +PNG_INTERNAL_FUNCTION(void,png_progressive_read_reset,(png_structp png_ptr), | |
551 | + PNG_EMPTY); | |
552 | +#endif /* PROGRESSIVE_READ */ | |
553 | +#endif /* READ_APNG */ | |
554 | + | |
555 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
556 | +PNG_INTERNAL_FUNCTION(void,png_write_acTL,(png_structp png_ptr, | |
557 | + png_uint_32 num_frames, png_uint_32 num_plays),PNG_EMPTY); | |
558 | +PNG_INTERNAL_FUNCTION(void,png_write_fcTL,(png_structp png_ptr, | |
559 | + png_uint_32 width, png_uint_32 height, | |
560 | + png_uint_32 x_offset, png_uint_32 y_offset, | |
561 | + png_uint_16 delay_num, png_uint_16 delay_den, | |
562 | + png_byte dispose_op, png_byte blend_op),PNG_EMPTY); | |
563 | +PNG_INTERNAL_FUNCTION(void,png_write_fdAT,(png_structp png_ptr, | |
564 | + png_const_bytep data, png_size_t length),PNG_EMPTY); | |
565 | +PNG_INTERNAL_FUNCTION(void,png_write_reset,(png_structp png_ptr),PNG_EMPTY); | |
566 | +PNG_INTERNAL_FUNCTION(void,png_write_reinit,(png_structp png_ptr, | |
567 | + png_infop info_ptr, png_uint_32 width, png_uint_32 height),PNG_EMPTY); | |
568 | +#endif /* WRITE_APNG */ | |
569 | +#endif /* APNG */ | |
570 | + | |
571 | /* Added at libpng version 1.6.0 */ | |
572 | #ifdef PNG_GAMMA_SUPPORTED | |
573 | PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, | |
574 | Index: pnginfo.h | |
575 | =================================================================== | |
576 | --- pnginfo.h | |
577 | +++ pnginfo.h | |
578 | @@ -255,5 +255,18 @@ | |
579 | png_bytepp row_pointers; /* the image bits */ | |
580 | #endif | |
581 | ||
582 | +#ifdef PNG_APNG_SUPPORTED | |
583 | + png_uint_32 num_frames; /* including default image */ | |
584 | + png_uint_32 num_plays; | |
585 | + png_uint_32 next_frame_width; | |
586 | + png_uint_32 next_frame_height; | |
587 | + png_uint_32 next_frame_x_offset; | |
588 | + png_uint_32 next_frame_y_offset; | |
589 | + png_uint_16 next_frame_delay_num; | |
590 | + png_uint_16 next_frame_delay_den; | |
591 | + png_byte next_frame_dispose_op; | |
592 | + png_byte next_frame_blend_op; | |
593 | +#endif | |
594 | + | |
595 | }; | |
596 | #endif /* PNGINFO_H */ | |
597 | Index: pngstruct.h | |
598 | =================================================================== | |
599 | --- pngstruct.h | |
600 | +++ pngstruct.h | |
601 | @@ -403,6 +403,27 @@ | |
602 | png_byte filter_type; | |
603 | #endif | |
604 | ||
605 | +#ifdef PNG_APNG_SUPPORTED | |
606 | + png_uint_32 apng_flags; | |
607 | + png_uint_32 next_seq_num; /* next fcTL/fdAT chunk sequence number */ | |
608 | + png_uint_32 first_frame_width; | |
609 | + png_uint_32 first_frame_height; | |
610 | + | |
611 | +#ifdef PNG_READ_APNG_SUPPORTED | |
612 | + png_uint_32 num_frames_read; /* incremented after all image data of */ | |
613 | + /* a frame is read */ | |
614 | +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED | |
615 | + png_progressive_frame_ptr frame_info_fn; /* frame info read callback */ | |
616 | + png_progressive_frame_ptr frame_end_fn; /* frame data read callback */ | |
617 | +#endif | |
618 | +#endif | |
619 | + | |
620 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
621 | + png_uint_32 num_frames_to_write; | |
622 | + png_uint_32 num_frames_written; | |
623 | +#endif | |
624 | +#endif /* APNG */ | |
625 | + | |
626 | /* New members added in libpng-1.2.0 */ | |
627 | ||
628 | /* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ | |
629 | Index: pngwrite.c | |
630 | =================================================================== | |
631 | --- pngwrite.c | |
632 | +++ pngwrite.c | |
633 | @@ -128,6 +128,10 @@ | |
634 | * the application continues writing the PNG. So check the 'invalid' | |
635 | * flag here too. | |
636 | */ | |
637 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
638 | + if ((info_ptr->valid & PNG_INFO_acTL) != 0) | |
639 | + png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays); | |
640 | +#endif | |
641 | #ifdef PNG_GAMMA_SUPPORTED | |
642 | # ifdef PNG_WRITE_gAMA_SUPPORTED | |
643 | if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && | |
644 | @@ -360,6 +364,11 @@ | |
645 | if ((png_ptr->mode & PNG_HAVE_IDAT) == 0) | |
646 | png_error(png_ptr, "No IDATs written into file"); | |
647 | ||
648 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
649 | + if (png_ptr->num_frames_written != png_ptr->num_frames_to_write) | |
650 | + png_error(png_ptr, "Not enough frames written"); | |
651 | +#endif | |
652 | + | |
653 | #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED | |
654 | if (png_ptr->num_palette_max > png_ptr->num_palette) | |
655 | png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); | |
656 | @@ -2382,4 +2391,42 @@ | |
657 | } | |
658 | #endif /* SIMPLIFIED_WRITE_STDIO */ | |
659 | #endif /* SIMPLIFIED_WRITE */ | |
660 | + | |
661 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
662 | +void PNGAPI | |
663 | +png_write_frame_head(png_structp png_ptr, png_infop info_ptr, | |
664 | + png_bytepp row_pointers, 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, png_byte dispose_op, | |
667 | + png_byte blend_op) | |
668 | +{ | |
669 | + png_debug(1, "in png_write_frame_head"); | |
670 | + | |
671 | + /* there is a chance this has been set after png_write_info was called, | |
672 | + * so it would be set but not written. is there a way to be sure? */ | |
673 | + if ((info_ptr->valid & PNG_INFO_acTL) == 0) | |
674 | + png_error(png_ptr, "png_write_frame_head(): acTL not set"); | |
675 | + | |
676 | + png_write_reset(png_ptr); | |
677 | + | |
678 | + png_write_reinit(png_ptr, info_ptr, width, height); | |
679 | + | |
680 | + if ((png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) == 0 || | |
681 | + png_ptr->num_frames_written != 0) | |
682 | + png_write_fcTL(png_ptr, width, height, x_offset, y_offset, | |
683 | + delay_num, delay_den, dispose_op, blend_op); | |
684 | + | |
685 | + PNG_UNUSED(row_pointers) | |
686 | +} | |
687 | + | |
688 | +void PNGAPI | |
689 | +png_write_frame_tail(png_structp png_ptr, png_infop info_ptr) | |
690 | +{ | |
691 | + png_debug(1, "in png_write_frame_tail"); | |
692 | + | |
693 | + png_ptr->num_frames_written++; | |
694 | + | |
695 | + PNG_UNUSED(info_ptr) | |
696 | +} | |
697 | +#endif /* WRITE_APNG */ | |
698 | #endif /* WRITE */ | |
699 | Index: pngpread.c | |
700 | =================================================================== | |
701 | --- pngpread.c | |
702 | +++ pngpread.c | |
703 | @@ -194,6 +194,89 @@ | |
704 | ||
705 | chunk_name = png_ptr->chunk_name; | |
706 | ||
707 | +#ifdef PNG_READ_APNG_SUPPORTED | |
708 | + if (png_ptr->num_frames_read > 0 && | |
709 | + png_ptr->num_frames_read < info_ptr->num_frames) | |
710 | + { | |
711 | + if (chunk_name == png_IDAT) | |
712 | + { | |
713 | + /* Discard trailing IDATs for the first frame */ | |
714 | + if ((png_ptr->mode & PNG_HAVE_fcTL) != 0 || | |
715 | + png_ptr->num_frames_read > 1) | |
716 | + png_error(png_ptr, "out of place IDAT"); | |
717 | + | |
718 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
719 | + png_crc_finish(png_ptr, png_ptr->push_length); | |
720 | + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; | |
721 | + } | |
722 | + | |
723 | + else if (chunk_name == png_fdAT) | |
724 | + { | |
725 | + PNG_PUSH_SAVE_BUFFER_IF_LT(4) | |
726 | + png_ensure_sequence_number(png_ptr, 4); | |
727 | + | |
728 | + if ((png_ptr->mode & PNG_HAVE_fcTL) == 0) | |
729 | + { | |
730 | + /* Discard trailing fdATs for frames other than the first */ | |
731 | + if (png_ptr->num_frames_read < 2) | |
732 | + png_error(png_ptr, "out of place fdAT"); | |
733 | + | |
734 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
735 | + png_crc_finish(png_ptr, png_ptr->push_length); | |
736 | + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; | |
737 | + } | |
738 | + | |
739 | + else | |
740 | + { | |
741 | + /* frame data follows */ | |
742 | + png_ptr->idat_size = png_ptr->push_length - 4; | |
743 | + png_ptr->mode |= PNG_HAVE_IDAT; | |
744 | + png_ptr->process_mode = PNG_READ_IDAT_MODE; | |
745 | + } | |
746 | + } | |
747 | + | |
748 | + else if (chunk_name == png_fcTL) | |
749 | + { | |
750 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
751 | + png_read_reset(png_ptr); | |
752 | + png_ptr->mode &= ~PNG_HAVE_fcTL; | |
753 | + | |
754 | + png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length); | |
755 | + | |
756 | + if ((png_ptr->mode & PNG_HAVE_fcTL) == 0) | |
757 | + png_error(png_ptr, "missing required fcTL chunk"); | |
758 | + | |
759 | + png_read_reinit(png_ptr, info_ptr); | |
760 | + png_progressive_read_reset(png_ptr); | |
761 | + | |
762 | + if (png_ptr->frame_info_fn != NULL) | |
763 | + (*(png_ptr->frame_info_fn))(png_ptr, png_ptr->num_frames_read); | |
764 | + | |
765 | + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; | |
766 | + } | |
767 | + | |
768 | + else if (chunk_name == png_IEND) | |
769 | + { | |
770 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
771 | + png_warning(png_ptr, "Number of actual frames fewer than expected"); | |
772 | + png_crc_finish(png_ptr, png_ptr->push_length); | |
773 | + png_ptr->process_mode = PNG_READ_DONE_MODE; | |
774 | + png_push_have_end(png_ptr, info_ptr); | |
775 | + } | |
776 | + | |
777 | + else | |
778 | + { | |
779 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
780 | + png_warning(png_ptr, "Skipped (ignored) a chunk " | |
781 | + "between APNG chunks"); | |
782 | + png_crc_finish(png_ptr, png_ptr->push_length); | |
783 | + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; | |
784 | + } | |
785 | + | |
786 | + return; | |
787 | + } | |
788 | +#endif /* READ_APNG */ | |
789 | + | |
790 | if (chunk_name == png_IDAT) | |
791 | { | |
792 | if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) | |
793 | @@ -260,6 +343,9 @@ | |
794 | ||
795 | else if (chunk_name == png_IDAT) | |
796 | { | |
797 | +#ifdef PNG_READ_APNG_SUPPORTED | |
798 | + png_have_info(png_ptr, info_ptr); | |
799 | +#endif | |
800 | png_ptr->idat_size = png_ptr->push_length; | |
801 | png_ptr->process_mode = PNG_READ_IDAT_MODE; | |
802 | png_push_have_info(png_ptr, info_ptr); | |
803 | @@ -406,6 +492,20 @@ | |
804 | } | |
805 | #endif | |
806 | ||
807 | +#ifdef PNG_READ_APNG_SUPPORTED | |
808 | + else if (chunk_name == png_acTL) | |
809 | + { | |
810 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
811 | + png_handle_acTL(png_ptr, info_ptr, png_ptr->push_length); | |
812 | + } | |
813 | + | |
814 | + else if (chunk_name == png_fcTL) | |
815 | + { | |
816 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
817 | + png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length); | |
818 | + } | |
819 | + | |
820 | +#endif /* READ_APNG */ | |
821 | else | |
822 | { | |
823 | PNG_PUSH_SAVE_BUFFER_IF_FULL | |
824 | @@ -538,7 +638,11 @@ | |
825 | png_byte chunk_tag[4]; | |
826 | ||
827 | /* TODO: this code can be commoned up with the same code in push_read */ | |
828 | +#ifdef PNG_READ_APNG_SUPPORTED | |
829 | + PNG_PUSH_SAVE_BUFFER_IF_LT(12) | |
830 | +#else | |
831 | PNG_PUSH_SAVE_BUFFER_IF_LT(8) | |
832 | +#endif | |
833 | png_push_fill_buffer(png_ptr, chunk_length, 4); | |
834 | png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); | |
835 | png_reset_crc(png_ptr); | |
836 | @@ -546,17 +650,60 @@ | |
837 | png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); | |
838 | png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; | |
839 | ||
840 | +#ifdef PNG_READ_APNG_SUPPORTED | |
841 | + if (png_ptr->chunk_name != png_fdAT && png_ptr->num_frames_read > 0) | |
842 | + { | |
843 | + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) != 0) | |
844 | + { | |
845 | + png_ptr->process_mode = PNG_READ_CHUNK_MODE; | |
846 | + if (png_ptr->frame_end_fn != NULL) | |
847 | + (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read); | |
848 | + png_ptr->num_frames_read++; | |
849 | + return; | |
850 | + } | |
851 | + else | |
852 | + { | |
853 | + if (png_ptr->chunk_name == png_IEND) | |
854 | + png_error(png_ptr, "Not enough image data"); | |
855 | + PNG_PUSH_SAVE_BUFFER_IF_FULL | |
856 | + png_warning(png_ptr, "Skipping (ignoring) a chunk between " | |
857 | + "APNG chunks"); | |
858 | + png_crc_finish(png_ptr, png_ptr->push_length); | |
859 | + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; | |
860 | + return; | |
861 | + } | |
862 | + } | |
863 | + else | |
864 | +#endif | |
865 | +#ifdef PNG_READ_APNG_SUPPORTED | |
866 | + if (png_ptr->chunk_name != png_IDAT && png_ptr->num_frames_read == 0) | |
867 | +#else | |
868 | if (png_ptr->chunk_name != png_IDAT) | |
869 | +#endif | |
870 | { | |
871 | png_ptr->process_mode = PNG_READ_CHUNK_MODE; | |
872 | ||
873 | if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) | |
874 | png_error(png_ptr, "Not enough compressed data"); | |
875 | ||
876 | +#ifdef PNG_READ_APNG_SUPPORTED | |
877 | + if (png_ptr->frame_end_fn != NULL) | |
878 | + (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read); | |
879 | + png_ptr->num_frames_read++; | |
880 | +#endif | |
881 | + | |
882 | return; | |
883 | } | |
884 | ||
885 | png_ptr->idat_size = png_ptr->push_length; | |
886 | + | |
887 | +#ifdef PNG_READ_APNG_SUPPORTED | |
888 | + if (png_ptr->num_frames_read > 0) | |
889 | + { | |
890 | + png_ensure_sequence_number(png_ptr, 4); | |
891 | + png_ptr->idat_size -= 4; | |
892 | + } | |
893 | +#endif | |
894 | } | |
895 | ||
896 | if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0) | |
897 | @@ -630,6 +777,16 @@ | |
898 | if (!(buffer_length > 0) || buffer == NULL) | |
899 | png_error(png_ptr, "No IDAT data (internal error)"); | |
900 | ||
901 | +#ifdef PNG_READ_APNG_SUPPORTED | |
902 | + /* If the app is not APNG-aware, decode only the first frame */ | |
903 | + if ((png_ptr->apng_flags & PNG_APNG_APP) == 0 && | |
904 | + png_ptr->num_frames_read > 0) | |
905 | + { | |
906 | + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; | |
907 | + return; | |
908 | + } | |
909 | +#endif | |
910 | + | |
911 | /* This routine must process all the data it has been given | |
912 | * before returning, calling the row callback as required to | |
913 | * handle the uncompressed results. | |
914 | @@ -1084,6 +1241,18 @@ | |
915 | png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); | |
916 | } | |
917 | ||
918 | +#ifdef PNG_READ_APNG_SUPPORTED | |
919 | +void PNGAPI | |
920 | +png_set_progressive_frame_fn(png_structp png_ptr, | |
921 | + png_progressive_frame_ptr frame_info_fn, | |
922 | + png_progressive_frame_ptr frame_end_fn) | |
923 | +{ | |
924 | + png_ptr->frame_info_fn = frame_info_fn; | |
925 | + png_ptr->frame_end_fn = frame_end_fn; | |
926 | + png_ptr->apng_flags |= PNG_APNG_APP; | |
927 | +} | |
928 | +#endif | |
929 | + | |
930 | png_voidp PNGAPI | |
931 | png_get_progressive_ptr(png_const_structrp png_ptr) | |
932 | { | |
933 | Index: pngset.c | |
934 | =================================================================== | |
935 | --- pngset.c | |
936 | +++ pngset.c | |
937 | @@ -241,6 +241,11 @@ | |
938 | info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); | |
939 | ||
940 | info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); | |
941 | + | |
942 | +#ifdef PNG_APNG_SUPPORTED | |
943 | + /* for non-animated png. this may be overwritten from an acTL chunk later */ | |
944 | + info_ptr->num_frames = 1; | |
945 | +#endif | |
946 | } | |
947 | ||
948 | #ifdef PNG_oFFs_SUPPORTED | |
949 | @@ -1111,6 +1116,146 @@ | |
950 | } | |
951 | #endif /* sPLT */ | |
952 | ||
953 | +#ifdef PNG_APNG_SUPPORTED | |
954 | +png_uint_32 PNGAPI | |
955 | +png_set_acTL(png_structp png_ptr, png_infop info_ptr, | |
956 | + png_uint_32 num_frames, png_uint_32 num_plays) | |
957 | +{ | |
958 | + png_debug1(1, "in %s storage function", "acTL"); | |
959 | + | |
960 | + if (png_ptr == NULL || info_ptr == NULL) | |
961 | + { | |
962 | + png_warning(png_ptr, | |
963 | + "Call to png_set_acTL() with NULL png_ptr " | |
964 | + "or info_ptr ignored"); | |
965 | + return (0); | |
966 | + } | |
967 | + if (num_frames == 0) | |
968 | + { | |
969 | + png_warning(png_ptr, | |
970 | + "Ignoring attempt to set acTL with num_frames zero"); | |
971 | + return (0); | |
972 | + } | |
973 | + if (num_frames > PNG_UINT_31_MAX) | |
974 | + { | |
975 | + png_warning(png_ptr, | |
976 | + "Ignoring attempt to set acTL with num_frames > 2^31-1"); | |
977 | + return (0); | |
978 | + } | |
979 | + if (num_plays > PNG_UINT_31_MAX) | |
980 | + { | |
981 | + png_warning(png_ptr, | |
982 | + "Ignoring attempt to set acTL with num_plays > 2^31-1"); | |
983 | + return (0); | |
984 | + } | |
985 | + | |
986 | + info_ptr->num_frames = num_frames; | |
987 | + info_ptr->num_plays = num_plays; | |
988 | + | |
989 | + info_ptr->valid |= PNG_INFO_acTL; | |
990 | + | |
991 | + return (1); | |
992 | +} | |
993 | + | |
994 | +/* delay_num and delay_den can hold any 16-bit values including zero */ | |
995 | +png_uint_32 PNGAPI | |
996 | +png_set_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, | |
997 | + png_uint_32 width, png_uint_32 height, | |
998 | + png_uint_32 x_offset, png_uint_32 y_offset, | |
999 | + png_uint_16 delay_num, png_uint_16 delay_den, | |
1000 | + png_byte dispose_op, png_byte blend_op) | |
1001 | +{ | |
1002 | + png_debug1(1, "in %s storage function", "fcTL"); | |
1003 | + | |
1004 | + if (png_ptr == NULL || info_ptr == NULL) | |
1005 | + { | |
1006 | + png_warning(png_ptr, | |
1007 | + "Call to png_set_fcTL() with NULL png_ptr or info_ptr " | |
1008 | + "ignored"); | |
1009 | + return (0); | |
1010 | + } | |
1011 | + | |
1012 | + png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, | |
1013 | + delay_num, delay_den, dispose_op, blend_op); | |
1014 | + | |
1015 | + if (blend_op == PNG_BLEND_OP_OVER) | |
1016 | + { | |
1017 | + if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0 && | |
1018 | + png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) == 0) | |
1019 | + { | |
1020 | + png_warning(png_ptr, "PNG_BLEND_OP_OVER is meaningless " | |
1021 | + "and wasteful for opaque images, ignored"); | |
1022 | + blend_op = PNG_BLEND_OP_SOURCE; | |
1023 | + } | |
1024 | + } | |
1025 | + | |
1026 | + info_ptr->next_frame_width = width; | |
1027 | + info_ptr->next_frame_height = height; | |
1028 | + info_ptr->next_frame_x_offset = x_offset; | |
1029 | + info_ptr->next_frame_y_offset = y_offset; | |
1030 | + info_ptr->next_frame_delay_num = delay_num; | |
1031 | + info_ptr->next_frame_delay_den = delay_den; | |
1032 | + info_ptr->next_frame_dispose_op = dispose_op; | |
1033 | + info_ptr->next_frame_blend_op = blend_op; | |
1034 | + | |
1035 | + info_ptr->valid |= PNG_INFO_fcTL; | |
1036 | + | |
1037 | + return (1); | |
1038 | +} | |
1039 | + | |
1040 | +void /* PRIVATE */ | |
1041 | +png_ensure_fcTL_is_valid(png_structp png_ptr, | |
1042 | + png_uint_32 width, png_uint_32 height, | |
1043 | + png_uint_32 x_offset, png_uint_32 y_offset, | |
1044 | + png_uint_16 delay_num, png_uint_16 delay_den, | |
1045 | + png_byte dispose_op, png_byte blend_op) | |
1046 | +{ | |
1047 | + if (width == 0 || width > PNG_UINT_31_MAX) | |
1048 | + png_error(png_ptr, "invalid width in fcTL (0 or > 2^31-1)"); | |
1049 | + if (height == 0 || height > PNG_UINT_31_MAX) | |
1050 | + png_error(png_ptr, "invalid height in fcTL (0 or > 2^31-1)"); | |
1051 | + if (x_offset > PNG_UINT_31_MAX) | |
1052 | + png_error(png_ptr, "invalid x_offset in fcTL (> 2^31-1)"); | |
1053 | + if (y_offset > PNG_UINT_31_MAX) | |
1054 | + png_error(png_ptr, "invalid y_offset in fcTL (> 2^31-1)"); | |
1055 | + if (width + x_offset > png_ptr->first_frame_width || | |
1056 | + height + y_offset > png_ptr->first_frame_height) | |
1057 | + png_error(png_ptr, "dimensions of a frame are greater than " | |
1058 | + "the ones in IHDR"); | |
1059 | + | |
1060 | + if (dispose_op != PNG_DISPOSE_OP_NONE && | |
1061 | + dispose_op != PNG_DISPOSE_OP_BACKGROUND && | |
1062 | + dispose_op != PNG_DISPOSE_OP_PREVIOUS) | |
1063 | + png_error(png_ptr, "invalid dispose_op in fcTL"); | |
1064 | + | |
1065 | + if (blend_op != PNG_BLEND_OP_SOURCE && | |
1066 | + blend_op != PNG_BLEND_OP_OVER) | |
1067 | + png_error(png_ptr, "invalid blend_op in fcTL"); | |
1068 | + | |
1069 | + PNG_UNUSED(delay_num) | |
1070 | + PNG_UNUSED(delay_den) | |
1071 | +} | |
1072 | + | |
1073 | +png_uint_32 PNGAPI | |
1074 | +png_set_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr, | |
1075 | + png_byte is_hidden) | |
1076 | +{ | |
1077 | + png_debug(1, "in png_first_frame_is_hidden()"); | |
1078 | + | |
1079 | + if (png_ptr == NULL) | |
1080 | + return 0; | |
1081 | + | |
1082 | + if (is_hidden != 0) | |
1083 | + png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; | |
1084 | + else | |
1085 | + png_ptr->apng_flags &= ~PNG_FIRST_FRAME_HIDDEN; | |
1086 | + | |
1087 | + PNG_UNUSED(info_ptr) | |
1088 | + | |
1089 | + return 1; | |
1090 | +} | |
1091 | +#endif /* APNG */ | |
1092 | + | |
1093 | #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED | |
1094 | static png_byte | |
1095 | check_location(png_const_structrp png_ptr, int location) | |
1096 | Index: pngrutil.c | |
1097 | =================================================================== | |
1098 | --- pngrutil.c | |
1099 | +++ pngrutil.c | |
1100 | @@ -861,6 +861,11 @@ | |
1101 | filter_type = buf[11]; | |
1102 | interlace_type = buf[12]; | |
1103 | ||
1104 | +#ifdef PNG_READ_APNG_SUPPORTED | |
1105 | + png_ptr->first_frame_width = width; | |
1106 | + png_ptr->first_frame_height = height; | |
1107 | +#endif | |
1108 | + | |
1109 | /* Set internal variables */ | |
1110 | png_ptr->width = width; | |
1111 | png_ptr->height = height; | |
1112 | @@ -2764,6 +2769,180 @@ | |
1113 | } | |
1114 | #endif | |
1115 | ||
1116 | +#ifdef PNG_READ_APNG_SUPPORTED | |
1117 | +void /* PRIVATE */ | |
1118 | +png_handle_acTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) | |
1119 | +{ | |
1120 | + png_byte data[8]; | |
1121 | + png_uint_32 num_frames; | |
1122 | + png_uint_32 num_plays; | |
1123 | + png_uint_32 didSet; | |
1124 | + | |
1125 | + png_debug(1, "in png_handle_acTL"); | |
1126 | + | |
1127 | + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) | |
1128 | + { | |
1129 | + png_error(png_ptr, "Missing IHDR before acTL"); | |
1130 | + } | |
1131 | + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) | |
1132 | + { | |
1133 | + png_warning(png_ptr, "Invalid acTL after IDAT skipped"); | |
1134 | + png_crc_finish(png_ptr, length); | |
1135 | + return; | |
1136 | + } | |
1137 | + else if ((png_ptr->mode & PNG_HAVE_acTL) != 0) | |
1138 | + { | |
1139 | + png_warning(png_ptr, "Duplicate acTL skipped"); | |
1140 | + png_crc_finish(png_ptr, length); | |
1141 | + return; | |
1142 | + } | |
1143 | + else if (length != 8) | |
1144 | + { | |
1145 | + png_warning(png_ptr, "acTL with invalid length skipped"); | |
1146 | + png_crc_finish(png_ptr, length); | |
1147 | + return; | |
1148 | + } | |
1149 | + | |
1150 | + png_crc_read(png_ptr, data, 8); | |
1151 | + png_crc_finish(png_ptr, 0); | |
1152 | + | |
1153 | + num_frames = png_get_uint_31(png_ptr, data); | |
1154 | + num_plays = png_get_uint_31(png_ptr, data + 4); | |
1155 | + | |
1156 | + /* the set function will do error checking on num_frames */ | |
1157 | + didSet = png_set_acTL(png_ptr, info_ptr, num_frames, num_plays); | |
1158 | + if (didSet != 0) | |
1159 | + png_ptr->mode |= PNG_HAVE_acTL; | |
1160 | +} | |
1161 | + | |
1162 | +void /* PRIVATE */ | |
1163 | +png_handle_fcTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) | |
1164 | +{ | |
1165 | + png_byte data[22]; | |
1166 | + png_uint_32 width; | |
1167 | + png_uint_32 height; | |
1168 | + png_uint_32 x_offset; | |
1169 | + png_uint_32 y_offset; | |
1170 | + png_uint_16 delay_num; | |
1171 | + png_uint_16 delay_den; | |
1172 | + png_byte dispose_op; | |
1173 | + png_byte blend_op; | |
1174 | + | |
1175 | + png_debug(1, "in png_handle_fcTL"); | |
1176 | + | |
1177 | + png_ensure_sequence_number(png_ptr, length); | |
1178 | + | |
1179 | + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) | |
1180 | + { | |
1181 | + png_error(png_ptr, "Missing IHDR before fcTL"); | |
1182 | + } | |
1183 | + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) | |
1184 | + { | |
1185 | + /* for any frames other then the first this message may be misleading, | |
1186 | + * but correct. PNG_HAVE_IDAT is unset before the frame head is read | |
1187 | + * i can't think of a better message */ | |
1188 | + png_warning(png_ptr, "Invalid fcTL after IDAT skipped"); | |
1189 | + png_crc_finish(png_ptr, length-4); | |
1190 | + return; | |
1191 | + } | |
1192 | + else if ((png_ptr->mode & PNG_HAVE_fcTL) != 0) | |
1193 | + { | |
1194 | + png_warning(png_ptr, "Duplicate fcTL within one frame skipped"); | |
1195 | + png_crc_finish(png_ptr, length-4); | |
1196 | + return; | |
1197 | + } | |
1198 | + else if (length != 26) | |
1199 | + { | |
1200 | + png_warning(png_ptr, "fcTL with invalid length skipped"); | |
1201 | + png_crc_finish(png_ptr, length-4); | |
1202 | + return; | |
1203 | + } | |
1204 | + | |
1205 | + png_crc_read(png_ptr, data, 22); | |
1206 | + png_crc_finish(png_ptr, 0); | |
1207 | + | |
1208 | + width = png_get_uint_31(png_ptr, data); | |
1209 | + height = png_get_uint_31(png_ptr, data + 4); | |
1210 | + x_offset = png_get_uint_31(png_ptr, data + 8); | |
1211 | + y_offset = png_get_uint_31(png_ptr, data + 12); | |
1212 | + delay_num = png_get_uint_16(data + 16); | |
1213 | + delay_den = png_get_uint_16(data + 18); | |
1214 | + dispose_op = data[20]; | |
1215 | + blend_op = data[21]; | |
1216 | + | |
1217 | + if (png_ptr->num_frames_read == 0 && (x_offset != 0 || y_offset != 0)) | |
1218 | + { | |
1219 | + png_warning(png_ptr, "fcTL for the first frame must have zero offset"); | |
1220 | + return; | |
1221 | + } | |
1222 | + | |
1223 | + if (info_ptr != NULL) | |
1224 | + { | |
1225 | + if (png_ptr->num_frames_read == 0 && | |
1226 | + (width != info_ptr->width || height != info_ptr->height)) | |
1227 | + { | |
1228 | + png_warning(png_ptr, "size in first frame's fcTL must match " | |
1229 | + "the size in IHDR"); | |
1230 | + return; | |
1231 | + } | |
1232 | + | |
1233 | + /* The set function will do more error checking */ | |
1234 | + png_set_next_frame_fcTL(png_ptr, info_ptr, width, height, | |
1235 | + x_offset, y_offset, delay_num, delay_den, | |
1236 | + dispose_op, blend_op); | |
1237 | + | |
1238 | + png_read_reinit(png_ptr, info_ptr); | |
1239 | + | |
1240 | + png_ptr->mode |= PNG_HAVE_fcTL; | |
1241 | + } | |
1242 | +} | |
1243 | + | |
1244 | +void /* PRIVATE */ | |
1245 | +png_have_info(png_structp png_ptr, png_infop info_ptr) | |
1246 | +{ | |
1247 | + if ((info_ptr->valid & PNG_INFO_acTL) != 0 && | |
1248 | + (info_ptr->valid & PNG_INFO_fcTL) == 0) | |
1249 | + { | |
1250 | + png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; | |
1251 | + info_ptr->num_frames++; | |
1252 | + } | |
1253 | +} | |
1254 | + | |
1255 | +void /* PRIVATE */ | |
1256 | +png_handle_fdAT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) | |
1257 | +{ | |
1258 | + png_ensure_sequence_number(png_ptr, length); | |
1259 | + | |
1260 | + /* This function is only called from png_read_end(), png_read_info(), | |
1261 | + * and png_push_read_chunk() which means that: | |
1262 | + * - the user doesn't want to read this frame | |
1263 | + * - or this is an out-of-place fdAT | |
1264 | + * in either case it is safe to ignore the chunk with a warning */ | |
1265 | + png_warning(png_ptr, "ignoring fdAT chunk"); | |
1266 | + png_crc_finish(png_ptr, length - 4); | |
1267 | + PNG_UNUSED(info_ptr) | |
1268 | +} | |
1269 | + | |
1270 | +void /* PRIVATE */ | |
1271 | +png_ensure_sequence_number(png_structp png_ptr, png_uint_32 length) | |
1272 | +{ | |
1273 | + png_byte data[4]; | |
1274 | + png_uint_32 sequence_number; | |
1275 | + | |
1276 | + if (length < 4) | |
1277 | + png_error(png_ptr, "invalid fcTL or fdAT chunk found"); | |
1278 | + | |
1279 | + png_crc_read(png_ptr, data, 4); | |
1280 | + sequence_number = png_get_uint_31(png_ptr, data); | |
1281 | + | |
1282 | + if (sequence_number != png_ptr->next_seq_num) | |
1283 | + png_error(png_ptr, "fcTL or fdAT chunk with out-of-order sequence " | |
1284 | + "number found"); | |
1285 | + | |
1286 | + png_ptr->next_seq_num++; | |
1287 | +} | |
1288 | +#endif /* READ_APNG */ | |
1289 | + | |
1290 | #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED | |
1291 | /* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ | |
1292 | static int | |
1293 | @@ -4032,6 +4211,38 @@ | |
1294 | uInt avail_in; | |
1295 | png_bytep buffer; | |
1296 | ||
1297 | +#ifdef PNG_READ_APNG_SUPPORTED | |
1298 | + png_uint_32 bytes_to_skip = 0; | |
1299 | + | |
1300 | + while (png_ptr->idat_size == 0 || bytes_to_skip != 0) | |
1301 | + { | |
1302 | + png_crc_finish(png_ptr, bytes_to_skip); | |
1303 | + bytes_to_skip = 0; | |
1304 | + | |
1305 | + png_ptr->idat_size = png_read_chunk_header(png_ptr); | |
1306 | + if (png_ptr->num_frames_read == 0) | |
1307 | + { | |
1308 | + if (png_ptr->chunk_name != png_IDAT) | |
1309 | + png_error(png_ptr, "Not enough image data"); | |
1310 | + } | |
1311 | + else | |
1312 | + { | |
1313 | + if (png_ptr->chunk_name == png_IEND) | |
1314 | + png_error(png_ptr, "Not enough image data"); | |
1315 | + if (png_ptr->chunk_name != png_fdAT) | |
1316 | + { | |
1317 | + png_warning(png_ptr, "Skipped (ignored) a chunk " | |
1318 | + "between APNG chunks"); | |
1319 | + bytes_to_skip = png_ptr->idat_size; | |
1320 | + continue; | |
1321 | + } | |
1322 | + | |
1323 | + png_ensure_sequence_number(png_ptr, png_ptr->idat_size); | |
1324 | + | |
1325 | + png_ptr->idat_size -= 4; | |
1326 | + } | |
1327 | + } | |
1328 | +#else | |
1329 | while (png_ptr->idat_size == 0) | |
1330 | { | |
1331 | png_crc_finish(png_ptr, 0); | |
1332 | @@ -4043,6 +4254,7 @@ | |
1333 | if (png_ptr->chunk_name != png_IDAT) | |
1334 | png_error(png_ptr, "Not enough image data"); | |
1335 | } | |
1336 | +#endif /* READ_APNG */ | |
1337 | ||
1338 | avail_in = png_ptr->IDAT_read_size; | |
1339 | ||
1340 | @@ -4106,6 +4318,9 @@ | |
1341 | ||
1342 | png_ptr->mode |= PNG_AFTER_IDAT; | |
1343 | png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; | |
1344 | +#ifdef PNG_READ_APNG_SUPPORTED | |
1345 | + png_ptr->num_frames_read++; | |
1346 | +#endif | |
1347 | ||
1348 | if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) | |
1349 | png_chunk_benign_error(png_ptr, "Extra compressed data"); | |
1350 | @@ -4544,4 +4759,80 @@ | |
1351 | ||
1352 | png_ptr->flags |= PNG_FLAG_ROW_INIT; | |
1353 | } | |
1354 | + | |
1355 | +#ifdef PNG_READ_APNG_SUPPORTED | |
1356 | +/* This function is to be called after the main IDAT set has been read and | |
1357 | + * before a new IDAT is read. It resets some parts of png_ptr | |
1358 | + * to make them usable by the read functions again */ | |
1359 | +void /* PRIVATE */ | |
1360 | +png_read_reset(png_structp png_ptr) | |
1361 | +{ | |
1362 | + png_ptr->mode &= ~PNG_HAVE_IDAT; | |
1363 | + png_ptr->mode &= ~PNG_AFTER_IDAT; | |
1364 | + png_ptr->row_number = 0; | |
1365 | + png_ptr->pass = 0; | |
1366 | +} | |
1367 | + | |
1368 | +void /* PRIVATE */ | |
1369 | +png_read_reinit(png_structp png_ptr, png_infop info_ptr) | |
1370 | +{ | |
1371 | + png_ptr->width = info_ptr->next_frame_width; | |
1372 | + png_ptr->height = info_ptr->next_frame_height; | |
1373 | + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); | |
1374 | + png_ptr->info_rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, | |
1375 | + png_ptr->width); | |
1376 | + if (png_ptr->prev_row != NULL) | |
1377 | + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); | |
1378 | +} | |
1379 | + | |
1380 | +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED | |
1381 | +/* same as png_read_reset() but for the progressive reader */ | |
1382 | +void /* PRIVATE */ | |
1383 | +png_progressive_read_reset(png_structp png_ptr) | |
1384 | +{ | |
1385 | +#ifdef PNG_READ_INTERLACING_SUPPORTED | |
1386 | + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ | |
1387 | + | |
1388 | + /* Start of interlace block */ | |
1389 | + static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; | |
1390 | + | |
1391 | + /* Offset to next interlace block */ | |
1392 | + static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; | |
1393 | + | |
1394 | + /* Start of interlace block in the y direction */ | |
1395 | + static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; | |
1396 | + | |
1397 | + /* Offset to next interlace block in the y direction */ | |
1398 | + static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; | |
1399 | + | |
1400 | + if (png_ptr->interlaced != 0) | |
1401 | + { | |
1402 | + if ((png_ptr->transformations & PNG_INTERLACE) == 0) | |
1403 | + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - | |
1404 | + png_pass_ystart[0]) / png_pass_yinc[0]; | |
1405 | + else | |
1406 | + png_ptr->num_rows = png_ptr->height; | |
1407 | + | |
1408 | + png_ptr->iwidth = (png_ptr->width + | |
1409 | + png_pass_inc[png_ptr->pass] - 1 - | |
1410 | + png_pass_start[png_ptr->pass]) / | |
1411 | + png_pass_inc[png_ptr->pass]; | |
1412 | + } | |
1413 | + else | |
1414 | +#endif /* READ_INTERLACING */ | |
1415 | + { | |
1416 | + png_ptr->num_rows = png_ptr->height; | |
1417 | + png_ptr->iwidth = png_ptr->width; | |
1418 | + } | |
1419 | + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED; | |
1420 | + if (inflateReset(&(png_ptr->zstream)) != Z_OK) | |
1421 | + png_error(png_ptr, "inflateReset failed"); | |
1422 | + png_ptr->zstream.avail_in = 0; | |
1423 | + png_ptr->zstream.next_in = 0; | |
1424 | + png_ptr->zstream.next_out = png_ptr->row_buf; | |
1425 | + png_ptr->zstream.avail_out = (uInt)PNG_ROWBYTES(png_ptr->pixel_depth, | |
1426 | + png_ptr->iwidth) + 1; | |
1427 | +} | |
1428 | +#endif /* PROGRESSIVE_READ */ | |
1429 | +#endif /* READ_APNG */ | |
1430 | #endif /* READ */ | |
1431 | Index: pngwutil.c | |
1432 | =================================================================== | |
1433 | --- pngwutil.c | |
1434 | +++ pngwutil.c | |
1435 | @@ -822,6 +822,11 @@ | |
1436 | /* Write the chunk */ | |
1437 | png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); | |
1438 | ||
1439 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1440 | + png_ptr->first_frame_width = width; | |
1441 | + png_ptr->first_frame_height = height; | |
1442 | +#endif | |
1443 | + | |
1444 | if ((png_ptr->do_filter) == PNG_NO_FILTERS) | |
1445 | { | |
1446 | if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || | |
1447 | @@ -1004,7 +1009,17 @@ | |
1448 | #endif | |
1449 | ||
1450 | if (size > 0) | |
1451 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1452 | + { | |
1453 | + if (png_ptr->num_frames_written == 0) | |
1454 | +#endif | |
1455 | png_write_complete_chunk(png_ptr, png_IDAT, data, size); | |
1456 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1457 | + else | |
1458 | + png_write_fdAT(png_ptr, data, size); | |
1459 | + } | |
1460 | +#endif /* WRITE_APNG */ | |
1461 | + | |
1462 | png_ptr->mode |= PNG_HAVE_IDAT; | |
1463 | ||
1464 | png_ptr->zstream.next_out = data; | |
1465 | @@ -1051,7 +1066,17 @@ | |
1466 | #endif | |
1467 | ||
1468 | if (size > 0) | |
1469 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1470 | + { | |
1471 | + if (png_ptr->num_frames_written == 0) | |
1472 | +#endif | |
1473 | png_write_complete_chunk(png_ptr, png_IDAT, data, size); | |
1474 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1475 | + else | |
1476 | + png_write_fdAT(png_ptr, data, size); | |
1477 | + } | |
1478 | +#endif /* WRITE_APNG */ | |
1479 | + | |
1480 | png_ptr->zstream.avail_out = 0; | |
1481 | png_ptr->zstream.next_out = NULL; | |
1482 | png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; | |
1483 | @@ -1865,6 +1890,82 @@ | |
1484 | } | |
1485 | #endif | |
1486 | ||
1487 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1488 | +void /* PRIVATE */ | |
1489 | +png_write_acTL(png_structp png_ptr, | |
1490 | + png_uint_32 num_frames, png_uint_32 num_plays) | |
1491 | +{ | |
1492 | + png_byte buf[8]; | |
1493 | + | |
1494 | + png_debug(1, "in png_write_acTL"); | |
1495 | + | |
1496 | + png_ptr->num_frames_to_write = num_frames; | |
1497 | + | |
1498 | + if ((png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) != 0) | |
1499 | + num_frames--; | |
1500 | + | |
1501 | + png_save_uint_32(buf, num_frames); | |
1502 | + png_save_uint_32(buf + 4, num_plays); | |
1503 | + | |
1504 | + png_write_complete_chunk(png_ptr, png_acTL, buf, (png_size_t)8); | |
1505 | +} | |
1506 | + | |
1507 | +void /* PRIVATE */ | |
1508 | +png_write_fcTL(png_structp png_ptr, png_uint_32 width, png_uint_32 height, | |
1509 | + png_uint_32 x_offset, png_uint_32 y_offset, | |
1510 | + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, | |
1511 | + png_byte blend_op) | |
1512 | +{ | |
1513 | + png_byte buf[26]; | |
1514 | + | |
1515 | + png_debug(1, "in png_write_fcTL"); | |
1516 | + | |
1517 | + if (png_ptr->num_frames_written == 0 && (x_offset != 0 || y_offset != 0)) | |
1518 | + png_error(png_ptr, "x and/or y offset for the first frame aren't 0"); | |
1519 | + if (png_ptr->num_frames_written == 0 && | |
1520 | + (width != png_ptr->first_frame_width || | |
1521 | + height != png_ptr->first_frame_height)) | |
1522 | + png_error(png_ptr, "width and/or height in the first frame's fcTL " | |
1523 | + "don't match the ones in IHDR"); | |
1524 | + | |
1525 | + /* more error checking */ | |
1526 | + png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, | |
1527 | + delay_num, delay_den, dispose_op, blend_op); | |
1528 | + | |
1529 | + png_save_uint_32(buf, png_ptr->next_seq_num); | |
1530 | + png_save_uint_32(buf + 4, width); | |
1531 | + png_save_uint_32(buf + 8, height); | |
1532 | + png_save_uint_32(buf + 12, x_offset); | |
1533 | + png_save_uint_32(buf + 16, y_offset); | |
1534 | + png_save_uint_16(buf + 20, delay_num); | |
1535 | + png_save_uint_16(buf + 22, delay_den); | |
1536 | + buf[24] = dispose_op; | |
1537 | + buf[25] = blend_op; | |
1538 | + | |
1539 | + png_write_complete_chunk(png_ptr, png_fcTL, buf, (png_size_t)26); | |
1540 | + | |
1541 | + png_ptr->next_seq_num++; | |
1542 | +} | |
1543 | + | |
1544 | +void /* PRIVATE */ | |
1545 | +png_write_fdAT(png_structp png_ptr, | |
1546 | + png_const_bytep data, png_size_t length) | |
1547 | +{ | |
1548 | + png_byte buf[4]; | |
1549 | + | |
1550 | + png_write_chunk_header(png_ptr, png_fdAT, (png_uint_32)(4 + length)); | |
1551 | + | |
1552 | + png_save_uint_32(buf, png_ptr->next_seq_num); | |
1553 | + png_write_chunk_data(png_ptr, buf, 4); | |
1554 | + | |
1555 | + png_write_chunk_data(png_ptr, data, length); | |
1556 | + | |
1557 | + png_write_chunk_end(png_ptr); | |
1558 | + | |
1559 | + png_ptr->next_seq_num++; | |
1560 | +} | |
1561 | +#endif /* WRITE_APNG */ | |
1562 | + | |
1563 | /* Initializes the row writing capability of libpng */ | |
1564 | void /* PRIVATE */ | |
1565 | png_write_start_row(png_structrp png_ptr) | |
1566 | @@ -2759,4 +2860,39 @@ | |
1567 | } | |
1568 | #endif /* WRITE_FLUSH */ | |
1569 | } | |
1570 | + | |
1571 | +#ifdef PNG_WRITE_APNG_SUPPORTED | |
1572 | +void /* PRIVATE */ | |
1573 | +png_write_reset(png_structp png_ptr) | |
1574 | +{ | |
1575 | + png_ptr->row_number = 0; | |
1576 | + png_ptr->pass = 0; | |
1577 | + png_ptr->mode &= ~PNG_HAVE_IDAT; | |
1578 | +} | |
1579 | + | |
1580 | +void /* PRIVATE */ | |
1581 | +png_write_reinit(png_structp png_ptr, png_infop info_ptr, | |
1582 | + png_uint_32 width, png_uint_32 height) | |
1583 | +{ | |
1584 | + if (png_ptr->num_frames_written == 0 && | |
1585 | + (width != png_ptr->first_frame_width || | |
1586 | + height != png_ptr->first_frame_height)) | |
1587 | + png_error(png_ptr, "width and/or height in the first frame's fcTL " | |
1588 | + "don't match the ones in IHDR"); | |
1589 | + if (width > png_ptr->first_frame_width || | |
1590 | + height > png_ptr->first_frame_height) | |
1591 | + png_error(png_ptr, "width and/or height for a frame greater than " | |
1592 | + "the ones in IHDR"); | |
1593 | + | |
1594 | + png_set_IHDR(png_ptr, info_ptr, width, height, | |
1595 | + info_ptr->bit_depth, info_ptr->color_type, | |
1596 | + info_ptr->interlace_type, info_ptr->compression_type, | |
1597 | + info_ptr->filter_type); | |
1598 | + | |
1599 | + png_ptr->width = width; | |
1600 | + png_ptr->height = height; | |
1601 | + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); | |
1602 | + png_ptr->usr_width = png_ptr->width; | |
1603 | +} | |
1604 | +#endif /* WRITE_APNG */ | |
1605 | #endif /* WRITE */ | |
1606 | Index: scripts/symbols.def | |
1607 | =================================================================== | |
1608 | --- scripts/symbols.def | |
1609 | +++ scripts/symbols.def | |
1610 | @@ -250,3 +250,23 @@ | |
1611 | png_get_palette_max @243 | |
1612 | png_set_option @244 | |
1613 | png_image_write_to_memory @245 | |
1614 | + png_get_acTL @246 | |
1615 | + png_set_acTL @247 | |
1616 | + png_get_num_frames @248 | |
1617 | + png_get_num_plays @249 | |
1618 | + png_get_next_frame_fcTL @250 | |
1619 | + png_set_next_frame_fcTL @251 | |
1620 | + png_get_next_frame_width @252 | |
1621 | + png_get_next_frame_height @253 | |
1622 | + png_get_next_frame_x_offset @254 | |
1623 | + png_get_next_frame_y_offset @255 | |
1624 | + png_get_next_frame_delay_num @256 | |
1625 | + png_get_next_frame_delay_den @257 | |
1626 | + png_get_next_frame_dispose_op @258 | |
1627 | + png_get_next_frame_blend_op @259 | |
1628 | + png_get_first_frame_is_hidden @260 | |
1629 | + png_set_first_frame_is_hidden @261 | |
1630 | + png_read_frame_head @262 | |
1631 | + png_set_progressive_frame_fn @263 | |
1632 | + png_write_frame_head @264 | |
1633 | + png_write_frame_tail @265 |