Cleanup: Get this rid of "this->" in fpdfsdk/
[pdfium.git] / samples / fx_lpng / lpng_v163 / fx_pngwrite.c
1 \r
2 /* pngwrite.c - general routines to write a PNG file\r
3  *\r
4  * Last changed in libpng 1.6.2 [April 25, 2013]\r
5  * Copyright (c) 1998-2013 Glenn Randers-Pehrson\r
6  * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)\r
7  * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)\r
8  *\r
9  * This code is released under the libpng license.\r
10  * For conditions of distribution and use, see the disclaimer\r
11  * and license in png.h\r
12  */\r
13 #include "pngpriv.h"\r
14 #if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)\r
15 #  include <errno.h>\r
16 #endif\r
17 \r
18 #ifdef PNG_WRITE_SUPPORTED\r
19 \r
20 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED\r
21 /* Write out all the unknown chunks for the current given location */\r
22 static void\r
23 write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,\r
24    unsigned int where)\r
25 {\r
26    if (info_ptr->unknown_chunks_num)\r
27    {\r
28       png_const_unknown_chunkp up;\r
29 \r
30       png_debug(5, "writing extra chunks");\r
31 \r
32       for (up = info_ptr->unknown_chunks;\r
33            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;\r
34            ++up)\r
35          if (up->location & where)\r
36       {\r
37          /* If per-chunk unknown chunk handling is enabled use it, otherwise\r
38           * just write the chunks the application has set.\r
39           */\r
40 #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED\r
41          int keep = png_handle_as_unknown(png_ptr, up->name);\r
42 \r
43          /* NOTE: this code is radically different from the read side in the\r
44           * matter of handling an ancillary unknown chunk.  In the read side\r
45           * the default behavior is to discard it, in the code below the default\r
46           * behavior is to write it.  Critical chunks are, however, only\r
47           * written if explicitly listed or if the default is set to write all\r
48           * unknown chunks.\r
49           *\r
50           * The default handling is also slightly weird - it is not possible to\r
51           * stop the writing of all unsafe-to-copy chunks!\r
52           *\r
53           * TODO: REVIEW: this would seem to be a bug.\r
54           */\r
55          if (keep != PNG_HANDLE_CHUNK_NEVER &&\r
56              ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ ||\r
57               keep == PNG_HANDLE_CHUNK_ALWAYS ||\r
58               (keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&\r
59                png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS)))\r
60 #endif\r
61          {\r
62             /* TODO: review, what is wrong with a zero length unknown chunk? */\r
63             if (up->size == 0)\r
64                png_warning(png_ptr, "Writing zero-length unknown chunk");\r
65 \r
66             png_write_chunk(png_ptr, up->name, up->data, up->size);\r
67          }\r
68       }\r
69    }\r
70 }\r
71 #endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */\r
72 \r
73 /* Writes all the PNG information.  This is the suggested way to use the\r
74  * library.  If you have a new chunk to add, make a function to write it,\r
75  * and put it in the correct location here.  If you want the chunk written\r
76  * after the image data, put it in png_write_end().  I strongly encourage\r
77  * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing\r
78  * the chunk, as that will keep the code from breaking if you want to just\r
79  * write a plain PNG file.  If you have long comments, I suggest writing\r
80  * them in png_write_end(), and compressing them.\r
81  */\r
82 void PNGAPI\r
83 png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)\r
84 {\r
85    png_debug(1, "in png_write_info_before_PLTE");\r
86 \r
87    if (png_ptr == NULL || info_ptr == NULL)\r
88       return;\r
89 \r
90    if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))\r
91    {\r
92    /* Write PNG signature */\r
93    png_write_sig(png_ptr);\r
94 \r
95 #ifdef PNG_MNG_FEATURES_SUPPORTED\r
96    if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \\r
97        (png_ptr->mng_features_permitted))\r
98    {\r
99       png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");\r
100       png_ptr->mng_features_permitted = 0;\r
101    }\r
102 #endif\r
103 \r
104    /* Write IHDR information. */\r
105    png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,\r
106        info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,\r
107        info_ptr->filter_type,\r
108 #ifdef PNG_WRITE_INTERLACING_SUPPORTED\r
109        info_ptr->interlace_type\r
110 #else\r
111        0\r
112 #endif\r
113       );\r
114 \r
115    /* The rest of these check to see if the valid field has the appropriate\r
116     * flag set, and if it does, writes the chunk.\r
117     *\r
118     * 1.6.0: COLORSPACE support controls the writing of these chunks too, and\r
119     * the chunks will be written if the WRITE routine is there and information\r
120     * is available in the COLORSPACE.  (See png_colorspace_sync_info in png.c\r
121     * for where the valid flags get set.)\r
122     *\r
123     * Under certain circumstances the colorspace can be invalidated without\r
124     * syncing the info_struct 'valid' flags; this happens if libpng detects and\r
125     * error and calls png_error while the color space is being set, yet the\r
126     * application continues writing the PNG.  So check the 'invalid' flag here\r
127     * too.\r
128     */\r
129 #ifdef PNG_GAMMA_SUPPORTED\r
130 #  ifdef PNG_WRITE_gAMA_SUPPORTED\r
131       if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&\r
132          (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) &&\r
133          (info_ptr->valid & PNG_INFO_gAMA))\r
134          png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);\r
135 #  endif\r
136 #endif\r
137 \r
138 #ifdef PNG_COLORSPACE_SUPPORTED\r
139    /* Write only one of sRGB or an ICC profile.  If a profile was supplied\r
140     * and it matches one of the known sRGB ones issue a warning.\r
141     */\r
142 #  ifdef PNG_WRITE_iCCP_SUPPORTED\r
143       if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&\r
144          (info_ptr->valid & PNG_INFO_iCCP))\r
145       {\r
146 #        ifdef PNG_WRITE_sRGB_SUPPORTED\r
147             if (info_ptr->valid & PNG_INFO_sRGB)\r
148                png_app_warning(png_ptr,\r
149                   "profile matches sRGB but writing iCCP instead");\r
150 #        endif\r
151 \r
152          png_write_iCCP(png_ptr, info_ptr->iccp_name,\r
153             info_ptr->iccp_profile);\r
154       }\r
155 #     ifdef PNG_WRITE_sRGB_SUPPORTED\r
156          else\r
157 #     endif\r
158 #  endif\r
159 \r
160 #  ifdef PNG_WRITE_sRGB_SUPPORTED\r
161       if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&\r
162          (info_ptr->valid & PNG_INFO_sRGB))\r
163          png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);\r
164 #  endif /* WRITE_sRGB */\r
165 #endif /* COLORSPACE */\r
166 \r
167 #ifdef PNG_WRITE_sBIT_SUPPORTED\r
168    if (info_ptr->valid & PNG_INFO_sBIT)\r
169       png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);\r
170 #endif\r
171 \r
172 #ifdef PNG_COLORSPACE_SUPPORTED\r
173 #  ifdef PNG_WRITE_cHRM_SUPPORTED\r
174       if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&\r
175          (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) &&\r
176          (info_ptr->valid & PNG_INFO_cHRM))\r
177          png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);\r
178 #  endif\r
179 #endif\r
180 \r
181 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED\r
182       write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);\r
183 #endif\r
184 \r
185       png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;\r
186    }\r
187 }\r
188 \r
189 void PNGAPI\r
190 png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)\r
191 {\r
192 #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)\r
193    int i;\r
194 #endif\r
195 \r
196    png_debug(1, "in png_write_info");\r
197 \r
198    if (png_ptr == NULL || info_ptr == NULL)\r
199       return;\r
200 \r
201    png_write_info_before_PLTE(png_ptr, info_ptr);\r
202 \r
203    if (info_ptr->valid & PNG_INFO_PLTE)\r
204       png_write_PLTE(png_ptr, info_ptr->palette,\r
205           (png_uint_32)info_ptr->num_palette);\r
206 \r
207    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)\r
208       png_error(png_ptr, "Valid palette required for paletted images");\r
209 \r
210 #ifdef PNG_WRITE_tRNS_SUPPORTED\r
211    if (info_ptr->valid & PNG_INFO_tRNS)\r
212    {\r
213 #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED\r
214       /* Invert the alpha channel (in tRNS) */\r
215       if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&\r
216           info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)\r
217       {\r
218          int j;\r
219          for (j = 0; j<(int)info_ptr->num_trans; j++)\r
220             info_ptr->trans_alpha[j] =\r
221                (png_byte)(255 - info_ptr->trans_alpha[j]);\r
222       }\r
223 #endif\r
224       png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),\r
225           info_ptr->num_trans, info_ptr->color_type);\r
226    }\r
227 #endif\r
228 #ifdef PNG_WRITE_bKGD_SUPPORTED\r
229    if (info_ptr->valid & PNG_INFO_bKGD)\r
230       png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);\r
231 #endif\r
232 \r
233 #ifdef PNG_WRITE_hIST_SUPPORTED\r
234    if (info_ptr->valid & PNG_INFO_hIST)\r
235       png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);\r
236 #endif\r
237 \r
238 #ifdef PNG_WRITE_oFFs_SUPPORTED\r
239    if (info_ptr->valid & PNG_INFO_oFFs)\r
240       png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,\r
241           info_ptr->offset_unit_type);\r
242 #endif\r
243 \r
244 #ifdef PNG_WRITE_pCAL_SUPPORTED\r
245    if (info_ptr->valid & PNG_INFO_pCAL)\r
246       png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,\r
247           info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,\r
248           info_ptr->pcal_units, info_ptr->pcal_params);\r
249 #endif\r
250 \r
251 #ifdef PNG_WRITE_sCAL_SUPPORTED\r
252    if (info_ptr->valid & PNG_INFO_sCAL)\r
253       png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,\r
254           info_ptr->scal_s_width, info_ptr->scal_s_height);\r
255 #endif /* sCAL */\r
256 \r
257 #ifdef PNG_WRITE_pHYs_SUPPORTED\r
258    if (info_ptr->valid & PNG_INFO_pHYs)\r
259       png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,\r
260           info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);\r
261 #endif /* pHYs */\r
262 \r
263 #ifdef PNG_WRITE_tIME_SUPPORTED\r
264    if (info_ptr->valid & PNG_INFO_tIME)\r
265    {\r
266       png_write_tIME(png_ptr, &(info_ptr->mod_time));\r
267       png_ptr->mode |= PNG_WROTE_tIME;\r
268    }\r
269 #endif /* tIME */\r
270 \r
271 #ifdef PNG_WRITE_sPLT_SUPPORTED\r
272    if (info_ptr->valid & PNG_INFO_sPLT)\r
273       for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)\r
274          png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);\r
275 #endif /* sPLT */\r
276 \r
277 #ifdef PNG_WRITE_TEXT_SUPPORTED\r
278    /* Check to see if we need to write text chunks */\r
279    for (i = 0; i < info_ptr->num_text; i++)\r
280    {\r
281       png_debug2(2, "Writing header text chunk %d, type %d", i,\r
282           info_ptr->text[i].compression);\r
283       /* An internationalized chunk? */\r
284       if (info_ptr->text[i].compression > 0)\r
285       {\r
286 #ifdef PNG_WRITE_iTXt_SUPPORTED\r
287          /* Write international chunk */\r
288          png_write_iTXt(png_ptr,\r
289              info_ptr->text[i].compression,\r
290              info_ptr->text[i].key,\r
291              info_ptr->text[i].lang,\r
292              info_ptr->text[i].lang_key,\r
293              info_ptr->text[i].text);\r
294 #else\r
295           png_warning(png_ptr, "Unable to write international text");\r
296 #endif\r
297           /* Mark this chunk as written */\r
298           info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;\r
299       }\r
300 \r
301       /* If we want a compressed text chunk */\r
302       else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)\r
303       {\r
304 #ifdef PNG_WRITE_zTXt_SUPPORTED\r
305          /* Write compressed chunk */\r
306          png_write_zTXt(png_ptr, info_ptr->text[i].key,\r
307              info_ptr->text[i].text, 0,\r
308              info_ptr->text[i].compression);\r
309 #else\r
310          png_warning(png_ptr, "Unable to write compressed text");\r
311 #endif\r
312          /* Mark this chunk as written */\r
313          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;\r
314       }\r
315 \r
316       else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)\r
317       {\r
318 #ifdef PNG_WRITE_tEXt_SUPPORTED\r
319          /* Write uncompressed chunk */\r
320          png_write_tEXt(png_ptr, info_ptr->text[i].key,\r
321              info_ptr->text[i].text,\r
322              0);\r
323          /* Mark this chunk as written */\r
324          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;\r
325 #else\r
326          /* Can't get here */\r
327          png_warning(png_ptr, "Unable to write uncompressed text");\r
328 #endif\r
329       }\r
330    }\r
331 #endif /* tEXt */\r
332 \r
333 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED\r
334    write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);\r
335 #endif\r
336 }\r
337 \r
338 /* Writes the end of the PNG file.  If you don't want to write comments or\r
339  * time information, you can pass NULL for info.  If you already wrote these\r
340  * in png_write_info(), do not write them again here.  If you have long\r
341  * comments, I suggest writing them here, and compressing them.\r
342  */\r
343 void PNGAPI\r
344 png_write_end(png_structrp png_ptr, png_inforp info_ptr)\r
345 {\r
346    png_debug(1, "in png_write_end");\r
347 \r
348    if (png_ptr == NULL)\r
349       return;\r
350 \r
351    if (!(png_ptr->mode & PNG_HAVE_IDAT))\r
352       png_error(png_ptr, "No IDATs written into file");\r
353 \r
354 #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED\r
355    if (png_ptr->num_palette_max > png_ptr->num_palette)\r
356       png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");\r
357 #endif\r
358 \r
359    /* See if user wants us to write information chunks */\r
360    if (info_ptr != NULL)\r
361    {\r
362 #ifdef PNG_WRITE_TEXT_SUPPORTED\r
363       int i; /* local index variable */\r
364 #endif\r
365 #ifdef PNG_WRITE_tIME_SUPPORTED\r
366       /* Check to see if user has supplied a time chunk */\r
367       if ((info_ptr->valid & PNG_INFO_tIME) &&\r
368           !(png_ptr->mode & PNG_WROTE_tIME))\r
369          png_write_tIME(png_ptr, &(info_ptr->mod_time));\r
370 \r
371 #endif\r
372 #ifdef PNG_WRITE_TEXT_SUPPORTED\r
373       /* Loop through comment chunks */\r
374       for (i = 0; i < info_ptr->num_text; i++)\r
375       {\r
376          png_debug2(2, "Writing trailer text chunk %d, type %d", i,\r
377             info_ptr->text[i].compression);\r
378          /* An internationalized chunk? */\r
379          if (info_ptr->text[i].compression > 0)\r
380          {\r
381 #ifdef PNG_WRITE_iTXt_SUPPORTED\r
382             /* Write international chunk */\r
383             png_write_iTXt(png_ptr,\r
384                 info_ptr->text[i].compression,\r
385                 info_ptr->text[i].key,\r
386                 info_ptr->text[i].lang,\r
387                 info_ptr->text[i].lang_key,\r
388                 info_ptr->text[i].text);\r
389 #else\r
390             png_warning(png_ptr, "Unable to write international text");\r
391 #endif\r
392             /* Mark this chunk as written */\r
393             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;\r
394          }\r
395 \r
396          else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)\r
397          {\r
398 #ifdef PNG_WRITE_zTXt_SUPPORTED\r
399             /* Write compressed chunk */\r
400             png_write_zTXt(png_ptr, info_ptr->text[i].key,\r
401                 info_ptr->text[i].text, 0,\r
402                 info_ptr->text[i].compression);\r
403 #else\r
404             png_warning(png_ptr, "Unable to write compressed text");\r
405 #endif\r
406             /* Mark this chunk as written */\r
407             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;\r
408          }\r
409 \r
410          else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)\r
411          {\r
412 #ifdef PNG_WRITE_tEXt_SUPPORTED\r
413             /* Write uncompressed chunk */\r
414             png_write_tEXt(png_ptr, info_ptr->text[i].key,\r
415                 info_ptr->text[i].text, 0);\r
416 #else\r
417             png_warning(png_ptr, "Unable to write uncompressed text");\r
418 #endif\r
419 \r
420             /* Mark this chunk as written */\r
421             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;\r
422          }\r
423       }\r
424 #endif\r
425 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED\r
426       write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);\r
427 #endif\r
428    }\r
429 \r
430    png_ptr->mode |= PNG_AFTER_IDAT;\r
431 \r
432    /* Write end of PNG file */\r
433    png_write_IEND(png_ptr);\r
434    /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,\r
435     * and restored again in libpng-1.2.30, may cause some applications that\r
436     * do not set png_ptr->output_flush_fn to crash.  If your application\r
437     * experiences a problem, please try building libpng with\r
438     * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to\r
439     * png-mng-implement at lists.sf.net .\r
440     */\r
441 #ifdef PNG_WRITE_FLUSH_SUPPORTED\r
442 #  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED\r
443    png_flush(png_ptr);\r
444 #  endif\r
445 #endif\r
446 }\r
447 \r
448 #ifdef PNG_CONVERT_tIME_SUPPORTED\r
449 void PNGAPI\r
450 png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)\r
451 {\r
452    png_debug(1, "in png_convert_from_struct_tm");\r
453 \r
454    ptime->year = (png_uint_16)(1900 + ttime->tm_year);\r
455    ptime->month = (png_byte)(ttime->tm_mon + 1);\r
456    ptime->day = (png_byte)ttime->tm_mday;\r
457    ptime->hour = (png_byte)ttime->tm_hour;\r
458    ptime->minute = (png_byte)ttime->tm_min;\r
459    ptime->second = (png_byte)ttime->tm_sec;\r
460 }\r
461 \r
462 void PNGAPI\r
463 png_convert_from_time_t(png_timep ptime, time_t ttime)\r
464 {\r
465    struct tm *tbuf;\r
466 \r
467    png_debug(1, "in png_convert_from_time_t");\r
468 \r
469    tbuf = gmtime(&ttime);\r
470    png_convert_from_struct_tm(ptime, tbuf);\r
471 }\r
472 #endif\r
473 \r
474 /* Initialize png_ptr structure, and allocate any memory needed */\r
475 PNG_FUNCTION(png_structp,PNGAPI\r
476 png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,\r
477     png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)\r
478 {\r
479 #ifndef PNG_USER_MEM_SUPPORTED\r
480    png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,\r
481       error_fn, warn_fn, NULL, NULL, NULL);\r
482 #else\r
483    return png_create_write_struct_2(user_png_ver, error_ptr, error_fn,\r
484        warn_fn, NULL, NULL, NULL);\r
485 }\r
486 \r
487 /* Alternate initialize png_ptr structure, and allocate any memory needed */\r
488 PNG_FUNCTION(png_structp,PNGAPI\r
489 png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,\r
490     png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,\r
491     png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)\r
492 {\r
493    png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,\r
494       error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);\r
495 #endif /* PNG_USER_MEM_SUPPORTED */\r
496    if (png_ptr != NULL)\r
497    {\r
498       /* Set the zlib control values to defaults; they can be overridden by the\r
499        * application after the struct has been created.\r
500        */\r
501       png_ptr->zbuffer_size = PNG_ZBUF_SIZE;\r
502 \r
503       /* The 'zlib_strategy' setting is irrelevant because png_default_claim in\r
504        * pngwutil.c defaults it according to whether or not filters will be\r
505        * used, and ignores this setting.\r
506        */\r
507       png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY;\r
508       png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION;\r
509       png_ptr->zlib_mem_level = 8;\r
510       png_ptr->zlib_window_bits = 15;\r
511       png_ptr->zlib_method = 8;\r
512 \r
513 #ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED\r
514       png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;\r
515       png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;\r
516       png_ptr->zlib_text_mem_level = 8;\r
517       png_ptr->zlib_text_window_bits = 15;\r
518       png_ptr->zlib_text_method = 8;\r
519 #endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */\r
520 \r
521       /* This is a highly dubious configuration option; by default it is off,\r
522        * but it may be appropriate for private builds that are testing\r
523        * extensions not conformant to the current specification, or of\r
524        * applications that must not fail to write at all costs!\r
525        */\r
526 #ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED\r
527       png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;\r
528       /* In stable builds only warn if an application error can be completely\r
529        * handled.\r
530        */\r
531 #endif\r
532 \r
533       /* App warnings are warnings in release (or release candidate) builds but\r
534        * are errors during development.\r
535        */\r
536 #if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC\r
537       png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;\r
538 #endif\r
539 \r
540       /* TODO: delay this, it can be done in png_init_io() (if the app doesn't\r
541        * do it itself) avoiding setting the default function if it is not\r
542        * required.\r
543        */\r
544       png_set_write_fn(png_ptr, NULL, NULL, NULL);\r
545    }\r
546 \r
547    return png_ptr;\r
548 }\r
549 \r
550 \r
551 /* Write a few rows of image data.  If the image is interlaced,\r
552  * either you will have to write the 7 sub images, or, if you\r
553  * have called png_set_interlace_handling(), you will have to\r
554  * "write" the image seven times.\r
555  */\r
556 void PNGAPI\r
557 png_write_rows(png_structrp png_ptr, png_bytepp row,\r
558     png_uint_32 num_rows)\r
559 {\r
560    png_uint_32 i; /* row counter */\r
561    png_bytepp rp; /* row pointer */\r
562 \r
563    png_debug(1, "in png_write_rows");\r
564 \r
565    if (png_ptr == NULL)\r
566       return;\r
567 \r
568    /* Loop through the rows */\r
569    for (i = 0, rp = row; i < num_rows; i++, rp++)\r
570    {\r
571       png_write_row(png_ptr, *rp);\r
572    }\r
573 }\r
574 \r
575 /* Write the image.  You only need to call this function once, even\r
576  * if you are writing an interlaced image.\r
577  */\r
578 void PNGAPI\r
579 png_write_image(png_structrp png_ptr, png_bytepp image)\r
580 {\r
581    png_uint_32 i; /* row index */\r
582    int pass, num_pass; /* pass variables */\r
583    png_bytepp rp; /* points to current row */\r
584 \r
585    if (png_ptr == NULL)\r
586       return;\r
587 \r
588    png_debug(1, "in png_write_image");\r
589 \r
590 #ifdef PNG_WRITE_INTERLACING_SUPPORTED\r
591    /* Initialize interlace handling.  If image is not interlaced,\r
592     * this will set pass to 1\r
593     */\r
594    num_pass = png_set_interlace_handling(png_ptr);\r
595 #else\r
596    num_pass = 1;\r
597 #endif\r
598    /* Loop through passes */\r
599    for (pass = 0; pass < num_pass; pass++)\r
600    {\r
601       /* Loop through image */\r
602       for (i = 0, rp = image; i < png_ptr->height; i++, rp++)\r
603       {\r
604          png_write_row(png_ptr, *rp);\r
605       }\r
606    }\r
607 }\r
608 \r
609 /* Called by user to write a row of image data */\r
610 void PNGAPI\r
611 png_write_row(png_structrp png_ptr, png_const_bytep row)\r
612 {\r
613    /* 1.5.6: moved from png_struct to be a local structure: */\r
614    png_row_info row_info;\r
615 \r
616    if (png_ptr == NULL)\r
617       return;\r
618 \r
619    png_debug2(1, "in png_write_row (row %u, pass %d)",\r
620       png_ptr->row_number, png_ptr->pass);\r
621 \r
622    /* Initialize transformations and other stuff if first time */\r
623    if (png_ptr->row_number == 0 && png_ptr->pass == 0)\r
624    {\r
625       /* Make sure we wrote the header info */\r
626       if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))\r
627          png_error(png_ptr,\r
628              "png_write_info was never called before png_write_row");\r
629 \r
630       /* Check for transforms that have been set but were defined out */\r
631 #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)\r
632       if (png_ptr->transformations & PNG_INVERT_MONO)\r
633          png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");\r
634 #endif\r
635 \r
636 #if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)\r
637       if (png_ptr->transformations & PNG_FILLER)\r
638          png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");\r
639 #endif\r
640 #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \\r
641     defined(PNG_READ_PACKSWAP_SUPPORTED)\r
642       if (png_ptr->transformations & PNG_PACKSWAP)\r
643          png_warning(png_ptr,\r
644              "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");\r
645 #endif\r
646 \r
647 #if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)\r
648       if (png_ptr->transformations & PNG_PACK)\r
649          png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");\r
650 #endif\r
651 \r
652 #if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)\r
653       if (png_ptr->transformations & PNG_SHIFT)\r
654          png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");\r
655 #endif\r
656 \r
657 #if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)\r
658       if (png_ptr->transformations & PNG_BGR)\r
659          png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");\r
660 #endif\r
661 \r
662 #if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)\r
663       if (png_ptr->transformations & PNG_SWAP_BYTES)\r
664          png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");\r
665 #endif\r
666 \r
667       png_write_start_row(png_ptr);\r
668    }\r
669 \r
670 #ifdef PNG_WRITE_INTERLACING_SUPPORTED\r
671    /* If interlaced and not interested in row, return */\r
672    if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))\r
673    {\r
674       switch (png_ptr->pass)\r
675       {\r
676          case 0:\r
677             if (png_ptr->row_number & 0x07)\r
678             {\r
679                png_write_finish_row(png_ptr);\r
680                return;\r
681             }\r
682             break;\r
683 \r
684          case 1:\r
685             if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)\r
686             {\r
687                png_write_finish_row(png_ptr);\r
688                return;\r
689             }\r
690             break;\r
691 \r
692          case 2:\r
693             if ((png_ptr->row_number & 0x07) != 4)\r
694             {\r
695                png_write_finish_row(png_ptr);\r
696                return;\r
697             }\r
698             break;\r
699 \r
700          case 3:\r
701             if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)\r
702             {\r
703                png_write_finish_row(png_ptr);\r
704                return;\r
705             }\r
706             break;\r
707 \r
708          case 4:\r
709             if ((png_ptr->row_number & 0x03) != 2)\r
710             {\r
711                png_write_finish_row(png_ptr);\r
712                return;\r
713             }\r
714             break;\r
715 \r
716          case 5:\r
717             if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)\r
718             {\r
719                png_write_finish_row(png_ptr);\r
720                return;\r
721             }\r
722             break;\r
723 \r
724          case 6:\r
725             if (!(png_ptr->row_number & 0x01))\r
726             {\r
727                png_write_finish_row(png_ptr);\r
728                return;\r
729             }\r
730             break;\r
731 \r
732          default: /* error: ignore it */\r
733             break;\r
734       }\r
735    }\r
736 #endif\r
737 \r
738    /* Set up row info for transformations */\r
739    row_info.color_type = png_ptr->color_type;\r
740    row_info.width = png_ptr->usr_width;\r
741    row_info.channels = png_ptr->usr_channels;\r
742    row_info.bit_depth = png_ptr->usr_bit_depth;\r
743    row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);\r
744    row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);\r
745 \r
746    png_debug1(3, "row_info->color_type = %d", row_info.color_type);\r
747    png_debug1(3, "row_info->width = %u", row_info.width);\r
748    png_debug1(3, "row_info->channels = %d", row_info.channels);\r
749    png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);\r
750    png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);\r
751    png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);\r
752 \r
753    /* Copy user's row into buffer, leaving room for filter byte. */\r
754    memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);\r
755 \r
756 #ifdef PNG_WRITE_INTERLACING_SUPPORTED\r
757    /* Handle interlacing */\r
758    if (png_ptr->interlaced && png_ptr->pass < 6 &&\r
759        (png_ptr->transformations & PNG_INTERLACE))\r
760    {\r
761       png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);\r
762       /* This should always get caught above, but still ... */\r
763       if (!(row_info.width))\r
764       {\r
765          png_write_finish_row(png_ptr);\r
766          return;\r
767       }\r
768    }\r
769 #endif\r
770 \r
771 #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED\r
772    /* Handle other transformations */\r
773    if (png_ptr->transformations)\r
774       png_do_write_transformations(png_ptr, &row_info);\r
775 #endif\r
776 \r
777    /* At this point the row_info pixel depth must match the 'transformed' depth,\r
778     * which is also the output depth.\r
779     */\r
780    if (row_info.pixel_depth != png_ptr->pixel_depth ||\r
781       row_info.pixel_depth != png_ptr->transformed_pixel_depth)\r
782       png_error(png_ptr, "internal write transform logic error");\r
783 \r
784 #ifdef PNG_MNG_FEATURES_SUPPORTED\r
785    /* Write filter_method 64 (intrapixel differencing) only if\r
786     * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and\r
787     * 2. Libpng did not write a PNG signature (this filter_method is only\r
788     *    used in PNG datastreams that are embedded in MNG datastreams) and\r
789     * 3. The application called png_permit_mng_features with a mask that\r
790     *    included PNG_FLAG_MNG_FILTER_64 and\r
791     * 4. The filter_method is 64 and\r
792     * 5. The color_type is RGB or RGBA\r
793     */\r
794    if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&\r
795        (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))\r
796    {\r
797       /* Intrapixel differencing */\r
798       png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);\r
799    }\r
800 #endif\r
801 \r
802 /* Added at libpng-1.5.10 */\r
803 #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED\r
804    /* Check for out-of-range palette index */\r
805    if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&\r
806        png_ptr->num_palette_max >= 0)\r
807       png_do_check_palette_indexes(png_ptr, &row_info);\r
808 #endif\r
809 \r
810    /* Find a filter if necessary, filter the row and write it out. */\r
811    png_write_find_filter(png_ptr, &row_info);\r
812 \r
813    if (png_ptr->write_row_fn != NULL)\r
814       (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);\r
815 }\r
816 \r
817 #ifdef PNG_WRITE_FLUSH_SUPPORTED\r
818 /* Set the automatic flush interval or 0 to turn flushing off */\r
819 void PNGAPI\r
820 png_set_flush(png_structrp png_ptr, int nrows)\r
821 {\r
822    png_debug(1, "in png_set_flush");\r
823 \r
824    if (png_ptr == NULL)\r
825       return;\r
826 \r
827    png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);\r
828 }\r
829 \r
830 /* Flush the current output buffers now */\r
831 void PNGAPI\r
832 png_write_flush(png_structrp png_ptr)\r
833 {\r
834    png_debug(1, "in png_write_flush");\r
835 \r
836    if (png_ptr == NULL)\r
837       return;\r
838 \r
839    /* We have already written out all of the data */\r
840    if (png_ptr->row_number >= png_ptr->num_rows)\r
841       return;\r
842 \r
843    png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);\r
844    png_ptr->flush_rows = 0;\r
845    png_flush(png_ptr);\r
846 }\r
847 #endif /* PNG_WRITE_FLUSH_SUPPORTED */\r
848 \r
849 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED\r
850 static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */\r
851 #endif\r
852 \r
853 /* Free any memory used in png_ptr struct without freeing the struct itself. */\r
854 static void\r
855 png_write_destroy(png_structrp png_ptr)\r
856 {\r
857    png_debug(1, "in png_write_destroy");\r
858 \r
859    /* Free any memory zlib uses */\r
860    if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)\r
861       deflateEnd(&png_ptr->zstream);\r
862 \r
863    /* Free our memory.  png_free checks NULL for us. */\r
864    png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);\r
865    png_free(png_ptr, png_ptr->row_buf);\r
866 #ifdef PNG_WRITE_FILTER_SUPPORTED\r
867    png_free(png_ptr, png_ptr->prev_row);\r
868    png_free(png_ptr, png_ptr->sub_row);\r
869    png_free(png_ptr, png_ptr->up_row);\r
870    png_free(png_ptr, png_ptr->avg_row);\r
871    png_free(png_ptr, png_ptr->paeth_row);\r
872 #endif\r
873 \r
874 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED\r
875    /* Use this to save a little code space, it doesn't free the filter_costs */\r
876    png_reset_filter_heuristics(png_ptr);\r
877    png_free(png_ptr, png_ptr->filter_costs);\r
878    png_free(png_ptr, png_ptr->inv_filter_costs);\r
879 #endif\r
880 \r
881 #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED\r
882    png_free(png_ptr, png_ptr->chunk_list);\r
883 #endif\r
884 \r
885    /* The error handling and memory handling information is left intact at this\r
886     * point: the jmp_buf may still have to be freed.  See png_destroy_png_struct\r
887     * for how this happens.\r
888     */\r
889 }\r
890 \r
891 /* Free all memory used by the write.\r
892  * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for\r
893  * *png_ptr_ptr.  Prior to 1.6.0 it would accept such a value and it would free\r
894  * the passed in info_structs but it would quietly fail to free any of the data\r
895  * inside them.  In 1.6.0 it quietly does nothing (it has to be quiet because it\r
896  * has no png_ptr.)\r
897  */\r
898 void PNGAPI\r
899 png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)\r
900 {\r
901    png_debug(1, "in png_destroy_write_struct");\r
902 \r
903    if (png_ptr_ptr != NULL)\r
904    {\r
905       png_structrp png_ptr = *png_ptr_ptr;\r
906 \r
907       if (png_ptr != NULL) /* added in libpng 1.6.0 */\r
908       {\r
909          png_destroy_info_struct(png_ptr, info_ptr_ptr);\r
910 \r
911          *png_ptr_ptr = NULL;\r
912          png_write_destroy(png_ptr);\r
913          png_destroy_png_struct(png_ptr);\r
914       }\r
915    }\r
916 }\r
917 \r
918 /* Allow the application to select one or more row filters to use. */\r
919 void PNGAPI\r
920 png_set_filter(png_structrp png_ptr, int method, int filters)\r
921 {\r
922    png_debug(1, "in png_set_filter");\r
923 \r
924    if (png_ptr == NULL)\r
925       return;\r
926 \r
927 #ifdef PNG_MNG_FEATURES_SUPPORTED\r
928    if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&\r
929        (method == PNG_INTRAPIXEL_DIFFERENCING))\r
930       method = PNG_FILTER_TYPE_BASE;\r
931 \r
932 #endif\r
933    if (method == PNG_FILTER_TYPE_BASE)\r
934    {\r
935       switch (filters & (PNG_ALL_FILTERS | 0x07))\r
936       {\r
937 #ifdef PNG_WRITE_FILTER_SUPPORTED\r
938          case 5:\r
939          case 6:\r
940          case 7: png_app_error(png_ptr, "Unknown row filter for method 0");\r
941             /* FALL THROUGH */\r
942 #endif /* PNG_WRITE_FILTER_SUPPORTED */\r
943          case PNG_FILTER_VALUE_NONE:\r
944             png_ptr->do_filter = PNG_FILTER_NONE; break;\r
945 \r
946 #ifdef PNG_WRITE_FILTER_SUPPORTED\r
947          case PNG_FILTER_VALUE_SUB:\r
948             png_ptr->do_filter = PNG_FILTER_SUB; break;\r
949 \r
950          case PNG_FILTER_VALUE_UP:\r
951             png_ptr->do_filter = PNG_FILTER_UP; break;\r
952 \r
953          case PNG_FILTER_VALUE_AVG:\r
954             png_ptr->do_filter = PNG_FILTER_AVG; break;\r
955 \r
956          case PNG_FILTER_VALUE_PAETH:\r
957             png_ptr->do_filter = PNG_FILTER_PAETH; break;\r
958 \r
959          default:\r
960             png_ptr->do_filter = (png_byte)filters; break;\r
961 #else\r
962          default:\r
963             png_app_error(png_ptr, "Unknown row filter for method 0");\r
964 #endif /* PNG_WRITE_FILTER_SUPPORTED */\r
965       }\r
966 \r
967       /* If we have allocated the row_buf, this means we have already started\r
968        * with the image and we should have allocated all of the filter buffers\r
969        * that have been selected.  If prev_row isn't already allocated, then\r
970        * it is too late to start using the filters that need it, since we\r
971        * will be missing the data in the previous row.  If an application\r
972        * wants to start and stop using particular filters during compression,\r
973        * it should start out with all of the filters, and then add and\r
974        * remove them after the start of compression.\r
975        */\r
976       if (png_ptr->row_buf != NULL)\r
977       {\r
978 #ifdef PNG_WRITE_FILTER_SUPPORTED\r
979          if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)\r
980          {\r
981             png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,\r
982                 (png_ptr->rowbytes + 1));\r
983             png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;\r
984          }\r
985 \r
986          if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)\r
987          {\r
988             if (png_ptr->prev_row == NULL)\r
989             {\r
990                png_warning(png_ptr, "Can't add Up filter after starting");\r
991                png_ptr->do_filter = (png_byte)(png_ptr->do_filter &\r
992                    ~PNG_FILTER_UP);\r
993             }\r
994 \r
995             else\r
996             {\r
997                png_ptr->up_row = (png_bytep)png_malloc(png_ptr,\r
998                    (png_ptr->rowbytes + 1));\r
999                png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;\r
1000             }\r
1001          }\r
1002 \r
1003          if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)\r
1004          {\r
1005             if (png_ptr->prev_row == NULL)\r
1006             {\r
1007                png_warning(png_ptr, "Can't add Average filter after starting");\r
1008                png_ptr->do_filter = (png_byte)(png_ptr->do_filter &\r
1009                    ~PNG_FILTER_AVG);\r
1010             }\r
1011 \r
1012             else\r
1013             {\r
1014                png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,\r
1015                    (png_ptr->rowbytes + 1));\r
1016                png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;\r
1017             }\r
1018          }\r
1019 \r
1020          if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&\r
1021              png_ptr->paeth_row == NULL)\r
1022          {\r
1023             if (png_ptr->prev_row == NULL)\r
1024             {\r
1025                png_warning(png_ptr, "Can't add Paeth filter after starting");\r
1026                png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);\r
1027             }\r
1028 \r
1029             else\r
1030             {\r
1031                png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,\r
1032                    (png_ptr->rowbytes + 1));\r
1033                png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;\r
1034             }\r
1035          }\r
1036 \r
1037          if (png_ptr->do_filter == PNG_NO_FILTERS)\r
1038 #endif /* PNG_WRITE_FILTER_SUPPORTED */\r
1039             png_ptr->do_filter = PNG_FILTER_NONE;\r
1040       }\r
1041    }\r
1042    else\r
1043       png_error(png_ptr, "Unknown custom filter method");\r
1044 }\r
1045 \r
1046 /* This allows us to influence the way in which libpng chooses the "best"\r
1047  * filter for the current scanline.  While the "minimum-sum-of-absolute-\r
1048  * differences metric is relatively fast and effective, there is some\r
1049  * question as to whether it can be improved upon by trying to keep the\r
1050  * filtered data going to zlib more consistent, hopefully resulting in\r
1051  * better compression.\r
1052  */\r
1053 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */\r
1054 /* Convenience reset API. */\r
1055 static void\r
1056 png_reset_filter_heuristics(png_structrp png_ptr)\r
1057 {\r
1058    /* Clear out any old values in the 'weights' - this must be done because if\r
1059     * the app calls set_filter_heuristics multiple times with different\r
1060     * 'num_weights' values we would otherwise potentially have wrong sized\r
1061     * arrays.\r
1062     */\r
1063    png_ptr->num_prev_filters = 0;\r
1064    png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;\r
1065    if (png_ptr->prev_filters != NULL)\r
1066    {\r
1067       png_bytep old = png_ptr->prev_filters;\r
1068       png_ptr->prev_filters = NULL;\r
1069       png_free(png_ptr, old);\r
1070    }\r
1071    if (png_ptr->filter_weights != NULL)\r
1072    {\r
1073       png_uint_16p old = png_ptr->filter_weights;\r
1074       png_ptr->filter_weights = NULL;\r
1075       png_free(png_ptr, old);\r
1076    }\r
1077 \r
1078    if (png_ptr->inv_filter_weights != NULL)\r
1079    {\r
1080       png_uint_16p old = png_ptr->inv_filter_weights;\r
1081       png_ptr->inv_filter_weights = NULL;\r
1082       png_free(png_ptr, old);\r
1083    }\r
1084 \r
1085    /* Leave the filter_costs - this array is fixed size. */\r
1086 }\r
1087 \r
1088 static int\r
1089 png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,\r
1090    int num_weights)\r
1091 {\r
1092    if (png_ptr == NULL)\r
1093       return 0;\r
1094 \r
1095    /* Clear out the arrays */\r
1096    png_reset_filter_heuristics(png_ptr);\r
1097 \r
1098    /* Check arguments; the 'reset' function makes the correct settings for the\r
1099     * unweighted case, but we must handle the weight case by initializing the\r
1100     * arrays for the caller.\r
1101     */\r
1102    if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)\r
1103    {\r
1104       int i;\r
1105 \r
1106       if (num_weights > 0)\r
1107       {\r
1108          png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,\r
1109              (png_uint_32)((sizeof (png_byte)) * num_weights));\r
1110 \r
1111          /* To make sure that the weighting starts out fairly */\r
1112          for (i = 0; i < num_weights; i++)\r
1113          {\r
1114             png_ptr->prev_filters[i] = 255;\r
1115          }\r
1116 \r
1117          png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,\r
1118              (png_uint_32)((sizeof (png_uint_16)) * num_weights));\r
1119 \r
1120          png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,\r
1121              (png_uint_32)((sizeof (png_uint_16)) * num_weights));\r
1122 \r
1123          for (i = 0; i < num_weights; i++)\r
1124          {\r
1125             png_ptr->inv_filter_weights[i] =\r
1126             png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;\r
1127          }\r
1128 \r
1129          /* Safe to set this now */\r
1130          png_ptr->num_prev_filters = (png_byte)num_weights;\r
1131       }\r
1132 \r
1133       /* If, in the future, there are other filter methods, this would\r
1134        * need to be based on png_ptr->filter.\r
1135        */\r
1136       if (png_ptr->filter_costs == NULL)\r
1137       {\r
1138          png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,\r
1139              (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));\r
1140 \r
1141          png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,\r
1142              (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));\r
1143       }\r
1144 \r
1145       for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)\r
1146       {\r
1147          png_ptr->inv_filter_costs[i] =\r
1148          png_ptr->filter_costs[i] = PNG_COST_FACTOR;\r
1149       }\r
1150 \r
1151       /* All the arrays are inited, safe to set this: */\r
1152       png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED;\r
1153 \r
1154       /* Return the 'ok' code. */\r
1155       return 1;\r
1156    }\r
1157    else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT ||\r
1158       heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)\r
1159    {\r
1160       return 1;\r
1161    }\r
1162    else\r
1163    {\r
1164       png_warning(png_ptr, "Unknown filter heuristic method");\r
1165       return 0;\r
1166    }\r
1167 }\r
1168 \r
1169 /* Provide floating and fixed point APIs */\r
1170 #ifdef PNG_FLOATING_POINT_SUPPORTED\r
1171 void PNGAPI\r
1172 png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,\r
1173     int num_weights, png_const_doublep filter_weights,\r
1174     png_const_doublep filter_costs)\r
1175 {\r
1176    png_debug(1, "in png_set_filter_heuristics");\r
1177 \r
1178    /* The internal API allocates all the arrays and ensures that the elements of\r
1179     * those arrays are set to the default value.\r
1180     */\r
1181    if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))\r
1182       return;\r
1183 \r
1184    /* If using the weighted method copy in the weights. */\r
1185    if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)\r
1186    {\r
1187       int i;\r
1188       for (i = 0; i < num_weights; i++)\r
1189       {\r
1190          if (filter_weights[i] <= 0.0)\r
1191          {\r
1192             png_ptr->inv_filter_weights[i] =\r
1193             png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;\r
1194          }\r
1195 \r
1196          else\r
1197          {\r
1198             png_ptr->inv_filter_weights[i] =\r
1199                 (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5);\r
1200 \r
1201             png_ptr->filter_weights[i] =\r
1202                 (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5);\r
1203          }\r
1204       }\r
1205 \r
1206       /* Here is where we set the relative costs of the different filters.  We\r
1207        * should take the desired compression level into account when setting\r
1208        * the costs, so that Paeth, for instance, has a high relative cost at low\r
1209        * compression levels, while it has a lower relative cost at higher\r
1210        * compression settings.  The filter types are in order of increasing\r
1211        * relative cost, so it would be possible to do this with an algorithm.\r
1212        */\r
1213       for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0)\r
1214       {\r
1215          png_ptr->inv_filter_costs[i] =\r
1216              (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);\r
1217 \r
1218          png_ptr->filter_costs[i] =\r
1219              (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);\r
1220       }\r
1221    }\r
1222 }\r
1223 #endif /* FLOATING_POINT */\r
1224 \r
1225 #ifdef PNG_FIXED_POINT_SUPPORTED\r
1226 void PNGAPI\r
1227 png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,\r
1228     int num_weights, png_const_fixed_point_p filter_weights,\r
1229     png_const_fixed_point_p filter_costs)\r
1230 {\r
1231    png_debug(1, "in png_set_filter_heuristics_fixed");\r
1232 \r
1233    /* The internal API allocates all the arrays and ensures that the elements of\r
1234     * those arrays are set to the default value.\r
1235     */\r
1236    if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))\r
1237       return;\r
1238 \r
1239    /* If using the weighted method copy in the weights. */\r
1240    if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)\r
1241    {\r
1242       int i;\r
1243       for (i = 0; i < num_weights; i++)\r
1244       {\r
1245          if (filter_weights[i] <= 0)\r
1246          {\r
1247             png_ptr->inv_filter_weights[i] =\r
1248             png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;\r
1249          }\r
1250 \r
1251          else\r
1252          {\r
1253             png_ptr->inv_filter_weights[i] = (png_uint_16)\r
1254                ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1);\r
1255 \r
1256             png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR*\r
1257                PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]);\r
1258          }\r
1259       }\r
1260 \r
1261       /* Here is where we set the relative costs of the different filters.  We\r
1262        * should take the desired compression level into account when setting\r
1263        * the costs, so that Paeth, for instance, has a high relative cost at low\r
1264        * compression levels, while it has a lower relative cost at higher\r
1265        * compression settings.  The filter types are in order of increasing\r
1266        * relative cost, so it would be possible to do this with an algorithm.\r
1267        */\r
1268       for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)\r
1269          if (filter_costs[i] >= PNG_FP_1)\r
1270       {\r
1271          png_uint_32 tmp;\r
1272 \r
1273          /* Use a 32 bit unsigned temporary here because otherwise the\r
1274           * intermediate value will be a 32 bit *signed* integer (ANSI rules)\r
1275           * and this will get the wrong answer on division.\r
1276           */\r
1277          tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2);\r
1278          tmp /= filter_costs[i];\r
1279 \r
1280          png_ptr->inv_filter_costs[i] = (png_uint_16)tmp;\r
1281 \r
1282          tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF;\r
1283          tmp /= PNG_FP_1;\r
1284 \r
1285          png_ptr->filter_costs[i] = (png_uint_16)tmp;\r
1286       }\r
1287    }\r
1288 }\r
1289 #endif /* FIXED_POINT */\r
1290 #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */\r
1291 \r
1292 void PNGAPI\r
1293 png_set_compression_level(png_structrp png_ptr, int level)\r
1294 {\r
1295    png_debug(1, "in png_set_compression_level");\r
1296 \r
1297    if (png_ptr == NULL)\r
1298       return;\r
1299 \r
1300    png_ptr->zlib_level = level;\r
1301 }\r
1302 \r
1303 void PNGAPI\r
1304 png_set_compression_mem_level(png_structrp png_ptr, int mem_level)\r
1305 {\r
1306    png_debug(1, "in png_set_compression_mem_level");\r
1307 \r
1308    if (png_ptr == NULL)\r
1309       return;\r
1310 \r
1311    png_ptr->zlib_mem_level = mem_level;\r
1312 }\r
1313 \r
1314 void PNGAPI\r
1315 png_set_compression_strategy(png_structrp png_ptr, int strategy)\r
1316 {\r
1317    png_debug(1, "in png_set_compression_strategy");\r
1318 \r
1319    if (png_ptr == NULL)\r
1320       return;\r
1321 \r
1322    /* The flag setting here prevents the libpng dynamic selection of strategy.\r
1323     */\r
1324    png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;\r
1325    png_ptr->zlib_strategy = strategy;\r
1326 }\r
1327 \r
1328 /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a\r
1329  * smaller value of window_bits if it can do so safely.\r
1330  */\r
1331 void PNGAPI\r
1332 png_set_compression_window_bits(png_structrp png_ptr, int window_bits)\r
1333 {\r
1334    if (png_ptr == NULL)\r
1335       return;\r
1336 \r
1337    /* Prior to 1.6.0 this would warn but then set the window_bits value, this\r
1338     * meant that negative window bits values could be selected which would cause\r
1339     * libpng to write a non-standard PNG file with raw deflate or gzip\r
1340     * compressed IDAT or ancillary chunks.  Such files can be read and there is\r
1341     * no warning on read, so this seems like a very bad idea.\r
1342     */\r
1343    if (window_bits > 15)\r
1344    {\r
1345       png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");\r
1346       window_bits = 15;\r
1347    }\r
1348 \r
1349    else if (window_bits < 8)\r
1350    {\r
1351       png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");\r
1352       window_bits = 8;\r
1353    }\r
1354 \r
1355    png_ptr->zlib_window_bits = window_bits;\r
1356 }\r
1357 \r
1358 void PNGAPI\r
1359 png_set_compression_method(png_structrp png_ptr, int method)\r
1360 {\r
1361    png_debug(1, "in png_set_compression_method");\r
1362 \r
1363    if (png_ptr == NULL)\r
1364       return;\r
1365 \r
1366    /* This would produce an invalid PNG file if it worked, but it doesn't and\r
1367     * deflate will fault it, so it is harmless to just warn here.\r
1368     */\r
1369    if (method != 8)\r
1370       png_warning(png_ptr, "Only compression method 8 is supported by PNG");\r
1371 \r
1372    png_ptr->zlib_method = method;\r
1373 }\r
1374 \r
1375 /* The following were added to libpng-1.5.4 */\r
1376 #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED\r
1377 void PNGAPI\r
1378 png_set_text_compression_level(png_structrp png_ptr, int level)\r
1379 {\r
1380    png_debug(1, "in png_set_text_compression_level");\r
1381 \r
1382    if (png_ptr == NULL)\r
1383       return;\r
1384 \r
1385    png_ptr->zlib_text_level = level;\r
1386 }\r
1387 \r
1388 void PNGAPI\r
1389 png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)\r
1390 {\r
1391    png_debug(1, "in png_set_text_compression_mem_level");\r
1392 \r
1393    if (png_ptr == NULL)\r
1394       return;\r
1395 \r
1396    png_ptr->zlib_text_mem_level = mem_level;\r
1397 }\r
1398 \r
1399 void PNGAPI\r
1400 png_set_text_compression_strategy(png_structrp png_ptr, int strategy)\r
1401 {\r
1402    png_debug(1, "in png_set_text_compression_strategy");\r
1403 \r
1404    if (png_ptr == NULL)\r
1405       return;\r
1406 \r
1407    png_ptr->zlib_text_strategy = strategy;\r
1408 }\r
1409 \r
1410 /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a\r
1411  * smaller value of window_bits if it can do so safely.\r
1412  */\r
1413 void PNGAPI\r
1414 png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)\r
1415 {\r
1416    if (png_ptr == NULL)\r
1417       return;\r
1418 \r
1419    if (window_bits > 15)\r
1420    {\r
1421       png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");\r
1422       window_bits = 15;\r
1423    }\r
1424 \r
1425    else if (window_bits < 8)\r
1426    {\r
1427       png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");\r
1428       window_bits = 8;\r
1429    }\r
1430 \r
1431    png_ptr->zlib_text_window_bits = window_bits;\r
1432 }\r
1433 \r
1434 void PNGAPI\r
1435 png_set_text_compression_method(png_structrp png_ptr, int method)\r
1436 {\r
1437    png_debug(1, "in png_set_text_compression_method");\r
1438 \r
1439    if (png_ptr == NULL)\r
1440       return;\r
1441 \r
1442    if (method != 8)\r
1443       png_warning(png_ptr, "Only compression method 8 is supported by PNG");\r
1444 \r
1445    png_ptr->zlib_text_method = method;\r
1446 }\r
1447 #endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */\r
1448 /* end of API added to libpng-1.5.4 */\r
1449 \r
1450 void PNGAPI\r
1451 png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)\r
1452 {\r
1453    if (png_ptr == NULL)\r
1454       return;\r
1455 \r
1456    png_ptr->write_row_fn = write_row_fn;\r
1457 }\r
1458 \r
1459 #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED\r
1460 void PNGAPI\r
1461 png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr\r
1462     write_user_transform_fn)\r
1463 {\r
1464    png_debug(1, "in png_set_write_user_transform_fn");\r
1465 \r
1466    if (png_ptr == NULL)\r
1467       return;\r
1468 \r
1469    png_ptr->transformations |= PNG_USER_TRANSFORM;\r
1470    png_ptr->write_user_transform_fn = write_user_transform_fn;\r
1471 }\r
1472 #endif\r
1473 \r
1474 \r
1475 #ifdef PNG_INFO_IMAGE_SUPPORTED\r
1476 void PNGAPI\r
1477 png_write_png(png_structrp png_ptr, png_inforp info_ptr,\r
1478     int transforms, voidp params)\r
1479 {\r
1480    if (png_ptr == NULL || info_ptr == NULL)\r
1481       return;\r
1482 \r
1483    /* Write the file header information. */\r
1484    png_write_info(png_ptr, info_ptr);\r
1485 \r
1486    /* ------ these transformations don't touch the info structure ------- */\r
1487 \r
1488 #ifdef PNG_WRITE_INVERT_SUPPORTED\r
1489    /* Invert monochrome pixels */\r
1490    if (transforms & PNG_TRANSFORM_INVERT_MONO)\r
1491       png_set_invert_mono(png_ptr);\r
1492 #endif\r
1493 \r
1494 #ifdef PNG_WRITE_SHIFT_SUPPORTED\r
1495    /* Shift the pixels up to a legal bit depth and fill in\r
1496     * as appropriate to correctly scale the image.\r
1497     */\r
1498    if ((transforms & PNG_TRANSFORM_SHIFT)\r
1499        && (info_ptr->valid & PNG_INFO_sBIT))\r
1500       png_set_shift(png_ptr, &info_ptr->sig_bit);\r
1501 #endif\r
1502 \r
1503 #ifdef PNG_WRITE_PACK_SUPPORTED\r
1504    /* Pack pixels into bytes */\r
1505    if (transforms & PNG_TRANSFORM_PACKING)\r
1506        png_set_packing(png_ptr);\r
1507 #endif\r
1508 \r
1509 #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED\r
1510    /* Swap location of alpha bytes from ARGB to RGBA */\r
1511    if (transforms & PNG_TRANSFORM_SWAP_ALPHA)\r
1512       png_set_swap_alpha(png_ptr);\r
1513 #endif\r
1514 \r
1515 #ifdef PNG_WRITE_FILLER_SUPPORTED\r
1516    /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */\r
1517    if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)\r
1518       png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);\r
1519 \r
1520    else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)\r
1521       png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);\r
1522 #endif\r
1523 \r
1524 #ifdef PNG_WRITE_BGR_SUPPORTED\r
1525    /* Flip BGR pixels to RGB */\r
1526    if (transforms & PNG_TRANSFORM_BGR)\r
1527       png_set_bgr(png_ptr);\r
1528 #endif\r
1529 \r
1530 #ifdef PNG_WRITE_SWAP_SUPPORTED\r
1531    /* Swap bytes of 16-bit files to most significant byte first */\r
1532    if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)\r
1533       png_set_swap(png_ptr);\r
1534 #endif\r
1535 \r
1536 #ifdef PNG_WRITE_PACKSWAP_SUPPORTED\r
1537    /* Swap bits of 1, 2, 4 bit packed pixel formats */\r
1538    if (transforms & PNG_TRANSFORM_PACKSWAP)\r
1539       png_set_packswap(png_ptr);\r
1540 #endif\r
1541 \r
1542 #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED\r
1543    /* Invert the alpha channel from opacity to transparency */\r
1544    if (transforms & PNG_TRANSFORM_INVERT_ALPHA)\r
1545       png_set_invert_alpha(png_ptr);\r
1546 #endif\r
1547 \r
1548    /* ----------------------- end of transformations ------------------- */\r
1549 \r
1550    /* Write the bits */\r
1551    if (info_ptr->valid & PNG_INFO_IDAT)\r
1552        png_write_image(png_ptr, info_ptr->row_pointers);\r
1553 \r
1554    /* It is REQUIRED to call this to finish writing the rest of the file */\r
1555    png_write_end(png_ptr, info_ptr);\r
1556 \r
1557    PNG_UNUSED(transforms)   /* Quiet compiler warnings */\r
1558    PNG_UNUSED(params)\r
1559 }\r
1560 #endif\r
1561 \r
1562 \r
1563 #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED\r
1564 #ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */\r
1565 /* Initialize the write structure - general purpose utility. */\r
1566 static int\r
1567 png_image_write_init(png_imagep image)\r
1568 {\r
1569    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image,\r
1570           png_safe_error, png_safe_warning);\r
1571 \r
1572    if (png_ptr != NULL)\r
1573    {\r
1574       png_infop info_ptr = png_create_info_struct(png_ptr);\r
1575 \r
1576       if (info_ptr != NULL)\r
1577       {\r
1578          png_controlp control = png_voidcast(png_controlp,\r
1579             png_malloc_warn(png_ptr, (sizeof *control)));\r
1580 \r
1581          if (control != NULL)\r
1582          {\r
1583             memset(control, 0, (sizeof *control));\r
1584 \r
1585             control->png_ptr = png_ptr;\r
1586             control->info_ptr = info_ptr;\r
1587             control->for_write = 1;\r
1588 \r
1589             image->opaque = control;\r
1590             return 1;\r
1591          }\r
1592 \r
1593          /* Error clean up */\r
1594          png_destroy_info_struct(png_ptr, &info_ptr);\r
1595       }\r
1596 \r
1597       png_destroy_write_struct(&png_ptr, NULL);\r
1598    }\r
1599 \r
1600    return png_image_error(image, "png_image_write_: out of memory");\r
1601 }\r
1602 \r
1603 /* Arguments to png_image_write_main: */\r
1604 typedef struct\r
1605 {\r
1606    /* Arguments: */\r
1607    png_imagep      image;\r
1608    png_const_voidp buffer;\r
1609    png_int_32      row_stride;\r
1610    png_const_voidp colormap;\r
1611    int             convert_to_8bit;\r
1612    /* Local variables: */\r
1613    png_const_voidp first_row;\r
1614    ptrdiff_t       row_bytes;\r
1615    png_voidp       local_row;\r
1616 } png_image_write_control;\r
1617 \r
1618 /* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to\r
1619  * do any necessary byte swapping.  The component order is defined by the\r
1620  * png_image format value.\r
1621  */\r
1622 static int\r
1623 png_write_image_16bit(png_voidp argument)\r
1624 {\r
1625    png_image_write_control *display = png_voidcast(png_image_write_control*,\r
1626       argument);\r
1627    png_imagep image = display->image;\r
1628    png_structrp png_ptr = image->opaque->png_ptr;\r
1629 \r
1630    png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,\r
1631       display->first_row);\r
1632    png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);\r
1633    png_uint_16p row_end;\r
1634    const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;\r
1635    int aindex = 0;\r
1636    png_uint_32 y = image->height;\r
1637 \r
1638    if (image->format & PNG_FORMAT_FLAG_ALPHA)\r
1639    {\r
1640       if (image->format & PNG_FORMAT_FLAG_AFIRST)\r
1641       {\r
1642          aindex = -1;\r
1643          ++input_row; /* To point to the first component */\r
1644          ++output_row;\r
1645       }\r
1646 \r
1647       else\r
1648          aindex = channels;\r
1649    }\r
1650 \r
1651    else\r
1652       png_error(png_ptr, "png_write_image: internal call error");\r
1653 \r
1654    /* Work out the output row end and count over this, note that the increment\r
1655     * above to 'row' means that row_end can actually be beyond the end of the\r
1656     * row; this is correct.\r
1657     */\r
1658    row_end = output_row + image->width * (channels+1);\r
1659 \r
1660    while (y-- > 0)\r
1661    {\r
1662       png_const_uint_16p in_ptr = input_row;\r
1663       png_uint_16p out_ptr = output_row;\r
1664 \r
1665       while (out_ptr < row_end)\r
1666       {\r
1667          const png_uint_16 alpha = in_ptr[aindex];\r
1668          png_uint_32 reciprocal = 0;\r
1669          int c;\r
1670 \r
1671          out_ptr[aindex] = alpha;\r
1672 \r
1673          /* Calculate a reciprocal.  The correct calculation is simply\r
1674           * component/alpha*65535 << 15. (I.e. 15 bits of precision); this\r
1675           * allows correct rounding by adding .5 before the shift.  'reciprocal'\r
1676           * is only initialized when required.\r
1677           */\r
1678          if (alpha > 0 && alpha < 65535)\r
1679             reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;\r
1680 \r
1681          c = channels;\r
1682          do /* always at least one channel */\r
1683          {\r
1684             png_uint_16 component = *in_ptr++;\r
1685 \r
1686             /* The following gives 65535 for an alpha of 0, which is fine,\r
1687              * otherwise if 0/0 is represented as some other value there is more\r
1688              * likely to be a discontinuity which will probably damage\r
1689              * compression when moving from a fully transparent area to a\r
1690              * nearly transparent one.  (The assumption here is that opaque\r
1691              * areas tend not to be 0 intensity.)\r
1692              */\r
1693             if (component >= alpha)\r
1694                component = 65535;\r
1695 \r
1696             /* component<alpha, so component/alpha is less than one and\r
1697              * component*reciprocal is less than 2^31.\r
1698              */\r
1699             else if (component > 0 && alpha < 65535)\r
1700             {\r
1701                png_uint_32 calc = component * reciprocal;\r
1702                calc += 16384; /* round to nearest */\r
1703                component = (png_uint_16)(calc >> 15);\r
1704             }\r
1705 \r
1706             *out_ptr++ = component;\r
1707          }\r
1708          while (--c > 0);\r
1709 \r
1710          /* Skip to next component (skip the intervening alpha channel) */\r
1711          ++in_ptr;\r
1712          ++out_ptr;\r
1713       }\r
1714 \r
1715       png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));\r
1716       input_row += display->row_bytes/(sizeof (png_uint_16));\r
1717    }\r
1718 \r
1719    return 1;\r
1720 }\r
1721 \r
1722 /* Given 16-bit input (1 to 4 channels) write 8-bit output.  If an alpha channel\r
1723  * is present it must be removed from the components, the components are then\r
1724  * written in sRGB encoding.  No components are added or removed.\r
1725  *\r
1726  * Calculate an alpha reciprocal to reverse pre-multiplication.  As above the\r
1727  * calculation can be done to 15 bits of accuracy; however, the output needs to\r
1728  * be scaled in the range 0..255*65535, so include that scaling here.\r
1729  */\r
1730 #define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha)\r
1731 \r
1732 static png_byte\r
1733 png_unpremultiply(png_uint_32 component, png_uint_32 alpha,\r
1734    png_uint_32 reciprocal/*from the above macro*/)\r
1735 {\r
1736    /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0\r
1737     * is represented as some other value there is more likely to be a\r
1738     * discontinuity which will probably damage compression when moving from a\r
1739     * fully transparent area to a nearly transparent one.  (The assumption here\r
1740     * is that opaque areas tend not to be 0 intensity.)\r
1741     *\r
1742     * There is a rounding problem here; if alpha is less than 128 it will end up\r
1743     * as 0 when scaled to 8 bits.  To avoid introducing spurious colors into the\r
1744     * output change for this too.\r
1745     */\r
1746    if (component >= alpha || alpha < 128)\r
1747       return 255;\r
1748 \r
1749    /* component<alpha, so component/alpha is less than one and\r
1750     * component*reciprocal is less than 2^31.\r
1751     */\r
1752    else if (component > 0)\r
1753    {\r
1754       /* The test is that alpha/257 (rounded) is less than 255, the first value\r
1755        * that becomes 255 is 65407.\r
1756        * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore,\r
1757        * be exact!)  [Could also test reciprocal != 0]\r
1758        */\r
1759       if (alpha < 65407)\r
1760       {\r
1761          component *= reciprocal;\r
1762          component += 64; /* round to nearest */\r
1763          component >>= 7;\r
1764       }\r
1765 \r
1766       else\r
1767          component *= 255;\r
1768 \r
1769       /* Convert the component to sRGB. */\r
1770       return (png_byte)PNG_sRGB_FROM_LINEAR(component);\r
1771    }\r
1772 \r
1773    else\r
1774       return 0;\r
1775 }\r
1776 \r
1777 static int\r
1778 png_write_image_8bit(png_voidp argument)\r
1779 {\r
1780    png_image_write_control *display = png_voidcast(png_image_write_control*,\r
1781       argument);\r
1782    png_imagep image = display->image;\r
1783    png_structrp png_ptr = image->opaque->png_ptr;\r
1784 \r
1785    png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,\r
1786       display->first_row);\r
1787    png_bytep output_row = png_voidcast(png_bytep, display->local_row);\r
1788    png_uint_32 y = image->height;\r
1789    const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;\r
1790 \r
1791    if (image->format & PNG_FORMAT_FLAG_ALPHA)\r
1792    {\r
1793       png_bytep row_end;\r
1794       int aindex;\r
1795 \r
1796       if (image->format & PNG_FORMAT_FLAG_AFIRST)\r
1797       {\r
1798          aindex = -1;\r
1799          ++input_row; /* To point to the first component */\r
1800          ++output_row;\r
1801       }\r
1802 \r
1803       else\r
1804          aindex = channels;\r
1805 \r
1806       /* Use row_end in place of a loop counter: */\r
1807       row_end = output_row + image->width * (channels+1);\r
1808 \r
1809       while (y-- > 0)\r
1810       {\r
1811          png_const_uint_16p in_ptr = input_row;\r
1812          png_bytep out_ptr = output_row;\r
1813 \r
1814          while (out_ptr < row_end)\r
1815          {\r
1816             png_uint_16 alpha = in_ptr[aindex];\r
1817             png_byte alphabyte = (png_byte)PNG_DIV257(alpha);\r
1818             png_uint_32 reciprocal = 0;\r
1819             int c;\r
1820 \r
1821             /* Scale and write the alpha channel. */\r
1822             out_ptr[aindex] = alphabyte;\r
1823 \r
1824             if (alphabyte > 0 && alphabyte < 255)\r
1825                reciprocal = UNP_RECIPROCAL(alpha);\r
1826 \r
1827             c = channels;\r
1828             do /* always at least one channel */\r
1829                *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);\r
1830             while (--c > 0);\r
1831 \r
1832             /* Skip to next component (skip the intervening alpha channel) */\r
1833             ++in_ptr;\r
1834             ++out_ptr;\r
1835          } /* while out_ptr < row_end */\r
1836 \r
1837          png_write_row(png_ptr, png_voidcast(png_const_bytep,\r
1838             display->local_row));\r
1839          input_row += display->row_bytes/(sizeof (png_uint_16));\r
1840       } /* while y */\r
1841    }\r
1842 \r
1843    else\r
1844    {\r
1845       /* No alpha channel, so the row_end really is the end of the row and it\r
1846        * is sufficient to loop over the components one by one.\r
1847        */\r
1848       png_bytep row_end = output_row + image->width * channels;\r
1849 \r
1850       while (y-- > 0)\r
1851       {\r
1852          png_const_uint_16p in_ptr = input_row;\r
1853          png_bytep out_ptr = output_row;\r
1854 \r
1855          while (out_ptr < row_end)\r
1856          {\r
1857             png_uint_32 component = *in_ptr++;\r
1858 \r
1859             component *= 255;\r
1860             *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);\r
1861          }\r
1862 \r
1863          png_write_row(png_ptr, output_row);\r
1864          input_row += display->row_bytes/(sizeof (png_uint_16));\r
1865       }\r
1866    }\r
1867 \r
1868    return 1;\r
1869 }\r
1870 \r
1871 static void\r
1872 png_image_set_PLTE(png_image_write_control *display)\r
1873 {\r
1874    const png_imagep image = display->image;\r
1875    const void *cmap = display->colormap;\r
1876    const int entries = image->colormap_entries > 256 ? 256 :\r
1877       (int)image->colormap_entries;\r
1878 \r
1879    /* NOTE: the caller must check for cmap != NULL and entries != 0 */\r
1880    const png_uint_32 format = image->format;\r
1881    const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);\r
1882 \r
1883 #  ifdef PNG_FORMAT_BGR_SUPPORTED\r
1884       const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&\r
1885          (format & PNG_FORMAT_FLAG_ALPHA) != 0;\r
1886 #  else\r
1887 #     define afirst 0\r
1888 #  endif\r
1889 \r
1890 #  ifdef PNG_FORMAT_BGR_SUPPORTED\r
1891       const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0;\r
1892 #  else\r
1893 #     define bgr 0\r
1894 #  endif\r
1895 \r
1896    int i, num_trans;\r
1897    png_color palette[256];\r
1898    png_byte tRNS[256];\r
1899 \r
1900    memset(tRNS, 255, (sizeof tRNS));\r
1901    memset(palette, 0, (sizeof palette));\r
1902 \r
1903    for (i=num_trans=0; i<entries; ++i)\r
1904    {\r
1905       /* This gets automatically converted to sRGB with reversal of the\r
1906        * pre-multiplication if the color-map has an alpha channel.\r
1907        */\r
1908       if (format & PNG_FORMAT_FLAG_LINEAR)\r
1909       {\r
1910          png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);\r
1911 \r
1912          entry += i * channels;\r
1913 \r
1914          if (channels & 1) /* no alpha */\r
1915          {\r
1916             if (channels >= 3) /* RGB */\r
1917             {\r
1918                palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 *\r
1919                   entry[(2 ^ bgr)]);\r
1920                palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 *\r
1921                   entry[1]);\r
1922                palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 *\r
1923                   entry[bgr]);\r
1924             }\r
1925 \r
1926             else /* Gray */\r
1927                palette[i].blue = palette[i].red = palette[i].green =\r
1928                   (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry);\r
1929          }\r
1930 \r
1931          else /* alpha */\r
1932          {\r
1933             png_uint_16 alpha = entry[afirst ? 0 : channels-1];\r
1934             png_byte alphabyte = (png_byte)PNG_DIV257(alpha);\r
1935             png_uint_32 reciprocal = 0;\r
1936 \r
1937             /* Calculate a reciprocal, as in the png_write_image_8bit code above\r
1938              * this is designed to produce a value scaled to 255*65535 when\r
1939              * divided by 128 (i.e. asr 7).\r
1940              */\r
1941             if (alphabyte > 0 && alphabyte < 255)\r
1942                reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;\r
1943 \r
1944             tRNS[i] = alphabyte;\r
1945             if (alphabyte < 255)\r
1946                num_trans = i+1;\r
1947 \r
1948             if (channels >= 3) /* RGB */\r
1949             {\r
1950                palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],\r
1951                   alpha, reciprocal);\r
1952                palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,\r
1953                   reciprocal);\r
1954                palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,\r
1955                   reciprocal);\r
1956             }\r
1957 \r
1958             else /* gray */\r
1959                palette[i].blue = palette[i].red = palette[i].green =\r
1960                   png_unpremultiply(entry[afirst], alpha, reciprocal);\r
1961          }\r
1962       }\r
1963 \r
1964       else /* Color-map has sRGB values */\r
1965       {\r
1966          png_const_bytep entry = png_voidcast(png_const_bytep, cmap);\r
1967 \r
1968          entry += i * channels;\r
1969 \r
1970          switch (channels)\r
1971          {\r
1972             case 4:\r
1973                tRNS[i] = entry[afirst ? 0 : 3];\r
1974                if (tRNS[i] < 255)\r
1975                   num_trans = i+1;\r
1976                /* FALL THROUGH */\r
1977             case 3:\r
1978                palette[i].blue = entry[afirst + (2 ^ bgr)];\r
1979                palette[i].green = entry[afirst + 1];\r
1980                palette[i].red = entry[afirst + bgr];\r
1981                break;\r
1982 \r
1983             case 2:\r
1984                tRNS[i] = entry[1 ^ afirst];\r
1985                if (tRNS[i] < 255)\r
1986                   num_trans = i+1;\r
1987                /* FALL THROUGH */\r
1988             case 1:\r
1989                palette[i].blue = palette[i].red = palette[i].green =\r
1990                   entry[afirst];\r
1991                break;\r
1992 \r
1993             default:\r
1994                break;\r
1995          }\r
1996       }\r
1997    }\r
1998 \r
1999 #  ifdef afirst\r
2000 #     undef afirst\r
2001 #  endif\r
2002 #  ifdef bgr\r
2003 #     undef bgr\r
2004 #  endif\r
2005 \r
2006    png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,\r
2007       entries);\r
2008 \r
2009    if (num_trans > 0)\r
2010       png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,\r
2011          num_trans, NULL);\r
2012 \r
2013    image->colormap_entries = entries;\r
2014 }\r
2015 \r
2016 static int\r
2017 png_image_write_main(png_voidp argument)\r
2018 {\r
2019    png_image_write_control *display = png_voidcast(png_image_write_control*,\r
2020       argument);\r
2021    png_imagep image = display->image;\r
2022    png_structrp png_ptr = image->opaque->png_ptr;\r
2023    png_inforp info_ptr = image->opaque->info_ptr;\r
2024    png_uint_32 format = image->format;\r
2025 \r
2026    int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0;\r
2027    int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */\r
2028    int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0;\r
2029    int write_16bit = linear && !colormap && !display->convert_to_8bit;\r
2030 \r
2031 #  ifdef PNG_BENIGN_ERRORS_SUPPORTED\r
2032       /* Make sure we error out on any bad situation */\r
2033       png_set_benign_errors(png_ptr, 0/*error*/);\r
2034 #  endif\r
2035 \r
2036    /* Default the 'row_stride' parameter if required. */\r
2037    if (display->row_stride == 0)\r
2038       display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);\r
2039 \r
2040    /* Set the required transforms then write the rows in the correct order. */\r
2041    if (format & PNG_FORMAT_FLAG_COLORMAP)\r
2042    {\r
2043       if (display->colormap != NULL && image->colormap_entries > 0)\r
2044       {\r
2045          png_uint_32 entries = image->colormap_entries;\r
2046 \r
2047          png_set_IHDR(png_ptr, info_ptr, image->width, image->height,\r
2048             entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),\r
2049             PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,\r
2050             PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);\r
2051 \r
2052          png_image_set_PLTE(display);\r
2053       }\r
2054 \r
2055       else\r
2056          png_error(image->opaque->png_ptr,\r
2057             "no color-map for color-mapped image");\r
2058    }\r
2059 \r
2060    else\r
2061       png_set_IHDR(png_ptr, info_ptr, image->width, image->height,\r
2062          write_16bit ? 16 : 8,\r
2063          ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +\r
2064          ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),\r
2065          PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);\r
2066 \r
2067    /* Counter-intuitively the data transformations must be called *after*\r
2068     * png_write_info, not before as in the read code, but the 'set' functions\r
2069     * must still be called before.  Just set the color space information, never\r
2070     * write an interlaced image.\r
2071     */\r
2072 \r
2073    if (write_16bit)\r
2074    {\r
2075       /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */\r
2076       png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);\r
2077 \r
2078       if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))\r
2079          png_set_cHRM_fixed(png_ptr, info_ptr,\r
2080             /* color      x       y */\r
2081             /* white */ 31270, 32900,\r
2082             /* red   */ 64000, 33000,\r
2083             /* green */ 30000, 60000,\r
2084             /* blue  */ 15000,  6000\r
2085          );\r
2086    }\r
2087 \r
2088    else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))\r
2089       png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);\r
2090 \r
2091    /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit\r
2092     * space must still be gamma encoded.\r
2093     */\r
2094    else\r
2095       png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);\r
2096 \r
2097    /* Write the file header. */\r
2098    png_write_info(png_ptr, info_ptr);\r
2099 \r
2100    /* Now set up the data transformations (*after* the header is written),\r
2101     * remove the handled transformations from the 'format' flags for checking.\r
2102     *\r
2103     * First check for a little endian system if writing 16 bit files.\r
2104     */\r
2105    if (write_16bit)\r
2106    {\r
2107       PNG_CONST png_uint_16 le = 0x0001;\r
2108 \r
2109       if (*(png_const_bytep)&le)\r
2110          png_set_swap(png_ptr);\r
2111    }\r
2112 \r
2113 #  ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED\r
2114       if (format & PNG_FORMAT_FLAG_BGR)\r
2115       {\r
2116          if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0)\r
2117             png_set_bgr(png_ptr);\r
2118          format &= ~PNG_FORMAT_FLAG_BGR;\r
2119       }\r
2120 #  endif\r
2121 \r
2122 #  ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED\r
2123       if (format & PNG_FORMAT_FLAG_AFIRST)\r
2124       {\r
2125          if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0)\r
2126             png_set_swap_alpha(png_ptr);\r
2127          format &= ~PNG_FORMAT_FLAG_AFIRST;\r
2128       }\r
2129 #  endif\r
2130 \r
2131    /* If there are 16 or fewer color-map entries we wrote a lower bit depth\r
2132     * above, but the application data is still byte packed.\r
2133     */\r
2134    if (colormap && image->colormap_entries <= 16)\r
2135       png_set_packing(png_ptr);\r
2136 \r
2137    /* That should have handled all (both) the transforms. */\r
2138    if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |\r
2139          PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)\r
2140       png_error(png_ptr, "png_write_image: unsupported transformation");\r
2141 \r
2142    {\r
2143       png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);\r
2144       ptrdiff_t row_bytes = display->row_stride;\r
2145 \r
2146       if (linear)\r
2147          row_bytes *= (sizeof (png_uint_16));\r
2148 \r
2149       if (row_bytes < 0)\r
2150          row += (image->height-1) * (-row_bytes);\r
2151 \r
2152       display->first_row = row;\r
2153       display->row_bytes = row_bytes;\r
2154    }\r
2155 \r
2156    /* Apply 'fast' options if the flag is set. */\r
2157    if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)\r
2158    {\r
2159       png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS);\r
2160       /* NOTE: determined by experiment using pngstest, this reflects some\r
2161        * balance between the time to write the image once and the time to read\r
2162        * it about 50 times.  The speed-up in pngstest was about 10-20% of the\r
2163        * total (user) time on a heavily loaded system.\r
2164        */\r
2165       png_set_compression_level(png_ptr, 3);\r
2166    }\r
2167 \r
2168    /* Check for the cases that currently require a pre-transform on the row\r
2169     * before it is written.  This only applies when the input is 16-bit and\r
2170     * either there is an alpha channel or it is converted to 8-bit.\r
2171     */\r
2172    if ((linear && alpha) || (!colormap && display->convert_to_8bit))\r
2173    {\r
2174       png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,\r
2175          png_get_rowbytes(png_ptr, info_ptr)));\r
2176       int result;\r
2177 \r
2178       display->local_row = row;\r
2179       if (write_16bit)\r
2180          result = png_safe_execute(image, png_write_image_16bit, display);\r
2181       else\r
2182          result = png_safe_execute(image, png_write_image_8bit, display);\r
2183       display->local_row = NULL;\r
2184 \r
2185       png_free(png_ptr, row);\r
2186 \r
2187       /* Skip the 'write_end' on error: */\r
2188       if (!result)\r
2189          return 0;\r
2190    }\r
2191 \r
2192    /* Otherwise this is the case where the input is in a format currently\r
2193     * supported by the rest of the libpng write code; call it directly.\r
2194     */\r
2195    else\r
2196    {\r
2197       png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);\r
2198       ptrdiff_t row_bytes = display->row_bytes;\r
2199       png_uint_32 y = image->height;\r
2200 \r
2201       while (y-- > 0)\r
2202       {\r
2203          png_write_row(png_ptr, row);\r
2204          row += row_bytes;\r
2205       }\r
2206    }\r
2207 \r
2208    png_write_end(png_ptr, info_ptr);\r
2209    return 1;\r
2210 }\r
2211 \r
2212 int PNGAPI\r
2213 png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,\r
2214    const void *buffer, png_int_32 row_stride, const void *colormap)\r
2215 {\r
2216    /* Write the image to the given (FILE*). */\r
2217    if (image != NULL && image->version == PNG_IMAGE_VERSION)\r
2218    {\r
2219       if (file != NULL)\r
2220       {\r
2221          if (png_image_write_init(image))\r
2222          {\r
2223             png_image_write_control display;\r
2224             int result;\r
2225 \r
2226             /* This is slightly evil, but png_init_io doesn't do anything other\r
2227              * than this and we haven't changed the standard IO functions so\r
2228              * this saves a 'safe' function.\r
2229              */\r
2230             image->opaque->png_ptr->io_ptr = file;\r
2231 \r
2232             memset(&display, 0, (sizeof display));\r
2233             display.image = image;\r
2234             display.buffer = buffer;\r
2235             display.row_stride = row_stride;\r
2236             display.colormap = colormap;\r
2237             display.convert_to_8bit = convert_to_8bit;\r
2238 \r
2239             result = png_safe_execute(image, png_image_write_main, &display);\r
2240             png_image_free(image);\r
2241             return result;\r
2242          }\r
2243 \r
2244          else\r
2245             return 0;\r
2246       }\r
2247 \r
2248       else\r
2249          return png_image_error(image,\r
2250             "png_image_write_to_stdio: invalid argument");\r
2251    }\r
2252 \r
2253    else if (image != NULL)\r
2254       return png_image_error(image,\r
2255          "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");\r
2256 \r
2257    else\r
2258       return 0;\r
2259 }\r
2260 \r
2261 int PNGAPI\r
2262 png_image_write_to_file(png_imagep image, const char *file_name,\r
2263    int convert_to_8bit, const void *buffer, png_int_32 row_stride,\r
2264    const void *colormap)\r
2265 {\r
2266    /* Write the image to the named file. */\r
2267    if (image != NULL && image->version == PNG_IMAGE_VERSION)\r
2268    {\r
2269       if (file_name != NULL)\r
2270       {\r
2271          FILE *fp = fopen(file_name, "wb");\r
2272 \r
2273          if (fp != NULL)\r
2274          {\r
2275             if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,\r
2276                row_stride, colormap))\r
2277             {\r
2278                int error; /* from fflush/fclose */\r
2279 \r
2280                /* Make sure the file is flushed correctly. */\r
2281                if (fflush(fp) == 0 && ferror(fp) == 0)\r
2282                {\r
2283                   if (fclose(fp) == 0)\r
2284                      return 1;\r
2285 \r
2286                   error = errno; /* from fclose */\r
2287                }\r
2288 \r
2289                else\r
2290                {\r
2291                   error = errno; /* from fflush or ferror */\r
2292                   (void)fclose(fp);\r
2293                }\r
2294 \r
2295                (void)remove(file_name);\r
2296                /* The image has already been cleaned up; this is just used to\r
2297                 * set the error (because the original write succeeded).\r
2298                 */\r
2299                return png_image_error(image, strerror(error));\r
2300             }\r
2301 \r
2302             else\r
2303             {\r
2304                /* Clean up: just the opened file. */\r
2305                (void)fclose(fp);\r
2306                (void)remove(file_name);\r
2307                return 0;\r
2308             }\r
2309          }\r
2310 \r
2311          else\r
2312             return png_image_error(image, strerror(errno));\r
2313       }\r
2314 \r
2315       else\r
2316          return png_image_error(image,\r
2317             "png_image_write_to_file: invalid argument");\r
2318    }\r
2319 \r
2320    else if (image != NULL)\r
2321       return png_image_error(image,\r
2322          "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");\r
2323 \r
2324    else\r
2325       return 0;\r
2326 }\r
2327 #endif /* PNG_STDIO_SUPPORTED */\r
2328 #endif /* SIMPLIFIED_WRITE */\r
2329 #endif /* PNG_WRITE_SUPPORTED */\r