1 :
2 : /* pngwrite.c - general routines to write a PNG file
3 : *
4 : * Last changed in libpng 1.5.7 [December 15, 2011]
5 : * Copyright (c) 1998-2011 Glenn Randers-Pehrson
6 : * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
7 : * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
8 : *
9 : * This code is released under the libpng license.
10 : * For conditions of distribution and use, see the disclaimer
11 : * and license in png.h
12 : */
13 :
14 : #include "pngpriv.h"
15 :
16 : #ifdef PNG_WRITE_SUPPORTED
17 :
18 : /* Writes all the PNG information. This is the suggested way to use the
19 : * library. If you have a new chunk to add, make a function to write it,
20 : * and put it in the correct location here. If you want the chunk written
21 : * after the image data, put it in png_write_end(). I strongly encourage
22 : * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
23 : * the chunk, as that will keep the code from breaking if you want to just
24 : * write a plain PNG file. If you have long comments, I suggest writing
25 : * them in png_write_end(), and compressing them.
26 : */
27 : void PNGAPI
28 26 : png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
29 : {
30 : png_debug(1, "in png_write_info_before_PLTE");
31 :
32 26 : if (png_ptr == NULL || info_ptr == NULL)
33 0 : return;
34 :
35 26 : if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
36 : {
37 : /* Write PNG signature */
38 26 : png_write_sig(png_ptr);
39 :
40 : #ifdef PNG_MNG_FEATURES_SUPPORTED
41 : if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \
42 : (png_ptr->mng_features_permitted))
43 : {
44 : png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
45 : png_ptr->mng_features_permitted = 0;
46 : }
47 : #endif
48 :
49 : /* Write IHDR information. */
50 104 : png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
51 78 : info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
52 26 : info_ptr->filter_type,
53 : #ifdef PNG_WRITE_INTERLACING_SUPPORTED
54 : info_ptr->interlace_type);
55 : #else
56 : 0);
57 : #endif
58 : /* The rest of these check to see if the valid field has the appropriate
59 : * flag set, and if it does, writes the chunk.
60 : */
61 : #ifdef PNG_WRITE_APNG_SUPPORTED
62 26 : if (info_ptr->valid & PNG_INFO_acTL)
63 6 : png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays);
64 : #endif
65 : #ifdef PNG_WRITE_gAMA_SUPPORTED
66 : if (info_ptr->valid & PNG_INFO_gAMA)
67 : png_write_gAMA_fixed(png_ptr, info_ptr->gamma);
68 : #endif
69 : #ifdef PNG_WRITE_sRGB_SUPPORTED
70 : if (info_ptr->valid & PNG_INFO_sRGB)
71 : png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
72 : #endif
73 :
74 : #ifdef PNG_WRITE_iCCP_SUPPORTED
75 : if (info_ptr->valid & PNG_INFO_iCCP)
76 : png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
77 : (png_charp)info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
78 : #endif
79 : #ifdef PNG_WRITE_sBIT_SUPPORTED
80 : if (info_ptr->valid & PNG_INFO_sBIT)
81 : png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
82 : #endif
83 : #ifdef PNG_WRITE_cHRM_SUPPORTED
84 : if (info_ptr->valid & PNG_INFO_cHRM)
85 : png_write_cHRM_fixed(png_ptr,
86 : info_ptr->x_white, info_ptr->y_white,
87 : info_ptr->x_red, info_ptr->y_red,
88 : info_ptr->x_green, info_ptr->y_green,
89 : info_ptr->x_blue, info_ptr->y_blue);
90 : #endif
91 :
92 : #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
93 : if (info_ptr->unknown_chunks_num)
94 : {
95 : png_unknown_chunk *up;
96 :
97 : png_debug(5, "writing extra chunks");
98 :
99 : for (up = info_ptr->unknown_chunks;
100 : up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
101 : up++)
102 : {
103 : int keep = png_handle_as_unknown(png_ptr, up->name);
104 :
105 : if (keep != PNG_HANDLE_CHUNK_NEVER &&
106 : up->location &&
107 : !(up->location & PNG_HAVE_PLTE) &&
108 : !(up->location & PNG_HAVE_IDAT) &&
109 : !(up->location & PNG_AFTER_IDAT) &&
110 : ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
111 : (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
112 : {
113 : if (up->size == 0)
114 : png_warning(png_ptr, "Writing zero-length unknown chunk");
115 :
116 : png_write_chunk(png_ptr, up->name, up->data, up->size);
117 : }
118 : }
119 : }
120 : #endif
121 26 : png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
122 : }
123 : }
124 :
125 : void PNGAPI
126 26 : png_write_info(png_structp png_ptr, png_infop info_ptr)
127 : {
128 : #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
129 : int i;
130 : #endif
131 :
132 : png_debug(1, "in png_write_info");
133 :
134 26 : if (png_ptr == NULL || info_ptr == NULL)
135 0 : return;
136 :
137 26 : png_write_info_before_PLTE(png_ptr, info_ptr);
138 :
139 26 : if (info_ptr->valid & PNG_INFO_PLTE)
140 0 : png_write_PLTE(png_ptr, info_ptr->palette,
141 0 : (png_uint_32)info_ptr->num_palette);
142 :
143 26 : else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
144 0 : png_error(png_ptr, "Valid palette required for paletted images");
145 :
146 : #ifdef PNG_WRITE_tRNS_SUPPORTED
147 26 : if (info_ptr->valid & PNG_INFO_tRNS)
148 : {
149 : #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
150 : /* Invert the alpha channel (in tRNS) */
151 : if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
152 : info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
153 : {
154 : int j;
155 : for (j = 0; j<(int)info_ptr->num_trans; j++)
156 : info_ptr->trans_alpha[j] =
157 : (png_byte)(255 - info_ptr->trans_alpha[j]);
158 : }
159 : #endif
160 0 : png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
161 0 : info_ptr->num_trans, info_ptr->color_type);
162 : }
163 : #endif
164 : #ifdef PNG_WRITE_bKGD_SUPPORTED
165 : if (info_ptr->valid & PNG_INFO_bKGD)
166 : png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
167 : #endif
168 :
169 : #ifdef PNG_WRITE_hIST_SUPPORTED
170 : if (info_ptr->valid & PNG_INFO_hIST)
171 : png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
172 : #endif
173 :
174 : #ifdef PNG_WRITE_oFFs_SUPPORTED
175 : if (info_ptr->valid & PNG_INFO_oFFs)
176 : png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
177 : info_ptr->offset_unit_type);
178 : #endif
179 :
180 : #ifdef PNG_WRITE_pCAL_SUPPORTED
181 : if (info_ptr->valid & PNG_INFO_pCAL)
182 : png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
183 : info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
184 : info_ptr->pcal_units, info_ptr->pcal_params);
185 : #endif
186 :
187 : #ifdef PNG_WRITE_sCAL_SUPPORTED
188 : if (info_ptr->valid & PNG_INFO_sCAL)
189 : png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
190 : info_ptr->scal_s_width, info_ptr->scal_s_height);
191 : #endif /* sCAL */
192 :
193 : #ifdef PNG_WRITE_pHYs_SUPPORTED
194 : if (info_ptr->valid & PNG_INFO_pHYs)
195 : png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
196 : info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
197 : #endif /* pHYs */
198 :
199 : #ifdef PNG_WRITE_tIME_SUPPORTED
200 : if (info_ptr->valid & PNG_INFO_tIME)
201 : {
202 : png_write_tIME(png_ptr, &(info_ptr->mod_time));
203 : png_ptr->mode |= PNG_WROTE_tIME;
204 : }
205 : #endif /* tIME */
206 :
207 : #ifdef PNG_WRITE_sPLT_SUPPORTED
208 : if (info_ptr->valid & PNG_INFO_sPLT)
209 : for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
210 : png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
211 : #endif /* sPLT */
212 :
213 : #ifdef PNG_WRITE_TEXT_SUPPORTED
214 : /* Check to see if we need to write text chunks */
215 : for (i = 0; i < info_ptr->num_text; i++)
216 : {
217 : png_debug2(2, "Writing header text chunk %d, type %d", i,
218 : info_ptr->text[i].compression);
219 : /* An internationalized chunk? */
220 : if (info_ptr->text[i].compression > 0)
221 : {
222 : #ifdef PNG_WRITE_iTXt_SUPPORTED
223 : /* Write international chunk */
224 : png_write_iTXt(png_ptr,
225 : info_ptr->text[i].compression,
226 : info_ptr->text[i].key,
227 : info_ptr->text[i].lang,
228 : info_ptr->text[i].lang_key,
229 : info_ptr->text[i].text);
230 : #else
231 : png_warning(png_ptr, "Unable to write international text");
232 : #endif
233 : /* Mark this chunk as written */
234 : info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
235 : }
236 :
237 : /* If we want a compressed text chunk */
238 : else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
239 : {
240 : #ifdef PNG_WRITE_zTXt_SUPPORTED
241 : /* Write compressed chunk */
242 : png_write_zTXt(png_ptr, info_ptr->text[i].key,
243 : info_ptr->text[i].text, 0,
244 : info_ptr->text[i].compression);
245 : #else
246 : png_warning(png_ptr, "Unable to write compressed text");
247 : #endif
248 : /* Mark this chunk as written */
249 : info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
250 : }
251 :
252 : else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
253 : {
254 : #ifdef PNG_WRITE_tEXt_SUPPORTED
255 : /* Write uncompressed chunk */
256 : png_write_tEXt(png_ptr, info_ptr->text[i].key,
257 : info_ptr->text[i].text,
258 : 0);
259 : /* Mark this chunk as written */
260 : info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
261 : #else
262 : /* Can't get here */
263 : png_warning(png_ptr, "Unable to write uncompressed text");
264 : #endif
265 : }
266 : }
267 : #endif /* tEXt */
268 :
269 : #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
270 : if (info_ptr->unknown_chunks_num)
271 : {
272 : png_unknown_chunk *up;
273 :
274 : png_debug(5, "writing extra chunks");
275 :
276 : for (up = info_ptr->unknown_chunks;
277 : up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
278 : up++)
279 : {
280 : int keep = png_handle_as_unknown(png_ptr, up->name);
281 : if (keep != PNG_HANDLE_CHUNK_NEVER &&
282 : up->location &&
283 : (up->location & PNG_HAVE_PLTE) &&
284 : !(up->location & PNG_HAVE_IDAT) &&
285 : !(up->location & PNG_AFTER_IDAT) &&
286 : ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
287 : (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
288 : {
289 : png_write_chunk(png_ptr, up->name, up->data, up->size);
290 : }
291 : }
292 : }
293 : #endif
294 : }
295 :
296 : /* Writes the end of the PNG file. If you don't want to write comments or
297 : * time information, you can pass NULL for info. If you already wrote these
298 : * in png_write_info(), do not write them again here. If you have long
299 : * comments, I suggest writing them here, and compressing them.
300 : */
301 : void PNGAPI
302 26 : png_write_end(png_structp png_ptr, png_infop info_ptr)
303 : {
304 : png_debug(1, "in png_write_end");
305 :
306 26 : if (png_ptr == NULL)
307 0 : return;
308 :
309 26 : if (!(png_ptr->mode & PNG_HAVE_IDAT))
310 0 : png_error(png_ptr, "No IDATs written into file");
311 :
312 : #ifdef PNG_WRITE_APNG_SUPPORTED
313 26 : if (png_ptr->num_frames_written != png_ptr->num_frames_to_write)
314 0 : png_error(png_ptr, "Not enough frames written");
315 : #endif
316 :
317 : /* See if user wants us to write information chunks */
318 : if (info_ptr != NULL)
319 : {
320 : #ifdef PNG_WRITE_TEXT_SUPPORTED
321 : int i; /* local index variable */
322 : #endif
323 : #ifdef PNG_WRITE_tIME_SUPPORTED
324 : /* Check to see if user has supplied a time chunk */
325 : if ((info_ptr->valid & PNG_INFO_tIME) &&
326 : !(png_ptr->mode & PNG_WROTE_tIME))
327 : png_write_tIME(png_ptr, &(info_ptr->mod_time));
328 :
329 : #endif
330 : #ifdef PNG_WRITE_TEXT_SUPPORTED
331 : /* Loop through comment chunks */
332 : for (i = 0; i < info_ptr->num_text; i++)
333 : {
334 : png_debug2(2, "Writing trailer text chunk %d, type %d", i,
335 : info_ptr->text[i].compression);
336 : /* An internationalized chunk? */
337 : if (info_ptr->text[i].compression > 0)
338 : {
339 : #ifdef PNG_WRITE_iTXt_SUPPORTED
340 : /* Write international chunk */
341 : png_write_iTXt(png_ptr,
342 : info_ptr->text[i].compression,
343 : info_ptr->text[i].key,
344 : info_ptr->text[i].lang,
345 : info_ptr->text[i].lang_key,
346 : info_ptr->text[i].text);
347 : #else
348 : png_warning(png_ptr, "Unable to write international text");
349 : #endif
350 : /* Mark this chunk as written */
351 : info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
352 : }
353 :
354 : else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
355 : {
356 : #ifdef PNG_WRITE_zTXt_SUPPORTED
357 : /* Write compressed chunk */
358 : png_write_zTXt(png_ptr, info_ptr->text[i].key,
359 : info_ptr->text[i].text, 0,
360 : info_ptr->text[i].compression);
361 : #else
362 : png_warning(png_ptr, "Unable to write compressed text");
363 : #endif
364 : /* Mark this chunk as written */
365 : info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
366 : }
367 :
368 : else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
369 : {
370 : #ifdef PNG_WRITE_tEXt_SUPPORTED
371 : /* Write uncompressed chunk */
372 : png_write_tEXt(png_ptr, info_ptr->text[i].key,
373 : info_ptr->text[i].text, 0);
374 : #else
375 : png_warning(png_ptr, "Unable to write uncompressed text");
376 : #endif
377 :
378 : /* Mark this chunk as written */
379 : info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
380 : }
381 : }
382 : #endif
383 : #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
384 : if (info_ptr->unknown_chunks_num)
385 : {
386 : png_unknown_chunk *up;
387 :
388 : png_debug(5, "writing extra chunks");
389 :
390 : for (up = info_ptr->unknown_chunks;
391 : up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
392 : up++)
393 : {
394 : int keep = png_handle_as_unknown(png_ptr, up->name);
395 : if (keep != PNG_HANDLE_CHUNK_NEVER &&
396 : up->location &&
397 : (up->location & PNG_AFTER_IDAT) &&
398 : ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
399 : (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
400 : {
401 : png_write_chunk(png_ptr, up->name, up->data, up->size);
402 : }
403 : }
404 : }
405 : #endif
406 : }
407 :
408 26 : png_ptr->mode |= PNG_AFTER_IDAT;
409 :
410 : /* Write end of PNG file */
411 26 : png_write_IEND(png_ptr);
412 : /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
413 : * and restored again in libpng-1.2.30, may cause some applications that
414 : * do not set png_ptr->output_flush_fn to crash. If your application
415 : * experiences a problem, please try building libpng with
416 : * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
417 : * png-mng-implement at lists.sf.net .
418 : */
419 : #ifdef PNG_WRITE_FLUSH_SUPPORTED
420 : # ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
421 : png_flush(png_ptr);
422 : # endif
423 : #endif
424 : }
425 :
426 : #ifdef PNG_CONVERT_tIME_SUPPORTED
427 : /* "tm" structure is not supported on WindowsCE */
428 : void PNGAPI
429 : png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm FAR * ttime)
430 : {
431 : png_debug(1, "in png_convert_from_struct_tm");
432 :
433 : ptime->year = (png_uint_16)(1900 + ttime->tm_year);
434 : ptime->month = (png_byte)(ttime->tm_mon + 1);
435 : ptime->day = (png_byte)ttime->tm_mday;
436 : ptime->hour = (png_byte)ttime->tm_hour;
437 : ptime->minute = (png_byte)ttime->tm_min;
438 : ptime->second = (png_byte)ttime->tm_sec;
439 : }
440 :
441 : void PNGAPI
442 : png_convert_from_time_t(png_timep ptime, time_t ttime)
443 : {
444 : struct tm *tbuf;
445 :
446 : png_debug(1, "in png_convert_from_time_t");
447 :
448 : tbuf = gmtime(&ttime);
449 : png_convert_from_struct_tm(ptime, tbuf);
450 : }
451 : #endif
452 :
453 : /* Initialize png_ptr structure, and allocate any memory needed */
454 26 : PNG_FUNCTION(png_structp,PNGAPI
455 : png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
456 : png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
457 : {
458 : #ifdef PNG_USER_MEM_SUPPORTED
459 : return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
460 : warn_fn, NULL, NULL, NULL));
461 : }
462 :
463 : /* Alternate initialize png_ptr structure, and allocate any memory needed */
464 : static void png_reset_filter_heuristics(png_structp png_ptr); /* forward decl */
465 :
466 : PNG_FUNCTION(png_structp,PNGAPI
467 : png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
468 : png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
469 : png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
470 : {
471 : #endif /* PNG_USER_MEM_SUPPORTED */
472 26 : volatile int png_cleanup_needed = 0;
473 : #ifdef PNG_SETJMP_SUPPORTED
474 : volatile
475 : #endif
476 : png_structp png_ptr;
477 : #ifdef PNG_SETJMP_SUPPORTED
478 : #ifdef USE_FAR_KEYWORD
479 : jmp_buf tmp_jmpbuf;
480 : #endif
481 : #endif
482 :
483 : png_debug(1, "in png_create_write_struct");
484 :
485 : #ifdef PNG_USER_MEM_SUPPORTED
486 : png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
487 : (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
488 : #else
489 26 : png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
490 : #endif /* PNG_USER_MEM_SUPPORTED */
491 26 : if (png_ptr == NULL)
492 0 : return (NULL);
493 :
494 : /* Added at libpng-1.2.6 */
495 : #ifdef PNG_SET_USER_LIMITS_SUPPORTED
496 : png_ptr->user_width_max = PNG_USER_WIDTH_MAX;
497 : png_ptr->user_height_max = PNG_USER_HEIGHT_MAX;
498 : #endif
499 :
500 : #ifdef PNG_SETJMP_SUPPORTED
501 : /* Applications that neglect to set up their own setjmp() and then
502 : * encounter a png_error() will longjmp here. Since the jmpbuf is
503 : * then meaningless we abort instead of returning.
504 : */
505 : #ifdef USE_FAR_KEYWORD
506 : if (setjmp(tmp_jmpbuf))
507 : #else
508 26 : if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */
509 : #endif
510 : #ifdef USE_FAR_KEYWORD
511 : png_memcpy(png_jmpbuf(png_ptr), tmp_jmpbuf, png_sizeof(jmp_buf));
512 : #endif
513 0 : PNG_ABORT();
514 : #endif
515 :
516 : #ifdef PNG_USER_MEM_SUPPORTED
517 : png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
518 : #endif /* PNG_USER_MEM_SUPPORTED */
519 26 : png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
520 :
521 26 : if (!png_user_version_check(png_ptr, user_png_ver))
522 0 : png_cleanup_needed = 1;
523 :
524 : /* Initialize zbuf - compression buffer */
525 26 : png_ptr->zbuf_size = PNG_ZBUF_SIZE;
526 :
527 26 : if (!png_cleanup_needed)
528 : {
529 52 : png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr,
530 26 : png_ptr->zbuf_size);
531 26 : if (png_ptr->zbuf == NULL)
532 0 : png_cleanup_needed = 1;
533 : }
534 :
535 26 : if (png_cleanup_needed)
536 : {
537 : /* Clean up PNG structure and deallocate any memory. */
538 0 : png_free(png_ptr, png_ptr->zbuf);
539 0 : png_ptr->zbuf = NULL;
540 : #ifdef PNG_USER_MEM_SUPPORTED
541 : png_destroy_struct_2((png_voidp)png_ptr,
542 : (png_free_ptr)free_fn, (png_voidp)mem_ptr);
543 : #else
544 0 : png_destroy_struct((png_voidp)png_ptr);
545 : #endif
546 0 : return (NULL);
547 : }
548 :
549 26 : png_set_write_fn(png_ptr, NULL, NULL, NULL);
550 :
551 : #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
552 : png_reset_filter_heuristics(png_ptr);
553 : #endif
554 :
555 26 : return (png_ptr);
556 : }
557 :
558 :
559 : /* Write a few rows of image data. If the image is interlaced,
560 : * either you will have to write the 7 sub images, or, if you
561 : * have called png_set_interlace_handling(), you will have to
562 : * "write" the image seven times.
563 : */
564 : void PNGAPI
565 0 : png_write_rows(png_structp png_ptr, png_bytepp row,
566 : png_uint_32 num_rows)
567 : {
568 : png_uint_32 i; /* row counter */
569 : png_bytepp rp; /* row pointer */
570 :
571 : png_debug(1, "in png_write_rows");
572 :
573 0 : if (png_ptr == NULL)
574 0 : return;
575 :
576 : /* Loop through the rows */
577 0 : for (i = 0, rp = row; i < num_rows; i++, rp++)
578 : {
579 0 : png_write_row(png_ptr, *rp);
580 : }
581 : }
582 :
583 : /* Write the image. You only need to call this function once, even
584 : * if you are writing an interlaced image.
585 : */
586 : void PNGAPI
587 0 : png_write_image(png_structp png_ptr, png_bytepp image)
588 : {
589 : png_uint_32 i; /* row index */
590 : int pass, num_pass; /* pass variables */
591 : png_bytepp rp; /* points to current row */
592 :
593 0 : if (png_ptr == NULL)
594 0 : return;
595 :
596 : png_debug(1, "in png_write_image");
597 :
598 : #ifdef PNG_WRITE_INTERLACING_SUPPORTED
599 : /* Initialize interlace handling. If image is not interlaced,
600 : * this will set pass to 1
601 : */
602 : num_pass = png_set_interlace_handling(png_ptr);
603 : #else
604 0 : num_pass = 1;
605 : #endif
606 : /* Loop through passes */
607 0 : for (pass = 0; pass < num_pass; pass++)
608 : {
609 : /* Loop through image */
610 0 : for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
611 : {
612 0 : png_write_row(png_ptr, *rp);
613 : }
614 : }
615 : }
616 :
617 : /* Called by user to write a row of image data */
618 : void PNGAPI
619 299 : png_write_row(png_structp png_ptr, png_const_bytep row)
620 : {
621 : /* 1.5.6: moved from png_struct to be a local structure: */
622 : png_row_info row_info;
623 :
624 299 : if (png_ptr == NULL)
625 0 : return;
626 :
627 : png_debug2(1, "in png_write_row (row %u, pass %d)",
628 : png_ptr->row_number, png_ptr->pass);
629 :
630 : /* Initialize transformations and other stuff if first time */
631 299 : if (png_ptr->row_number == 0 && png_ptr->pass == 0)
632 : {
633 : /* Make sure we wrote the header info */
634 39 : if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
635 0 : png_error(png_ptr,
636 : "png_write_info was never called before png_write_row");
637 :
638 : /* Check for transforms that have been set but were defined out */
639 : #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
640 : if (png_ptr->transformations & PNG_INVERT_MONO)
641 : png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
642 : #endif
643 :
644 : #if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
645 : if (png_ptr->transformations & PNG_FILLER)
646 : png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
647 : #endif
648 : #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
649 : defined(PNG_READ_PACKSWAP_SUPPORTED)
650 : if (png_ptr->transformations & PNG_PACKSWAP)
651 : png_warning(png_ptr,
652 : "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
653 : #endif
654 :
655 : #if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
656 : if (png_ptr->transformations & PNG_PACK)
657 : png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
658 : #endif
659 :
660 : #if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
661 : if (png_ptr->transformations & PNG_SHIFT)
662 : png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
663 : #endif
664 :
665 : #if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
666 : if (png_ptr->transformations & PNG_BGR)
667 : png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
668 : #endif
669 :
670 : #if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
671 : if (png_ptr->transformations & PNG_SWAP_BYTES)
672 : png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
673 : #endif
674 :
675 39 : png_write_start_row(png_ptr);
676 : }
677 :
678 : #ifdef PNG_WRITE_INTERLACING_SUPPORTED
679 : /* If interlaced and not interested in row, return */
680 : if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
681 : {
682 : switch (png_ptr->pass)
683 : {
684 : case 0:
685 : if (png_ptr->row_number & 0x07)
686 : {
687 : png_write_finish_row(png_ptr);
688 : return;
689 : }
690 : break;
691 :
692 : case 1:
693 : if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
694 : {
695 : png_write_finish_row(png_ptr);
696 : return;
697 : }
698 : break;
699 :
700 : case 2:
701 : if ((png_ptr->row_number & 0x07) != 4)
702 : {
703 : png_write_finish_row(png_ptr);
704 : return;
705 : }
706 : break;
707 :
708 : case 3:
709 : if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
710 : {
711 : png_write_finish_row(png_ptr);
712 : return;
713 : }
714 : break;
715 :
716 : case 4:
717 : if ((png_ptr->row_number & 0x03) != 2)
718 : {
719 : png_write_finish_row(png_ptr);
720 : return;
721 : }
722 : break;
723 :
724 : case 5:
725 : if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
726 : {
727 : png_write_finish_row(png_ptr);
728 : return;
729 : }
730 : break;
731 :
732 : case 6:
733 : if (!(png_ptr->row_number & 0x01))
734 : {
735 : png_write_finish_row(png_ptr);
736 : return;
737 : }
738 : break;
739 :
740 : default: /* error: ignore it */
741 : break;
742 : }
743 : }
744 : #endif
745 :
746 : /* Set up row info for transformations */
747 299 : row_info.color_type = png_ptr->color_type;
748 299 : row_info.width = png_ptr->usr_width;
749 299 : row_info.channels = png_ptr->usr_channels;
750 299 : row_info.bit_depth = png_ptr->usr_bit_depth;
751 299 : row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);
752 299 : row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
753 :
754 : png_debug1(3, "row_info->color_type = %d", row_info.color_type);
755 : png_debug1(3, "row_info->width = %u", row_info.width);
756 : png_debug1(3, "row_info->channels = %d", row_info.channels);
757 : png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);
758 : png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);
759 : png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);
760 :
761 : /* Copy user's row into buffer, leaving room for filter byte. */
762 299 : png_memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
763 :
764 : #ifdef PNG_WRITE_INTERLACING_SUPPORTED
765 : /* Handle interlacing */
766 : if (png_ptr->interlaced && png_ptr->pass < 6 &&
767 : (png_ptr->transformations & PNG_INTERLACE))
768 : {
769 : png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
770 : /* This should always get caught above, but still ... */
771 : if (!(row_info.width))
772 : {
773 : png_write_finish_row(png_ptr);
774 : return;
775 : }
776 : }
777 : #endif
778 :
779 : #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
780 : /* Handle other transformations */
781 : if (png_ptr->transformations)
782 : png_do_write_transformations(png_ptr, &row_info);
783 : #endif
784 :
785 : /* At this point the row_info pixel depth must match the 'transformed' depth,
786 : * which is also the output depth.
787 : */
788 598 : if (row_info.pixel_depth != png_ptr->pixel_depth ||
789 299 : row_info.pixel_depth != png_ptr->transformed_pixel_depth)
790 0 : png_error(png_ptr, "internal write transform logic error");
791 :
792 : #ifdef PNG_MNG_FEATURES_SUPPORTED
793 : /* Write filter_method 64 (intrapixel differencing) only if
794 : * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
795 : * 2. Libpng did not write a PNG signature (this filter_method is only
796 : * used in PNG datastreams that are embedded in MNG datastreams) and
797 : * 3. The application called png_permit_mng_features with a mask that
798 : * included PNG_FLAG_MNG_FILTER_64 and
799 : * 4. The filter_method is 64 and
800 : * 5. The color_type is RGB or RGBA
801 : */
802 : if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
803 : (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
804 : {
805 : /* Intrapixel differencing */
806 : png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
807 : }
808 : #endif
809 :
810 : /* Find a filter if necessary, filter the row and write it out. */
811 299 : png_write_find_filter(png_ptr, &row_info);
812 :
813 299 : if (png_ptr->write_row_fn != NULL)
814 0 : (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
815 : }
816 :
817 : #ifdef PNG_WRITE_FLUSH_SUPPORTED
818 : /* Set the automatic flush interval or 0 to turn flushing off */
819 : void PNGAPI
820 0 : png_set_flush(png_structp png_ptr, int nrows)
821 : {
822 : png_debug(1, "in png_set_flush");
823 :
824 0 : if (png_ptr == NULL)
825 0 : return;
826 :
827 0 : png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
828 : }
829 :
830 : /* Flush the current output buffers now */
831 : void PNGAPI
832 0 : png_write_flush(png_structp png_ptr)
833 : {
834 : int wrote_IDAT;
835 :
836 : png_debug(1, "in png_write_flush");
837 :
838 0 : if (png_ptr == NULL)
839 0 : return;
840 :
841 : /* We have already written out all of the data */
842 0 : if (png_ptr->row_number >= png_ptr->num_rows)
843 0 : return;
844 :
845 : do
846 : {
847 : int ret;
848 :
849 : /* Compress the data */
850 0 : ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
851 0 : wrote_IDAT = 0;
852 :
853 : /* Check for compression errors */
854 0 : if (ret != Z_OK)
855 : {
856 0 : if (png_ptr->zstream.msg != NULL)
857 0 : png_error(png_ptr, png_ptr->zstream.msg);
858 :
859 : else
860 0 : png_error(png_ptr, "zlib error");
861 : }
862 :
863 0 : if (!(png_ptr->zstream.avail_out))
864 : {
865 : /* Write the IDAT and reset the zlib output buffer */
866 0 : png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
867 0 : wrote_IDAT = 1;
868 : }
869 0 : } while (wrote_IDAT == 1);
870 :
871 : /* If there is any data left to be output, write it into a new IDAT */
872 0 : if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
873 : {
874 : /* Write the IDAT and reset the zlib output buffer */
875 0 : png_write_IDAT(png_ptr, png_ptr->zbuf,
876 0 : png_ptr->zbuf_size - png_ptr->zstream.avail_out);
877 : }
878 0 : png_ptr->flush_rows = 0;
879 0 : png_flush(png_ptr);
880 : }
881 : #endif /* PNG_WRITE_FLUSH_SUPPORTED */
882 :
883 : /* Free all memory used by the write */
884 : void PNGAPI
885 26 : png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
886 : {
887 26 : png_structp png_ptr = NULL;
888 26 : png_infop info_ptr = NULL;
889 : #ifdef PNG_USER_MEM_SUPPORTED
890 : png_free_ptr free_fn = NULL;
891 : png_voidp mem_ptr = NULL;
892 : #endif
893 :
894 : png_debug(1, "in png_destroy_write_struct");
895 :
896 26 : if (png_ptr_ptr != NULL)
897 26 : png_ptr = *png_ptr_ptr;
898 :
899 : #ifdef PNG_USER_MEM_SUPPORTED
900 : if (png_ptr != NULL)
901 : {
902 : free_fn = png_ptr->free_fn;
903 : mem_ptr = png_ptr->mem_ptr;
904 : }
905 : #endif
906 :
907 26 : if (info_ptr_ptr != NULL)
908 26 : info_ptr = *info_ptr_ptr;
909 :
910 26 : if (info_ptr != NULL)
911 : {
912 26 : if (png_ptr != NULL)
913 : {
914 26 : png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
915 :
916 : #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
917 : if (png_ptr->num_chunk_list)
918 : {
919 : png_free(png_ptr, png_ptr->chunk_list);
920 : png_ptr->num_chunk_list = 0;
921 : }
922 : #endif
923 : }
924 :
925 : #ifdef PNG_USER_MEM_SUPPORTED
926 : png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
927 : (png_voidp)mem_ptr);
928 : #else
929 26 : png_destroy_struct((png_voidp)info_ptr);
930 : #endif
931 26 : *info_ptr_ptr = NULL;
932 : }
933 :
934 26 : if (png_ptr != NULL)
935 : {
936 26 : png_write_destroy(png_ptr);
937 : #ifdef PNG_USER_MEM_SUPPORTED
938 : png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
939 : (png_voidp)mem_ptr);
940 : #else
941 26 : png_destroy_struct((png_voidp)png_ptr);
942 : #endif
943 26 : *png_ptr_ptr = NULL;
944 : }
945 26 : }
946 :
947 :
948 : /* Free any memory used in png_ptr struct (old method) */
949 : void /* PRIVATE */
950 26 : png_write_destroy(png_structp png_ptr)
951 : {
952 : #ifdef PNG_SETJMP_SUPPORTED
953 : jmp_buf tmp_jmp; /* Save jump buffer */
954 : #endif
955 : png_error_ptr error_fn;
956 : #ifdef PNG_WARNINGS_SUPPORTED
957 : png_error_ptr warning_fn;
958 : #endif
959 : png_voidp error_ptr;
960 : #ifdef PNG_USER_MEM_SUPPORTED
961 : png_free_ptr free_fn;
962 : #endif
963 :
964 : png_debug(1, "in png_write_destroy");
965 :
966 : /* Free any memory zlib uses */
967 26 : if (png_ptr->zlib_state != PNG_ZLIB_UNINITIALIZED)
968 26 : deflateEnd(&png_ptr->zstream);
969 :
970 : /* Free our memory. png_free checks NULL for us. */
971 26 : png_free(png_ptr, png_ptr->zbuf);
972 26 : png_free(png_ptr, png_ptr->row_buf);
973 : #ifdef PNG_WRITE_FILTER_SUPPORTED
974 : png_free(png_ptr, png_ptr->prev_row);
975 : png_free(png_ptr, png_ptr->sub_row);
976 : png_free(png_ptr, png_ptr->up_row);
977 : png_free(png_ptr, png_ptr->avg_row);
978 : png_free(png_ptr, png_ptr->paeth_row);
979 : #endif
980 :
981 : #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
982 : /* Use this to save a little code space, it doesn't free the filter_costs */
983 : png_reset_filter_heuristics(png_ptr);
984 : png_free(png_ptr, png_ptr->filter_costs);
985 : png_free(png_ptr, png_ptr->inv_filter_costs);
986 : #endif
987 :
988 : #ifdef PNG_SETJMP_SUPPORTED
989 : /* Reset structure */
990 26 : png_memcpy(tmp_jmp, png_ptr->longjmp_buffer, png_sizeof(jmp_buf));
991 : #endif
992 :
993 26 : error_fn = png_ptr->error_fn;
994 : #ifdef PNG_WARNINGS_SUPPORTED
995 : warning_fn = png_ptr->warning_fn;
996 : #endif
997 26 : error_ptr = png_ptr->error_ptr;
998 : #ifdef PNG_USER_MEM_SUPPORTED
999 : free_fn = png_ptr->free_fn;
1000 : #endif
1001 :
1002 26 : png_memset(png_ptr, 0, png_sizeof(png_struct));
1003 :
1004 26 : png_ptr->error_fn = error_fn;
1005 : #ifdef PNG_WARNINGS_SUPPORTED
1006 : png_ptr->warning_fn = warning_fn;
1007 : #endif
1008 26 : png_ptr->error_ptr = error_ptr;
1009 : #ifdef PNG_USER_MEM_SUPPORTED
1010 : png_ptr->free_fn = free_fn;
1011 : #endif
1012 :
1013 : #ifdef PNG_SETJMP_SUPPORTED
1014 26 : png_memcpy(png_ptr->longjmp_buffer, tmp_jmp, png_sizeof(jmp_buf));
1015 : #endif
1016 26 : }
1017 :
1018 : /* Allow the application to select one or more row filters to use. */
1019 : void PNGAPI
1020 0 : png_set_filter(png_structp png_ptr, int method, int filters)
1021 : {
1022 : png_debug(1, "in png_set_filter");
1023 :
1024 0 : if (png_ptr == NULL)
1025 0 : return;
1026 :
1027 : #ifdef PNG_MNG_FEATURES_SUPPORTED
1028 : if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
1029 : (method == PNG_INTRAPIXEL_DIFFERENCING))
1030 : method = PNG_FILTER_TYPE_BASE;
1031 :
1032 : #endif
1033 0 : if (method == PNG_FILTER_TYPE_BASE)
1034 : {
1035 0 : switch (filters & (PNG_ALL_FILTERS | 0x07))
1036 : {
1037 : #ifdef PNG_WRITE_FILTER_SUPPORTED
1038 : case 5:
1039 : case 6:
1040 : case 7: png_warning(png_ptr, "Unknown row filter for method 0");
1041 : #endif /* PNG_WRITE_FILTER_SUPPORTED */
1042 : case PNG_FILTER_VALUE_NONE:
1043 0 : png_ptr->do_filter = PNG_FILTER_NONE; break;
1044 :
1045 : #ifdef PNG_WRITE_FILTER_SUPPORTED
1046 : case PNG_FILTER_VALUE_SUB:
1047 : png_ptr->do_filter = PNG_FILTER_SUB; break;
1048 :
1049 : case PNG_FILTER_VALUE_UP:
1050 : png_ptr->do_filter = PNG_FILTER_UP; break;
1051 :
1052 : case PNG_FILTER_VALUE_AVG:
1053 : png_ptr->do_filter = PNG_FILTER_AVG; break;
1054 :
1055 : case PNG_FILTER_VALUE_PAETH:
1056 : png_ptr->do_filter = PNG_FILTER_PAETH; break;
1057 :
1058 : default:
1059 : png_ptr->do_filter = (png_byte)filters; break;
1060 : #else
1061 : default:
1062 : png_warning(png_ptr, "Unknown row filter for method 0");
1063 : #endif /* PNG_WRITE_FILTER_SUPPORTED */
1064 : }
1065 :
1066 : /* If we have allocated the row_buf, this means we have already started
1067 : * with the image and we should have allocated all of the filter buffers
1068 : * that have been selected. If prev_row isn't already allocated, then
1069 : * it is too late to start using the filters that need it, since we
1070 : * will be missing the data in the previous row. If an application
1071 : * wants to start and stop using particular filters during compression,
1072 : * it should start out with all of the filters, and then add and
1073 : * remove them after the start of compression.
1074 : */
1075 0 : if (png_ptr->row_buf != NULL)
1076 : {
1077 : #ifdef PNG_WRITE_FILTER_SUPPORTED
1078 : if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
1079 : {
1080 : png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
1081 : (png_ptr->rowbytes + 1));
1082 : png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
1083 : }
1084 :
1085 : if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
1086 : {
1087 : if (png_ptr->prev_row == NULL)
1088 : {
1089 : png_warning(png_ptr, "Can't add Up filter after starting");
1090 : png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
1091 : ~PNG_FILTER_UP);
1092 : }
1093 :
1094 : else
1095 : {
1096 : png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
1097 : (png_ptr->rowbytes + 1));
1098 : png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
1099 : }
1100 : }
1101 :
1102 : if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
1103 : {
1104 : if (png_ptr->prev_row == NULL)
1105 : {
1106 : png_warning(png_ptr, "Can't add Average filter after starting");
1107 : png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
1108 : ~PNG_FILTER_AVG);
1109 : }
1110 :
1111 : else
1112 : {
1113 : png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
1114 : (png_ptr->rowbytes + 1));
1115 : png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
1116 : }
1117 : }
1118 :
1119 : if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
1120 : png_ptr->paeth_row == NULL)
1121 : {
1122 : if (png_ptr->prev_row == NULL)
1123 : {
1124 : png_warning(png_ptr, "Can't add Paeth filter after starting");
1125 : png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
1126 : }
1127 :
1128 : else
1129 : {
1130 : png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
1131 : (png_ptr->rowbytes + 1));
1132 : png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
1133 : }
1134 : }
1135 :
1136 : if (png_ptr->do_filter == PNG_NO_FILTERS)
1137 : #endif /* PNG_WRITE_FILTER_SUPPORTED */
1138 0 : png_ptr->do_filter = PNG_FILTER_NONE;
1139 : }
1140 : }
1141 : else
1142 0 : png_error(png_ptr, "Unknown custom filter method");
1143 : }
1144 :
1145 : /* This allows us to influence the way in which libpng chooses the "best"
1146 : * filter for the current scanline. While the "minimum-sum-of-absolute-
1147 : * differences metric is relatively fast and effective, there is some
1148 : * question as to whether it can be improved upon by trying to keep the
1149 : * filtered data going to zlib more consistent, hopefully resulting in
1150 : * better compression.
1151 : */
1152 : #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */
1153 : /* Convenience reset API. */
1154 : static void
1155 : png_reset_filter_heuristics(png_structp png_ptr)
1156 : {
1157 : /* Clear out any old values in the 'weights' - this must be done because if
1158 : * the app calls set_filter_heuristics multiple times with different
1159 : * 'num_weights' values we would otherwise potentially have wrong sized
1160 : * arrays.
1161 : */
1162 : png_ptr->num_prev_filters = 0;
1163 : png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
1164 : if (png_ptr->prev_filters != NULL)
1165 : {
1166 : png_bytep old = png_ptr->prev_filters;
1167 : png_ptr->prev_filters = NULL;
1168 : png_free(png_ptr, old);
1169 : }
1170 : if (png_ptr->filter_weights != NULL)
1171 : {
1172 : png_uint_16p old = png_ptr->filter_weights;
1173 : png_ptr->filter_weights = NULL;
1174 : png_free(png_ptr, old);
1175 : }
1176 :
1177 : if (png_ptr->inv_filter_weights != NULL)
1178 : {
1179 : png_uint_16p old = png_ptr->inv_filter_weights;
1180 : png_ptr->inv_filter_weights = NULL;
1181 : png_free(png_ptr, old);
1182 : }
1183 :
1184 : /* Leave the filter_costs - this array is fixed size. */
1185 : }
1186 :
1187 : static int
1188 : png_init_filter_heuristics(png_structp png_ptr, int heuristic_method,
1189 : int num_weights)
1190 : {
1191 : if (png_ptr == NULL)
1192 : return 0;
1193 :
1194 : /* Clear out the arrays */
1195 : png_reset_filter_heuristics(png_ptr);
1196 :
1197 : /* Check arguments; the 'reset' function makes the correct settings for the
1198 : * unweighted case, but we must handle the weight case by initializing the
1199 : * arrays for the caller.
1200 : */
1201 : if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
1202 : {
1203 : int i;
1204 :
1205 : if (num_weights > 0)
1206 : {
1207 : png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
1208 : (png_uint_32)(png_sizeof(png_byte) * num_weights));
1209 :
1210 : /* To make sure that the weighting starts out fairly */
1211 : for (i = 0; i < num_weights; i++)
1212 : {
1213 : png_ptr->prev_filters[i] = 255;
1214 : }
1215 :
1216 : png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
1217 : (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1218 :
1219 : png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
1220 : (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1221 :
1222 : for (i = 0; i < num_weights; i++)
1223 : {
1224 : png_ptr->inv_filter_weights[i] =
1225 : png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
1226 : }
1227 :
1228 : /* Safe to set this now */
1229 : png_ptr->num_prev_filters = (png_byte)num_weights;
1230 : }
1231 :
1232 : /* If, in the future, there are other filter methods, this would
1233 : * need to be based on png_ptr->filter.
1234 : */
1235 : if (png_ptr->filter_costs == NULL)
1236 : {
1237 : png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
1238 : (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1239 :
1240 : png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
1241 : (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1242 : }
1243 :
1244 : for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
1245 : {
1246 : png_ptr->inv_filter_costs[i] =
1247 : png_ptr->filter_costs[i] = PNG_COST_FACTOR;
1248 : }
1249 :
1250 : /* All the arrays are inited, safe to set this: */
1251 : png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED;
1252 :
1253 : /* Return the 'ok' code. */
1254 : return 1;
1255 : }
1256 : else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT ||
1257 : heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
1258 : {
1259 : return 1;
1260 : }
1261 : else
1262 : {
1263 : png_warning(png_ptr, "Unknown filter heuristic method");
1264 : return 0;
1265 : }
1266 : }
1267 :
1268 : /* Provide floating and fixed point APIs */
1269 : #ifdef PNG_FLOATING_POINT_SUPPORTED
1270 : void PNGAPI
1271 : png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
1272 : int num_weights, png_const_doublep filter_weights,
1273 : png_const_doublep filter_costs)
1274 : {
1275 : png_debug(1, "in png_set_filter_heuristics");
1276 :
1277 : /* The internal API allocates all the arrays and ensures that the elements of
1278 : * those arrays are set to the default value.
1279 : */
1280 : if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
1281 : return;
1282 :
1283 : /* If using the weighted method copy in the weights. */
1284 : if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
1285 : {
1286 : int i;
1287 : for (i = 0; i < num_weights; i++)
1288 : {
1289 : if (filter_weights[i] <= 0.0)
1290 : {
1291 : png_ptr->inv_filter_weights[i] =
1292 : png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
1293 : }
1294 :
1295 : else
1296 : {
1297 : png_ptr->inv_filter_weights[i] =
1298 : (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5);
1299 :
1300 : png_ptr->filter_weights[i] =
1301 : (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5);
1302 : }
1303 : }
1304 :
1305 : /* Here is where we set the relative costs of the different filters. We
1306 : * should take the desired compression level into account when setting
1307 : * the costs, so that Paeth, for instance, has a high relative cost at low
1308 : * compression levels, while it has a lower relative cost at higher
1309 : * compression settings. The filter types are in order of increasing
1310 : * relative cost, so it would be possible to do this with an algorithm.
1311 : */
1312 : for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0)
1313 : {
1314 : png_ptr->inv_filter_costs[i] =
1315 : (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
1316 :
1317 : png_ptr->filter_costs[i] =
1318 : (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
1319 : }
1320 : }
1321 : }
1322 : #endif /* FLOATING_POINT */
1323 :
1324 : #ifdef PNG_FIXED_POINT_SUPPORTED
1325 : void PNGAPI
1326 : png_set_filter_heuristics_fixed(png_structp png_ptr, int heuristic_method,
1327 : int num_weights, png_const_fixed_point_p filter_weights,
1328 : png_const_fixed_point_p filter_costs)
1329 : {
1330 : png_debug(1, "in png_set_filter_heuristics_fixed");
1331 :
1332 : /* The internal API allocates all the arrays and ensures that the elements of
1333 : * those arrays are set to the default value.
1334 : */
1335 : if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
1336 : return;
1337 :
1338 : /* If using the weighted method copy in the weights. */
1339 : if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
1340 : {
1341 : int i;
1342 : for (i = 0; i < num_weights; i++)
1343 : {
1344 : if (filter_weights[i] <= 0)
1345 : {
1346 : png_ptr->inv_filter_weights[i] =
1347 : png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
1348 : }
1349 :
1350 : else
1351 : {
1352 : png_ptr->inv_filter_weights[i] = (png_uint_16)
1353 : ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1);
1354 :
1355 : png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR*
1356 : PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]);
1357 : }
1358 : }
1359 :
1360 : /* Here is where we set the relative costs of the different filters. We
1361 : * should take the desired compression level into account when setting
1362 : * the costs, so that Paeth, for instance, has a high relative cost at low
1363 : * compression levels, while it has a lower relative cost at higher
1364 : * compression settings. The filter types are in order of increasing
1365 : * relative cost, so it would be possible to do this with an algorithm.
1366 : */
1367 : for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
1368 : if (filter_costs[i] >= PNG_FP_1)
1369 : {
1370 : png_uint_32 tmp;
1371 :
1372 : /* Use a 32 bit unsigned temporary here because otherwise the
1373 : * intermediate value will be a 32 bit *signed* integer (ANSI rules)
1374 : * and this will get the wrong answer on division.
1375 : */
1376 : tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2);
1377 : tmp /= filter_costs[i];
1378 :
1379 : png_ptr->inv_filter_costs[i] = (png_uint_16)tmp;
1380 :
1381 : tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF;
1382 : tmp /= PNG_FP_1;
1383 :
1384 : png_ptr->filter_costs[i] = (png_uint_16)tmp;
1385 : }
1386 : }
1387 : }
1388 : #endif /* FIXED_POINT */
1389 : #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
1390 :
1391 : void PNGAPI
1392 0 : png_set_compression_level(png_structp png_ptr, int level)
1393 : {
1394 : png_debug(1, "in png_set_compression_level");
1395 :
1396 0 : if (png_ptr == NULL)
1397 0 : return;
1398 :
1399 0 : png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
1400 0 : png_ptr->zlib_level = level;
1401 : }
1402 :
1403 : void PNGAPI
1404 0 : png_set_compression_mem_level(png_structp png_ptr, int mem_level)
1405 : {
1406 : png_debug(1, "in png_set_compression_mem_level");
1407 :
1408 0 : if (png_ptr == NULL)
1409 0 : return;
1410 :
1411 0 : png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
1412 0 : png_ptr->zlib_mem_level = mem_level;
1413 : }
1414 :
1415 : void PNGAPI
1416 0 : png_set_compression_strategy(png_structp png_ptr, int strategy)
1417 : {
1418 : png_debug(1, "in png_set_compression_strategy");
1419 :
1420 0 : if (png_ptr == NULL)
1421 0 : return;
1422 :
1423 0 : png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
1424 0 : png_ptr->zlib_strategy = strategy;
1425 : }
1426 :
1427 : /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
1428 : * smaller value of window_bits if it can do so safely.
1429 : */
1430 : void PNGAPI
1431 0 : png_set_compression_window_bits(png_structp png_ptr, int window_bits)
1432 : {
1433 0 : if (png_ptr == NULL)
1434 0 : return;
1435 :
1436 : if (window_bits > 15)
1437 : png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1438 :
1439 : else if (window_bits < 8)
1440 : png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1441 :
1442 : #ifndef WBITS_8_OK
1443 : /* Avoid libpng bug with 256-byte windows */
1444 0 : if (window_bits == 8)
1445 : {
1446 : png_warning(png_ptr, "Compression window is being reset to 512");
1447 0 : window_bits = 9;
1448 : }
1449 :
1450 : #endif
1451 0 : png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
1452 0 : png_ptr->zlib_window_bits = window_bits;
1453 : }
1454 :
1455 : void PNGAPI
1456 0 : png_set_compression_method(png_structp png_ptr, int method)
1457 : {
1458 : png_debug(1, "in png_set_compression_method");
1459 :
1460 0 : if (png_ptr == NULL)
1461 0 : return;
1462 :
1463 : if (method != 8)
1464 : png_warning(png_ptr, "Only compression method 8 is supported by PNG");
1465 :
1466 0 : png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
1467 0 : png_ptr->zlib_method = method;
1468 : }
1469 :
1470 : /* The following were added to libpng-1.5.4 */
1471 : #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1472 : void PNGAPI
1473 : png_set_text_compression_level(png_structp png_ptr, int level)
1474 : {
1475 : png_debug(1, "in png_set_text_compression_level");
1476 :
1477 : if (png_ptr == NULL)
1478 : return;
1479 :
1480 : png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_LEVEL;
1481 : png_ptr->zlib_text_level = level;
1482 : }
1483 :
1484 : void PNGAPI
1485 : png_set_text_compression_mem_level(png_structp png_ptr, int mem_level)
1486 : {
1487 : png_debug(1, "in png_set_text_compression_mem_level");
1488 :
1489 : if (png_ptr == NULL)
1490 : return;
1491 :
1492 : png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL;
1493 : png_ptr->zlib_text_mem_level = mem_level;
1494 : }
1495 :
1496 : void PNGAPI
1497 : png_set_text_compression_strategy(png_structp png_ptr, int strategy)
1498 : {
1499 : png_debug(1, "in png_set_text_compression_strategy");
1500 :
1501 : if (png_ptr == NULL)
1502 : return;
1503 :
1504 : png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_STRATEGY;
1505 : png_ptr->zlib_text_strategy = strategy;
1506 : }
1507 :
1508 : /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
1509 : * smaller value of window_bits if it can do so safely.
1510 : */
1511 : void PNGAPI
1512 : png_set_text_compression_window_bits(png_structp png_ptr, int window_bits)
1513 : {
1514 : if (png_ptr == NULL)
1515 : return;
1516 :
1517 : if (window_bits > 15)
1518 : png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1519 :
1520 : else if (window_bits < 8)
1521 : png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1522 :
1523 : #ifndef WBITS_8_OK
1524 : /* Avoid libpng bug with 256-byte windows */
1525 : if (window_bits == 8)
1526 : {
1527 : png_warning(png_ptr, "Text compression window is being reset to 512");
1528 : window_bits = 9;
1529 : }
1530 :
1531 : #endif
1532 : png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS;
1533 : png_ptr->zlib_text_window_bits = window_bits;
1534 : }
1535 :
1536 : void PNGAPI
1537 : png_set_text_compression_method(png_structp png_ptr, int method)
1538 : {
1539 : png_debug(1, "in png_set_text_compression_method");
1540 :
1541 : if (png_ptr == NULL)
1542 : return;
1543 :
1544 : if (method != 8)
1545 : png_warning(png_ptr, "Only compression method 8 is supported by PNG");
1546 :
1547 : png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_METHOD;
1548 : png_ptr->zlib_text_method = method;
1549 : }
1550 : #endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
1551 : /* end of API added to libpng-1.5.4 */
1552 :
1553 : void PNGAPI
1554 0 : png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
1555 : {
1556 0 : if (png_ptr == NULL)
1557 0 : return;
1558 :
1559 0 : png_ptr->write_row_fn = write_row_fn;
1560 : }
1561 :
1562 : #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1563 : void PNGAPI
1564 : png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
1565 : write_user_transform_fn)
1566 : {
1567 : png_debug(1, "in png_set_write_user_transform_fn");
1568 :
1569 : if (png_ptr == NULL)
1570 : return;
1571 :
1572 : png_ptr->transformations |= PNG_USER_TRANSFORM;
1573 : png_ptr->write_user_transform_fn = write_user_transform_fn;
1574 : }
1575 : #endif
1576 :
1577 :
1578 : #ifdef PNG_INFO_IMAGE_SUPPORTED
1579 : void PNGAPI
1580 : png_write_png(png_structp png_ptr, png_infop info_ptr,
1581 : int transforms, voidp params)
1582 : {
1583 : if (png_ptr == NULL || info_ptr == NULL)
1584 : return;
1585 :
1586 : /* Write the file header information. */
1587 : png_write_info(png_ptr, info_ptr);
1588 :
1589 : /* ------ these transformations don't touch the info structure ------- */
1590 :
1591 : #ifdef PNG_WRITE_INVERT_SUPPORTED
1592 : /* Invert monochrome pixels */
1593 : if (transforms & PNG_TRANSFORM_INVERT_MONO)
1594 : png_set_invert_mono(png_ptr);
1595 : #endif
1596 :
1597 : #ifdef PNG_WRITE_SHIFT_SUPPORTED
1598 : /* Shift the pixels up to a legal bit depth and fill in
1599 : * as appropriate to correctly scale the image.
1600 : */
1601 : if ((transforms & PNG_TRANSFORM_SHIFT)
1602 : && (info_ptr->valid & PNG_INFO_sBIT))
1603 : png_set_shift(png_ptr, &info_ptr->sig_bit);
1604 : #endif
1605 :
1606 : #ifdef PNG_WRITE_PACK_SUPPORTED
1607 : /* Pack pixels into bytes */
1608 : if (transforms & PNG_TRANSFORM_PACKING)
1609 : png_set_packing(png_ptr);
1610 : #endif
1611 :
1612 : #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
1613 : /* Swap location of alpha bytes from ARGB to RGBA */
1614 : if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
1615 : png_set_swap_alpha(png_ptr);
1616 : #endif
1617 :
1618 : #ifdef PNG_WRITE_FILLER_SUPPORTED
1619 : /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */
1620 : if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
1621 : png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
1622 :
1623 : else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
1624 : png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1625 : #endif
1626 :
1627 : #ifdef PNG_WRITE_BGR_SUPPORTED
1628 : /* Flip BGR pixels to RGB */
1629 : if (transforms & PNG_TRANSFORM_BGR)
1630 : png_set_bgr(png_ptr);
1631 : #endif
1632 :
1633 : #ifdef PNG_WRITE_SWAP_SUPPORTED
1634 : /* Swap bytes of 16-bit files to most significant byte first */
1635 : if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
1636 : png_set_swap(png_ptr);
1637 : #endif
1638 :
1639 : #ifdef PNG_WRITE_PACKSWAP_SUPPORTED
1640 : /* Swap bits of 1, 2, 4 bit packed pixel formats */
1641 : if (transforms & PNG_TRANSFORM_PACKSWAP)
1642 : png_set_packswap(png_ptr);
1643 : #endif
1644 :
1645 : #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
1646 : /* Invert the alpha channel from opacity to transparency */
1647 : if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
1648 : png_set_invert_alpha(png_ptr);
1649 : #endif
1650 :
1651 : /* ----------------------- end of transformations ------------------- */
1652 :
1653 : /* Write the bits */
1654 : if (info_ptr->valid & PNG_INFO_IDAT)
1655 : png_write_image(png_ptr, info_ptr->row_pointers);
1656 :
1657 : /* It is REQUIRED to call this to finish writing the rest of the file */
1658 : png_write_end(png_ptr, info_ptr);
1659 :
1660 : PNG_UNUSED(transforms) /* Quiet compiler warnings */
1661 : PNG_UNUSED(params)
1662 : }
1663 : #endif
1664 :
1665 : #ifdef PNG_WRITE_APNG_SUPPORTED
1666 : void PNGAPI
1667 19 : png_write_frame_head(png_structp png_ptr, png_infop info_ptr,
1668 : png_bytepp row_pointers, png_uint_32 width, png_uint_32 height,
1669 : png_uint_32 x_offset, png_uint_32 y_offset,
1670 : png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
1671 : png_byte blend_op)
1672 : {
1673 : png_debug(1, "in png_write_frame_head");
1674 :
1675 : /* there is a chance this has been set after png_write_info was called,
1676 : * so it would be set but not written. is there a way to be sure? */
1677 19 : if (!(info_ptr->valid & PNG_INFO_acTL))
1678 0 : png_error(png_ptr, "png_write_frame_head(): acTL not set");
1679 :
1680 19 : png_write_reset(png_ptr);
1681 :
1682 19 : png_write_reinit(png_ptr, info_ptr, width, height);
1683 :
1684 25 : if ( !(png_ptr->num_frames_written == 0 &&
1685 6 : (png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) ) )
1686 18 : png_write_fcTL(png_ptr, width, height, x_offset, y_offset,
1687 : delay_num, delay_den, dispose_op, blend_op);
1688 :
1689 : PNG_UNUSED(row_pointers)
1690 19 : }
1691 :
1692 : void PNGAPI
1693 19 : png_write_frame_tail(png_structp png_ptr, png_infop info_ptr)
1694 : {
1695 : png_debug(1, "in png_write_frame_tail");
1696 :
1697 19 : png_ptr->num_frames_written++;
1698 :
1699 : PNG_UNUSED(info_ptr)
1700 19 : }
1701 : #endif /* PNG_WRITE_APNG_SUPPORTED */
1702 : #endif /* PNG_WRITE_SUPPORTED */
|