]> git.saurik.com Git - wxWidgets.git/blob - src/png/pngwrite.c
Some doc tweaks
[wxWidgets.git] / src / png / pngwrite.c
1
2 /* pngwrite.c - general routines to write a PNG file
3 *
4 * libpng 1.0.3 - January 14, 1999
5 * For conditions of distribution and use, see copyright notice in png.h
6 * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
7 * Copyright (c) 1996, 1997 Andreas Dilger
8 * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
9 */
10
11 /* get internal access to png.h */
12 #define PNG_INTERNAL
13 #include "png.h"
14
15 /* Writes all the PNG information. This is the suggested way to use the
16 * library. If you have a new chunk to add, make a function to write it,
17 * and put it in the correct location here. If you want the chunk written
18 * after the image data, put it in png_write_end(). I strongly encourage
19 * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
20 * the chunk, as that will keep the code from breaking if you want to just
21 * write a plain PNG file. If you have long comments, I suggest writing
22 * them in png_write_end(), and compressing them.
23 */
24 void
25 png_write_info(png_structp png_ptr, png_infop info_ptr)
26 {
27 #if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
28 int i;
29 #endif
30
31 png_debug(1, "in png_write_info\n");
32 png_write_sig(png_ptr); /* write PNG signature */
33 /* write IHDR information. */
34 png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
35 info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
36 info_ptr->filter_type,
37 #if defined(PNG_WRITE_INTERLACING_SUPPORTED)
38 info_ptr->interlace_type);
39 #else
40 0);
41 #endif
42 /* the rest of these check to see if the valid field has the appropriate
43 flag set, and if it does, writes the chunk. */
44 #if defined(PNG_WRITE_gAMA_SUPPORTED)
45 if (info_ptr->valid & PNG_INFO_gAMA)
46 png_write_gAMA(png_ptr, info_ptr->gamma);
47 #endif
48 #if defined(PNG_WRITE_sRGB_SUPPORTED)
49 if (info_ptr->valid & PNG_INFO_sRGB)
50 png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
51 #endif
52 #if defined(PNG_WRITE_sBIT_SUPPORTED)
53 if (info_ptr->valid & PNG_INFO_sBIT)
54 png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
55 #endif
56 #if defined(PNG_WRITE_cHRM_SUPPORTED)
57 if (info_ptr->valid & PNG_INFO_cHRM)
58 png_write_cHRM(png_ptr,
59 info_ptr->x_white, info_ptr->y_white,
60 info_ptr->x_red, info_ptr->y_red,
61 info_ptr->x_green, info_ptr->y_green,
62 info_ptr->x_blue, info_ptr->y_blue);
63 #endif
64 if (info_ptr->valid & PNG_INFO_PLTE)
65 png_write_PLTE(png_ptr, info_ptr->palette,
66 (png_uint_32)info_ptr->num_palette);
67 else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
68 png_error(png_ptr, "Valid palette required for paletted images\n");
69
70 #if defined(PNG_WRITE_tRNS_SUPPORTED)
71 if (info_ptr->valid & PNG_INFO_tRNS)
72 {
73 #if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
74 /* invert the alpha channel (in tRNS) */
75 if (png_ptr->transformations & PNG_INVERT_ALPHA &&
76 info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
77 {
78 int j;
79 for (j=0; j<(int)info_ptr->num_trans; j++)
80 info_ptr->trans[j] = 255 - info_ptr->trans[j];
81 }
82 #endif
83 png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
84 info_ptr->num_trans, info_ptr->color_type);
85 }
86 #endif
87 #if defined(PNG_WRITE_bKGD_SUPPORTED)
88 if (info_ptr->valid & PNG_INFO_bKGD)
89 png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
90 #endif
91 #if defined(PNG_WRITE_hIST_SUPPORTED)
92 if (info_ptr->valid & PNG_INFO_hIST)
93 png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
94 #endif
95 #if defined(PNG_WRITE_oFFs_SUPPORTED)
96 if (info_ptr->valid & PNG_INFO_oFFs)
97 png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
98 info_ptr->offset_unit_type);
99 #endif
100 #if defined(PNG_WRITE_pCAL_SUPPORTED)
101 if (info_ptr->valid & PNG_INFO_pCAL)
102 png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
103 info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
104 info_ptr->pcal_units, info_ptr->pcal_params);
105 #endif
106 #if defined(PNG_WRITE_pHYs_SUPPORTED)
107 if (info_ptr->valid & PNG_INFO_pHYs)
108 png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
109 info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
110 #endif
111 #if defined(PNG_WRITE_tIME_SUPPORTED)
112 if (info_ptr->valid & PNG_INFO_tIME)
113 {
114 png_write_tIME(png_ptr, &(info_ptr->mod_time));
115 png_ptr->flags |= PNG_FLAG_WROTE_tIME;
116 }
117 #endif
118 #if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
119 /* Check to see if we need to write text chunks */
120 for (i = 0; i < info_ptr->num_text; i++)
121 {
122 png_debug2(2, "Writing header text chunk %d, type %d\n", i,
123 info_ptr->text[i].compression);
124 /* If we want a compressed text chunk */
125 if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
126 {
127 #if defined(PNG_WRITE_zTXt_SUPPORTED)
128 /* write compressed chunk */
129 png_write_zTXt(png_ptr, info_ptr->text[i].key,
130 info_ptr->text[i].text, info_ptr->text[i].text_length,
131 info_ptr->text[i].compression);
132 #else
133 png_warning(png_ptr, "Unable to write compressed text\n");
134 #endif
135 /* Mark this chunk as written */
136 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
137 }
138 else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
139 {
140 #if defined(PNG_WRITE_tEXt_SUPPORTED)
141 /* write uncompressed chunk */
142 png_write_tEXt(png_ptr, info_ptr->text[i].key,
143 info_ptr->text[i].text, info_ptr->text[i].text_length);
144 #else
145 png_warning(png_ptr, "Unable to write uncompressed text\n");
146 #endif
147 /* Mark this chunk as written */
148 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
149 }
150 }
151 #endif
152 }
153
154 /* Writes the end of the PNG file. If you don't want to write comments or
155 * time information, you can pass NULL for info. If you already wrote these
156 * in png_write_info(), do not write them again here. If you have long
157 * comments, I suggest writing them here, and compressing them.
158 */
159 void
160 png_write_end(png_structp png_ptr, png_infop info_ptr)
161 {
162 png_debug(1, "in png_write_end\n");
163 if (!(png_ptr->mode & PNG_HAVE_IDAT))
164 png_error(png_ptr, "No IDATs written into file");
165
166 /* see if user wants us to write information chunks */
167 if (info_ptr != NULL)
168 {
169 #if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
170 int i; /* local index variable */
171 #endif
172 #if defined(PNG_WRITE_tIME_SUPPORTED)
173 /* check to see if user has supplied a time chunk */
174 if (info_ptr->valid & PNG_INFO_tIME &&
175 !(png_ptr->flags & PNG_FLAG_WROTE_tIME))
176 png_write_tIME(png_ptr, &(info_ptr->mod_time));
177 #endif
178 #if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
179 /* loop through comment chunks */
180 for (i = 0; i < info_ptr->num_text; i++)
181 {
182 png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
183 info_ptr->text[i].compression);
184 if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
185 {
186 #if defined(PNG_WRITE_zTXt_SUPPORTED)
187 /* write compressed chunk */
188 png_write_zTXt(png_ptr, info_ptr->text[i].key,
189 info_ptr->text[i].text, info_ptr->text[i].text_length,
190 info_ptr->text[i].compression);
191 #else
192 png_warning(png_ptr, "Unable to write compressed text\n");
193 #endif
194 /* Mark this chunk as written */
195 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
196 }
197 else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
198 {
199 #if defined(PNG_WRITE_tEXt_SUPPORTED)
200 /* write uncompressed chunk */
201 png_write_tEXt(png_ptr, info_ptr->text[i].key,
202 info_ptr->text[i].text, info_ptr->text[i].text_length);
203 #else
204 png_warning(png_ptr, "Unable to write uncompressed text\n");
205 #endif
206
207 /* Mark this chunk as written */
208 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
209 }
210 }
211 #endif
212 }
213
214 png_ptr->mode |= PNG_AFTER_IDAT;
215
216 /* write end of PNG file */
217 png_write_IEND(png_ptr);
218 }
219
220 #if defined(PNG_WRITE_tIME_SUPPORTED)
221 void
222 png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
223 {
224 png_debug(1, "in png_convert_from_struct_tm\n");
225 ptime->year = (png_uint_16)(1900 + ttime->tm_year);
226 ptime->month = (png_byte)(ttime->tm_mon + 1);
227 ptime->day = (png_byte)ttime->tm_mday;
228 ptime->hour = (png_byte)ttime->tm_hour;
229 ptime->minute = (png_byte)ttime->tm_min;
230 ptime->second = (png_byte)ttime->tm_sec;
231 }
232
233 void
234 png_convert_from_time_t(png_timep ptime, time_t ttime)
235 {
236 struct tm *tbuf;
237
238 png_debug(1, "in png_convert_from_time_t\n");
239 tbuf = gmtime(&ttime);
240 png_convert_from_struct_tm(ptime, tbuf);
241 }
242 #endif
243
244 /* Initialize png_ptr structure, and allocate any memory needed */
245 png_structp
246 png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
247 png_error_ptr error_fn, png_error_ptr warn_fn)
248 {
249 #ifdef PNG_USER_MEM_SUPPORTED
250 return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
251 warn_fn, NULL, NULL, NULL));
252 }
253
254 /* Alternate initialize png_ptr structure, and allocate any memory needed */
255 png_structp
256 png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
257 png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
258 png_malloc_ptr malloc_fn, png_free_ptr free_fn)
259 {
260 #endif /* PNG_USER_MEM_SUPPORTED */
261 png_structp png_ptr;
262 #ifdef USE_FAR_KEYWORD
263 jmp_buf jmpbuf;
264 #endif
265 png_debug(1, "in png_create_write_struct\n");
266 #ifdef PNG_USER_MEM_SUPPORTED
267 if ((png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
268 (png_malloc_ptr)malloc_fn)) == NULL)
269 #else
270 if ((png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG)) == NULL)
271 #endif /* PNG_USER_MEM_SUPPORTED */
272 {
273 return ((png_structp)NULL);
274 }
275 #ifdef USE_FAR_KEYWORD
276 if (setjmp(jmpbuf))
277 #else
278 if (setjmp(png_ptr->jmpbuf))
279 #endif
280 {
281 png_free(png_ptr, png_ptr->zbuf);
282 png_destroy_struct(png_ptr);
283 return ((png_structp)NULL);
284 }
285 #ifdef USE_FAR_KEYWORD
286 png_memcpy(png_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
287 #endif
288 #ifdef PNG_USER_MEM_SUPPORTED
289 png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
290 #endif /* PNG_USER_MEM_SUPPORTED */
291 png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
292
293 /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
294 * we must recompile any applications that use any older library version.
295 * For versions after libpng 1.0, we will be compatible, so we need
296 * only check the first digit.
297 */
298 if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
299 (png_libpng_ver[0] == '0' && user_png_ver[2] < '9'))
300 {
301 png_error(png_ptr,
302 "Incompatible libpng version in application and library");
303 }
304
305 /* initialize zbuf - compression buffer */
306 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
307 png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
308 (png_uint_32)png_ptr->zbuf_size);
309
310 png_set_write_fn(png_ptr, NULL, NULL, NULL);
311
312 #if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
313 png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
314 1, NULL, NULL);
315 #endif
316
317 return ((png_structp)png_ptr);
318 }
319
320 /* Initialize png_ptr structure, and allocate any memory needed */
321 void
322 png_write_init(png_structp png_ptr)
323 {
324 jmp_buf tmp_jmp; /* to save current jump buffer */
325
326 png_debug(1, "in png_write_init\n");
327 /* save jump buffer and error functions */
328 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
329
330 /* reset all variables to 0 */
331 png_memset(png_ptr, 0, sizeof (png_struct));
332
333 /* restore jump buffer */
334 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
335
336 /* initialize zbuf - compression buffer */
337 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
338 png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
339 (png_uint_32)png_ptr->zbuf_size);
340 png_set_write_fn(png_ptr, NULL, NULL, NULL);
341
342 #if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
343 png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
344 1, NULL, NULL);
345 #endif
346 }
347
348 /* Write a few rows of image data. If the image is interlaced,
349 * either you will have to write the 7 sub images, or, if you
350 * have called png_set_interlace_handling(), you will have to
351 * "write" the image seven times.
352 */
353 void
354 png_write_rows(png_structp png_ptr, png_bytepp row,
355 png_uint_32 num_rows)
356 {
357 png_uint_32 i; /* row counter */
358 png_bytepp rp; /* row pointer */
359
360 png_debug(1, "in png_write_rows\n");
361 /* loop through the rows */
362 for (i = 0, rp = row; i < num_rows; i++, rp++)
363 {
364 png_write_row(png_ptr, *rp);
365 }
366 }
367
368 /* Write the image. You only need to call this function once, even
369 * if you are writing an interlaced image.
370 */
371 void
372 png_write_image(png_structp png_ptr, png_bytepp image)
373 {
374 png_uint_32 i; /* row index */
375 int pass, num_pass; /* pass variables */
376 png_bytepp rp; /* points to current row */
377
378 png_debug(1, "in png_write_image\n");
379 #if defined(PNG_WRITE_INTERLACING_SUPPORTED)
380 /* intialize interlace handling. If image is not interlaced,
381 this will set pass to 1 */
382 num_pass = png_set_interlace_handling(png_ptr);
383 #else
384 num_pass = 1;
385 #endif
386 /* loop through passes */
387 for (pass = 0; pass < num_pass; pass++)
388 {
389 /* loop through image */
390 for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
391 {
392 png_write_row(png_ptr, *rp);
393 }
394 }
395 }
396
397 /* called by user to write a row of image data */
398 void
399 png_write_row(png_structp png_ptr, png_bytep row)
400 {
401 png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
402 png_ptr->row_number, png_ptr->pass);
403 /* initialize transformations and other stuff if first time */
404 if (png_ptr->row_number == 0 && png_ptr->pass == 0)
405 {
406 /* check for transforms that have been set but were defined out */
407 #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
408 if (png_ptr->transformations & PNG_INVERT_MONO)
409 png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined.");
410 #endif
411 #if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
412 if (png_ptr->transformations & PNG_FILLER)
413 png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined.");
414 #endif
415 #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED)
416 if (png_ptr->transformations & PNG_PACKSWAP)
417 png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
418 #endif
419 #if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
420 if (png_ptr->transformations & PNG_PACK)
421 png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
422 #endif
423 #if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
424 if (png_ptr->transformations & PNG_SHIFT)
425 png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
426 #endif
427 #if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
428 if (png_ptr->transformations & PNG_BGR)
429 png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
430 #endif
431 #if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
432 if (png_ptr->transformations & PNG_SWAP_BYTES)
433 png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
434 #endif
435
436 png_write_start_row(png_ptr);
437 }
438
439 #if defined(PNG_WRITE_INTERLACING_SUPPORTED)
440 /* if interlaced and not interested in row, return */
441 if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
442 {
443 switch (png_ptr->pass)
444 {
445 case 0:
446 if (png_ptr->row_number & 7)
447 {
448 png_write_finish_row(png_ptr);
449 return;
450 }
451 break;
452 case 1:
453 if ((png_ptr->row_number & 7) || png_ptr->width < 5)
454 {
455 png_write_finish_row(png_ptr);
456 return;
457 }
458 break;
459 case 2:
460 if ((png_ptr->row_number & 7) != 4)
461 {
462 png_write_finish_row(png_ptr);
463 return;
464 }
465 break;
466 case 3:
467 if ((png_ptr->row_number & 3) || png_ptr->width < 3)
468 {
469 png_write_finish_row(png_ptr);
470 return;
471 }
472 break;
473 case 4:
474 if ((png_ptr->row_number & 3) != 2)
475 {
476 png_write_finish_row(png_ptr);
477 return;
478 }
479 break;
480 case 5:
481 if ((png_ptr->row_number & 1) || png_ptr->width < 2)
482 {
483 png_write_finish_row(png_ptr);
484 return;
485 }
486 break;
487 case 6:
488 if (!(png_ptr->row_number & 1))
489 {
490 png_write_finish_row(png_ptr);
491 return;
492 }
493 break;
494 }
495 }
496 #endif
497
498 /* set up row info for transformations */
499 png_ptr->row_info.color_type = png_ptr->color_type;
500 png_ptr->row_info.width = png_ptr->usr_width;
501 png_ptr->row_info.channels = png_ptr->usr_channels;
502 png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
503 png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
504 png_ptr->row_info.channels);
505
506 png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
507 (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
508
509 png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
510 png_debug1(3, "row_info->width = %d\n", png_ptr->row_info.width);
511 png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
512 png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
513 png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
514 png_debug1(3, "row_info->rowbytes = %d\n", png_ptr->row_info.rowbytes);
515
516 /* Copy user's row into buffer, leaving room for filter byte. */
517 png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
518 png_ptr->row_info.rowbytes);
519
520 #if defined(PNG_WRITE_INTERLACING_SUPPORTED)
521 /* handle interlacing */
522 if (png_ptr->interlaced && png_ptr->pass < 6 &&
523 (png_ptr->transformations & PNG_INTERLACE))
524 {
525 png_do_write_interlace(&(png_ptr->row_info),
526 png_ptr->row_buf + 1, png_ptr->pass);
527 /* this should always get caught above, but still ... */
528 if (!(png_ptr->row_info.width))
529 {
530 png_write_finish_row(png_ptr);
531 return;
532 }
533 }
534 #endif
535
536 /* handle other transformations */
537 if (png_ptr->transformations)
538 png_do_write_transformations(png_ptr);
539
540 /* Find a filter if necessary, filter the row and write it out. */
541 png_write_find_filter(png_ptr, &(png_ptr->row_info));
542
543 if (png_ptr->write_row_fn != NULL)
544 (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
545 }
546
547 #if defined(PNG_WRITE_FLUSH_SUPPORTED)
548 /* Set the automatic flush interval or 0 to turn flushing off */
549 void
550 png_set_flush(png_structp png_ptr, int nrows)
551 {
552 png_debug(1, "in png_set_flush\n");
553 png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
554 }
555
556 /* flush the current output buffers now */
557 void
558 png_write_flush(png_structp png_ptr)
559 {
560 int wrote_IDAT;
561
562 png_debug(1, "in png_write_flush\n");
563 /* We have already written out all of the data */
564 if (png_ptr->row_number >= png_ptr->num_rows)
565 return;
566
567 do
568 {
569 int ret;
570
571 /* compress the data */
572 ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
573 wrote_IDAT = 0;
574
575 /* check for compression errors */
576 if (ret != Z_OK)
577 {
578 if (png_ptr->zstream.msg != NULL)
579 png_error(png_ptr, png_ptr->zstream.msg);
580 else
581 png_error(png_ptr, "zlib error");
582 }
583
584 if (!(png_ptr->zstream.avail_out))
585 {
586 /* write the IDAT and reset the zlib output buffer */
587 png_write_IDAT(png_ptr, png_ptr->zbuf,
588 png_ptr->zbuf_size);
589 png_ptr->zstream.next_out = png_ptr->zbuf;
590 png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
591 wrote_IDAT = 1;
592 }
593 } while(wrote_IDAT == 1);
594
595 /* If there is any data left to be output, write it into a new IDAT */
596 if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
597 {
598 /* write the IDAT and reset the zlib output buffer */
599 png_write_IDAT(png_ptr, png_ptr->zbuf,
600 png_ptr->zbuf_size - png_ptr->zstream.avail_out);
601 png_ptr->zstream.next_out = png_ptr->zbuf;
602 png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
603 }
604 png_ptr->flush_rows = 0;
605 png_flush(png_ptr);
606 }
607 #endif /* PNG_WRITE_FLUSH_SUPPORTED */
608
609 /* free all memory used by the write */
610 void
611 png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
612 {
613 png_structp png_ptr = NULL;
614 png_infop info_ptr = NULL;
615 #ifdef PNG_USER_MEM_SUPPORTED
616 png_free_ptr free_fn = NULL;
617 #endif
618
619 png_debug(1, "in png_destroy_write_struct\n");
620 if (png_ptr_ptr != NULL)
621 {
622 png_ptr = *png_ptr_ptr;
623 #ifdef PNG_USER_MEM_SUPPORTED
624 free_fn = png_ptr->free_fn;
625 #endif
626 }
627
628 if (info_ptr_ptr != NULL)
629 info_ptr = *info_ptr_ptr;
630
631 if (info_ptr != NULL)
632 {
633 #ifdef PNG_WRITE_tEXt_SUPPORTED
634 png_free(png_ptr, info_ptr->text);
635 #endif
636 #if defined(PNG_READ_pCAL_SUPPORTED)
637 png_free(png_ptr, info_ptr->pcal_purpose);
638 png_free(png_ptr, info_ptr->pcal_units);
639 if (info_ptr->pcal_params != NULL)
640 {
641 int i;
642 for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
643 {
644 png_free(png_ptr, info_ptr->pcal_params[i]);
645 }
646 png_free(png_ptr, info_ptr->pcal_params);
647 }
648 #endif
649 #ifdef PNG_USER_MEM_SUPPORTED
650 png_destroy_struct_2((png_voidp)info_ptr, free_fn);
651 #else
652 png_destroy_struct((png_voidp)info_ptr);
653 #endif
654 *info_ptr_ptr = (png_infop)NULL;
655 }
656
657 if (png_ptr != NULL)
658 {
659 png_write_destroy(png_ptr);
660 #ifdef PNG_USER_MEM_SUPPORTED
661 png_destroy_struct_2((png_voidp)png_ptr, free_fn);
662 #else
663 png_destroy_struct((png_voidp)png_ptr);
664 #endif
665 *png_ptr_ptr = (png_structp)NULL;
666 }
667 }
668
669
670 /* Free any memory used in png_ptr struct (old method) */
671 void
672 png_write_destroy(png_structp png_ptr)
673 {
674 jmp_buf tmp_jmp; /* save jump buffer */
675 png_error_ptr error_fn;
676 png_error_ptr warning_fn;
677 png_voidp error_ptr;
678 #ifdef PNG_USER_MEM_SUPPORTED
679 png_free_ptr free_fn;
680 #endif
681
682 png_debug(1, "in png_write_destroy\n");
683 /* free any memory zlib uses */
684 deflateEnd(&png_ptr->zstream);
685
686 /* free our memory. png_free checks NULL for us. */
687 png_free(png_ptr, png_ptr->zbuf);
688 png_free(png_ptr, png_ptr->row_buf);
689 png_free(png_ptr, png_ptr->prev_row);
690 png_free(png_ptr, png_ptr->sub_row);
691 png_free(png_ptr, png_ptr->up_row);
692 png_free(png_ptr, png_ptr->avg_row);
693 png_free(png_ptr, png_ptr->paeth_row);
694 #if defined(PNG_TIME_RFC1123_SUPPORTED)
695 png_free(png_ptr, png_ptr->time_buffer);
696 #endif /* PNG_TIME_RFC1123_SUPPORTED */
697 #if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
698 png_free(png_ptr, png_ptr->prev_filters);
699 png_free(png_ptr, png_ptr->filter_weights);
700 png_free(png_ptr, png_ptr->inv_filter_weights);
701 png_free(png_ptr, png_ptr->filter_costs);
702 png_free(png_ptr, png_ptr->inv_filter_costs);
703 #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
704
705 /* reset structure */
706 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
707
708 error_fn = png_ptr->error_fn;
709 warning_fn = png_ptr->warning_fn;
710 error_ptr = png_ptr->error_ptr;
711 #ifdef PNG_USER_MEM_SUPPORTED
712 free_fn = png_ptr->free_fn;
713 #endif
714
715 png_memset(png_ptr, 0, sizeof (png_struct));
716
717 png_ptr->error_fn = error_fn;
718 png_ptr->warning_fn = warning_fn;
719 png_ptr->error_ptr = error_ptr;
720 #ifdef PNG_USER_MEM_SUPPORTED
721 png_ptr->free_fn = free_fn;
722 #endif
723
724 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
725 }
726
727 /* Allow the application to select one or more row filters to use. */
728 void
729 png_set_filter(png_structp png_ptr, int method, int filters)
730 {
731 png_debug(1, "in png_set_filter\n");
732 /* We allow 'method' only for future expansion of the base filter method. */
733 if (method == PNG_FILTER_TYPE_BASE)
734 {
735 switch (filters & (PNG_ALL_FILTERS | 0x07))
736 {
737 case 5:
738 case 6:
739 case 7: png_warning(png_ptr, "Unknown row filter for method 0");
740 case PNG_FILTER_VALUE_NONE: png_ptr->do_filter=PNG_FILTER_NONE; break;
741 case PNG_FILTER_VALUE_SUB: png_ptr->do_filter=PNG_FILTER_SUB; break;
742 case PNG_FILTER_VALUE_UP: png_ptr->do_filter=PNG_FILTER_UP; break;
743 case PNG_FILTER_VALUE_AVG: png_ptr->do_filter=PNG_FILTER_AVG; break;
744 case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break;
745 default: png_ptr->do_filter = (png_byte)filters; break;
746 }
747
748 /* If we have allocated the row_buf, this means we have already started
749 * with the image and we should have allocated all of the filter buffers
750 * that have been selected. If prev_row isn't already allocated, then
751 * it is too late to start using the filters that need it, since we
752 * will be missing the data in the previous row. If an application
753 * wants to start and stop using particular filters during compression,
754 * it should start out with all of the filters, and then add and
755 * remove them after the start of compression.
756 */
757 if (png_ptr->row_buf != NULL)
758 {
759 if (png_ptr->do_filter & PNG_FILTER_SUB && png_ptr->sub_row == NULL)
760 {
761 png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
762 (png_ptr->rowbytes + 1));
763 png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
764 }
765
766 if (png_ptr->do_filter & PNG_FILTER_UP && png_ptr->up_row == NULL)
767 {
768 if (png_ptr->prev_row == NULL)
769 {
770 png_warning(png_ptr, "Can't add Up filter after starting");
771 png_ptr->do_filter &= ~PNG_FILTER_UP;
772 }
773 else
774 {
775 png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
776 (png_ptr->rowbytes + 1));
777 png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
778 }
779 }
780
781 if (png_ptr->do_filter & PNG_FILTER_AVG && png_ptr->avg_row == NULL)
782 {
783 if (png_ptr->prev_row == NULL)
784 {
785 png_warning(png_ptr, "Can't add Average filter after starting");
786 png_ptr->do_filter &= ~PNG_FILTER_AVG;
787 }
788 else
789 {
790 png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
791 (png_ptr->rowbytes + 1));
792 png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
793 }
794 }
795
796 if (png_ptr->do_filter & PNG_FILTER_PAETH &&
797 png_ptr->paeth_row == NULL)
798 {
799 if (png_ptr->prev_row == NULL)
800 {
801 png_warning(png_ptr, "Can't add Paeth filter after starting");
802 png_ptr->do_filter &= ~PNG_FILTER_PAETH;
803 }
804 else
805 {
806 png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
807 (png_ptr->rowbytes + 1));
808 png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
809 }
810 }
811
812 if (png_ptr->do_filter == PNG_NO_FILTERS)
813 png_ptr->do_filter = PNG_FILTER_NONE;
814 }
815 }
816 else
817 png_error(png_ptr, "Unknown custom filter method");
818 }
819
820 /* This allows us to influence the way in which libpng chooses the "best"
821 * filter for the current scanline. While the "minimum-sum-of-absolute-
822 * differences metric is relatively fast and effective, there is some
823 * question as to whether it can be improved upon by trying to keep the
824 * filtered data going to zlib more consistent, hopefully resulting in
825 * better compression.
826 */
827 #if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */
828 void
829 png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
830 int num_weights, png_doublep filter_weights,
831 png_doublep filter_costs)
832 {
833 int i;
834
835 png_debug(1, "in png_set_filter_heuristics\n");
836 if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
837 {
838 png_warning(png_ptr, "Unknown filter heuristic method");
839 return;
840 }
841
842 if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
843 {
844 heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
845 }
846
847 if (num_weights < 0 || filter_weights == NULL ||
848 heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
849 {
850 num_weights = 0;
851 }
852
853 png_ptr->num_prev_filters = num_weights;
854 png_ptr->heuristic_method = heuristic_method;
855
856 if (num_weights > 0)
857 {
858 if (png_ptr->prev_filters == NULL)
859 {
860 png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
861 (png_uint_32)(sizeof(png_byte) * num_weights));
862
863 /* To make sure that the weighting starts out fairly */
864 for (i = 0; i < num_weights; i++)
865 {
866 png_ptr->prev_filters[i] = 255;
867 }
868 }
869
870 if (png_ptr->filter_weights == NULL)
871 {
872 png_ptr->filter_weights = (png_uint_16p) png_malloc(png_ptr,
873 (png_uint_32)(sizeof(png_uint_16) * num_weights));
874
875 png_ptr->inv_filter_weights = (png_uint_16p) png_malloc(png_ptr,
876 (png_uint_32)(sizeof(png_uint_16) * num_weights));
877
878 for (i = 0; i < num_weights; i++)
879 {
880 png_ptr->inv_filter_weights[i] =
881 png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
882 }
883 }
884
885 for (i = 0; i < num_weights; i++)
886 {
887 if (filter_weights[i] < 0.0)
888 {
889 png_ptr->inv_filter_weights[i] =
890 png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
891 }
892 else
893 {
894 png_ptr->inv_filter_weights[i] =
895 (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
896 png_ptr->filter_weights[i] =
897 (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
898 }
899 }
900 }
901
902 /* If, in the future, there are other filter methods, this would
903 * need to be based on png_ptr->filter.
904 */
905 if (png_ptr->filter_costs == NULL)
906 {
907 png_ptr->filter_costs = (png_uint_16p) png_malloc(png_ptr,
908 (png_uint_32)(sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
909
910 png_ptr->inv_filter_costs = (png_uint_16p) png_malloc(png_ptr,
911 (png_uint_32)(sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
912
913 for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
914 {
915 png_ptr->inv_filter_costs[i] =
916 png_ptr->filter_costs[i] = PNG_COST_FACTOR;
917 }
918 }
919
920 /* Here is where we set the relative costs of the different filters. We
921 * should take the desired compression level into account when setting
922 * the costs, so that Paeth, for instance, has a high relative cost at low
923 * compression levels, while it has a lower relative cost at higher
924 * compression settings. The filter types are in order of increasing
925 * relative cost, so it would be possible to do this with an algorithm.
926 */
927 for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
928 {
929 if (filter_costs == NULL || filter_costs[i] < 0.0)
930 {
931 png_ptr->inv_filter_costs[i] =
932 png_ptr->filter_costs[i] = PNG_COST_FACTOR;
933 }
934 else if (filter_costs[i] >= 1.0)
935 {
936 png_ptr->inv_filter_costs[i] =
937 (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
938 png_ptr->filter_costs[i] =
939 (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
940 }
941 }
942 }
943 #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
944
945 void
946 png_set_compression_level(png_structp png_ptr, int level)
947 {
948 png_debug(1, "in png_set_compression_level\n");
949 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
950 png_ptr->zlib_level = level;
951 }
952
953 void
954 png_set_compression_mem_level(png_structp png_ptr, int mem_level)
955 {
956 png_debug(1, "in png_set_compression_mem_level\n");
957 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
958 png_ptr->zlib_mem_level = mem_level;
959 }
960
961 void
962 png_set_compression_strategy(png_structp png_ptr, int strategy)
963 {
964 png_debug(1, "in png_set_compression_strategy\n");
965 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
966 png_ptr->zlib_strategy = strategy;
967 }
968
969 void
970 png_set_compression_window_bits(png_structp png_ptr, int window_bits)
971 {
972 if (window_bits > 15)
973 png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
974 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
975 png_ptr->zlib_window_bits = window_bits;
976 }
977
978 void
979 png_set_compression_method(png_structp png_ptr, int method)
980 {
981 png_debug(1, "in png_set_compression_method\n");
982 if (method != 8)
983 png_warning(png_ptr, "Only compression method 8 is supported by PNG");
984 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
985 png_ptr->zlib_method = method;
986 }
987
988 void
989 png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
990 {
991 png_ptr->write_row_fn = write_row_fn;
992 }
993
994 #if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
995 void
996 png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
997 write_user_transform_fn)
998 {
999 png_debug(1, "in png_set_write_user_transform_fn\n");
1000 png_ptr->transformations |= PNG_USER_TRANSFORM;
1001 png_ptr->write_user_transform_fn = write_user_transform_fn;
1002 }
1003 #endif
1004