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