2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/layer.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/MagickCore.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/module.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/profile.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/semaphore.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/static.h"
84 #include "MagickCore/statistic.h"
85 #include "MagickCore/string_.h"
86 #include "MagickCore/string-private.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/utility.h"
89 #if defined(MAGICKCORE_PNG_DELEGATE)
91 /* Suppress libpng pedantic warnings that were added in
92 * libpng-1.2.41 and libpng-1.4.0. If you are working on
93 * migration to libpng-1.5, remove these defines and then
94 * fix any code that generates warnings.
96 /* #define PNG_DEPRECATED Use of this function is deprecated */
97 /* #define PNG_USE_RESULT The result of this function must be checked */
98 /* #define PNG_NORETURN This function does not return */
99 /* #define PNG_ALLOCATED The result of the function is new memory */
100 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
102 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
103 #define PNG_PTR_NORETURN
108 /* ImageMagick differences */
109 #define first_scene scene
111 #if PNG_LIBPNG_VER > 10011
113 Optional declarations. Define or undefine them as you like.
115 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
118 Features under construction. Define these to work on them.
120 #undef MNG_OBJECT_BUFFERS
121 #undef MNG_BASI_SUPPORTED
122 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
123 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
124 #if defined(MAGICKCORE_JPEG_DELEGATE)
125 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
127 #if !defined(RGBColorMatchExact)
128 #define IsPNGColorEqual(color,target) \
129 (((color).red == (target).red) && \
130 ((color).green == (target).green) && \
131 ((color).blue == (target).blue))
134 /* Table of recognized sRGB ICC profiles */
135 struct sRGB_info_struct
142 const struct sRGB_info_struct sRGB_info[] =
144 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
145 { 3048, 0x3b8772b9UL, 0},
147 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
148 { 3052, 0x427ebb21UL, 1},
150 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
151 {60988, 0x306fd8aeUL, 0},
153 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
154 {60960, 0xbbef7812UL, 0},
156 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
157 { 3024, 0x5d5129ceUL, 1},
159 /* HP-Microsoft sRGB v2 perceptual */
160 { 3144, 0x182ea552UL, 0},
162 /* HP-Microsoft sRGB v2 media-relative */
163 { 3144, 0xf29e526dUL, 1},
165 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
166 { 524, 0xd4938c39UL, 0},
168 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
169 { 3212, 0x034af5a1UL, 0},
172 { 0, 0x00000000UL, 0},
175 /* Macros for left-bit-replication to ensure that pixels
176 * and PixelInfos all have the same image->depth, and for use
177 * in PNG8 quantization.
180 /* LBR01: Replicate top bit */
182 #define LBR01PacketRed(pixelpacket) \
183 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
186 #define LBR01PacketGreen(pixelpacket) \
187 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
190 #define LBR01PacketBlue(pixelpacket) \
191 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
194 #define LBR01PacketAlpha(pixelpacket) \
195 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
198 #define LBR01PacketRGB(pixelpacket) \
200 LBR01PacketRed((pixelpacket)); \
201 LBR01PacketGreen((pixelpacket)); \
202 LBR01PacketBlue((pixelpacket)); \
205 #define LBR01PacketRGBO(pixelpacket) \
207 LBR01PacketRGB((pixelpacket)); \
208 LBR01PacketAlpha((pixelpacket)); \
211 #define LBR01PixelRed(pixel) \
212 (SetPixelRed(image, \
213 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
214 0 : QuantumRange,(pixel)));
216 #define LBR01PixelGreen(pixel) \
217 (SetPixelGreen(image, \
218 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
219 0 : QuantumRange,(pixel)));
221 #define LBR01PixelBlue(pixel) \
222 (SetPixelBlue(image, \
223 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
224 0 : QuantumRange,(pixel)));
226 #define LBR01PixelAlpha(pixel) \
227 (SetPixelAlpha(image, \
228 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
229 0 : QuantumRange,(pixel)));
231 #define LBR01PixelRGB(pixel) \
233 LBR01PixelRed((pixel)); \
234 LBR01PixelGreen((pixel)); \
235 LBR01PixelBlue((pixel)); \
238 #define LBR01PixelRGBA(pixel) \
240 LBR01PixelRGB((pixel)); \
241 LBR01PixelAlpha((pixel)); \
244 /* LBR02: Replicate top 2 bits */
246 #define LBR02PacketRed(pixelpacket) \
248 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
249 (pixelpacket).red=ScaleCharToQuantum( \
250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
252 #define LBR02PacketGreen(pixelpacket) \
254 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
255 (pixelpacket).green=ScaleCharToQuantum( \
256 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
258 #define LBR02PacketBlue(pixelpacket) \
260 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
261 (pixelpacket).blue=ScaleCharToQuantum( \
262 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
264 #define LBR02PacketAlpha(pixelpacket) \
266 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
267 (pixelpacket).alpha=ScaleCharToQuantum( \
268 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
271 #define LBR02PacketRGB(pixelpacket) \
273 LBR02PacketRed((pixelpacket)); \
274 LBR02PacketGreen((pixelpacket)); \
275 LBR02PacketBlue((pixelpacket)); \
278 #define LBR02PacketRGBO(pixelpacket) \
280 LBR02PacketRGB((pixelpacket)); \
281 LBR02PacketAlpha((pixelpacket)); \
284 #define LBR02PixelRed(pixel) \
286 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
288 SetPixelRed(image, ScaleCharToQuantum( \
289 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
292 #define LBR02PixelGreen(pixel) \
294 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
296 SetPixelGreen(image, ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
300 #define LBR02PixelBlue(pixel) \
302 unsigned char lbr_bits= \
303 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
304 SetPixelBlue(image, ScaleCharToQuantum( \
305 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
308 #define LBR02PixelAlpha(pixel) \
310 unsigned char lbr_bits= \
311 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
312 SetPixelAlpha(image, ScaleCharToQuantum( \
313 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
317 #define LBR02PixelRGB(pixel) \
319 LBR02PixelRed((pixel)); \
320 LBR02PixelGreen((pixel)); \
321 LBR02PixelBlue((pixel)); \
324 #define LBR02PixelRGBA(pixel) \
326 LBR02PixelRGB((pixel)); \
327 LBR02PixelAlpha((pixel)); \
330 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
331 PNG8 quantization) */
333 #define LBR03PacketRed(pixelpacket) \
335 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
336 (pixelpacket).red=ScaleCharToQuantum( \
337 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
339 #define LBR03PacketGreen(pixelpacket) \
341 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
342 (pixelpacket).green=ScaleCharToQuantum( \
343 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
345 #define LBR03PacketBlue(pixelpacket) \
347 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
348 (pixelpacket).blue=ScaleCharToQuantum( \
349 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
352 #define LBR03PacketRGB(pixelpacket) \
354 LBR03PacketRed((pixelpacket)); \
355 LBR03PacketGreen((pixelpacket)); \
356 LBR03PacketBlue((pixelpacket)); \
359 #define LBR03PixelRed(pixel) \
361 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
363 SetPixelRed(image, ScaleCharToQuantum( \
364 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
366 #define LBR03Green(pixel) \
368 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
370 SetPixelGreen(image, ScaleCharToQuantum( \
371 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
373 #define LBR03Blue(pixel) \
375 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
377 SetPixelBlue(image, ScaleCharToQuantum( \
378 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
381 #define LBR03RGB(pixel) \
383 LBR03PixelRed((pixel)); \
384 LBR03Green((pixel)); \
385 LBR03Blue((pixel)); \
388 /* LBR04: Replicate top 4 bits */
390 #define LBR04PacketRed(pixelpacket) \
392 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
393 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
395 #define LBR04PacketGreen(pixelpacket) \
397 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
398 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
400 #define LBR04PacketBlue(pixelpacket) \
402 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
403 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
405 #define LBR04PacketAlpha(pixelpacket) \
407 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
408 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
411 #define LBR04PacketRGB(pixelpacket) \
413 LBR04PacketRed((pixelpacket)); \
414 LBR04PacketGreen((pixelpacket)); \
415 LBR04PacketBlue((pixelpacket)); \
418 #define LBR04PacketRGBO(pixelpacket) \
420 LBR04PacketRGB((pixelpacket)); \
421 LBR04PacketAlpha((pixelpacket)); \
424 #define LBR04PixelRed(pixel) \
426 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
429 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
431 #define LBR04PixelGreen(pixel) \
433 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
435 SetPixelGreen(image,\
436 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
438 #define LBR04PixelBlue(pixel) \
440 unsigned char lbr_bits= \
441 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
443 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
445 #define LBR04PixelAlpha(pixel) \
447 unsigned char lbr_bits= \
448 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
449 SetPixelAlpha(image,\
450 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
453 #define LBR04PixelRGB(pixel) \
455 LBR04PixelRed((pixel)); \
456 LBR04PixelGreen((pixel)); \
457 LBR04PixelBlue((pixel)); \
460 #define LBR04PixelRGBA(pixel) \
462 LBR04PixelRGB((pixel)); \
463 LBR04PixelAlpha((pixel)); \
467 Establish thread safety.
468 setjmp/longjmp is claimed to be safe on these platforms:
469 setjmp/longjmp is alleged to be unsafe on these platforms:
471 #ifdef PNG_SETJMP_SUPPORTED
472 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
473 # define IMPNG_SETJMP_NOT_THREAD_SAFE
476 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
478 *ping_semaphore = (SemaphoreInfo *) NULL;
483 This temporary until I set up malloc'ed object attributes array.
484 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
487 #define MNG_MAX_OBJECTS 256
490 If this not defined, spec is interpreted strictly. If it is
491 defined, an attempt will be made to recover from some errors,
493 o global PLTE too short
498 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
499 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
500 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
501 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
502 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
503 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
504 will be enabled by default in libpng-1.2.0.
506 #ifdef PNG_MNG_FEATURES_SUPPORTED
507 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
508 # define PNG_READ_EMPTY_PLTE_SUPPORTED
510 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
511 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
516 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
517 This macro is only defined in libpng-1.0.3 and later.
518 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
520 #ifndef PNG_UINT_31_MAX
521 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
525 Constant strings for known chunk types. If you need to add a chunk,
526 add a string holding the name here. To make the code more
527 portable, we use ASCII numbers like this, not characters.
530 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
531 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
532 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
533 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
534 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
535 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
536 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
537 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
538 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
539 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
540 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
541 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
542 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
543 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
544 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
545 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
546 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
547 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
548 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
549 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
550 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
551 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
552 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
553 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
554 static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
555 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
556 static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
557 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
558 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
559 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
560 static const png_byte mng_orNT[5]={111, 114, 78, 84, (png_byte) '\0'};
561 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
562 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
563 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
564 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
565 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
566 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
568 #if defined(JNG_SUPPORTED)
569 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
570 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
571 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
572 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
573 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
574 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
578 /* Other known chunks that are not yet supported by ImageMagick: */
579 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
580 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
581 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
582 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
583 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
584 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
585 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
588 typedef struct _MngBox
597 typedef struct _MngPair
604 #ifdef MNG_OBJECT_BUFFERS
605 typedef struct _MngBuffer
637 typedef struct _MngInfo
640 #ifdef MNG_OBJECT_BUFFERS
642 *ob[MNG_MAX_OBJECTS];
653 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
654 bytes_in_read_buffer,
660 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
661 defined(PNG_MNG_FEATURES_SUPPORTED)
673 have_saved_bkgd_index,
674 have_write_global_chrm,
675 have_write_global_gama,
676 have_write_global_plte,
677 have_write_global_srgb,
691 x_off[MNG_MAX_OBJECTS],
692 y_off[MNG_MAX_OBJECTS];
698 object_clip[MNG_MAX_OBJECTS];
701 /* These flags could be combined into one byte */
702 exists[MNG_MAX_OBJECTS],
703 frozen[MNG_MAX_OBJECTS],
705 invisible[MNG_MAX_OBJECTS],
706 viewable[MNG_MAX_OBJECTS];
718 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
736 global_x_pixels_per_unit,
737 global_y_pixels_per_unit,
747 global_phys_unit_type,
762 write_png_compression_level,
763 write_png_compression_strategy,
764 write_png_compression_filter,
771 #ifdef MNG_BASI_SUPPORTED
779 basi_compression_method,
781 basi_interlace_method,
804 /* Added at version 6.6.6-7 */
813 /* ping_exclude_iTXt, */
820 ping_exclude_zCCP, /* hex-encoded iCCP */
822 ping_preserve_colormap,
823 /* Added at version 6.8.5-7 */
825 /* Added at version 6.8.9-9 */
832 Forward declarations.
834 static MagickBooleanType
835 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
837 static MagickBooleanType
838 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
840 #if defined(JNG_SUPPORTED)
841 static MagickBooleanType
842 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
845 #if PNG_LIBPNG_VER > 10011
848 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
849 static MagickBooleanType
850 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
852 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
854 * This is true if the high byte and the next highest byte of
855 * each sample of the image, the colormap, and the background color
856 * are equal to each other. We check this by seeing if the samples
857 * are unchanged when we scale them down to 8 and back up to Quantum.
859 * We don't use the method GetImageDepth() because it doesn't check
860 * background and doesn't handle PseudoClass specially.
863 #define QuantumToCharToQuantumEqQuantum(quantum) \
864 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
867 ok_to_reduce=MagickFalse;
869 if (image->depth >= 16)
876 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
877 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
878 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
879 MagickTrue : MagickFalse;
881 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
885 for (indx=0; indx < (ssize_t) image->colors; indx++)
888 QuantumToCharToQuantumEqQuantum(
889 image->colormap[indx].red) &&
890 QuantumToCharToQuantumEqQuantum(
891 image->colormap[indx].green) &&
892 QuantumToCharToQuantumEqQuantum(
893 image->colormap[indx].blue)) ?
894 MagickTrue : MagickFalse;
896 if (ok_to_reduce == MagickFalse)
901 if ((ok_to_reduce != MagickFalse) &&
902 (image->storage_class != PseudoClass))
910 for (y=0; y < (ssize_t) image->rows; y++)
912 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
914 if (p == (const Quantum *) NULL)
916 ok_to_reduce = MagickFalse;
920 for (x=(ssize_t) image->columns-1; x >= 0; x--)
923 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
924 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
925 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
926 MagickTrue : MagickFalse;
928 if (ok_to_reduce == MagickFalse)
931 p+=GetPixelChannels(image);
938 if (ok_to_reduce != MagickFalse)
940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
941 " OK to reduce PNG bit depth to 8 without loss of info");
945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
946 " Not OK to reduce PNG bit depth to 8 without losing info");
952 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
954 static const char* PngColorTypeToString(const unsigned int color_type)
961 case PNG_COLOR_TYPE_GRAY:
964 case PNG_COLOR_TYPE_GRAY_ALPHA:
965 result = "Gray+Alpha";
967 case PNG_COLOR_TYPE_PALETTE:
970 case PNG_COLOR_TYPE_RGB:
973 case PNG_COLOR_TYPE_RGB_ALPHA:
974 result = "RGB+Alpha";
982 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
986 /* Convert to Exif orientations as defined in "Exchangeable image file
987 * format for digital still cameras: Exif Version 2.31",
988 * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
991 case TopLeftOrientation:
993 case TopRightOrientation:
995 case BottomRightOrientation:
997 case BottomLeftOrientation:
999 case LeftTopOrientation:
1001 case RightTopOrientation:
1003 case RightBottomOrientation:
1005 case LeftBottomOrientation:
1007 case UndefinedOrientation:
1012 static OrientationType
1013 Magick_Orientation_from_Exif_Orientation(const int orientation)
1015 switch (orientation)
1018 return TopLeftOrientation;
1020 return TopRightOrientation;
1022 return BottomRightOrientation;
1024 return BottomLeftOrientation;
1026 return LeftTopOrientation;
1028 return RightTopOrientation;
1030 return RightBottomOrientation;
1032 return LeftBottomOrientation;
1035 return UndefinedOrientation;
1040 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1044 case PerceptualIntent:
1047 case RelativeIntent:
1050 case SaturationIntent:
1053 case AbsoluteIntent:
1061 static RenderingIntent
1062 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1064 switch (ping_intent)
1067 return PerceptualIntent;
1070 return RelativeIntent;
1073 return SaturationIntent;
1076 return AbsoluteIntent;
1079 return UndefinedIntent;
1084 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1086 switch (ping_intent)
1089 return "Perceptual Intent";
1092 return "Relative Intent";
1095 return "Saturation Intent";
1098 return "Absolute Intent";
1101 return "Undefined Intent";
1106 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1108 switch (ping_colortype)
1126 return "UndefinedColorType";
1130 #endif /* PNG_LIBPNG_VER > 10011 */
1131 #endif /* MAGICKCORE_PNG_DELEGATE */
1134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1144 % IsMNG() returns MagickTrue if the image format type, identified by the
1145 % magick string, is MNG.
1147 % The format of the IsMNG method is:
1149 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1151 % A description of each parameter follows:
1153 % o magick: compare image format pattern against these bytes.
1155 % o length: Specifies the length of the magick string.
1159 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1162 return(MagickFalse);
1164 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1167 return(MagickFalse);
1171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1181 % IsJNG() returns MagickTrue if the image format type, identified by the
1182 % magick string, is JNG.
1184 % The format of the IsJNG method is:
1186 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1188 % A description of each parameter follows:
1190 % o magick: compare image format pattern against these bytes.
1192 % o length: Specifies the length of the magick string.
1196 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1199 return(MagickFalse);
1201 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1204 return(MagickFalse);
1208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1218 % IsPNG() returns MagickTrue if the image format type, identified by the
1219 % magick string, is PNG.
1221 % The format of the IsPNG method is:
1223 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1225 % A description of each parameter follows:
1227 % o magick: compare image format pattern against these bytes.
1229 % o length: Specifies the length of the magick string.
1232 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1235 return(MagickFalse);
1237 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1240 return(MagickFalse);
1243 #if defined(MAGICKCORE_PNG_DELEGATE)
1244 #if defined(__cplusplus) || defined(c_plusplus)
1248 #if (PNG_LIBPNG_VER > 10011)
1249 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1254 assert(image != (Image *) NULL);
1255 assert(image->signature == MagickCoreSignature);
1256 buffer[0]=(unsigned char) (value >> 24);
1257 buffer[1]=(unsigned char) (value >> 16);
1258 buffer[2]=(unsigned char) (value >> 8);
1259 buffer[3]=(unsigned char) value;
1260 return((size_t) WriteBlob(image,4,buffer));
1263 static void PNGLong(png_bytep p,png_uint_32 value)
1265 *p++=(png_byte) ((value >> 24) & 0xff);
1266 *p++=(png_byte) ((value >> 16) & 0xff);
1267 *p++=(png_byte) ((value >> 8) & 0xff);
1268 *p++=(png_byte) (value & 0xff);
1271 static void PNGsLong(png_bytep p,png_int_32 value)
1273 *p++=(png_byte) ((value >> 24) & 0xff);
1274 *p++=(png_byte) ((value >> 16) & 0xff);
1275 *p++=(png_byte) ((value >> 8) & 0xff);
1276 *p++=(png_byte) (value & 0xff);
1279 static void PNGShort(png_bytep p,png_uint_16 value)
1281 *p++=(png_byte) ((value >> 8) & 0xff);
1282 *p++=(png_byte) (value & 0xff);
1285 static void PNGType(png_bytep p,const png_byte *type)
1287 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1290 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1293 if (logging != MagickFalse)
1294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1295 " Writing %c%c%c%c chunk, length: %.20g",
1296 type[0],type[1],type[2],type[3],(double) length);
1298 #endif /* PNG_LIBPNG_VER > 10011 */
1300 #if defined(__cplusplus) || defined(c_plusplus)
1304 #if PNG_LIBPNG_VER > 10011
1306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310 % R e a d P N G I m a g e %
1314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1316 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1317 % Multiple-image Network Graphics (MNG) image file and returns it. It
1318 % allocates the memory necessary for the new Image structure and returns a
1319 % pointer to the new image or set of images.
1321 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1323 % The format of the ReadPNGImage method is:
1325 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1327 % A description of each parameter follows:
1329 % o image_info: the image info.
1331 % o exception: return any errors or warnings in this structure.
1333 % To do, more or less in chronological order (as of version 5.5.2,
1334 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1336 % Get 16-bit cheap transparency working.
1338 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1340 % Preserve all unknown and not-yet-handled known chunks found in input
1341 % PNG file and copy them into output PNG files according to the PNG
1344 % (At this point, PNG encoding should be in full MNG compliance)
1346 % Provide options for choice of background to use when the MNG BACK
1347 % chunk is not present or is not mandatory (i.e., leave transparent,
1348 % user specified, MNG BACK, PNG bKGD)
1350 % Implement LOOP/ENDL [done, but could do discretionary loops more
1351 % efficiently by linking in the duplicate frames.].
1353 % Decode and act on the MHDR simplicity profile (offer option to reject
1354 % files or attempt to process them anyway when the profile isn't LC or VLC).
1356 % Upgrade to full MNG without Delta-PNG.
1358 % o BACK [done a while ago except for background image ID]
1359 % o MOVE [done 15 May 1999]
1360 % o CLIP [done 15 May 1999]
1361 % o DISC [done 19 May 1999]
1362 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1363 % o SEEK [partially done 19 May 1999 (discard function only)]
1367 % o MNG-level tEXt/iTXt/zTXt
1372 % o iTXt (wait for libpng implementation).
1374 % Use the scene signature to discover when an identical scene is
1375 % being reused, and just point to the original image->exception instead
1376 % of storing another set of pixels. This not specific to MNG
1377 % but could be applied generally.
1379 % Upgrade to full MNG with Delta-PNG.
1381 % JNG tEXt/iTXt/zTXt
1383 % We will not attempt to read files containing the CgBI chunk.
1384 % They are really Xcode files meant for display on the iPhone.
1385 % These are not valid PNG files and it is impossible to recover
1386 % the original PNG from files that have been converted to Xcode-PNG,
1387 % since irretrievable loss of color data has occurred due to the
1388 % use of premultiplied alpha.
1391 #if defined(__cplusplus) || defined(c_plusplus)
1396 This the function that does the actual reading of data. It is
1397 the same as the one supplied in libpng, except that it receives the
1398 datastream from the ReadBlob() function instead of standard input.
1400 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1405 image=(Image *) png_get_io_ptr(png_ptr);
1411 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1412 if (check != length)
1415 msg[MagickPathExtent];
1417 (void) FormatLocaleString(msg,MagickPathExtent,
1418 "Expected %.20g bytes; found %.20g bytes",(double) length,
1420 png_warning(png_ptr,msg);
1421 png_error(png_ptr,"Read Exception");
1426 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1427 !defined(PNG_MNG_FEATURES_SUPPORTED)
1428 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1429 * older than libpng-1.0.3a, which was the first to allow the empty
1430 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1431 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1432 * encountered after an empty PLTE, so we have to look ahead for bKGD
1433 * chunks and remove them from the datastream that is passed to libpng,
1434 * and store their contents for later use.
1436 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1451 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1452 image=(Image *) mng_info->image;
1453 while (mng_info->bytes_in_read_buffer && length)
1455 data[i]=mng_info->read_buffer[i];
1456 mng_info->bytes_in_read_buffer--;
1462 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1464 if (check != length)
1465 png_error(png_ptr,"Read Exception");
1469 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1472 check=(png_size_t) ReadBlob(image,(size_t) length,
1473 (char *) mng_info->read_buffer);
1474 mng_info->read_buffer[4]=0;
1475 mng_info->bytes_in_read_buffer=4;
1476 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1477 mng_info->found_empty_plte=MagickTrue;
1478 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1480 mng_info->found_empty_plte=MagickFalse;
1481 mng_info->have_saved_bkgd_index=MagickFalse;
1485 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1488 check=(png_size_t) ReadBlob(image,(size_t) length,
1489 (char *) mng_info->read_buffer);
1490 mng_info->read_buffer[4]=0;
1491 mng_info->bytes_in_read_buffer=4;
1492 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1493 if (mng_info->found_empty_plte)
1496 Skip the bKGD data byte and CRC.
1499 ReadBlob(image,5,(char *) mng_info->read_buffer);
1500 check=(png_size_t) ReadBlob(image,(size_t) length,
1501 (char *) mng_info->read_buffer);
1502 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1503 mng_info->have_saved_bkgd_index=MagickTrue;
1504 mng_info->bytes_in_read_buffer=0;
1512 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1517 image=(Image *) png_get_io_ptr(png_ptr);
1523 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1525 if (check != length)
1526 png_error(png_ptr,"WriteBlob Failed");
1530 static void png_flush_data(png_structp png_ptr)
1535 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1536 static int PalettesAreEqual(Image *a,Image *b)
1541 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1542 return((int) MagickFalse);
1544 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1545 return((int) MagickFalse);
1547 if (a->colors != b->colors)
1548 return((int) MagickFalse);
1550 for (i=0; i < (ssize_t) a->colors; i++)
1552 if ((a->colormap[i].red != b->colormap[i].red) ||
1553 (a->colormap[i].green != b->colormap[i].green) ||
1554 (a->colormap[i].blue != b->colormap[i].blue))
1555 return((int) MagickFalse);
1558 return((int) MagickTrue);
1562 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1564 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1565 mng_info->exists[i] && !mng_info->frozen[i])
1567 #ifdef MNG_OBJECT_BUFFERS
1568 if (mng_info->ob[i] != (MngBuffer *) NULL)
1570 if (mng_info->ob[i]->reference_count > 0)
1571 mng_info->ob[i]->reference_count--;
1573 if (mng_info->ob[i]->reference_count == 0)
1575 if (mng_info->ob[i]->image != (Image *) NULL)
1576 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1578 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1581 mng_info->ob[i]=(MngBuffer *) NULL;
1583 mng_info->exists[i]=MagickFalse;
1584 mng_info->invisible[i]=MagickFalse;
1585 mng_info->viewable[i]=MagickFalse;
1586 mng_info->frozen[i]=MagickFalse;
1587 mng_info->x_off[i]=0;
1588 mng_info->y_off[i]=0;
1589 mng_info->object_clip[i].left=0;
1590 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1591 mng_info->object_clip[i].top=0;
1592 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1596 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1601 if (mng_info == (MngInfo *) NULL)
1602 return((MngInfo *) NULL);
1604 for (i=1; i < MNG_MAX_OBJECTS; i++)
1605 MngInfoDiscardObject(mng_info,i);
1607 mng_info->global_plte=(png_colorp)
1608 RelinquishMagickMemory(mng_info->global_plte);
1610 return((MngInfo *) RelinquishMagickMemory(mng_info));
1613 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1619 if (box.left < box2.left)
1622 if (box.top < box2.top)
1625 if (box.right > box2.right)
1626 box.right=box2.right;
1628 if (box.bottom > box2.bottom)
1629 box.bottom=box2.bottom;
1634 static long mng_get_long(unsigned char *p)
1636 return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1637 ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1640 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1647 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1649 box.left=mng_get_long(p);
1650 box.right=mng_get_long(&p[4]);
1651 box.top=mng_get_long(&p[8]);
1652 box.bottom=mng_get_long(&p[12]);
1653 if (delta_type != 0)
1655 box.left+=previous_box.left;
1656 box.right+=previous_box.right;
1657 box.top+=previous_box.top;
1658 box.bottom+=previous_box.bottom;
1664 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1671 Read two ssize_t's from CLON, MOVE or PAST chunk
1673 pair.a=mng_get_long(p);
1674 pair.b=mng_get_long(&p[4]);
1675 if (delta_type != 0)
1677 pair.a+=previous_pair.a;
1678 pair.b+=previous_pair.b;
1684 typedef struct _PNGErrorInfo
1693 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1704 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1705 image=error_info->image;
1706 exception=error_info->exception;
1708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1709 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1711 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1712 "`%s'",image->filename);
1714 #if (PNG_LIBPNG_VER < 10500)
1715 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1716 * are building with libpng-1.4.x and can be ignored.
1718 longjmp(ping->jmpbuf,1);
1720 png_longjmp(ping,1);
1724 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1735 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1736 png_error(ping, message);
1738 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1739 image=error_info->image;
1740 exception=error_info->exception;
1741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1742 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1744 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1745 message,"`%s'",image->filename);
1748 #ifdef PNG_USER_MEM_SUPPORTED
1749 #if PNG_LIBPNG_VER >= 10400
1750 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1752 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1756 return((png_voidp) AcquireMagickMemory((size_t) size));
1760 Free a pointer. It is removed from the list at the same time.
1762 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1765 ptr=RelinquishMagickMemory(ptr);
1766 return((png_free_ptr) NULL);
1770 #if defined(__cplusplus) || defined(c_plusplus)
1775 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1776 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1781 register unsigned char
1795 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1796 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1797 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1798 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1799 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1803 /* look for newline */
1807 /* look for length */
1808 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1811 length=(png_uint_32) StringToLong(sp);
1813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814 " length: %lu",(unsigned long) length);
1816 while (*sp != ' ' && *sp != '\n')
1819 /* allocate space */
1822 png_warning(ping,"invalid profile length");
1823 return(MagickFalse);
1826 profile=BlobToStringInfo((const void *) NULL,length);
1828 if (profile == (StringInfo *) NULL)
1830 png_warning(ping, "unable to copy profile");
1831 return(MagickFalse);
1834 /* copy profile, skipping white space and column 1 "=" signs */
1835 dp=GetStringInfoDatum(profile);
1838 for (i=0; i < (ssize_t) nibbles; i++)
1840 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1844 png_warning(ping, "ran out of profile data");
1845 profile=DestroyStringInfo(profile);
1846 return(MagickFalse);
1852 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1855 (*dp++)+=unhex[(int) *sp++];
1858 We have already read "Raw profile type.
1860 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1861 profile=DestroyStringInfo(profile);
1863 if (image_info->verbose)
1864 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1869 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1871 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1877 /* The unknown chunk structure contains the chunk data:
1882 Note that libpng has already taken care of the CRC handling.
1884 Returns one of the following:
1885 return(-n); chunk had an error
1886 return(0); did not recognize
1890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1891 " read_user_chunk: found %c%c%c%c chunk",
1892 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1894 if (chunk->name[0] == 101 &&
1895 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
1896 chunk->name[2] == 73 &&
1897 chunk-> name[3] == 102)
1899 /* process eXIf or exIf chunk */
1916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917 " recognized eXIf chunk");
1919 image=(Image *) png_get_user_chunk_ptr(ping);
1921 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1923 profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1925 if (profile == (StringInfo *) NULL)
1927 (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1928 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1932 p=GetStringInfoDatum(profile);
1936 /* Initialize profile with "Exif\0\0" if it is not
1937 already present by accident
1948 if (p[1] != 'x' || p[2] != 'i' || p[3] != 'f' ||
1949 p[4] != '\0' || p[5] != '\0')
1951 /* Chunk is malformed */
1952 profile=DestroyStringInfo(profile);
1957 /* copy chunk->data to profile */
1959 for (i=0; i<chunk->size; i++)
1962 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1963 (void) SetImageProfile(image,"exif",profile,
1964 error_info->exception);
1966 profile=DestroyStringInfo(profile);
1972 if (chunk->name[0] == 111 &&
1973 chunk->name[1] == 114 &&
1974 chunk->name[2] == 78 &&
1975 chunk->name[3] == 84)
1977 /* recognized orNT */
1978 if (chunk->size != 1)
1979 return(-1); /* Error return */
1981 image=(Image *) png_get_user_chunk_ptr(ping);
1984 Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
1989 /* vpAg (deprecated, replaced by caNv) */
1990 if (chunk->name[0] == 118 &&
1991 chunk->name[1] == 112 &&
1992 chunk->name[2] == 65 &&
1993 chunk->name[3] == 103)
1995 /* recognized vpAg */
1997 if (chunk->size != 9)
1998 return(-1); /* Error return */
2000 if (chunk->data[8] != 0)
2001 return(0); /* ImageMagick requires pixel units */
2003 image=(Image *) png_get_user_chunk_ptr(ping);
2005 image->page.width=(size_t)mng_get_long(chunk->data);
2006 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2012 if (chunk->name[0] == 99 &&
2013 chunk->name[1] == 97 &&
2014 chunk->name[2] == 78 &&
2015 chunk->name[3] == 118)
2017 /* recognized caNv */
2019 if (chunk->size != 16)
2020 return(-1); /* Error return */
2022 image=(Image *) png_get_user_chunk_ptr(ping);
2024 image->page.width=(size_t)mng_get_long(chunk->data);
2025 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2026 image->page.x=(size_t)mng_get_long(&chunk->data[8]);
2027 image->page.y=(size_t)mng_get_long(&chunk->data[12]);
2032 return(0); /* Did not recognize */
2034 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2036 #if defined(PNG_tIME_SUPPORTED)
2037 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2038 ExceptionInfo *exception)
2043 if (png_get_tIME(ping,info,&time))
2048 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2049 time->year,time->month,time->day,time->hour,time->minute,time->second);
2050 SetImageProperty(image,"png:tIME",timestamp,exception);
2056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2060 % R e a d O n e P N G I m a g e %
2064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2066 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2067 % (minus the 8-byte signature) and returns it. It allocates the memory
2068 % necessary for the new Image structure and returns a pointer to the new
2071 % The format of the ReadOnePNGImage method is:
2073 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2074 % ExceptionInfo *exception)
2076 % A description of each parameter follows:
2078 % o mng_info: Specifies a pointer to a MngInfo structure.
2080 % o image_info: the image info.
2082 % o exception: return any errors or warnings in this structure.
2085 static Image *ReadOnePNGImage(MngInfo *mng_info,
2086 const ImageInfo *image_info, ExceptionInfo *exception)
2088 /* Read one PNG image */
2090 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2103 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2113 ping_interlace_method,
2114 ping_compression_method,
2128 ping_found_sRGB_cHRM,
2133 *volatile pixel_info;
2165 *volatile quantum_info;
2168 *volatile quantum_scanline;
2174 register unsigned char
2194 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2195 png_byte unused_chunks[]=
2197 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2198 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2199 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2200 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2201 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2202 #if !defined(PNG_tIME_SUPPORTED)
2203 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2205 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2206 /* ignore the APNG chunks */
2207 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2208 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2209 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2214 /* Define these outside of the following "if logging()" block so they will
2215 * show in debuggers.
2218 (void) ConcatenateMagickString(im_vers,
2219 MagickLibVersionText,32);
2220 (void) ConcatenateMagickString(im_vers,
2221 MagickLibAddendum,32);
2224 (void) ConcatenateMagickString(libpng_vers,
2225 PNG_LIBPNG_VER_STRING,32);
2227 (void) ConcatenateMagickString(libpng_runv,
2228 png_get_libpng_ver(NULL),32);
2231 (void) ConcatenateMagickString(zlib_vers,
2234 (void) ConcatenateMagickString(zlib_runv,
2237 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2238 " Enter ReadOnePNGImage()\n"
2239 " IM version = %s\n"
2240 " Libpng version = %s",
2241 im_vers, libpng_vers);
2243 if (logging != MagickFalse)
2245 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2248 " running with %s", libpng_runv);
2250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2251 " Zlib version = %s", zlib_vers);
2252 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255 " running with %s", zlib_runv);
2259 #if (PNG_LIBPNG_VER < 10200)
2260 if (image_info->verbose)
2261 printf("Your PNG library (libpng-%s) is rather old.\n",
2262 PNG_LIBPNG_VER_STRING);
2265 #if (PNG_LIBPNG_VER >= 10400)
2266 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2267 if (image_info->verbose)
2269 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2270 PNG_LIBPNG_VER_STRING);
2271 printf("Please update it.\n");
2277 quantum_info = (QuantumInfo *) NULL;
2278 image=mng_info->image;
2280 if (logging != MagickFalse)
2282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2283 " Before reading:\n"
2284 " image->alpha_trait=%d\n"
2285 " image->rendering_intent=%d\n"
2286 " image->colorspace=%d\n"
2288 (int) image->alpha_trait, (int) image->rendering_intent,
2289 (int) image->colorspace, image->gamma);
2292 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2294 /* Set to an out-of-range color unless tRNS chunk is present */
2295 transparent_color.red=65537;
2296 transparent_color.green=65537;
2297 transparent_color.blue=65537;
2298 transparent_color.alpha=65537;
2303 num_raw_profiles = 0;
2305 ping_found_cHRM = MagickFalse;
2306 ping_found_gAMA = MagickFalse;
2307 ping_found_iCCP = MagickFalse;
2308 ping_found_sRGB = MagickFalse;
2309 ping_found_sRGB_cHRM = MagickFalse;
2310 ping_preserve_iCCP = MagickFalse;
2314 Allocate the PNG structures
2316 #ifdef PNG_USER_MEM_SUPPORTED
2317 error_info.image=image;
2318 error_info.exception=exception;
2319 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2320 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2321 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2323 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2324 MagickPNGErrorHandler,MagickPNGWarningHandler);
2326 if (ping == (png_struct *) NULL)
2327 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2329 ping_info=png_create_info_struct(ping);
2331 if (ping_info == (png_info *) NULL)
2333 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2334 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2337 end_info=png_create_info_struct(ping);
2339 if (end_info == (png_info *) NULL)
2341 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2342 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2345 pixel_info=(MemoryInfo *) NULL;
2346 quantum_scanline = (Quantum *) NULL;
2347 quantum_info = (QuantumInfo *) NULL;
2349 if (setjmp(png_jmpbuf(ping)))
2352 PNG image is corrupt.
2354 png_destroy_read_struct(&ping,&ping_info,&end_info);
2356 if (pixel_info != (MemoryInfo *) NULL)
2357 pixel_info=RelinquishVirtualMemory(pixel_info);
2359 if (quantum_info != (QuantumInfo *) NULL)
2360 quantum_info=DestroyQuantumInfo(quantum_info);
2362 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2364 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2365 UnlockSemaphoreInfo(ping_semaphore);
2368 if (logging != MagickFalse)
2369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2370 " exit ReadOnePNGImage() with error.");
2372 return(GetFirstImageInList(image));
2375 /* { For navigation to end of SETJMP-protected block. Within this
2376 * block, use png_error() instead of Throwing an Exception, to ensure
2377 * that libpng is able to clean up, and that the semaphore is unlocked.
2380 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2381 LockSemaphoreInfo(ping_semaphore);
2384 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2385 /* Allow benign errors */
2386 png_set_benign_errors(ping, 1);
2389 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2390 /* Reject images with too many rows or columns */
2391 png_set_user_limits(ping,
2392 (png_uint_32) MagickMin(0x7fffffffL,
2393 GetMagickResourceLimit(WidthResource)),
2394 (png_uint_32) MagickMin(0x7fffffffL,
2395 GetMagickResourceLimit(HeightResource)));
2396 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2399 Prepare PNG for reading.
2402 mng_info->image_found++;
2403 png_set_sig_bytes(ping,8);
2405 if (LocaleCompare(image_info->magick,"MNG") == 0)
2407 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2408 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2409 png_set_read_fn(ping,image,png_get_data);
2411 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2412 png_permit_empty_plte(ping,MagickTrue);
2413 png_set_read_fn(ping,image,png_get_data);
2415 mng_info->image=image;
2416 mng_info->bytes_in_read_buffer=0;
2417 mng_info->found_empty_plte=MagickFalse;
2418 mng_info->have_saved_bkgd_index=MagickFalse;
2419 png_set_read_fn(ping,mng_info,mng_get_data);
2425 png_set_read_fn(ping,image,png_get_data);
2431 value=GetImageOption(image_info,"profile:skip");
2433 if (IsOptionMember("ICC",value) == MagickFalse)
2436 value=GetImageOption(image_info,"png:preserve-iCCP");
2439 value=GetImageArtifact(image,"png:preserve-iCCP");
2442 ping_preserve_iCCP=MagickTrue;
2444 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2445 /* Don't let libpng check for ICC/sRGB profile because we're going
2446 * to do that anyway. This feature was added at libpng-1.6.12.
2447 * If logging, go ahead and check and issue a warning as appropriate.
2449 if (logging == MagickFalse)
2450 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2453 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2456 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2460 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2461 /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2462 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2463 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2465 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2467 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2468 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2469 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2470 (int)sizeof(unused_chunks)/5);
2471 /* Callback for other unknown chunks */
2472 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2475 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2476 # if (PNG_LIBPNG_VER >= 10400)
2477 /* Limit the size of the chunk storage cache used for sPLT, text,
2478 * and unknown chunks.
2480 png_set_chunk_cache_max(ping, 32767);
2484 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2485 /* Disable new libpng-1.5.10 feature */
2486 png_set_check_for_invalid_index (ping, 0);
2489 #if (PNG_LIBPNG_VER < 10400)
2490 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2491 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2492 /* Disable thread-unsafe features of pnggccrd */
2493 if (png_access_version_number() >= 10200)
2495 png_uint_32 mmx_disable_mask=0;
2496 png_uint_32 asm_flags;
2498 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2499 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2500 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2501 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2502 asm_flags=png_get_asm_flags(ping);
2503 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2508 png_read_info(ping,ping_info);
2510 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2511 &ping_bit_depth,&ping_color_type,
2512 &ping_interlace_method,&ping_compression_method,
2513 &ping_filter_method);
2515 ping_file_depth = ping_bit_depth;
2517 /* Swap bytes if requested */
2518 if (ping_file_depth == 16)
2523 value=GetImageOption(image_info,"png:swap-bytes");
2526 value=GetImageArtifact(image,"png:swap-bytes");
2532 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2535 msg[MagickPathExtent];
2537 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2538 (int) ping_color_type);
2539 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2541 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2542 (int) ping_bit_depth);
2543 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2546 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2549 (void) png_get_bKGD(ping, ping_info, &ping_background);
2551 if (ping_bit_depth < 8)
2553 png_set_packing(ping);
2557 image->depth=ping_bit_depth;
2558 image->depth=GetImageQuantumDepth(image,MagickFalse);
2559 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2561 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2562 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2564 image->rendering_intent=UndefinedIntent;
2565 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2566 (void) ResetMagickMemory(&image->chromaticity,0,
2567 sizeof(image->chromaticity));
2570 if (logging != MagickFalse)
2572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2573 " PNG width: %.20g, height: %.20g\n"
2574 " PNG color_type: %d, bit_depth: %d\n"
2575 " PNG compression_method: %d\n"
2576 " PNG interlace_method: %d, filter_method: %d",
2577 (double) ping_width, (double) ping_height,
2578 ping_color_type, ping_bit_depth,
2579 ping_compression_method,
2580 ping_interlace_method,ping_filter_method);
2584 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2586 ping_found_iCCP=MagickTrue;
2587 if (logging != MagickFalse)
2588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2589 " Found PNG iCCP chunk.");
2592 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2594 ping_found_gAMA=MagickTrue;
2595 if (logging != MagickFalse)
2596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2597 " Found PNG gAMA chunk.");
2600 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2602 ping_found_cHRM=MagickTrue;
2603 if (logging != MagickFalse)
2604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2605 " Found PNG cHRM chunk.");
2608 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2611 ping_found_sRGB=MagickTrue;
2612 if (logging != MagickFalse)
2613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2614 " Found PNG sRGB chunk.");
2617 #ifdef PNG_READ_iCCP_SUPPORTED
2618 if (ping_found_iCCP !=MagickTrue &&
2619 ping_found_sRGB != MagickTrue &&
2620 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2622 ping_found_iCCP=MagickTrue;
2623 if (logging != MagickFalse)
2624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2625 " Found PNG iCCP chunk.");
2628 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2633 #if (PNG_LIBPNG_VER < 10500)
2647 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2650 if (profile_length != 0)
2655 if (logging != MagickFalse)
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " Reading PNG iCCP chunk.");
2659 profile=BlobToStringInfo(info,profile_length);
2661 if (profile == (StringInfo *) NULL)
2663 png_warning(ping, "ICC profile is NULL");
2664 profile=DestroyStringInfo(profile);
2668 if (ping_preserve_iCCP == MagickFalse)
2682 length=(png_uint_32) GetStringInfoLength(profile);
2684 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2686 if (length == sRGB_info[icheck].len)
2690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2691 " Got a %lu-byte ICC profile (potentially sRGB)",
2692 (unsigned long) length);
2694 data=GetStringInfoDatum(profile);
2695 profile_crc=crc32(0,data,length);
2697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2698 " with crc=%8x",(unsigned int) profile_crc);
2702 if (profile_crc == sRGB_info[icheck].crc)
2704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2705 " It is sRGB with rendering intent = %s",
2706 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2707 sRGB_info[icheck].intent));
2708 if (image->rendering_intent==UndefinedIntent)
2710 image->rendering_intent=
2711 Magick_RenderingIntent_from_PNG_RenderingIntent(
2712 sRGB_info[icheck].intent);
2718 if (sRGB_info[icheck].len == 0)
2720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2721 " Got %lu-byte ICC profile not recognized as sRGB",
2722 (unsigned long) length);
2723 (void) SetImageProfile(image,"icc",profile,exception);
2726 else /* Preserve-iCCP */
2728 (void) SetImageProfile(image,"icc",profile,exception);
2731 profile=DestroyStringInfo(profile);
2737 #if defined(PNG_READ_sRGB_SUPPORTED)
2739 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2742 if (png_get_sRGB(ping,ping_info,&intent))
2744 if (image->rendering_intent == UndefinedIntent)
2745 image->rendering_intent=
2746 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2748 if (logging != MagickFalse)
2749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2754 else if (mng_info->have_global_srgb)
2756 if (image->rendering_intent == UndefinedIntent)
2757 image->rendering_intent=
2758 Magick_RenderingIntent_from_PNG_RenderingIntent
2759 (mng_info->global_srgb_intent);
2766 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2767 if (mng_info->have_global_gama)
2768 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2770 if (png_get_gAMA(ping,ping_info,&file_gamma))
2772 image->gamma=(float) file_gamma;
2773 if (logging != MagickFalse)
2774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2775 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2779 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2781 if (mng_info->have_global_chrm != MagickFalse)
2783 (void) png_set_cHRM(ping,ping_info,
2784 mng_info->global_chrm.white_point.x,
2785 mng_info->global_chrm.white_point.y,
2786 mng_info->global_chrm.red_primary.x,
2787 mng_info->global_chrm.red_primary.y,
2788 mng_info->global_chrm.green_primary.x,
2789 mng_info->global_chrm.green_primary.y,
2790 mng_info->global_chrm.blue_primary.x,
2791 mng_info->global_chrm.blue_primary.y);
2795 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2797 (void) png_get_cHRM(ping,ping_info,
2798 &image->chromaticity.white_point.x,
2799 &image->chromaticity.white_point.y,
2800 &image->chromaticity.red_primary.x,
2801 &image->chromaticity.red_primary.y,
2802 &image->chromaticity.green_primary.x,
2803 &image->chromaticity.green_primary.y,
2804 &image->chromaticity.blue_primary.x,
2805 &image->chromaticity.blue_primary.y);
2807 ping_found_cHRM=MagickTrue;
2809 if (image->chromaticity.red_primary.x>0.6399f &&
2810 image->chromaticity.red_primary.x<0.6401f &&
2811 image->chromaticity.red_primary.y>0.3299f &&
2812 image->chromaticity.red_primary.y<0.3301f &&
2813 image->chromaticity.green_primary.x>0.2999f &&
2814 image->chromaticity.green_primary.x<0.3001f &&
2815 image->chromaticity.green_primary.y>0.5999f &&
2816 image->chromaticity.green_primary.y<0.6001f &&
2817 image->chromaticity.blue_primary.x>0.1499f &&
2818 image->chromaticity.blue_primary.x<0.1501f &&
2819 image->chromaticity.blue_primary.y>0.0599f &&
2820 image->chromaticity.blue_primary.y<0.0601f &&
2821 image->chromaticity.white_point.x>0.3126f &&
2822 image->chromaticity.white_point.x<0.3128f &&
2823 image->chromaticity.white_point.y>0.3289f &&
2824 image->chromaticity.white_point.y<0.3291f)
2825 ping_found_sRGB_cHRM=MagickTrue;
2828 if (image->rendering_intent != UndefinedIntent)
2830 if (ping_found_sRGB != MagickTrue &&
2831 (ping_found_gAMA != MagickTrue ||
2832 (image->gamma > .45 && image->gamma < .46)) &&
2833 (ping_found_cHRM != MagickTrue ||
2834 ping_found_sRGB_cHRM != MagickFalse) &&
2835 ping_found_iCCP != MagickTrue)
2837 png_set_sRGB(ping,ping_info,
2838 Magick_RenderingIntent_to_PNG_RenderingIntent
2839 (image->rendering_intent));
2840 file_gamma=1.000f/2.200f;
2841 ping_found_sRGB=MagickTrue;
2842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2843 " Setting sRGB as if in input");
2847 #if defined(PNG_oFFs_SUPPORTED)
2848 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2850 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2851 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2853 if (logging != MagickFalse)
2854 if (image->page.x || image->page.y)
2855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2856 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2857 image->page.x,(double) image->page.y);
2860 #if defined(PNG_pHYs_SUPPORTED)
2861 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2863 if (mng_info->have_global_phys)
2865 png_set_pHYs(ping,ping_info,
2866 mng_info->global_x_pixels_per_unit,
2867 mng_info->global_y_pixels_per_unit,
2868 mng_info->global_phys_unit_type);
2875 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2878 Set image resolution.
2880 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2882 image->resolution.x=(double) x_resolution;
2883 image->resolution.y=(double) y_resolution;
2885 if (unit_type == PNG_RESOLUTION_METER)
2887 image->units=PixelsPerCentimeterResolution;
2888 image->resolution.x=(double) x_resolution/100.0;
2889 image->resolution.y=(double) y_resolution/100.0;
2892 if (logging != MagickFalse)
2893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2894 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2895 (double) x_resolution,(double) y_resolution,unit_type);
2899 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2904 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2906 if ((number_colors == 0) &&
2907 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2909 if (mng_info->global_plte_length)
2911 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2912 (int) mng_info->global_plte_length);
2914 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2916 if (mng_info->global_trns_length)
2919 "global tRNS has more entries than global PLTE");
2923 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2924 (int) mng_info->global_trns_length,NULL);
2927 #ifdef PNG_READ_bKGD_SUPPORTED
2929 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2930 mng_info->have_saved_bkgd_index ||
2932 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2937 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2938 if (mng_info->have_saved_bkgd_index)
2939 background.index=mng_info->saved_bkgd_index;
2941 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2942 background.index=ping_background->index;
2944 background.red=(png_uint_16)
2945 mng_info->global_plte[background.index].red;
2947 background.green=(png_uint_16)
2948 mng_info->global_plte[background.index].green;
2950 background.blue=(png_uint_16)
2951 mng_info->global_plte[background.index].blue;
2953 background.gray=(png_uint_16)
2954 mng_info->global_plte[background.index].green;
2956 png_set_bKGD(ping,ping_info,&background);
2961 png_error(ping,"No global PLTE in file");
2965 #ifdef PNG_READ_bKGD_SUPPORTED
2966 if (mng_info->have_global_bkgd &&
2967 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2968 image->background_color=mng_info->mng_global_bkgd;
2970 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2975 /* Set image background color.
2976 * Scale background components to 16-bit, then scale
2982 if (ping_file_depth == 1)
2985 else if (ping_file_depth == 2)
2988 else if (ping_file_depth == 4)
2991 if (ping_file_depth <= 8)
2994 ping_background->red *= bkgd_scale;
2995 ping_background->green *= bkgd_scale;
2996 ping_background->blue *= bkgd_scale;
2998 if (logging != MagickFalse)
3000 if (logging != MagickFalse)
3001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3002 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3003 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
3004 ping_background->red,ping_background->green,
3005 ping_background->blue,
3006 bkgd_scale,ping_background->red,
3007 ping_background->green,ping_background->blue);
3010 image->background_color.red=
3011 ScaleShortToQuantum(ping_background->red);
3013 image->background_color.green=
3014 ScaleShortToQuantum(ping_background->green);
3016 image->background_color.blue=
3017 ScaleShortToQuantum(ping_background->blue);
3019 image->background_color.alpha=OpaqueAlpha;
3021 if (logging != MagickFalse)
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " image->background_color=(%.20g,%.20g,%.20g).",
3024 (double) image->background_color.red,
3025 (double) image->background_color.green,
3026 (double) image->background_color.blue);
3028 #endif /* PNG_READ_bKGD_SUPPORTED */
3030 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3033 Image has a tRNS chunk.
3041 if (logging != MagickFalse)
3042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3043 " Reading PNG tRNS chunk.");
3045 max_sample = (int) ((one << ping_file_depth) - 1);
3047 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3048 (int)ping_trans_color->gray > max_sample) ||
3049 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3050 ((int)ping_trans_color->red > max_sample ||
3051 (int)ping_trans_color->green > max_sample ||
3052 (int)ping_trans_color->blue > max_sample)))
3054 if (logging != MagickFalse)
3055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3056 " Ignoring PNG tRNS chunk with out-of-range sample.");
3057 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3058 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3059 image->alpha_trait=UndefinedPixelTrait;
3066 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3068 /* Scale transparent_color to short */
3069 transparent_color.red= scale_to_short*ping_trans_color->red;
3070 transparent_color.green= scale_to_short*ping_trans_color->green;
3071 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3072 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3074 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3076 if (logging != MagickFalse)
3078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3079 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3080 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3083 transparent_color.red=transparent_color.alpha;
3084 transparent_color.green=transparent_color.alpha;
3085 transparent_color.blue=transparent_color.alpha;
3089 #if defined(PNG_READ_sBIT_SUPPORTED)
3090 if (mng_info->have_global_sbit)
3092 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3093 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3096 num_passes=png_set_interlace_handling(ping);
3098 png_read_update_info(ping,ping_info);
3100 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3103 Initialize image structure.
3105 mng_info->image_box.left=0;
3106 mng_info->image_box.right=(ssize_t) ping_width;
3107 mng_info->image_box.top=0;
3108 mng_info->image_box.bottom=(ssize_t) ping_height;
3109 if (mng_info->mng_type == 0)
3111 mng_info->mng_width=ping_width;
3112 mng_info->mng_height=ping_height;
3113 mng_info->frame=mng_info->image_box;
3114 mng_info->clip=mng_info->image_box;
3119 image->page.y=mng_info->y_off[mng_info->object_id];
3122 image->compression=ZipCompression;
3123 image->columns=ping_width;
3124 image->rows=ping_height;
3126 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3127 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3130 image_gamma = image->gamma;
3132 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3133 " image->gamma=%f",(float) image_gamma);
3135 if (image_gamma > 0.75)
3137 /* Set image->rendering_intent to Undefined,
3138 * image->colorspace to GRAY, and reset image->chromaticity.
3140 image->intensity = Rec709LuminancePixelIntensityMethod;
3141 SetImageColorspace(image,GRAYColorspace,exception);
3146 save_rendering_intent = image->rendering_intent;
3148 save_chromaticity = image->chromaticity;
3150 SetImageColorspace(image,GRAYColorspace,exception);
3151 image->rendering_intent = save_rendering_intent;
3152 image->chromaticity = save_chromaticity;
3155 image->gamma = image_gamma;
3160 image_gamma = image->gamma;
3162 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3163 " image->gamma=%f",(float) image_gamma);
3165 if (image_gamma > 0.75)
3167 /* Set image->rendering_intent to Undefined,
3168 * image->colorspace to GRAY, and reset image->chromaticity.
3170 image->intensity = Rec709LuminancePixelIntensityMethod;
3171 SetImageColorspace(image,RGBColorspace,exception);
3176 save_rendering_intent = image->rendering_intent;
3178 save_chromaticity = image->chromaticity;
3180 SetImageColorspace(image,sRGBColorspace,exception);
3181 image->rendering_intent = save_rendering_intent;
3182 image->chromaticity = save_chromaticity;
3185 image->gamma = image_gamma;
3188 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3189 " image->colorspace=%d",(int) image->colorspace);
3191 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3192 ((int) ping_bit_depth < 16 &&
3193 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3198 image->storage_class=PseudoClass;
3200 image->colors=one << ping_file_depth;
3201 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3202 if (image->colors > 256)
3205 if (image->colors > 65536L)
3206 image->colors=65536L;
3208 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3213 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3214 image->colors=(size_t) number_colors;
3216 if (logging != MagickFalse)
3217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3218 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3222 if (image->storage_class == PseudoClass)
3225 Initialize image colormap.
3227 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3228 png_error(ping,"Memory allocation failed");
3230 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3235 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3237 for (i=0; i < (ssize_t) number_colors; i++)
3239 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3240 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3241 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3244 for ( ; i < (ssize_t) image->colors; i++)
3246 image->colormap[i].red=0;
3247 image->colormap[i].green=0;
3248 image->colormap[i].blue=0;
3257 scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3259 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3260 scale = ScaleShortToQuantum(scale);
3263 for (i=0; i < (ssize_t) image->colors; i++)
3265 image->colormap[i].red=(Quantum) (i*scale);
3266 image->colormap[i].green=(Quantum) (i*scale);
3267 image->colormap[i].blue=(Quantum) (i*scale);
3272 /* Set some properties for reporting by "identify" */
3275 msg[MagickPathExtent];
3277 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3278 ping_interlace_method in value */
3280 (void) FormatLocaleString(msg,MagickPathExtent,
3281 "%d, %d",(int) ping_width, (int) ping_height);
3282 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3284 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3285 (int) ping_file_depth);
3286 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3288 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3289 (int) ping_color_type,
3290 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3291 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3293 if (ping_interlace_method == 0)
3295 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3296 (int) ping_interlace_method);
3298 else if (ping_interlace_method == 1)
3300 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3301 (int) ping_interlace_method);
3305 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3306 (int) ping_interlace_method);
3308 (void) SetImageProperty(image,"png:IHDR.interlace_method",
3311 if (number_colors != 0)
3313 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3314 (int) number_colors);
3315 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3319 #if defined(PNG_tIME_SUPPORTED)
3320 read_tIME_chunk(image,ping,ping_info,exception);
3325 Read image scanlines.
3327 if (image->delay != 0)
3328 mng_info->scenes_found++;
3330 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3331 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3332 (image_info->first_scene+image_info->number_scenes))))
3334 /* This happens later in non-ping decodes */
3335 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3336 image->storage_class=DirectClass;
3338 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3339 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3340 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3341 BlendPixelTrait : UndefinedPixelTrait;
3343 if (logging != MagickFalse)
3344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3345 " Skipping PNG image data for scene %.20g",(double)
3346 mng_info->scenes_found-1);
3347 png_destroy_read_struct(&ping,&ping_info,&end_info);
3349 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3350 UnlockSemaphoreInfo(ping_semaphore);
3353 if (logging != MagickFalse)
3354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3355 " exit ReadOnePNGImage().");
3360 if (logging != MagickFalse)
3361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3362 " Reading PNG IDAT chunk(s)");
3364 status=SetImageExtent(image,image->columns,image->rows,exception);
3365 if (status == MagickFalse)
3367 png_destroy_read_struct(&ping,&ping_info,&end_info);
3368 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3369 UnlockSemaphoreInfo(ping_semaphore);
3371 return(DestroyImageList(image));
3375 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3376 sizeof(*ping_pixels));
3378 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3380 if (pixel_info == (MemoryInfo *) NULL)
3381 png_error(ping,"Memory allocation failed");
3382 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3384 if (logging != MagickFalse)
3385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386 " Converting PNG pixels to pixel packets");
3388 Convert PNG pixels to pixel packets.
3390 quantum_info=AcquireQuantumInfo(image_info,image);
3392 if (quantum_info == (QuantumInfo *) NULL)
3393 png_error(ping,"Failed to allocate quantum_info");
3395 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3400 found_transparent_pixel;
3402 found_transparent_pixel=MagickFalse;
3404 if (image->storage_class == DirectClass)
3406 for (pass=0; pass < num_passes; pass++)
3409 Convert image to DirectClass pixel packets.
3412 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3413 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3414 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3415 BlendPixelTrait : UndefinedPixelTrait;
3417 for (y=0; y < (ssize_t) image->rows; y++)
3420 row_offset=ping_rowbytes*y;
3425 png_read_row(ping,ping_pixels+row_offset,NULL);
3427 if (pass < num_passes-1)
3430 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3432 if (q == (Quantum *) NULL)
3435 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3436 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3437 GrayQuantum,ping_pixels+row_offset,exception);
3439 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3440 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3441 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3443 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3444 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3445 RGBAQuantum,ping_pixels+row_offset,exception);
3447 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3448 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3449 IndexQuantum,ping_pixels+row_offset,exception);
3451 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3452 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3453 RGBQuantum,ping_pixels+row_offset,exception);
3455 if (found_transparent_pixel == MagickFalse)
3457 /* Is there a transparent pixel in the row? */
3458 if (y== 0 && logging != MagickFalse)
3459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3460 " Looking for cheap transparent pixel");
3462 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3464 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3465 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3466 (GetPixelAlpha(image,q) != OpaqueAlpha))
3468 if (logging != MagickFalse)
3469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3472 found_transparent_pixel = MagickTrue;
3475 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3476 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3477 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3478 transparent_color.red &&
3479 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3480 transparent_color.green &&
3481 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3482 transparent_color.blue))
3484 if (logging != MagickFalse)
3485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3487 found_transparent_pixel = MagickTrue;
3490 q+=GetPixelChannels(image);
3494 if (num_passes == 1)
3496 status=SetImageProgress(image,LoadImageTag,
3497 (MagickOffsetType) y, image->rows);
3499 if (status == MagickFalse)
3502 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3506 if (num_passes != 1)
3508 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3509 if (status == MagickFalse)
3515 else /* image->storage_class != DirectClass */
3517 for (pass=0; pass < num_passes; pass++)
3523 Convert grayscale image to PseudoClass pixel packets.
3525 if (logging != MagickFalse)
3526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3527 " Converting grayscale pixels to pixel packets");
3529 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3530 BlendPixelTrait : UndefinedPixelTrait;
3532 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3533 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3534 sizeof(*quantum_scanline));
3536 if (quantum_scanline == (Quantum *) NULL)
3537 png_error(ping,"Memory allocation failed");
3539 for (y=0; y < (ssize_t) image->rows; y++)
3545 row_offset=ping_rowbytes*y;
3550 png_read_row(ping,ping_pixels+row_offset,NULL);
3552 if (pass < num_passes-1)
3555 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3557 if (q == (Quantum *) NULL)
3560 p=ping_pixels+row_offset;
3563 switch (ping_bit_depth)
3568 if (ping_color_type == 4)
3569 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3573 alpha=ScaleCharToQuantum((unsigned char)*p++);
3575 SetPixelAlpha(image,alpha,q);
3577 if (alpha != OpaqueAlpha)
3578 found_transparent_pixel = MagickTrue;
3580 q+=GetPixelChannels(image);
3584 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3592 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3594 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3598 if (image->colors > 256)
3599 quantum=(((unsigned int) *p++) << 8);
3605 *r=ScaleShortToQuantum(quantum);
3608 if (ping_color_type == 4)
3610 if (image->colors > 256)
3611 quantum=(((unsigned int) *p++) << 8);
3617 alpha=ScaleShortToQuantum(quantum);
3618 SetPixelAlpha(image,alpha,q);
3620 if (alpha != OpaqueAlpha)
3621 found_transparent_pixel = MagickTrue;
3623 q+=GetPixelChannels(image);
3626 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3628 p++; /* strip low byte */
3630 if (ping_color_type == 4)
3632 SetPixelAlpha(image,*p++,q);
3634 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3635 found_transparent_pixel = MagickTrue;
3638 q+=GetPixelChannels(image);
3651 Transfer image scanline.
3655 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3657 if (q == (Quantum *) NULL)
3659 for (x=0; x < (ssize_t) image->columns; x++)
3661 SetPixelIndex(image,*r++,q);
3662 q+=GetPixelChannels(image);
3665 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3668 if (num_passes == 1)
3670 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3673 if (status == MagickFalse)
3678 if (num_passes != 1)
3680 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3682 if (status == MagickFalse)
3686 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3689 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3690 UndefinedPixelTrait;
3692 if (logging != MagickFalse)
3694 if (found_transparent_pixel != MagickFalse)
3695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3696 " Found transparent pixel");
3699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3700 " No transparent pixel was found");
3702 ping_color_type&=0x03;
3707 quantum_info=DestroyQuantumInfo(quantum_info);
3709 if (image->storage_class == PseudoClass)
3714 alpha_trait=image->alpha_trait;
3715 image->alpha_trait=UndefinedPixelTrait;
3716 (void) SyncImage(image,exception);
3717 image->alpha_trait=alpha_trait;
3720 png_read_end(ping,end_info);
3722 if (logging != MagickFalse)
3724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725 " image->storage_class=%d\n",(int) image->storage_class);
3728 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3729 (ssize_t) image_info->first_scene && image->delay != 0)
3731 png_destroy_read_struct(&ping,&ping_info,&end_info);
3732 pixel_info=RelinquishVirtualMemory(pixel_info);
3734 (void) SetImageBackgroundColor(image,exception);
3735 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3736 UnlockSemaphoreInfo(ping_semaphore);
3738 if (logging != MagickFalse)
3739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3740 " exit ReadOnePNGImage() early.");
3744 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3750 Image has a transparent background.
3752 storage_class=image->storage_class;
3753 image->alpha_trait=BlendPixelTrait;
3755 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3757 if (storage_class == PseudoClass)
3759 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3761 for (x=0; x < ping_num_trans; x++)
3763 image->colormap[x].alpha_trait=BlendPixelTrait;
3764 image->colormap[x].alpha =
3765 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3769 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3771 for (x=0; x < (int) image->colors; x++)
3773 if (ScaleQuantumToShort(image->colormap[x].red) ==
3774 transparent_color.alpha)
3776 image->colormap[x].alpha_trait=BlendPixelTrait;
3777 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3781 (void) SyncImage(image,exception);
3784 #if 1 /* Should have already been done above, but glennrp problem P10
3789 for (y=0; y < (ssize_t) image->rows; y++)
3791 image->storage_class=storage_class;
3792 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3794 if (q == (Quantum *) NULL)
3798 /* Caution: on a Q8 build, this does not distinguish between
3799 * 16-bit colors that differ only in the low byte
3801 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3803 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3804 transparent_color.red &&
3805 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3806 transparent_color.green &&
3807 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3808 transparent_color.blue)
3810 SetPixelAlpha(image,TransparentAlpha,q);
3813 #if 0 /* I have not found a case where this is needed. */
3816 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3820 q+=GetPixelChannels(image);
3823 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3829 image->storage_class=DirectClass;
3832 for (j = 0; j < 2; j++)
3835 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3836 MagickTrue : MagickFalse;
3838 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3839 MagickTrue : MagickFalse;
3841 if (status != MagickFalse)
3842 for (i=0; i < (ssize_t) num_text; i++)
3844 /* Check for a profile */
3846 if (logging != MagickFalse)
3847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3848 " Reading PNG text chunk");
3850 if (strlen(text[i].key) > 16 &&
3851 memcmp(text[i].key, "Raw profile type ",17) == 0)
3856 value=GetImageOption(image_info,"profile:skip");
3858 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3860 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3863 if (logging != MagickFalse)
3864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3865 " Read raw profile %s",text[i].key+17);
3869 if (logging != MagickFalse)
3870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3871 " Skipping raw profile %s",text[i].key+17);
3880 length=text[i].text_length;
3881 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3883 if (value == (char *) NULL)
3885 png_error(ping,"Memory allocation failed");
3889 (void) ConcatenateMagickString(value,text[i].text,length+2);
3891 /* Don't save "density" or "units" property if we have a pHYs
3894 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3895 (LocaleCompare(text[i].key,"density") != 0 &&
3896 LocaleCompare(text[i].key,"units") != 0))
3897 (void) SetImageProperty(image,text[i].key,value,exception);
3899 if (logging != MagickFalse)
3901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3904 (unsigned long) length,
3908 value=DestroyString(value);
3911 num_text_total += num_text;
3914 #ifdef MNG_OBJECT_BUFFERS
3916 Store the object if necessary.
3918 if (object_id && !mng_info->frozen[object_id])
3920 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3923 create a new object buffer.
3925 mng_info->ob[object_id]=(MngBuffer *)
3926 AcquireMagickMemory(sizeof(MngBuffer));
3928 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3930 mng_info->ob[object_id]->image=(Image *) NULL;
3931 mng_info->ob[object_id]->reference_count=1;
3935 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3936 mng_info->ob[object_id]->frozen)
3938 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3939 png_error(ping,"Memory allocation failed");
3941 if (mng_info->ob[object_id]->frozen)
3942 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3948 if (mng_info->ob[object_id]->image != (Image *) NULL)
3949 mng_info->ob[object_id]->image=DestroyImage
3950 (mng_info->ob[object_id]->image);
3952 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3955 if (mng_info->ob[object_id]->image != (Image *) NULL)
3956 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3959 png_error(ping, "Cloning image for object buffer failed");
3961 if (ping_width > 250000L || ping_height > 250000L)
3962 png_error(ping,"PNG Image dimensions are too large.");
3964 mng_info->ob[object_id]->width=ping_width;
3965 mng_info->ob[object_id]->height=ping_height;
3966 mng_info->ob[object_id]->color_type=ping_color_type;
3967 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3968 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3969 mng_info->ob[object_id]->compression_method=
3970 ping_compression_method;
3971 mng_info->ob[object_id]->filter_method=ping_filter_method;
3973 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3979 Copy the PLTE to the object buffer.
3981 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3982 mng_info->ob[object_id]->plte_length=number_colors;
3984 for (i=0; i < number_colors; i++)
3986 mng_info->ob[object_id]->plte[i]=plte[i];
3991 mng_info->ob[object_id]->plte_length=0;
3996 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3997 * alpha or if a valid tRNS chunk is present, no matter whether there
3998 * is actual transparency present.
4000 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4001 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4002 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4003 BlendPixelTrait : UndefinedPixelTrait;
4005 #if 0 /* I'm not sure what's wrong here but it does not work. */
4006 if (image->alpha_trait != UndefinedPixelTrait)
4008 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4009 (void) SetImageType(image,GrayscaleAlphaType,exception);
4011 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4012 (void) SetImageType(image,PaletteAlphaType,exception);
4015 (void) SetImageType(image,TrueColorAlphaType,exception);
4020 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4021 (void) SetImageType(image,GrayscaleType,exception);
4023 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4024 (void) SetImageType(image,PaletteType,exception);
4027 (void) SetImageType(image,TrueColorType,exception);
4031 /* Set more properties for identify to retrieve */
4034 msg[MagickPathExtent];
4036 if (num_text_total != 0)
4038 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4039 (void) FormatLocaleString(msg,MagickPathExtent,
4040 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4041 (void) SetImageProperty(image,"png:text",msg,
4045 if (num_raw_profiles != 0)
4047 (void) FormatLocaleString(msg,MagickPathExtent,
4048 "%d were found", num_raw_profiles);
4049 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4054 if (ping_found_cHRM != MagickFalse)
4056 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4057 "chunk was found (see Chromaticity, above)");
4058 (void) SetImageProperty(image,"png:cHRM",msg,
4063 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4065 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4066 "chunk was found (see Background color, above)");
4067 (void) SetImageProperty(image,"png:bKGD",msg,
4071 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4074 #if defined(PNG_iCCP_SUPPORTED)
4076 if (ping_found_iCCP != MagickFalse)
4077 (void) SetImageProperty(image,"png:iCCP",msg,
4081 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4082 (void) SetImageProperty(image,"png:tRNS",msg,
4085 #if defined(PNG_sRGB_SUPPORTED)
4087 if (ping_found_sRGB != MagickFalse)
4089 (void) FormatLocaleString(msg,MagickPathExtent,
4092 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4093 (void) SetImageProperty(image,"png:sRGB",msg,
4099 if (ping_found_gAMA != MagickFalse)
4101 (void) FormatLocaleString(msg,MagickPathExtent,
4102 "gamma=%.8g (See Gamma, above)",
4104 (void) SetImageProperty(image,"png:gAMA",msg,
4108 #if defined(PNG_pHYs_SUPPORTED)
4110 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4112 (void) FormatLocaleString(msg,MagickPathExtent,
4113 "x_res=%.10g, y_res=%.10g, units=%d",
4114 (double) x_resolution,(double) y_resolution, unit_type);
4115 (void) SetImageProperty(image,"png:pHYs",msg,
4120 #if defined(PNG_oFFs_SUPPORTED)
4122 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4124 (void) FormatLocaleString(msg,MagickPathExtent,
4125 "x_off=%.20g, y_off=%.20g",
4126 (double) image->page.x,(double) image->page.y);
4127 (void) SetImageProperty(image,"png:oFFs",msg,
4132 #if defined(PNG_tIME_SUPPORTED)
4133 read_tIME_chunk(image,ping,end_info,exception);
4137 if ((image->page.width != 0 && image->page.width != image->columns) ||
4138 (image->page.height != 0 && image->page.height != image->rows) ||
4139 (image->page.x != 0 || image->page.y != 0))
4141 (void) FormatLocaleString(msg,MagickPathExtent,
4142 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4143 (double) image->page.width,(double) image->page.height,
4144 (double) image->page.x,(double) image->page.y);
4145 (void) SetImageProperty(image,"png:caNv",msg,
4151 Relinquish resources.
4153 png_destroy_read_struct(&ping,&ping_info,&end_info);
4155 pixel_info=RelinquishVirtualMemory(pixel_info);
4157 if (logging != MagickFalse)
4158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4159 " exit ReadOnePNGImage()");
4161 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4162 UnlockSemaphoreInfo(ping_semaphore);
4165 /* } for navigation to beginning of SETJMP-protected block, revert to
4166 * Throwing an Exception when an error occurs.
4171 /* end of reading one PNG image */
4174 static Image *ReadPNGImage(const ImageInfo *image_info,
4175 ExceptionInfo *exception)
4188 magic_number[MagickPathExtent];
4196 assert(image_info != (const ImageInfo *) NULL);
4197 assert(image_info->signature == MagickCoreSignature);
4199 if (image_info->debug != MagickFalse)
4200 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4201 image_info->filename);
4203 assert(exception != (ExceptionInfo *) NULL);
4204 assert(exception->signature == MagickCoreSignature);
4205 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4206 image=AcquireImage(image_info,exception);
4207 mng_info=(MngInfo *) NULL;
4208 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4210 if (status == MagickFalse)
4211 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4214 Verify PNG signature.
4216 count=ReadBlob(image,8,(unsigned char *) magic_number);
4218 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4219 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4222 Verify that file size large enough to contain a PNG datastream.
4224 if (GetBlobSize(image) < 61)
4225 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4228 Allocate a MngInfo structure.
4230 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4232 if (mng_info == (MngInfo *) NULL)
4233 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4236 Initialize members of the MngInfo structure.
4238 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4239 mng_info->image=image;
4241 image=ReadOnePNGImage(mng_info,image_info,exception);
4242 mng_info=MngInfoFreeStruct(mng_info);
4244 if (image == (Image *) NULL)
4246 if (logging != MagickFalse)
4247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4248 "exit ReadPNGImage() with error");
4250 return((Image *) NULL);
4253 (void) CloseBlob(image);
4255 if ((image->columns == 0) || (image->rows == 0))
4257 if (logging != MagickFalse)
4258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4259 "exit ReadPNGImage() with error.");
4261 ThrowReaderException(CorruptImageError,"CorruptImage");
4264 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4265 ((image->gamma < .45) || (image->gamma > .46)) &&
4266 !(image->chromaticity.red_primary.x>0.6399f &&
4267 image->chromaticity.red_primary.x<0.6401f &&
4268 image->chromaticity.red_primary.y>0.3299f &&
4269 image->chromaticity.red_primary.y<0.3301f &&
4270 image->chromaticity.green_primary.x>0.2999f &&
4271 image->chromaticity.green_primary.x<0.3001f &&
4272 image->chromaticity.green_primary.y>0.5999f &&
4273 image->chromaticity.green_primary.y<0.6001f &&
4274 image->chromaticity.blue_primary.x>0.1499f &&
4275 image->chromaticity.blue_primary.x<0.1501f &&
4276 image->chromaticity.blue_primary.y>0.0599f &&
4277 image->chromaticity.blue_primary.y<0.0601f &&
4278 image->chromaticity.white_point.x>0.3126f &&
4279 image->chromaticity.white_point.x<0.3128f &&
4280 image->chromaticity.white_point.y>0.3289f &&
4281 image->chromaticity.white_point.y<0.3291f))
4283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4284 "SetImageColorspace to RGBColorspace");
4285 SetImageColorspace(image,RGBColorspace,exception);
4289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4290 "NOT SetImageColorspace to RGBColorspace, image->gamma=%g",
4294 if (logging != MagickFalse)
4296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4297 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4298 (double) image->page.width,(double) image->page.height,
4299 (double) image->page.x,(double) image->page.y);
4300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301 " image->colorspace: %d", (int) image->colorspace);
4304 if (logging != MagickFalse)
4305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4312 #if defined(JNG_SUPPORTED)
4314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4318 % R e a d O n e J N G I m a g e %
4322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4324 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4325 % (minus the 8-byte signature) and returns it. It allocates the memory
4326 % necessary for the new Image structure and returns a pointer to the new
4329 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4331 % The format of the ReadOneJNGImage method is:
4333 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4334 % ExceptionInfo *exception)
4336 % A description of each parameter follows:
4338 % o mng_info: Specifies a pointer to a MngInfo structure.
4340 % o image_info: the image info.
4342 % o exception: return any errors or warnings in this structure.
4346 DestroyJNG(unsigned char *chunk,Image **color_image,
4347 ImageInfo **color_image_info,
4348 Image **alpha_image,ImageInfo **alpha_image_info)
4350 (void) RelinquishMagickMemory(chunk);
4351 if (*color_image_info)
4353 DestroyImageInfo(*color_image_info);
4354 *color_image_info = (ImageInfo *)NULL;
4356 if (*alpha_image_info)
4358 DestroyImageInfo(*alpha_image_info);
4359 *alpha_image_info = (ImageInfo *)NULL;
4363 DestroyImage(*color_image);
4364 *color_image = (Image *)NULL;
4368 DestroyImage(*alpha_image);
4369 *alpha_image = (Image *)NULL;
4372 static Image *ReadOneJNGImage(MngInfo *mng_info,
4373 const ImageInfo *image_info, ExceptionInfo *exception)
4400 jng_image_sample_depth,
4401 jng_image_compression_method,
4402 jng_image_interlace_method,
4403 jng_alpha_sample_depth,
4404 jng_alpha_compression_method,
4405 jng_alpha_filter_method,
4406 jng_alpha_interlace_method;
4408 register const Quantum
4418 register unsigned char
4428 jng_alpha_compression_method=0;
4429 jng_alpha_sample_depth=8;
4433 alpha_image=(Image *) NULL;
4434 color_image=(Image *) NULL;
4435 alpha_image_info=(ImageInfo *) NULL;
4436 color_image_info=(ImageInfo *) NULL;
4438 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4439 " Enter ReadOneJNGImage()");
4441 image=mng_info->image;
4443 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4446 Allocate next image structure.
4448 if (logging != MagickFalse)
4449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450 " AcquireNextImage()");
4452 AcquireNextImage(image_info,image,exception);
4454 if (GetNextImageInList(image) == (Image *) NULL)
4455 return(DestroyImageList(image));
4457 image=SyncNextImageInList(image);
4459 mng_info->image=image;
4462 Signature bytes have already been read.
4465 read_JSEP=MagickFalse;
4466 reading_idat=MagickFalse;
4470 type[MagickPathExtent];
4479 Read a new JNG chunk.
4481 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4482 2*GetBlobSize(image));
4484 if (status == MagickFalse)
4488 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4489 length=ReadBlobMSBLong(image);
4490 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4492 if (logging != MagickFalse)
4493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4494 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4495 type[0],type[1],type[2],type[3],(double) length);
4497 if (length > PNG_UINT_31_MAX || count == 0)
4499 DestroyJNG(NULL,&color_image,&color_image_info,
4500 &alpha_image,&alpha_image_info);
4501 ThrowReaderException(CorruptImageError,"CorruptImage");
4505 chunk=(unsigned char *) NULL;
4509 if (length > GetBlobSize(image))
4511 DestroyJNG(NULL,&color_image,&color_image_info,NULL,NULL);
4512 ThrowReaderException(CorruptImageError,
4513 "InsufficientImageDataInFile");
4515 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4517 if (chunk == (unsigned char *) NULL)
4518 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4520 for (i=0; i < (ssize_t) length; i++)
4525 c=ReadBlobByte(image);
4528 chunk[i]=(unsigned char) c;
4534 (void) ReadBlobMSBLong(image); /* read crc word */
4536 if (memcmp(type,mng_JHDR,4) == 0)
4540 jng_width=(png_uint_32)mng_get_long(p);
4541 jng_height=(png_uint_32)mng_get_long(&p[4]);
4542 if ((jng_width == 0) || (jng_height == 0))
4543 ThrowReaderException(CorruptImageError,
4544 "NegativeOrZeroImageSize");
4545 jng_color_type=p[8];
4546 jng_image_sample_depth=p[9];
4547 jng_image_compression_method=p[10];
4548 jng_image_interlace_method=p[11];
4550 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4553 jng_alpha_sample_depth=p[12];
4554 jng_alpha_compression_method=p[13];
4555 jng_alpha_filter_method=p[14];
4556 jng_alpha_interlace_method=p[15];
4558 if (logging != MagickFalse)
4560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4561 " jng_width: %16lu, jng_height: %16lu\n"
4562 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4563 " jng_image_compression_method:%3d",
4564 (unsigned long) jng_width, (unsigned long) jng_height,
4565 jng_color_type, jng_image_sample_depth,
4566 jng_image_compression_method);
4568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4569 " jng_image_interlace_method: %3d"
4570 " jng_alpha_sample_depth: %3d",
4571 jng_image_interlace_method,
4572 jng_alpha_sample_depth);
4574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4575 " jng_alpha_compression_method:%3d\n"
4576 " jng_alpha_filter_method: %3d\n"
4577 " jng_alpha_interlace_method: %3d",
4578 jng_alpha_compression_method,
4579 jng_alpha_filter_method,
4580 jng_alpha_interlace_method);
4584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4586 if (jng_width > 65535 || jng_height > 65535 ||
4587 (long) jng_width > GetMagickResourceLimit(WidthResource) ||
4588 (long) jng_height > GetMagickResourceLimit(HeightResource))
4590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4591 " JNG width or height too large: (%lu x %lu)",
4592 (long) jng_width, (long) jng_height);
4593 DestroyJNG(chunk,&color_image,&color_image_info,
4594 &alpha_image,&alpha_image_info);
4595 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4602 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4603 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4604 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4607 o create color_image
4608 o open color_blob, attached to color_image
4609 o if (color type has alpha)
4610 open alpha_blob, attached to alpha_image
4613 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4615 if (color_image_info == (ImageInfo *) NULL)
4616 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4618 GetImageInfo(color_image_info);
4619 color_image=AcquireImage(color_image_info,exception);
4621 if (color_image == (Image *) NULL)
4622 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4624 if (logging != MagickFalse)
4625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4626 " Creating color_blob.");
4628 (void) AcquireUniqueFilename(color_image->filename);
4629 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4632 if (status == MagickFalse)
4634 color_image=DestroyImage(color_image);
4635 return(DestroyImageList(image));
4638 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4640 alpha_image_info=(ImageInfo *)
4641 AcquireMagickMemory(sizeof(ImageInfo));
4643 if (alpha_image_info == (ImageInfo *) NULL)
4645 color_image=DestroyImage(color_image);
4646 ThrowReaderException(ResourceLimitError,
4647 "MemoryAllocationFailed");
4650 GetImageInfo(alpha_image_info);
4651 alpha_image=AcquireImage(alpha_image_info,exception);
4653 if (alpha_image == (Image *) NULL)
4655 alpha_image_info=DestroyImageInfo(alpha_image_info);
4656 color_image=DestroyImage(color_image);
4657 ThrowReaderException(ResourceLimitError,
4658 "MemoryAllocationFailed");
4661 if (logging != MagickFalse)
4662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4663 " Creating alpha_blob.");
4665 (void) AcquireUniqueFilename(alpha_image->filename);
4666 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4669 if (status == MagickFalse)
4671 alpha_image=DestroyImage(alpha_image);
4672 alpha_image_info=DestroyImageInfo(alpha_image_info);
4673 color_image=DestroyImage(color_image);
4674 return(DestroyImageList(image));
4677 if (jng_alpha_compression_method == 0)
4682 if (logging != MagickFalse)
4683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4684 " Writing IHDR chunk to alpha_blob.");
4686 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4687 "\211PNG\r\n\032\n");
4689 (void) WriteBlobMSBULong(alpha_image,13L);
4690 PNGType(data,mng_IHDR);
4691 LogPNGChunk(logging,mng_IHDR,13L);
4692 PNGLong(data+4,jng_width);
4693 PNGLong(data+8,jng_height);
4694 data[12]=jng_alpha_sample_depth;
4695 data[13]=0; /* color_type gray */
4696 data[14]=0; /* compression method 0 */
4697 data[15]=0; /* filter_method 0 */
4698 data[16]=0; /* interlace_method 0 */
4699 (void) WriteBlob(alpha_image,17,data);
4700 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4703 reading_idat=MagickTrue;
4706 if (memcmp(type,mng_JDAT,4) == 0)
4708 /* Copy chunk to color_image->blob */
4710 if (logging != MagickFalse)
4711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4712 " Copying JDAT chunk data to color_blob.");
4716 (void) WriteBlob(color_image,length,chunk);
4717 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4723 if (memcmp(type,mng_IDAT,4) == 0)
4728 /* Copy IDAT header and chunk data to alpha_image->blob */
4730 if (alpha_image != NULL && image_info->ping == MagickFalse)
4732 if (logging != MagickFalse)
4733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4734 " Copying IDAT chunk data to alpha_blob.");
4736 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4737 PNGType(data,mng_IDAT);
4738 LogPNGChunk(logging,mng_IDAT,length);
4739 (void) WriteBlob(alpha_image,4,data);
4740 (void) WriteBlob(alpha_image,length,chunk);
4741 (void) WriteBlobMSBULong(alpha_image,
4742 crc32(crc32(0,data,4),chunk,(uInt) length));
4745 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4750 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4752 /* Copy chunk data to alpha_image->blob */
4754 if (alpha_image != NULL && image_info->ping == MagickFalse)
4756 if (logging != MagickFalse)
4757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4758 " Copying JDAA chunk data to alpha_blob.");
4760 (void) WriteBlob(alpha_image,length,chunk);
4763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4768 if (memcmp(type,mng_JSEP,4) == 0)
4770 read_JSEP=MagickTrue;
4772 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4777 if (memcmp(type,mng_bKGD,4) == 0)
4781 image->background_color.red=ScaleCharToQuantum(p[1]);
4782 image->background_color.green=image->background_color.red;
4783 image->background_color.blue=image->background_color.red;
4788 image->background_color.red=ScaleCharToQuantum(p[1]);
4789 image->background_color.green=ScaleCharToQuantum(p[3]);
4790 image->background_color.blue=ScaleCharToQuantum(p[5]);
4793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4797 if (memcmp(type,mng_gAMA,4) == 0)
4800 image->gamma=((float) mng_get_long(p))*0.00001;
4802 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4806 if (memcmp(type,mng_cHRM,4) == 0)
4810 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4811 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4812 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4813 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4814 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4815 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4816 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4817 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4824 if (memcmp(type,mng_sRGB,4) == 0)
4828 image->rendering_intent=
4829 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4830 image->gamma=1.000f/2.200f;
4831 image->chromaticity.red_primary.x=0.6400f;
4832 image->chromaticity.red_primary.y=0.3300f;
4833 image->chromaticity.green_primary.x=0.3000f;
4834 image->chromaticity.green_primary.y=0.6000f;
4835 image->chromaticity.blue_primary.x=0.1500f;
4836 image->chromaticity.blue_primary.y=0.0600f;
4837 image->chromaticity.white_point.x=0.3127f;
4838 image->chromaticity.white_point.y=0.3290f;
4841 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4845 if (memcmp(type,mng_oFFs,4) == 0)
4849 image->page.x=(ssize_t) mng_get_long(p);
4850 image->page.y=(ssize_t) mng_get_long(&p[4]);
4852 if ((int) p[8] != 0)
4854 image->page.x/=10000;
4855 image->page.y/=10000;
4859 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4864 if (memcmp(type,mng_pHYs,4) == 0)
4868 image->resolution.x=(double) mng_get_long(p);
4869 image->resolution.y=(double) mng_get_long(&p[4]);
4870 if ((int) p[8] == PNG_RESOLUTION_METER)
4872 image->units=PixelsPerCentimeterResolution;
4873 image->resolution.x=image->resolution.x/100.0f;
4874 image->resolution.y=image->resolution.y/100.0f;
4878 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4883 if (memcmp(type,mng_iCCP,4) == 0)
4886 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4892 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4894 if (memcmp(type,mng_IEND,4))
4904 Finish up reading image data:
4906 o read main image from color_blob.
4910 o if (color_type has alpha)
4911 if alpha_encoding is PNG
4912 read secondary image from alpha_blob via ReadPNG
4913 if alpha_encoding is JPEG
4914 read secondary image from alpha_blob via ReadJPEG
4918 o copy intensity of secondary image into
4919 alpha samples of main image.
4921 o destroy the secondary image.
4924 if (color_image_info == (ImageInfo *) NULL)
4926 assert(color_image == (Image *) NULL);
4927 assert(alpha_image == (Image *) NULL);
4928 return(DestroyImageList(image));
4931 if (color_image == (Image *) NULL)
4933 assert(alpha_image == (Image *) NULL);
4934 return(DestroyImageList(image));
4937 (void) SeekBlob(color_image,0,SEEK_SET);
4939 if (logging != MagickFalse)
4940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4941 " Reading jng_image from color_blob.");
4943 assert(color_image_info != (ImageInfo *) NULL);
4944 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4945 color_image->filename);
4947 color_image_info->ping=MagickFalse; /* To do: avoid this */
4948 jng_image=ReadImage(color_image_info,exception);
4950 (void) RelinquishUniqueFileResource(color_image->filename);
4951 color_image=DestroyImage(color_image);
4952 color_image_info=DestroyImageInfo(color_image_info);
4954 if (jng_image == (Image *) NULL)
4955 return(DestroyImageList(image));
4957 if (logging != MagickFalse)
4958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4959 " Copying jng_image pixels to main image.");
4961 image->rows=jng_height;
4962 image->columns=jng_width;
4964 status=SetImageExtent(image,image->columns,image->rows,exception);
4965 if (status == MagickFalse)
4966 return(DestroyImageList(image));
4968 for (y=0; y < (ssize_t) image->rows; y++)
4970 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4971 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4972 for (x=(ssize_t) image->columns; x != 0; x--)
4974 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4975 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4976 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4977 q+=GetPixelChannels(image);
4978 s+=GetPixelChannels(jng_image);
4981 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4985 jng_image=DestroyImage(jng_image);
4987 if (image_info->ping == MagickFalse)
4989 if (jng_color_type >= 12)
4991 if (jng_alpha_compression_method == 0)
4995 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4996 PNGType(data,mng_IEND);
4997 LogPNGChunk(logging,mng_IEND,0L);
4998 (void) WriteBlob(alpha_image,4,data);
4999 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5002 (void) CloseBlob(alpha_image);
5004 if (logging != MagickFalse)
5005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5006 " Reading alpha from alpha_blob.");
5008 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5009 "%s",alpha_image->filename);
5011 jng_image=ReadImage(alpha_image_info,exception);
5013 if (jng_image != (Image *) NULL)
5014 for (y=0; y < (ssize_t) image->rows; y++)
5016 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5018 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5020 if (image->alpha_trait != UndefinedPixelTrait)
5021 for (x=(ssize_t) image->columns; x != 0; x--)
5023 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5024 q+=GetPixelChannels(image);
5025 s+=GetPixelChannels(jng_image);
5029 for (x=(ssize_t) image->columns; x != 0; x--)
5031 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5032 if (GetPixelAlpha(image,q) != OpaqueAlpha)
5033 image->alpha_trait=BlendPixelTrait;
5034 q+=GetPixelChannels(image);
5035 s+=GetPixelChannels(jng_image);
5038 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5041 (void) RelinquishUniqueFileResource(alpha_image->filename);
5042 alpha_image=DestroyImage(alpha_image);
5043 alpha_image_info=DestroyImageInfo(alpha_image_info);
5044 if (jng_image != (Image *) NULL)
5045 jng_image=DestroyImage(jng_image);
5049 /* Read the JNG image. */
5051 if (mng_info->mng_type == 0)
5053 mng_info->mng_width=jng_width;
5054 mng_info->mng_height=jng_height;
5057 if (image->page.width == 0 && image->page.height == 0)
5059 image->page.width=jng_width;
5060 image->page.height=jng_height;
5063 if (image->page.x == 0 && image->page.y == 0)
5065 image->page.x=mng_info->x_off[mng_info->object_id];
5066 image->page.y=mng_info->y_off[mng_info->object_id];
5071 image->page.y=mng_info->y_off[mng_info->object_id];
5074 mng_info->image_found++;
5075 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5076 2*GetBlobSize(image));
5078 if (status == MagickFalse)
5079 return(DestroyImageList(image));
5081 if (logging != MagickFalse)
5082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5083 " exit ReadOneJNGImage()");
5089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5093 % R e a d J N G I m a g e %
5097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5099 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5100 % (including the 8-byte signature) and returns it. It allocates the memory
5101 % necessary for the new Image structure and returns a pointer to the new
5104 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5106 % The format of the ReadJNGImage method is:
5108 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5111 % A description of each parameter follows:
5113 % o image_info: the image info.
5115 % o exception: return any errors or warnings in this structure.
5119 static Image *ReadJNGImage(const ImageInfo *image_info,
5120 ExceptionInfo *exception)
5133 magic_number[MagickPathExtent];
5141 assert(image_info != (const ImageInfo *) NULL);
5142 assert(image_info->signature == MagickCoreSignature);
5143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5144 image_info->filename);
5145 assert(exception != (ExceptionInfo *) NULL);
5146 assert(exception->signature == MagickCoreSignature);
5147 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5148 image=AcquireImage(image_info,exception);
5149 mng_info=(MngInfo *) NULL;
5150 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5152 if (status == MagickFalse)
5153 return((Image *) NULL);
5155 if (LocaleCompare(image_info->magick,"JNG") != 0)
5156 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5158 /* Verify JNG signature. */
5160 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5162 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5163 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5166 Verify that file size large enough to contain a JNG datastream.
5168 if (GetBlobSize(image) < 147)
5169 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5171 /* Allocate a MngInfo structure. */
5173 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5175 if (mng_info == (MngInfo *) NULL)
5176 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5178 /* Initialize members of the MngInfo structure. */
5180 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5182 mng_info->image=image;
5183 image=ReadOneJNGImage(mng_info,image_info,exception);
5184 mng_info=MngInfoFreeStruct(mng_info);
5186 if (image == (Image *) NULL)
5188 if (logging != MagickFalse)
5189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5190 "exit ReadJNGImage() with error");
5192 return((Image *) NULL);
5194 (void) CloseBlob(image);
5196 if (image->columns == 0 || image->rows == 0)
5198 if (logging != MagickFalse)
5199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5200 "exit ReadJNGImage() with error");
5202 ThrowReaderException(CorruptImageError,"CorruptImage");
5205 if (logging != MagickFalse)
5206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5212 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5213 ExceptionInfo *exception)
5216 page_geometry[MagickPathExtent];
5244 #if defined(MNG_INSERT_LAYERS)
5246 mng_background_color;
5249 register unsigned char
5264 #if defined(MNG_INSERT_LAYERS)
5269 volatile unsigned int
5270 #ifdef MNG_OBJECT_BUFFERS
5271 mng_background_object=0,
5273 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5276 default_frame_timeout,
5278 #if defined(MNG_INSERT_LAYERS)
5284 /* These delays are all measured in image ticks_per_second,
5285 * not in MNG ticks_per_second
5288 default_frame_delay,
5292 #if defined(MNG_INSERT_LAYERS)
5301 previous_fb.bottom=0;
5303 previous_fb.right=0;
5305 default_fb.bottom=0;
5309 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5310 " Enter ReadOneMNGImage()");
5312 image=mng_info->image;
5314 if (LocaleCompare(image_info->magick,"MNG") == 0)
5317 magic_number[MagickPathExtent];
5319 /* Verify MNG signature. */
5320 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5321 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5322 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5324 /* Initialize some nonzero members of the MngInfo structure. */
5325 for (i=0; i < MNG_MAX_OBJECTS; i++)
5327 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5328 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5330 mng_info->exists[0]=MagickTrue;
5334 first_mng_object=MagickTrue;
5336 #if defined(MNG_INSERT_LAYERS)
5337 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5339 default_frame_delay=0;
5340 default_frame_timeout=0;
5343 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5345 skip_to_iend=MagickFalse;
5346 term_chunk_found=MagickFalse;
5347 mng_info->framing_mode=1;
5348 #if defined(MNG_INSERT_LAYERS)
5349 mandatory_back=MagickFalse;
5351 #if defined(MNG_INSERT_LAYERS)
5352 mng_background_color=image->background_color;
5354 default_fb=mng_info->frame;
5355 previous_fb=mng_info->frame;
5359 type[MagickPathExtent];
5361 if (LocaleCompare(image_info->magick,"MNG") == 0)
5370 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5371 length=ReadBlobMSBLong(image);
5372 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5374 if (logging != MagickFalse)
5375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5376 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5377 type[0],type[1],type[2],type[3],(double) length);
5379 if (length > PNG_UINT_31_MAX)
5386 ThrowReaderException(CorruptImageError,"CorruptImage");
5389 chunk=(unsigned char *) NULL;
5393 if (length > GetBlobSize(image))
5394 ThrowReaderException(CorruptImageError,
5395 "InsufficientImageDataInFile");
5396 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5398 if (chunk == (unsigned char *) NULL)
5399 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5401 for (i=0; i < (ssize_t) length; i++)
5406 c=ReadBlobByte(image);
5409 chunk[i]=(unsigned char) c;
5415 (void) ReadBlobMSBLong(image); /* read crc word */
5417 #if !defined(JNG_SUPPORTED)
5418 if (memcmp(type,mng_JHDR,4) == 0)
5420 skip_to_iend=MagickTrue;
5422 if (mng_info->jhdr_warning == 0)
5423 (void) ThrowMagickException(exception,GetMagickModule(),
5424 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5426 mng_info->jhdr_warning++;
5429 if (memcmp(type,mng_DHDR,4) == 0)
5431 skip_to_iend=MagickTrue;
5433 if (mng_info->dhdr_warning == 0)
5434 (void) ThrowMagickException(exception,GetMagickModule(),
5435 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5437 mng_info->dhdr_warning++;
5439 if (memcmp(type,mng_MEND,4) == 0)
5441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5447 if (memcmp(type,mng_IEND,4) == 0)
5448 skip_to_iend=MagickFalse;
5450 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5452 if (logging != MagickFalse)
5453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5459 if (memcmp(type,mng_MHDR,4) == 0)
5463 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5464 ThrowReaderException(CorruptImageError,"CorruptImage");
5467 mng_info->mng_width=(unsigned long)mng_get_long(p);
5468 mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5470 if (logging != MagickFalse)
5472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5473 " MNG width: %.20g",(double) mng_info->mng_width);
5474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5475 " MNG height: %.20g",(double) mng_info->mng_height);
5479 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5481 if (mng_info->ticks_per_second == 0)
5482 default_frame_delay=0;
5485 default_frame_delay=1UL*image->ticks_per_second/
5486 mng_info->ticks_per_second;
5488 frame_delay=default_frame_delay;
5492 simplicity=(size_t) mng_get_long(p);
5494 mng_type=1; /* Full MNG */
5496 if ((simplicity != 0) && ((simplicity | 11) == 11))
5497 mng_type=2; /* LC */
5499 if ((simplicity != 0) && ((simplicity | 9) == 9))
5500 mng_type=3; /* VLC */
5502 #if defined(MNG_INSERT_LAYERS)
5504 insert_layers=MagickTrue;
5506 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5508 /* Allocate next image structure. */
5509 AcquireNextImage(image_info,image,exception);
5511 if (GetNextImageInList(image) == (Image *) NULL)
5512 return((Image *) NULL);
5514 image=SyncNextImageInList(image);
5515 mng_info->image=image;
5518 if ((mng_info->mng_width > 65535L) ||
5519 (mng_info->mng_height > 65535L))
5521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5522 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5525 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5526 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5527 mng_info->mng_height);
5529 mng_info->frame.left=0;
5530 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5531 mng_info->frame.top=0;
5532 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5533 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5535 for (i=0; i < MNG_MAX_OBJECTS; i++)
5536 mng_info->object_clip[i]=mng_info->frame;
5538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5542 if (memcmp(type,mng_TERM,4) == 0)
5550 if (repeat == 3 && length > 8)
5552 final_delay=(png_uint_32) mng_get_long(&p[2]);
5553 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5555 if (mng_iterations == PNG_UINT_31_MAX)
5558 image->iterations=mng_iterations;
5559 term_chunk_found=MagickTrue;
5562 if (logging != MagickFalse)
5564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5565 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5566 repeat,(double) final_delay, (double) image->iterations);
5569 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5572 if (memcmp(type,mng_DEFI,4) == 0)
5575 (void) ThrowMagickException(exception,GetMagickModule(),
5576 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5581 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5582 ThrowReaderException(CorruptImageError,"CorruptImage");
5585 object_id=(p[0] << 8) | p[1];
5587 if (mng_type == 2 && object_id != 0)
5588 (void) ThrowMagickException(exception,GetMagickModule(),
5589 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5592 if (object_id > MNG_MAX_OBJECTS)
5595 Instead of using a warning we should allocate a larger
5596 MngInfo structure and continue.
5598 (void) ThrowMagickException(exception,GetMagickModule(),
5599 CoderError,"object id too large","`%s'",image->filename);
5600 object_id=MNG_MAX_OBJECTS;
5603 if (mng_info->exists[object_id])
5604 if (mng_info->frozen[object_id])
5606 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5607 (void) ThrowMagickException(exception,
5608 GetMagickModule(),CoderError,
5609 "DEFI cannot redefine a frozen MNG object","`%s'",
5614 mng_info->exists[object_id]=MagickTrue;
5617 mng_info->invisible[object_id]=p[2];
5620 Extract object offset info.
5624 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5625 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5626 if (logging != MagickFalse)
5628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5629 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5630 object_id,(double) mng_info->x_off[object_id],
5631 object_id,(double) mng_info->y_off[object_id]);
5636 Extract object clipping info.
5639 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5642 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5645 if (memcmp(type,mng_bKGD,4) == 0)
5647 mng_info->have_global_bkgd=MagickFalse;
5651 mng_info->mng_global_bkgd.red=
5652 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5654 mng_info->mng_global_bkgd.green=
5655 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5657 mng_info->mng_global_bkgd.blue=
5658 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5660 mng_info->have_global_bkgd=MagickTrue;
5663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5666 if (memcmp(type,mng_BACK,4) == 0)
5668 #if defined(MNG_INSERT_LAYERS)
5670 mandatory_back=p[6];
5675 if (mandatory_back && length > 5)
5677 mng_background_color.red=
5678 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5680 mng_background_color.green=
5681 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5683 mng_background_color.blue=
5684 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5686 mng_background_color.alpha=OpaqueAlpha;
5689 #ifdef MNG_OBJECT_BUFFERS
5691 mng_background_object=(p[7] << 8) | p[8];
5694 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5698 if (memcmp(type,mng_PLTE,4) == 0)
5700 /* Read global PLTE. */
5702 if (length && (length < 769))
5704 if (mng_info->global_plte == (png_colorp) NULL)
5705 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5706 sizeof(*mng_info->global_plte));
5708 for (i=0; i < (ssize_t) (length/3); i++)
5710 mng_info->global_plte[i].red=p[3*i];
5711 mng_info->global_plte[i].green=p[3*i+1];
5712 mng_info->global_plte[i].blue=p[3*i+2];
5715 mng_info->global_plte_length=(unsigned int) (length/3);
5718 for ( ; i < 256; i++)
5720 mng_info->global_plte[i].red=i;
5721 mng_info->global_plte[i].green=i;
5722 mng_info->global_plte[i].blue=i;
5726 mng_info->global_plte_length=256;
5729 mng_info->global_plte_length=0;
5731 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5735 if (memcmp(type,mng_tRNS,4) == 0)
5737 /* read global tRNS */
5739 if (length > 0 && length < 257)
5740 for (i=0; i < (ssize_t) length; i++)
5741 mng_info->global_trns[i]=p[i];
5744 for ( ; i < 256; i++)
5745 mng_info->global_trns[i]=255;
5747 mng_info->global_trns_length=(unsigned int) length;
5748 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5751 if (memcmp(type,mng_gAMA,4) == 0)
5758 igamma=mng_get_long(p);
5759 mng_info->global_gamma=((float) igamma)*0.00001;
5760 mng_info->have_global_gama=MagickTrue;
5764 mng_info->have_global_gama=MagickFalse;
5766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5770 if (memcmp(type,mng_cHRM,4) == 0)
5772 /* Read global cHRM */
5776 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5777 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5778 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5779 mng_info->global_chrm.red_primary.y=0.00001*
5780 mng_get_long(&p[12]);
5781 mng_info->global_chrm.green_primary.x=0.00001*
5782 mng_get_long(&p[16]);
5783 mng_info->global_chrm.green_primary.y=0.00001*
5784 mng_get_long(&p[20]);
5785 mng_info->global_chrm.blue_primary.x=0.00001*
5786 mng_get_long(&p[24]);
5787 mng_info->global_chrm.blue_primary.y=0.00001*
5788 mng_get_long(&p[28]);
5789 mng_info->have_global_chrm=MagickTrue;
5792 mng_info->have_global_chrm=MagickFalse;
5794 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5798 if (memcmp(type,mng_sRGB,4) == 0)
5805 mng_info->global_srgb_intent=
5806 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5807 mng_info->have_global_srgb=MagickTrue;
5810 mng_info->have_global_srgb=MagickFalse;
5812 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5816 if (memcmp(type,mng_iCCP,4) == 0)
5823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5828 if (memcmp(type,mng_FRAM,4) == 0)
5831 (void) ThrowMagickException(exception,GetMagickModule(),
5832 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5835 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5836 image->delay=frame_delay;
5838 frame_delay=default_frame_delay;
5839 frame_timeout=default_frame_timeout;
5844 mng_info->framing_mode=p[0];
5846 if (logging != MagickFalse)
5847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5848 " Framing_mode=%d",mng_info->framing_mode);
5852 /* Note the delay and frame clipping boundaries. */
5854 p++; /* framing mode */
5856 while (*p && ((p-chunk) < (ssize_t) length))
5857 p++; /* frame name */
5859 p++; /* frame name terminator */
5861 if ((p-chunk) < (ssize_t) (length-4))
5868 change_delay=(*p++);
5869 change_timeout=(*p++);
5870 change_clipping=(*p++);
5871 p++; /* change_sync */
5873 if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5875 frame_delay=1UL*image->ticks_per_second*
5878 if (mng_info->ticks_per_second != 0)
5879 frame_delay/=mng_info->ticks_per_second;
5882 frame_delay=PNG_UINT_31_MAX;
5884 if (change_delay == 2)
5885 default_frame_delay=frame_delay;
5889 if (logging != MagickFalse)
5890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5891 " Framing_delay=%.20g",(double) frame_delay);
5894 if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5896 frame_timeout=1UL*image->ticks_per_second*
5899 if (mng_info->ticks_per_second != 0)
5900 frame_timeout/=mng_info->ticks_per_second;
5903 frame_timeout=PNG_UINT_31_MAX;
5905 if (change_timeout == 2)
5906 default_frame_timeout=frame_timeout;
5910 if (logging != MagickFalse)
5911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5912 " Framing_timeout=%.20g",(double) frame_timeout);
5915 if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
5917 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5921 if (logging != MagickFalse)
5922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5923 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5924 (double) fb.left,(double) fb.right,(double) fb.top,
5925 (double) fb.bottom);
5927 if (change_clipping == 2)
5933 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5935 subframe_width=(size_t) (mng_info->clip.right
5936 -mng_info->clip.left);
5938 subframe_height=(size_t) (mng_info->clip.bottom
5939 -mng_info->clip.top);
5941 Insert a background layer behind the frame if framing_mode is 4.
5943 #if defined(MNG_INSERT_LAYERS)
5944 if (logging != MagickFalse)
5945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5946 " subframe_width=%.20g, subframe_height=%.20g",(double)
5947 subframe_width,(double) subframe_height);
5949 if (insert_layers && (mng_info->framing_mode == 4) &&
5950 (subframe_width) && (subframe_height))
5952 /* Allocate next image structure. */
5953 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5955 AcquireNextImage(image_info,image,exception);
5957 if (GetNextImageInList(image) == (Image *) NULL)
5958 return(DestroyImageList(image));
5960 image=SyncNextImageInList(image);
5963 mng_info->image=image;
5965 if (term_chunk_found)
5967 image->start_loop=MagickTrue;
5968 image->iterations=mng_iterations;
5969 term_chunk_found=MagickFalse;
5973 image->start_loop=MagickFalse;
5975 image->columns=subframe_width;
5976 image->rows=subframe_height;
5977 image->page.width=subframe_width;
5978 image->page.height=subframe_height;
5979 image->page.x=mng_info->clip.left;
5980 image->page.y=mng_info->clip.top;
5981 image->background_color=mng_background_color;
5982 image->alpha_trait=UndefinedPixelTrait;
5984 (void) SetImageBackgroundColor(image,exception);
5986 if (logging != MagickFalse)
5987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5988 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5989 (double) mng_info->clip.left,
5990 (double) mng_info->clip.right,
5991 (double) mng_info->clip.top,
5992 (double) mng_info->clip.bottom);
5995 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5999 if (memcmp(type,mng_CLIP,4) == 0)
6010 first_object=(p[0] << 8) | p[1];
6011 last_object=(p[2] << 8) | p[3];
6014 for (i=(int) first_object; i <= (int) last_object; i++)
6016 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6019 if (mng_info->exists[i] && !mng_info->frozen[i])
6024 box=mng_info->object_clip[i];
6025 if ((p-chunk) < (ssize_t) (length-17))
6026 mng_info->object_clip[i]=
6027 mng_read_box(box,(char) p[0],&p[1]);
6032 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6036 if (memcmp(type,mng_SAVE,4) == 0)
6038 for (i=1; i < MNG_MAX_OBJECTS; i++)
6039 if (mng_info->exists[i])
6041 mng_info->frozen[i]=MagickTrue;
6042 #ifdef MNG_OBJECT_BUFFERS
6043 if (mng_info->ob[i] != (MngBuffer *) NULL)
6044 mng_info->ob[i]->frozen=MagickTrue;
6048 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6053 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6055 /* Read DISC or SEEK. */
6057 if ((length == 0) || !memcmp(type,mng_SEEK,4))
6059 for (i=1; i < MNG_MAX_OBJECTS; i++)
6060 MngInfoDiscardObject(mng_info,i);
6068 for (j=1; j < (ssize_t) length; j+=2)
6070 i=p[j-1] << 8 | p[j];
6071 MngInfoDiscardObject(mng_info,i);
6075 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6080 if (memcmp(type,mng_MOVE,4) == 0)
6090 first_object=(p[0] << 8) | p[1];
6091 last_object=(p[2] << 8) | p[3];
6094 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6096 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6099 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6100 (p-chunk) < (ssize_t) (length-8))
6108 old_pair.a=mng_info->x_off[i];
6109 old_pair.b=mng_info->y_off[i];
6110 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6111 mng_info->x_off[i]=new_pair.a;
6112 mng_info->y_off[i]=new_pair.b;
6117 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6121 if (memcmp(type,mng_LOOP,4) == 0)
6123 ssize_t loop_iters=1;
6126 loop_level=chunk[0];
6127 mng_info->loop_active[loop_level]=1; /* mark loop active */
6129 /* Record starting point. */
6130 loop_iters=mng_get_long(&chunk[1]);
6132 if (logging != MagickFalse)
6133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6134 " LOOP level %.20g has %.20g iterations ",
6135 (double) loop_level, (double) loop_iters);
6137 if (loop_iters == 0)
6138 skipping_loop=loop_level;
6142 mng_info->loop_jump[loop_level]=TellBlob(image);
6143 mng_info->loop_count[loop_level]=loop_iters;
6146 mng_info->loop_iteration[loop_level]=0;
6148 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6152 if (memcmp(type,mng_ENDL,4) == 0)
6156 loop_level=chunk[0];
6158 if (skipping_loop > 0)
6160 if (skipping_loop == loop_level)
6163 Found end of zero-iteration loop.
6166 mng_info->loop_active[loop_level]=0;
6172 if (mng_info->loop_active[loop_level] == 1)
6174 mng_info->loop_count[loop_level]--;
6175 mng_info->loop_iteration[loop_level]++;
6177 if (logging != MagickFalse)
6178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6179 " ENDL: LOOP level %.20g has %.20g remaining iters",
6180 (double) loop_level,(double)
6181 mng_info->loop_count[loop_level]);
6183 if (mng_info->loop_count[loop_level] != 0)
6186 SeekBlob(image,mng_info->loop_jump[loop_level],
6191 chunk=(unsigned char *) RelinquishMagickMemory(
6193 ThrowReaderException(CorruptImageError,
6194 "ImproperImageHeader");
6206 mng_info->loop_active[loop_level]=0;
6208 for (i=0; i < loop_level; i++)
6209 if (mng_info->loop_active[i] == 1)
6210 last_level=(short) i;
6211 loop_level=last_level;
6217 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6221 if (memcmp(type,mng_CLON,4) == 0)
6223 if (mng_info->clon_warning == 0)
6224 (void) ThrowMagickException(exception,GetMagickModule(),
6225 CoderError,"CLON is not implemented yet","`%s'",
6228 mng_info->clon_warning++;
6231 if (memcmp(type,mng_MAGN,4) == 0)
6246 magn_first=(p[0] << 8) | p[1];
6252 magn_last=(p[2] << 8) | p[3];
6255 magn_last=magn_first;
6256 #ifndef MNG_OBJECT_BUFFERS
6257 if (magn_first || magn_last)
6258 if (mng_info->magn_warning == 0)
6260 (void) ThrowMagickException(exception,
6261 GetMagickModule(),CoderError,
6262 "MAGN is not implemented yet for nonzero objects",
6263 "`%s'",image->filename);
6265 mng_info->magn_warning++;
6275 magn_mx=(p[5] << 8) | p[6];
6284 magn_my=(p[7] << 8) | p[8];
6293 magn_ml=(p[9] << 8) | p[10];
6302 magn_mr=(p[11] << 8) | p[12];
6311 magn_mt=(p[13] << 8) | p[14];
6320 magn_mb=(p[15] << 8) | p[16];
6332 magn_methy=magn_methx;
6335 if (magn_methx > 5 || magn_methy > 5)
6336 if (mng_info->magn_warning == 0)
6338 (void) ThrowMagickException(exception,
6339 GetMagickModule(),CoderError,
6340 "Unknown MAGN method in MNG datastream","`%s'",
6343 mng_info->magn_warning++;
6345 #ifdef MNG_OBJECT_BUFFERS
6346 /* Magnify existing objects in the range magn_first to magn_last */
6348 if (magn_first == 0 || magn_last == 0)
6350 /* Save the magnification factors for object 0 */
6351 mng_info->magn_mb=magn_mb;
6352 mng_info->magn_ml=magn_ml;
6353 mng_info->magn_mr=magn_mr;
6354 mng_info->magn_mt=magn_mt;
6355 mng_info->magn_mx=magn_mx;
6356 mng_info->magn_my=magn_my;
6357 mng_info->magn_methx=magn_methx;
6358 mng_info->magn_methy=magn_methy;
6362 if (memcmp(type,mng_PAST,4) == 0)
6364 if (mng_info->past_warning == 0)
6365 (void) ThrowMagickException(exception,GetMagickModule(),
6366 CoderError,"PAST is not implemented yet","`%s'",
6369 mng_info->past_warning++;
6372 if (memcmp(type,mng_SHOW,4) == 0)
6374 if (mng_info->show_warning == 0)
6375 (void) ThrowMagickException(exception,GetMagickModule(),
6376 CoderError,"SHOW is not implemented yet","`%s'",
6379 mng_info->show_warning++;
6382 if (memcmp(type,mng_sBIT,4) == 0)
6385 mng_info->have_global_sbit=MagickFalse;
6389 mng_info->global_sbit.gray=p[0];
6390 mng_info->global_sbit.red=p[0];
6391 mng_info->global_sbit.green=p[1];
6392 mng_info->global_sbit.blue=p[2];
6393 mng_info->global_sbit.alpha=p[3];
6394 mng_info->have_global_sbit=MagickTrue;
6397 if (memcmp(type,mng_pHYs,4) == 0)
6401 mng_info->global_x_pixels_per_unit=
6402 (size_t) mng_get_long(p);
6403 mng_info->global_y_pixels_per_unit=
6404 (size_t) mng_get_long(&p[4]);
6405 mng_info->global_phys_unit_type=p[8];
6406 mng_info->have_global_phys=MagickTrue;
6410 mng_info->have_global_phys=MagickFalse;
6412 if (memcmp(type,mng_pHYg,4) == 0)
6414 if (mng_info->phyg_warning == 0)
6415 (void) ThrowMagickException(exception,GetMagickModule(),
6416 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6418 mng_info->phyg_warning++;
6420 if (memcmp(type,mng_BASI,4) == 0)
6422 skip_to_iend=MagickTrue;
6424 if (mng_info->basi_warning == 0)
6425 (void) ThrowMagickException(exception,GetMagickModule(),
6426 CoderError,"BASI is not implemented yet","`%s'",
6429 mng_info->basi_warning++;
6430 #ifdef MNG_BASI_SUPPORTED
6431 basi_width=(unsigned long) mng_get_long(p);
6432 basi_width=(unsigned long) mng_get_long(&p[4]);
6433 basi_color_type=p[8];
6434 basi_compression_method=p[9];
6435 basi_filter_type=p[10];
6436 basi_interlace_method=p[11];
6438 basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6444 basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6450 basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6456 basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6460 if (basi_sample_depth == 16)
6467 basi_viewable=p[20];
6473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6477 if (memcmp(type,mng_IHDR,4)
6478 #if defined(JNG_SUPPORTED)
6479 && memcmp(type,mng_JHDR,4)
6483 /* Not an IHDR or JHDR chunk */
6484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6489 if (logging != MagickFalse)
6490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6491 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6493 mng_info->exists[object_id]=MagickTrue;
6494 mng_info->viewable[object_id]=MagickTrue;
6496 if (mng_info->invisible[object_id])
6498 if (logging != MagickFalse)
6499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6500 " Skipping invisible object");
6502 skip_to_iend=MagickTrue;
6503 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6506 #if defined(MNG_INSERT_LAYERS)
6509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6510 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6513 image_width=(size_t) mng_get_long(p);
6514 image_height=(size_t) mng_get_long(&p[4]);
6516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6519 Insert a transparent background layer behind the entire animation
6520 if it is not full screen.
6522 #if defined(MNG_INSERT_LAYERS)
6523 if (insert_layers && mng_type && first_mng_object)
6525 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6526 (image_width < mng_info->mng_width) ||
6527 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6528 (image_height < mng_info->mng_height) ||
6529 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6531 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6534 Allocate next image structure.
6536 AcquireNextImage(image_info,image,exception);
6538 if (GetNextImageInList(image) == (Image *) NULL)
6539 return(DestroyImageList(image));
6541 image=SyncNextImageInList(image);
6543 mng_info->image=image;
6545 if (term_chunk_found)
6547 image->start_loop=MagickTrue;
6548 image->iterations=mng_iterations;
6549 term_chunk_found=MagickFalse;
6553 image->start_loop=MagickFalse;
6555 /* Make a background rectangle. */
6558 image->columns=mng_info->mng_width;
6559 image->rows=mng_info->mng_height;
6560 image->page.width=mng_info->mng_width;
6561 image->page.height=mng_info->mng_height;
6564 image->background_color=mng_background_color;
6565 (void) SetImageBackgroundColor(image,exception);
6566 if (logging != MagickFalse)
6567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6568 " Inserted transparent background layer, W=%.20g, H=%.20g",
6569 (double) mng_info->mng_width,(double) mng_info->mng_height);
6573 Insert a background layer behind the upcoming image if
6574 framing_mode is 3, and we haven't already inserted one.
6576 if (insert_layers && (mng_info->framing_mode == 3) &&
6577 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6578 (simplicity & 0x08)))
6580 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6583 Allocate next image structure.
6585 AcquireNextImage(image_info,image,exception);
6587 if (GetNextImageInList(image) == (Image *) NULL)
6588 return(DestroyImageList(image));
6590 image=SyncNextImageInList(image);
6593 mng_info->image=image;
6595 if (term_chunk_found)
6597 image->start_loop=MagickTrue;
6598 image->iterations=mng_iterations;
6599 term_chunk_found=MagickFalse;
6603 image->start_loop=MagickFalse;
6606 image->columns=subframe_width;
6607 image->rows=subframe_height;
6608 image->page.width=subframe_width;
6609 image->page.height=subframe_height;
6610 image->page.x=mng_info->clip.left;
6611 image->page.y=mng_info->clip.top;
6612 image->background_color=mng_background_color;
6613 image->alpha_trait=UndefinedPixelTrait;
6614 (void) SetImageBackgroundColor(image,exception);
6616 if (logging != MagickFalse)
6617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6618 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6619 (double) mng_info->clip.left,(double) mng_info->clip.right,
6620 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6622 #endif /* MNG_INSERT_LAYERS */
6623 first_mng_object=MagickFalse;
6625 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6628 Allocate next image structure.
6630 AcquireNextImage(image_info,image,exception);
6632 if (GetNextImageInList(image) == (Image *) NULL)
6633 return(DestroyImageList(image));
6635 image=SyncNextImageInList(image);
6637 mng_info->image=image;
6638 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6639 GetBlobSize(image));
6641 if (status == MagickFalse)
6644 if (term_chunk_found)
6646 image->start_loop=MagickTrue;
6647 term_chunk_found=MagickFalse;
6651 image->start_loop=MagickFalse;
6653 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6655 image->delay=frame_delay;
6656 frame_delay=default_frame_delay;
6662 image->page.width=mng_info->mng_width;
6663 image->page.height=mng_info->mng_height;
6664 image->page.x=mng_info->x_off[object_id];
6665 image->page.y=mng_info->y_off[object_id];
6666 image->iterations=mng_iterations;
6669 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6672 if (logging != MagickFalse)
6673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6674 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6677 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6680 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6683 mng_info->image=image;
6684 mng_info->mng_type=mng_type;
6685 mng_info->object_id=object_id;
6687 if (memcmp(type,mng_IHDR,4) == 0)
6688 image=ReadOnePNGImage(mng_info,image_info,exception);
6690 #if defined(JNG_SUPPORTED)
6692 image=ReadOneJNGImage(mng_info,image_info,exception);
6695 if (image == (Image *) NULL)
6697 if (logging != MagickFalse)
6698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6699 "exit ReadJNGImage() with error");
6701 return((Image *) NULL);
6704 if (image->columns == 0 || image->rows == 0)
6706 (void) CloseBlob(image);
6707 return(DestroyImageList(image));
6710 mng_info->image=image;
6717 if (mng_info->magn_methx || mng_info->magn_methy)
6723 if (logging != MagickFalse)
6724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6725 " Processing MNG MAGN chunk");
6727 if (mng_info->magn_methx == 1)
6729 magnified_width=mng_info->magn_ml;
6731 if (image->columns > 1)
6732 magnified_width += mng_info->magn_mr;
6734 if (image->columns > 2)
6735 magnified_width += (png_uint_32)
6736 ((image->columns-2)*(mng_info->magn_mx));
6741 magnified_width=(png_uint_32) image->columns;
6743 if (image->columns > 1)
6744 magnified_width += mng_info->magn_ml-1;
6746 if (image->columns > 2)
6747 magnified_width += mng_info->magn_mr-1;
6749 if (image->columns > 3)
6750 magnified_width += (png_uint_32)
6751 ((image->columns-3)*(mng_info->magn_mx-1));
6754 if (mng_info->magn_methy == 1)
6756 magnified_height=mng_info->magn_mt;
6758 if (image->rows > 1)
6759 magnified_height += mng_info->magn_mb;
6761 if (image->rows > 2)
6762 magnified_height += (png_uint_32)
6763 ((image->rows-2)*(mng_info->magn_my));
6768 magnified_height=(png_uint_32) image->rows;
6770 if (image->rows > 1)
6771 magnified_height += mng_info->magn_mt-1;
6773 if (image->rows > 2)
6774 magnified_height += mng_info->magn_mb-1;
6776 if (image->rows > 3)
6777 magnified_height += (png_uint_32)
6778 ((image->rows-3)*(mng_info->magn_my-1));
6781 if (magnified_height > image->rows ||
6782 magnified_width > image->columns)
6809 /* Allocate next image structure. */
6811 if (logging != MagickFalse)
6812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6813 " Allocate magnified image");
6815 AcquireNextImage(image_info,image,exception);
6817 if (GetNextImageInList(image) == (Image *) NULL)
6818 return(DestroyImageList(image));
6820 large_image=SyncNextImageInList(image);
6822 large_image->columns=magnified_width;
6823 large_image->rows=magnified_height;
6825 magn_methx=mng_info->magn_methx;
6826 magn_methy=mng_info->magn_methy;
6828 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6829 #define QM unsigned short
6830 if (magn_methx != 1 || magn_methy != 1)
6833 Scale pixels to unsigned shorts to prevent
6834 overflow of intermediate values of interpolations
6836 for (y=0; y < (ssize_t) image->rows; y++)
6838 q=GetAuthenticPixels(image,0,y,image->columns,1,
6841 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6843 SetPixelRed(image,ScaleQuantumToShort(
6844 GetPixelRed(image,q)),q);
6845 SetPixelGreen(image,ScaleQuantumToShort(
6846 GetPixelGreen(image,q)),q);
6847 SetPixelBlue(image,ScaleQuantumToShort(
6848 GetPixelBlue(image,q)),q);
6849 SetPixelAlpha(image,ScaleQuantumToShort(
6850 GetPixelAlpha(image,q)),q);
6851 q+=GetPixelChannels(image);
6854 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6862 if (image->alpha_trait != UndefinedPixelTrait)
6863 (void) SetImageBackgroundColor(large_image,exception);
6867 large_image->background_color.alpha=OpaqueAlpha;
6868 (void) SetImageBackgroundColor(large_image,exception);
6870 if (magn_methx == 4)
6873 if (magn_methx == 5)
6876 if (magn_methy == 4)
6879 if (magn_methy == 5)
6883 /* magnify the rows into the right side of the large image */
6885 if (logging != MagickFalse)
6886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6887 " Magnify the rows to %.20g",
6888 (double) large_image->rows);
6889 m=(ssize_t) mng_info->magn_mt;
6891 length=(size_t) GetPixelChannels(image)*image->columns;
6892 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6893 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6895 if ((prev == (Quantum *) NULL) ||
6896 (next == (Quantum *) NULL))
6898 image=DestroyImageList(image);
6899 ThrowReaderException(ResourceLimitError,
6900 "MemoryAllocationFailed");
6903 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6904 (void) CopyMagickMemory(next,n,length);
6906 for (y=0; y < (ssize_t) image->rows; y++)
6909 m=(ssize_t) mng_info->magn_mt;
6911 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6912 m=(ssize_t) mng_info->magn_mb;
6914 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6915 m=(ssize_t) mng_info->magn_mb;
6917 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6921 m=(ssize_t) mng_info->magn_my;
6927 if (y < (ssize_t) image->rows-1)
6929 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6931 (void) CopyMagickMemory(next,n,length);
6934 for (i=0; i < m; i++, yy++)
6939 assert(yy < (ssize_t) large_image->rows);
6942 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6944 q+=(large_image->columns-image->columns)*
6945 GetPixelChannels(large_image);
6947 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6949 /* To do: get color as function of indexes[x] */
6951 if (image->storage_class == PseudoClass)
6956 if (magn_methy <= 1)
6958 /* replicate previous */
6959 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6960 SetPixelGreen(large_image,GetPixelGreen(image,
6962 SetPixelBlue(large_image,GetPixelBlue(image,
6964 SetPixelAlpha(large_image,GetPixelAlpha(image,
6968 else if (magn_methy == 2 || magn_methy == 4)
6972 SetPixelRed(large_image,GetPixelRed(image,
6974 SetPixelGreen(large_image,GetPixelGreen(image,
6976 SetPixelBlue(large_image,GetPixelBlue(image,
6978 SetPixelAlpha(large_image,GetPixelAlpha(image,
6985 SetPixelRed(large_image,((QM) (((ssize_t)
6986 (2*i*(GetPixelRed(image,n)
6987 -GetPixelRed(image,pixels)+m))/
6989 +GetPixelRed(image,pixels)))),q);
6990 SetPixelGreen(large_image,((QM) (((ssize_t)
6991 (2*i*(GetPixelGreen(image,n)
6992 -GetPixelGreen(image,pixels)+m))/
6994 +GetPixelGreen(image,pixels)))),q);
6995 SetPixelBlue(large_image,((QM) (((ssize_t)
6996 (2*i*(GetPixelBlue(image,n)
6997 -GetPixelBlue(image,pixels)+m))/
6999 +GetPixelBlue(image,pixels)))),q);
7001 if (image->alpha_trait != UndefinedPixelTrait)
7002 SetPixelAlpha(large_image, ((QM) (((ssize_t)
7003 (2*i*(GetPixelAlpha(image,n)
7004 -GetPixelAlpha(image,pixels)+m))
7006 GetPixelAlpha(image,pixels)))),q);
7009 if (magn_methy == 4)
7011 /* Replicate nearest */
7012 if (i <= ((m+1) << 1))
7013 SetPixelAlpha(large_image,GetPixelAlpha(image,
7016 SetPixelAlpha(large_image,GetPixelAlpha(image,
7021 else /* if (magn_methy == 3 || magn_methy == 5) */
7023 /* Replicate nearest */
7024 if (i <= ((m+1) << 1))
7026 SetPixelRed(large_image,GetPixelRed(image,
7028 SetPixelGreen(large_image,GetPixelGreen(image,
7030 SetPixelBlue(large_image,GetPixelBlue(image,
7032 SetPixelAlpha(large_image,GetPixelAlpha(image,
7038 SetPixelRed(large_image,GetPixelRed(image,n),q);
7039 SetPixelGreen(large_image,GetPixelGreen(image,n),
7041 SetPixelBlue(large_image,GetPixelBlue(image,n),
7043 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7047 if (magn_methy == 5)
7049 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7050 (GetPixelAlpha(image,n)
7051 -GetPixelAlpha(image,pixels))
7052 +m))/((ssize_t) (m*2))
7053 +GetPixelAlpha(image,pixels)),q);
7056 n+=GetPixelChannels(image);
7057 q+=GetPixelChannels(large_image);
7058 pixels+=GetPixelChannels(image);
7061 if (SyncAuthenticPixels(large_image,exception) == 0)
7067 prev=(Quantum *) RelinquishMagickMemory(prev);
7068 next=(Quantum *) RelinquishMagickMemory(next);
7070 length=image->columns;
7072 if (logging != MagickFalse)
7073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074 " Delete original image");
7076 DeleteImageFromList(&image);
7080 mng_info->image=image;
7082 /* magnify the columns */
7083 if (logging != MagickFalse)
7084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085 " Magnify the columns to %.20g",
7086 (double) image->columns);
7088 for (y=0; y < (ssize_t) image->rows; y++)
7093 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7094 pixels=q+(image->columns-length)*GetPixelChannels(image);
7095 n=pixels+GetPixelChannels(image);
7097 for (x=(ssize_t) (image->columns-length);
7098 x < (ssize_t) image->columns; x++)
7100 /* To do: Rewrite using Get/Set***PixelChannel() */
7102 if (x == (ssize_t) (image->columns-length))
7103 m=(ssize_t) mng_info->magn_ml;
7105 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7106 m=(ssize_t) mng_info->magn_mr;
7108 else if (magn_methx <= 1 &&
7109 x == (ssize_t) image->columns-1)
7110 m=(ssize_t) mng_info->magn_mr;
7112 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7116 m=(ssize_t) mng_info->magn_mx;
7118 for (i=0; i < m; i++)
7120 if (magn_methx <= 1)
7122 /* replicate previous */
7123 SetPixelRed(image,GetPixelRed(image,pixels),q);
7124 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7125 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7126 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7129 else if (magn_methx == 2 || magn_methx == 4)
7133 SetPixelRed(image,GetPixelRed(image,pixels),q);
7134 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7135 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7136 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7139 /* To do: Rewrite using Get/Set***PixelChannel() */
7143 SetPixelRed(image,(QM) ((2*i*(
7144 GetPixelRed(image,n)
7145 -GetPixelRed(image,pixels))+m)
7147 GetPixelRed(image,pixels)),q);
7149 SetPixelGreen(image,(QM) ((2*i*(
7150 GetPixelGreen(image,n)
7151 -GetPixelGreen(image,pixels))+m)
7153 GetPixelGreen(image,pixels)),q);
7155 SetPixelBlue(image,(QM) ((2*i*(
7156 GetPixelBlue(image,n)
7157 -GetPixelBlue(image,pixels))+m)
7159 GetPixelBlue(image,pixels)),q);
7160 if (image->alpha_trait != UndefinedPixelTrait)
7161 SetPixelAlpha(image,(QM) ((2*i*(
7162 GetPixelAlpha(image,n)
7163 -GetPixelAlpha(image,pixels))+m)
7165 GetPixelAlpha(image,pixels)),q);
7168 if (magn_methx == 4)
7170 /* Replicate nearest */
7171 if (i <= ((m+1) << 1))
7173 SetPixelAlpha(image,
7174 GetPixelAlpha(image,pixels)+0,q);
7178 SetPixelAlpha(image,
7179 GetPixelAlpha(image,n)+0,q);
7184 else /* if (magn_methx == 3 || magn_methx == 5) */
7186 /* Replicate nearest */
7187 if (i <= ((m+1) << 1))
7189 SetPixelRed(image,GetPixelRed(image,pixels),q);
7190 SetPixelGreen(image,GetPixelGreen(image,
7192 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7193 SetPixelAlpha(image,GetPixelAlpha(image,
7199 SetPixelRed(image,GetPixelRed(image,n),q);
7200 SetPixelGreen(image,GetPixelGreen(image,n),q);
7201 SetPixelBlue(image,GetPixelBlue(image,n),q);
7202 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7205 if (magn_methx == 5)
7208 SetPixelAlpha(image,
7209 (QM) ((2*i*( GetPixelAlpha(image,n)
7210 -GetPixelAlpha(image,pixels))+m)/
7212 +GetPixelAlpha(image,pixels)),q);
7215 q+=GetPixelChannels(image);
7217 n+=GetPixelChannels(image);
7220 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7223 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7224 if (magn_methx != 1 || magn_methy != 1)
7227 Rescale pixels to Quantum
7229 for (y=0; y < (ssize_t) image->rows; y++)
7231 q=GetAuthenticPixels(image,0,y,image->columns,1,
7234 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7236 SetPixelRed(image,ScaleShortToQuantum(
7237 GetPixelRed(image,q)),q);
7238 SetPixelGreen(image,ScaleShortToQuantum(
7239 GetPixelGreen(image,q)),q);
7240 SetPixelBlue(image,ScaleShortToQuantum(
7241 GetPixelBlue(image,q)),q);
7242 SetPixelAlpha(image,ScaleShortToQuantum(
7243 GetPixelAlpha(image,q)),q);
7244 q+=GetPixelChannels(image);
7247 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7252 if (logging != MagickFalse)
7253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254 " Finished MAGN processing");
7259 Crop_box is with respect to the upper left corner of the MNG.
7261 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7262 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7263 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7264 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7265 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7266 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7267 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7268 if ((crop_box.left != (mng_info->image_box.left
7269 +mng_info->x_off[object_id])) ||
7270 (crop_box.right != (mng_info->image_box.right
7271 +mng_info->x_off[object_id])) ||
7272 (crop_box.top != (mng_info->image_box.top
7273 +mng_info->y_off[object_id])) ||
7274 (crop_box.bottom != (mng_info->image_box.bottom
7275 +mng_info->y_off[object_id])))
7277 if (logging != MagickFalse)
7278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7279 " Crop the PNG image");
7281 if ((crop_box.left < crop_box.right) &&
7282 (crop_box.top < crop_box.bottom))
7291 Crop_info is with respect to the upper left corner of
7294 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7295 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7296 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7297 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7298 image->page.width=image->columns;
7299 image->page.height=image->rows;
7302 im=CropImage(image,&crop_info,exception);
7304 if (im != (Image *) NULL)
7306 image->columns=im->columns;
7307 image->rows=im->rows;
7308 im=DestroyImage(im);
7309 image->page.width=image->columns;
7310 image->page.height=image->rows;
7311 image->page.x=crop_box.left;
7312 image->page.y=crop_box.top;
7319 No pixels in crop area. The MNG spec still requires
7320 a layer, though, so make a single transparent pixel in
7321 the top left corner.
7326 (void) SetImageBackgroundColor(image,exception);
7327 image->page.width=1;
7328 image->page.height=1;
7333 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7334 image=mng_info->image;
7338 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7339 /* PNG does not handle depths greater than 16 so reduce it even
7342 if (image->depth > 16)
7346 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7347 if (image->depth > 8)
7349 /* To do: fill low byte properly */
7353 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7357 if (image_info->number_scenes != 0)
7359 if (mng_info->scenes_found >
7360 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7364 if (logging != MagickFalse)
7365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7366 " Finished reading image datastream.");
7368 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7370 (void) CloseBlob(image);
7372 if (logging != MagickFalse)
7373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7374 " Finished reading all image datastreams.");
7376 #if defined(MNG_INSERT_LAYERS)
7377 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7378 (mng_info->mng_height))
7381 Insert a background layer if nothing else was found.
7383 if (logging != MagickFalse)
7384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7385 " No images found. Inserting a background layer.");
7387 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7390 Allocate next image structure.
7392 AcquireNextImage(image_info,image,exception);
7393 if (GetNextImageInList(image) == (Image *) NULL)
7395 if (logging != MagickFalse)
7396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7397 " Allocation failed, returning NULL.");
7399 return(DestroyImageList(image));;
7401 image=SyncNextImageInList(image);
7403 image->columns=mng_info->mng_width;
7404 image->rows=mng_info->mng_height;
7405 image->page.width=mng_info->mng_width;
7406 image->page.height=mng_info->mng_height;
7409 image->background_color=mng_background_color;
7410 image->alpha_trait=UndefinedPixelTrait;
7412 if (image_info->ping == MagickFalse)
7413 (void) SetImageBackgroundColor(image,exception);
7415 mng_info->image_found++;
7418 image->iterations=mng_iterations;
7420 if (mng_iterations == 1)
7421 image->start_loop=MagickTrue;
7423 while (GetPreviousImageInList(image) != (Image *) NULL)
7426 if (image_count > 10*mng_info->image_found)
7428 if (logging != MagickFalse)
7429 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7431 (void) ThrowMagickException(exception,GetMagickModule(),
7432 CoderError,"Linked list is corrupted, beginning of list not found",
7433 "`%s'",image_info->filename);
7435 return(DestroyImageList(image));
7438 image=GetPreviousImageInList(image);
7440 if (GetNextImageInList(image) == (Image *) NULL)
7442 if (logging != MagickFalse)
7443 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7445 (void) ThrowMagickException(exception,GetMagickModule(),
7446 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7447 image_info->filename);
7451 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7452 GetNextImageInList(image) ==
7455 if (logging != MagickFalse)
7456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7457 " First image null");
7459 (void) ThrowMagickException(exception,GetMagickModule(),
7460 CoderError,"image->next for first image is NULL but shouldn't be.",
7461 "`%s'",image_info->filename);
7464 if (mng_info->image_found == 0)
7466 if (logging != MagickFalse)
7467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7468 " No visible images found.");
7470 (void) ThrowMagickException(exception,GetMagickModule(),
7471 CoderError,"No visible images in file","`%s'",image_info->filename);
7473 return(DestroyImageList(image));
7476 if (mng_info->ticks_per_second)
7477 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7478 final_delay/mng_info->ticks_per_second;
7481 image->start_loop=MagickTrue;
7483 /* Find final nonzero image delay */
7484 final_image_delay=0;
7486 while (GetNextImageInList(image) != (Image *) NULL)
7489 final_image_delay=image->delay;
7491 image=GetNextImageInList(image);
7494 if (final_delay < final_image_delay)
7495 final_delay=final_image_delay;
7497 image->delay=final_delay;
7499 if (logging != MagickFalse)
7500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7501 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7502 (double) final_delay);
7504 if (logging != MagickFalse)
7510 image=GetFirstImageInList(image);
7512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7513 " Before coalesce:");
7515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7516 " scene 0 delay=%.20g",(double) image->delay);
7518 while (GetNextImageInList(image) != (Image *) NULL)
7520 image=GetNextImageInList(image);
7521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7522 " scene %.20g delay=%.20g",(double) scene++,
7523 (double) image->delay);
7527 image=GetFirstImageInList(image);
7528 #ifdef MNG_COALESCE_LAYERS
7538 if (logging != MagickFalse)
7539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7540 " Coalesce Images");
7543 next_image=CoalesceImages(image,exception);
7545 if (next_image == (Image *) NULL)
7546 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7548 image=DestroyImageList(image);
7551 for (next=image; next != (Image *) NULL; next=next_image)
7553 next->page.width=mng_info->mng_width;
7554 next->page.height=mng_info->mng_height;
7557 next->scene=scene++;
7558 next_image=GetNextImageInList(next);
7560 if (next_image == (Image *) NULL)
7563 if (next->delay == 0)
7566 next_image->previous=GetPreviousImageInList(next);
7567 if (GetPreviousImageInList(next) == (Image *) NULL)
7570 next->previous->next=next_image;
7571 next=DestroyImage(next);
7577 while (GetNextImageInList(image) != (Image *) NULL)
7578 image=GetNextImageInList(image);
7580 image->dispose=BackgroundDispose;
7582 if (logging != MagickFalse)
7588 image=GetFirstImageInList(image);
7590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7591 " After coalesce:");
7593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7594 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7595 (double) image->dispose);
7597 while (GetNextImageInList(image) != (Image *) NULL)
7599 image=GetNextImageInList(image);
7601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7602 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7603 (double) image->delay,(double) image->dispose);
7607 if (logging != MagickFalse)
7608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7609 " exit ReadOneMNGImage();");
7614 static Image *ReadMNGImage(const ImageInfo *image_info,
7615 ExceptionInfo *exception)
7627 /* Open image file. */
7629 assert(image_info != (const ImageInfo *) NULL);
7630 assert(image_info->signature == MagickCoreSignature);
7631 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7632 image_info->filename);
7633 assert(exception != (ExceptionInfo *) NULL);
7634 assert(exception->signature == MagickCoreSignature);
7635 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7636 image=AcquireImage(image_info,exception);
7637 mng_info=(MngInfo *) NULL;
7638 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7640 if (status == MagickFalse)
7641 return((Image *) NULL);
7643 /* Allocate a MngInfo structure. */
7645 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7647 if (mng_info == (MngInfo *) NULL)
7648 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7650 /* Initialize members of the MngInfo structure. */
7652 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7653 mng_info->image=image;
7654 image=ReadOneMNGImage(mng_info,image_info,exception);
7655 mng_info=MngInfoFreeStruct(mng_info);
7657 if (image == (Image *) NULL)
7659 if (logging != MagickFalse)
7660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7661 "exit ReadMNGImage() with error");
7663 return((Image *) NULL);
7665 (void) CloseBlob(image);
7667 if (logging != MagickFalse)
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7670 return(GetFirstImageInList(image));
7672 #else /* PNG_LIBPNG_VER > 10011 */
7673 static Image *ReadPNGImage(const ImageInfo *image_info,
7674 ExceptionInfo *exception)
7676 printf("Your PNG library is too old: You have libpng-%s\n",
7677 PNG_LIBPNG_VER_STRING);
7679 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7680 "PNG library is too old","`%s'",image_info->filename);
7682 return(Image *) NULL;
7685 static Image *ReadMNGImage(const ImageInfo *image_info,
7686 ExceptionInfo *exception)
7688 return(ReadPNGImage(image_info,exception));
7690 #endif /* PNG_LIBPNG_VER > 10011 */
7694 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7698 % R e g i s t e r P N G I m a g e %
7702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7704 % RegisterPNGImage() adds properties for the PNG image format to
7705 % the list of supported formats. The properties include the image format
7706 % tag, a method to read and/or write the format, whether the format
7707 % supports the saving of more than one frame to the same file or blob,
7708 % whether the format supports native in-memory I/O, and a brief
7709 % description of the format.
7711 % The format of the RegisterPNGImage method is:
7713 % size_t RegisterPNGImage(void)
7716 ModuleExport size_t RegisterPNGImage(void)
7719 version[MagickPathExtent];
7727 "See http://www.libpng.org/ for details about the PNG format."
7732 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7738 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7744 #if defined(PNG_LIBPNG_VER_STRING)
7745 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7746 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7749 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7751 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7752 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7757 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7758 entry->flags|=CoderDecoderSeekableStreamFlag;
7760 #if defined(MAGICKCORE_PNG_DELEGATE)
7761 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7762 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7765 entry->magick=(IsImageFormatHandler *) IsMNG;
7767 if (*version != '\0')
7768 entry->version=ConstantString(version);
7770 entry->mime_type=ConstantString("video/x-mng");
7771 entry->note=ConstantString(MNGNote);
7772 (void) RegisterMagickInfo(entry);
7774 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7776 #if defined(MAGICKCORE_PNG_DELEGATE)
7777 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7778 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7781 entry->magick=(IsImageFormatHandler *) IsPNG;
7782 entry->flags|=CoderDecoderSeekableStreamFlag;
7783 entry->flags^=CoderAdjoinFlag;
7784 entry->mime_type=ConstantString("image/png");
7786 if (*version != '\0')
7787 entry->version=ConstantString(version);
7789 entry->note=ConstantString(PNGNote);
7790 (void) RegisterMagickInfo(entry);
7792 entry=AcquireMagickInfo("PNG","PNG8",
7793 "8-bit indexed with optional binary transparency");
7795 #if defined(MAGICKCORE_PNG_DELEGATE)
7796 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7797 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7800 entry->magick=(IsImageFormatHandler *) IsPNG;
7801 entry->flags|=CoderDecoderSeekableStreamFlag;
7802 entry->flags^=CoderAdjoinFlag;
7803 entry->mime_type=ConstantString("image/png");
7804 (void) RegisterMagickInfo(entry);
7806 entry=AcquireMagickInfo("PNG","PNG24",
7807 "opaque or binary transparent 24-bit RGB");
7810 #if defined(ZLIB_VERSION)
7811 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7812 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7814 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7816 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7817 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7821 if (*version != '\0')
7822 entry->version=ConstantString(version);
7824 #if defined(MAGICKCORE_PNG_DELEGATE)
7825 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7826 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7829 entry->magick=(IsImageFormatHandler *) IsPNG;
7830 entry->flags|=CoderDecoderSeekableStreamFlag;
7831 entry->flags^=CoderAdjoinFlag;
7832 entry->mime_type=ConstantString("image/png");
7833 (void) RegisterMagickInfo(entry);
7835 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7837 #if defined(MAGICKCORE_PNG_DELEGATE)
7838 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7839 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7842 entry->magick=(IsImageFormatHandler *) IsPNG;
7843 entry->flags|=CoderDecoderSeekableStreamFlag;
7844 entry->flags^=CoderAdjoinFlag;
7845 entry->mime_type=ConstantString("image/png");
7846 (void) RegisterMagickInfo(entry);
7848 entry=AcquireMagickInfo("PNG","PNG48",
7849 "opaque or binary transparent 48-bit RGB");
7851 #if defined(MAGICKCORE_PNG_DELEGATE)
7852 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7853 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7856 entry->magick=(IsImageFormatHandler *) IsPNG;
7857 entry->flags|=CoderDecoderSeekableStreamFlag;
7858 entry->flags^=CoderAdjoinFlag;
7859 entry->mime_type=ConstantString("image/png");
7860 (void) RegisterMagickInfo(entry);
7862 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7864 #if defined(MAGICKCORE_PNG_DELEGATE)
7865 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7866 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7869 entry->magick=(IsImageFormatHandler *) IsPNG;
7870 entry->flags|=CoderDecoderSeekableStreamFlag;
7871 entry->flags^=CoderAdjoinFlag;
7872 entry->mime_type=ConstantString("image/png");
7873 (void) RegisterMagickInfo(entry);
7875 entry=AcquireMagickInfo("PNG","PNG00",
7876 "PNG inheriting bit-depth, color-type from original, if possible");
7878 #if defined(MAGICKCORE_PNG_DELEGATE)
7879 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7880 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7883 entry->magick=(IsImageFormatHandler *) IsPNG;
7884 entry->flags|=CoderDecoderSeekableStreamFlag;
7885 entry->flags^=CoderAdjoinFlag;
7886 entry->mime_type=ConstantString("image/png");
7887 (void) RegisterMagickInfo(entry);
7889 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7891 #if defined(JNG_SUPPORTED)
7892 #if defined(MAGICKCORE_PNG_DELEGATE)
7893 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7894 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7898 entry->magick=(IsImageFormatHandler *) IsJNG;
7899 entry->flags|=CoderDecoderSeekableStreamFlag;
7900 entry->flags^=CoderAdjoinFlag;
7901 entry->mime_type=ConstantString("image/x-jng");
7902 entry->note=ConstantString(JNGNote);
7903 (void) RegisterMagickInfo(entry);
7905 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7906 ping_semaphore=AcquireSemaphoreInfo();
7909 return(MagickImageCoderSignature);
7913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7917 % U n r e g i s t e r P N G I m a g e %
7921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7923 % UnregisterPNGImage() removes format registrations made by the
7924 % PNG module from the list of supported formats.
7926 % The format of the UnregisterPNGImage method is:
7928 % UnregisterPNGImage(void)
7931 ModuleExport void UnregisterPNGImage(void)
7933 (void) UnregisterMagickInfo("MNG");
7934 (void) UnregisterMagickInfo("PNG");
7935 (void) UnregisterMagickInfo("PNG8");
7936 (void) UnregisterMagickInfo("PNG24");
7937 (void) UnregisterMagickInfo("PNG32");
7938 (void) UnregisterMagickInfo("PNG48");
7939 (void) UnregisterMagickInfo("PNG64");
7940 (void) UnregisterMagickInfo("PNG00");
7941 (void) UnregisterMagickInfo("JNG");
7943 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7944 if (ping_semaphore != (SemaphoreInfo *) NULL)
7945 RelinquishSemaphoreInfo(&ping_semaphore);
7949 #if defined(MAGICKCORE_PNG_DELEGATE)
7950 #if PNG_LIBPNG_VER > 10011
7952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7956 % W r i t e M N G I m a g e %
7960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7962 % WriteMNGImage() writes an image in the Portable Network Graphics
7963 % Group's "Multiple-image Network Graphics" encoded image format.
7965 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7967 % The format of the WriteMNGImage method is:
7969 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7970 % Image *image,ExceptionInfo *exception)
7972 % A description of each parameter follows.
7974 % o image_info: the image info.
7976 % o image: The image.
7978 % o exception: return any errors or warnings in this structure.
7980 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7981 % "To do" under ReadPNGImage):
7983 % Preserve all unknown and not-yet-handled known chunks found in input
7984 % PNG file and copy them into output PNG files according to the PNG
7987 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7989 % Improve selection of color type (use indexed-colour or indexed-colour
7990 % with tRNS when 256 or fewer unique RGBA values are present).
7992 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7993 % This will be complicated if we limit ourselves to generating MNG-LC
7994 % files. For now we ignore disposal method 3 and simply overlay the next
7997 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7998 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7999 % [mostly done 15 June 1999 but still need to take care of tRNS]
8001 % Check for identical sRGB and replace with a global sRGB (and remove
8002 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8003 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8004 % local gAMA/cHRM with local sRGB if appropriate).
8006 % Check for identical sBIT chunks and write global ones.
8008 % Provide option to skip writing the signature tEXt chunks.
8010 % Use signatures to detect identical objects and reuse the first
8011 % instance of such objects instead of writing duplicate objects.
8013 % Use a smaller-than-32k value of compression window size when
8016 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
8017 % ancillary text chunks and save profiles.
8019 % Provide an option to force LC files (to ensure exact framing rate)
8022 % Provide an option to force VLC files instead of LC, even when offsets
8023 % are present. This will involve expanding the embedded images with a
8024 % transparent region at the top and/or left.
8028 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8029 png_info *ping_info, unsigned char *profile_type, unsigned char
8030 *profile_description, unsigned char *profile_data, png_uint_32 length)
8049 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8051 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8054 if (image_info->verbose)
8056 (void) printf("writing raw profile: type=%s, length=%.20g\n",
8057 (char *) profile_type, (double) length);
8060 #if PNG_LIBPNG_VER >= 10400
8061 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8063 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8065 description_length=(png_uint_32) strlen((const char *) profile_description);
8066 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8067 + description_length);
8068 #if PNG_LIBPNG_VER >= 10400
8069 text[0].text=(png_charp) png_malloc(ping,
8070 (png_alloc_size_t) allocated_length);
8071 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8073 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8074 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8076 text[0].key[0]='\0';
8077 (void) ConcatenateMagickString(text[0].key,
8078 "Raw profile type ",MagickPathExtent);
8079 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8083 (void) CopyMagickString(dp,(const char *) profile_description,
8085 dp+=description_length;
8087 (void) FormatLocaleString(dp,allocated_length-
8088 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8091 for (i=0; i < (ssize_t) length; i++)
8095 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8096 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8101 text[0].text_length=(png_size_t) (dp-text[0].text);
8102 text[0].compression=image_info->compression == NoCompression ||
8103 (image_info->compression == UndefinedCompression &&
8104 text[0].text_length < 128) ? -1 : 0;
8106 if (text[0].text_length <= allocated_length)
8107 png_set_text(ping,ping_info,text,1);
8109 png_free(ping,text[0].text);
8110 png_free(ping,text[0].key);
8111 png_free(ping,text);
8114 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8115 const Quantum *p, const PixelInfo *q)
8120 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8121 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8122 return(MagickFalse);
8123 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8124 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8125 return(MagickFalse);
8126 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8127 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8128 return(MagickFalse);
8133 #if defined(PNG_tIME_SUPPORTED)
8134 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8135 const char *date,ExceptionInfo *exception)
8151 if (date != (const char *) NULL)
8153 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8156 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8157 "Invalid date format specified for png:tIME","`%s'",
8161 ptime.year=(png_uint_16) year;
8162 ptime.month=(png_byte) month;
8163 ptime.day=(png_byte) day;
8164 ptime.hour=(png_byte) hour;
8165 ptime.minute=(png_byte) minute;
8166 ptime.second=(png_byte) second;
8171 png_convert_from_time_t(&ptime,ttime);
8173 png_set_tIME(ping,info,&ptime);
8177 /* Write one PNG image */
8178 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8179 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8211 ping_trans_alpha[256];
8239 ping_have_cheap_transparency,
8253 /* ping_exclude_EXIF, */
8257 /* ping_exclude_iTXt, */
8263 /* ping_exclude_tRNS, */
8265 ping_exclude_zCCP, /* hex-encoded iCCP */
8268 ping_preserve_colormap,
8270 ping_need_colortype_warning,
8278 *volatile pixel_info;
8297 ping_interlace_method,
8298 ping_compression_method,
8315 number_semitransparent,
8317 ping_pHYs_unit_type;
8320 ping_pHYs_x_resolution,
8321 ping_pHYs_y_resolution;
8323 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8324 " Enter WriteOnePNGImage()");
8326 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8327 if (image == (Image *) NULL)
8328 return(MagickFalse);
8329 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8330 if (image_info == (ImageInfo *) NULL)
8331 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8333 /* Define these outside of the following "if logging()" block so they will
8334 * show in debuggers.
8337 (void) ConcatenateMagickString(im_vers,
8338 MagickLibVersionText,MagickPathExtent);
8339 (void) ConcatenateMagickString(im_vers,
8340 MagickLibAddendum,MagickPathExtent);
8343 (void) ConcatenateMagickString(libpng_vers,
8344 PNG_LIBPNG_VER_STRING,32);
8346 (void) ConcatenateMagickString(libpng_runv,
8347 png_get_libpng_ver(NULL),32);
8350 (void) ConcatenateMagickString(zlib_vers,
8353 (void) ConcatenateMagickString(zlib_runv,
8356 if (logging != MagickFalse)
8358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " IM version = %s", im_vers);
8360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8361 " Libpng version = %s", libpng_vers);
8362 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365 " running with %s", libpng_runv);
8367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8368 " Zlib version = %s", zlib_vers);
8369 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " running with %s", zlib_runv);
8376 /* Initialize some stuff */
8379 ping_interlace_method=0,
8380 ping_compression_method=0,
8381 ping_filter_method=0,
8384 ping_background.red = 0;
8385 ping_background.green = 0;
8386 ping_background.blue = 0;
8387 ping_background.gray = 0;
8388 ping_background.index = 0;
8390 ping_trans_color.red=0;
8391 ping_trans_color.green=0;
8392 ping_trans_color.blue=0;
8393 ping_trans_color.gray=0;
8395 ping_pHYs_unit_type = 0;
8396 ping_pHYs_x_resolution = 0;
8397 ping_pHYs_y_resolution = 0;
8399 ping_have_blob=MagickFalse;
8400 ping_have_cheap_transparency=MagickFalse;
8401 ping_have_color=MagickTrue;
8402 ping_have_non_bw=MagickTrue;
8403 ping_have_PLTE=MagickFalse;
8404 ping_have_bKGD=MagickFalse;
8405 ping_have_eXIf=MagickTrue;
8406 ping_have_iCCP=MagickFalse;
8407 ping_have_pHYs=MagickFalse;
8408 ping_have_sRGB=MagickFalse;
8409 ping_have_tRNS=MagickFalse;
8411 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8412 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8413 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8414 ping_exclude_date=mng_info->ping_exclude_date;
8415 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8416 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8417 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8418 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8419 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8420 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8421 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8422 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8423 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8424 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8425 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8426 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8428 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8429 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8430 ping_need_colortype_warning = MagickFalse;
8432 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8433 * i.e., eliminate the ICC profile and set image->rendering_intent.
8434 * Note that this will not involve any changes to the actual pixels
8435 * but merely passes information to applications that read the resulting
8438 * To do: recognize other variants of the sRGB profile, using the CRC to
8439 * verify all recognized variants including the 7 already known.
8441 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8443 * Use something other than image->rendering_intent to record the fact
8444 * that the sRGB profile was found.
8446 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8447 * profile. Record the Blackpoint Compensation, if any.
8449 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8457 ResetImageProfileIterator(image);
8458 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8460 profile=GetImageProfile(image,name);
8462 if (profile != (StringInfo *) NULL)
8464 if ((LocaleCompare(name,"ICC") == 0) ||
8465 (LocaleCompare(name,"ICM") == 0))
8480 length=(png_uint_32) GetStringInfoLength(profile);
8482 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8484 if (length == sRGB_info[icheck].len)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Got a %lu-byte ICC profile (potentially sRGB)",
8490 (unsigned long) length);
8492 data=GetStringInfoDatum(profile);
8493 profile_crc=crc32(0,data,length);
8495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496 " with crc=%8x",(unsigned int) profile_crc);
8500 if (profile_crc == sRGB_info[icheck].crc)
8502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503 " It is sRGB with rendering intent = %s",
8504 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8505 sRGB_info[icheck].intent));
8506 if (image->rendering_intent==UndefinedIntent)
8508 image->rendering_intent=
8509 Magick_RenderingIntent_from_PNG_RenderingIntent(
8510 sRGB_info[icheck].intent);
8512 ping_exclude_iCCP = MagickTrue;
8513 ping_exclude_zCCP = MagickTrue;
8514 ping_have_sRGB = MagickTrue;
8519 if (sRGB_info[icheck].len == 0)
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521 " Got %lu-byte ICC profile not recognized as sRGB",
8522 (unsigned long) length);
8525 name=GetNextImageProfile(image);
8530 number_semitransparent = 0;
8531 number_transparent = 0;
8533 if (logging != MagickFalse)
8535 if (image->storage_class == UndefinedClass)
8536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8537 " image->storage_class=UndefinedClass");
8538 if (image->storage_class == DirectClass)
8539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8540 " image->storage_class=DirectClass");
8541 if (image->storage_class == PseudoClass)
8542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8543 " image->storage_class=PseudoClass");
8544 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8545 " image->taint=MagickTrue":
8546 " image->taint=MagickFalse");
8547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8548 " image->gamma=%g", image->gamma);
8551 if (image->storage_class == PseudoClass &&
8552 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8553 mng_info->write_png48 || mng_info->write_png64 ||
8554 (mng_info->write_png_colortype != 1 &&
8555 mng_info->write_png_colortype != 5)))
8557 (void) SyncImage(image,exception);
8558 image->storage_class = DirectClass;
8561 if (ping_preserve_colormap == MagickFalse)
8563 if (image->storage_class != PseudoClass && image->colormap != NULL)
8565 /* Free the bogus colormap; it can cause trouble later */
8566 if (logging != MagickFalse)
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Freeing bogus colormap");
8569 (void) RelinquishMagickMemory(image->colormap);
8570 image->colormap=NULL;
8574 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8575 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8578 Sometimes we get PseudoClass images whose RGB values don't match
8579 the colors in the colormap. This code syncs the RGB values.
8581 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8582 (void) SyncImage(image,exception);
8584 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8585 if (image->depth > 8)
8587 if (logging != MagickFalse)
8588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8589 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8595 /* Respect the -depth option */
8596 if (image->depth < 4)
8601 if (image->depth > 2)
8603 /* Scale to 4-bit */
8604 LBR04PacketRGBO(image->background_color);
8606 for (y=0; y < (ssize_t) image->rows; y++)
8608 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8610 if (r == (Quantum *) NULL)
8613 for (x=0; x < (ssize_t) image->columns; x++)
8616 r+=GetPixelChannels(image);
8619 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8623 if (image->storage_class == PseudoClass && image->colormap != NULL)
8625 for (i=0; i < (ssize_t) image->colors; i++)
8627 LBR04PacketRGBO(image->colormap[i]);
8631 else if (image->depth > 1)
8633 /* Scale to 2-bit */
8634 LBR02PacketRGBO(image->background_color);
8636 for (y=0; y < (ssize_t) image->rows; y++)
8638 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8640 if (r == (Quantum *) NULL)
8643 for (x=0; x < (ssize_t) image->columns; x++)
8646 r+=GetPixelChannels(image);
8649 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8653 if (image->storage_class == PseudoClass && image->colormap != NULL)
8655 for (i=0; i < (ssize_t) image->colors; i++)
8657 LBR02PacketRGBO(image->colormap[i]);
8663 /* Scale to 1-bit */
8664 LBR01PacketRGBO(image->background_color);
8666 for (y=0; y < (ssize_t) image->rows; y++)
8668 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8670 if (r == (Quantum *) NULL)
8673 for (x=0; x < (ssize_t) image->columns; x++)
8676 r+=GetPixelChannels(image);
8679 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8683 if (image->storage_class == PseudoClass && image->colormap != NULL)
8685 for (i=0; i < (ssize_t) image->colors; i++)
8687 LBR01PacketRGBO(image->colormap[i]);
8693 /* To do: set to next higher multiple of 8 */
8694 if (image->depth < 8)
8697 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8698 /* PNG does not handle depths greater than 16 so reduce it even
8701 if (image->depth > 8)
8705 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8706 if (image->depth > 8)
8708 /* To do: fill low byte properly */
8712 if (image->depth == 16 && mng_info->write_png_depth != 16)
8713 if (mng_info->write_png8 ||
8714 LosslessReduceDepthOK(image,exception) != MagickFalse)
8718 image_colors = (int) image->colors;
8719 number_opaque = (int) image->colors;
8720 number_transparent = 0;
8721 number_semitransparent = 0;
8723 if (mng_info->write_png_colortype &&
8724 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8725 mng_info->write_png_colortype < 4 &&
8726 image->alpha_trait == UndefinedPixelTrait)))
8728 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8729 * are not going to need the result.
8731 if (mng_info->write_png_colortype == 1 ||
8732 mng_info->write_png_colortype == 5)
8733 ping_have_color=MagickFalse;
8735 if (image->alpha_trait != UndefinedPixelTrait)
8737 number_transparent = 2;
8738 number_semitransparent = 1;
8742 if (mng_info->write_png_colortype < 7)
8746 * Normally we run this just once, but in the case of writing PNG8
8747 * we reduce the transparency to binary and run again, then if there
8748 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8749 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8750 * palette. Then (To do) we take care of a final reduction that is only
8751 * needed if there are still 256 colors present and one of them has both
8752 * transparent and opaque instances.
8755 tried_332 = MagickFalse;
8756 tried_333 = MagickFalse;
8757 tried_444 = MagickFalse;
8762 * Sometimes we get DirectClass images that have 256 colors or fewer.
8763 * This code will build a colormap.
8765 * Also, sometimes we get PseudoClass images with an out-of-date
8766 * colormap. This code will replace the colormap with a new one.
8767 * Sometimes we get PseudoClass images that have more than 256 colors.
8768 * This code will delete the colormap and change the image to
8771 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8772 * even though it sometimes contains left-over non-opaque values.
8774 * Also we gather some information (number of opaque, transparent,
8775 * and semitransparent pixels, and whether the image has any non-gray
8776 * pixels or only black-and-white pixels) that we might need later.
8778 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8779 * we need to check for bogus non-opaque values, at least.
8787 semitransparent[260],
8790 register const Quantum
8797 if (logging != MagickFalse)
8798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8799 " Enter BUILD_PALETTE:");
8801 if (logging != MagickFalse)
8803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8804 " image->columns=%.20g",(double) image->columns);
8805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8806 " image->rows=%.20g",(double) image->rows);
8807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8808 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810 " image->depth=%.20g",(double) image->depth);
8812 if (image->storage_class == PseudoClass && image->colormap != NULL)
8814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8815 " Original colormap:");
8816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817 " i (red,green,blue,alpha)");
8819 for (i=0; i < 256; i++)
8821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8822 " %d (%d,%d,%d,%d)",
8824 (int) image->colormap[i].red,
8825 (int) image->colormap[i].green,
8826 (int) image->colormap[i].blue,
8827 (int) image->colormap[i].alpha);
8830 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8835 " %d (%d,%d,%d,%d)",
8837 (int) image->colormap[i].red,
8838 (int) image->colormap[i].green,
8839 (int) image->colormap[i].blue,
8840 (int) image->colormap[i].alpha);
8845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8846 " image->colors=%d",(int) image->colors);
8848 if (image->colors == 0)
8849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8850 " (zero means unknown)");
8852 if (ping_preserve_colormap == MagickFalse)
8853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854 " Regenerate the colormap");
8859 number_semitransparent = 0;
8860 number_transparent = 0;
8862 for (y=0; y < (ssize_t) image->rows; y++)
8864 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8866 if (q == (Quantum *) NULL)
8869 for (x=0; x < (ssize_t) image->columns; x++)
8871 if (image->alpha_trait == UndefinedPixelTrait ||
8872 GetPixelAlpha(image,q) == OpaqueAlpha)
8874 if (number_opaque < 259)
8876 if (number_opaque == 0)
8878 GetPixelInfoPixel(image, q, opaque);
8879 opaque[0].alpha=OpaqueAlpha;
8883 for (i=0; i< (ssize_t) number_opaque; i++)
8885 if (Magick_png_color_equal(image,q,opaque+i))
8889 if (i == (ssize_t) number_opaque && number_opaque < 259)
8892 GetPixelInfoPixel(image, q, opaque+i);
8893 opaque[i].alpha=OpaqueAlpha;
8897 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8899 if (number_transparent < 259)
8901 if (number_transparent == 0)
8903 GetPixelInfoPixel(image, q, transparent);
8904 ping_trans_color.red=(unsigned short)
8905 GetPixelRed(image,q);
8906 ping_trans_color.green=(unsigned short)
8907 GetPixelGreen(image,q);
8908 ping_trans_color.blue=(unsigned short)
8909 GetPixelBlue(image,q);
8910 ping_trans_color.gray=(unsigned short)
8911 GetPixelGray(image,q);
8912 number_transparent = 1;
8915 for (i=0; i< (ssize_t) number_transparent; i++)
8917 if (Magick_png_color_equal(image,q,transparent+i))
8921 if (i == (ssize_t) number_transparent &&
8922 number_transparent < 259)
8924 number_transparent++;
8925 GetPixelInfoPixel(image,q,transparent+i);
8931 if (number_semitransparent < 259)
8933 if (number_semitransparent == 0)
8935 GetPixelInfoPixel(image,q,semitransparent);
8936 number_semitransparent = 1;
8939 for (i=0; i< (ssize_t) number_semitransparent; i++)
8941 if (Magick_png_color_equal(image,q,semitransparent+i)
8942 && GetPixelAlpha(image,q) ==
8943 semitransparent[i].alpha)
8947 if (i == (ssize_t) number_semitransparent &&
8948 number_semitransparent < 259)
8950 number_semitransparent++;
8951 GetPixelInfoPixel(image, q, semitransparent+i);
8955 q+=GetPixelChannels(image);
8959 if (mng_info->write_png8 == MagickFalse &&
8960 ping_exclude_bKGD == MagickFalse)
8962 /* Add the background color to the palette, if it
8963 * isn't already there.
8965 if (logging != MagickFalse)
8967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8968 " Check colormap for background (%d,%d,%d)",
8969 (int) image->background_color.red,
8970 (int) image->background_color.green,
8971 (int) image->background_color.blue);
8973 for (i=0; i<number_opaque; i++)
8975 if (opaque[i].red == image->background_color.red &&
8976 opaque[i].green == image->background_color.green &&
8977 opaque[i].blue == image->background_color.blue)
8980 if (number_opaque < 259 && i == number_opaque)
8982 opaque[i] = image->background_color;
8983 ping_background.index = i;
8985 if (logging != MagickFalse)
8987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988 " background_color index is %d",(int) i);
8992 else if (logging != MagickFalse)
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994 " No room in the colormap to add background color");
8997 image_colors=number_opaque+number_transparent+number_semitransparent;
8999 if (logging != MagickFalse)
9001 if (image_colors > 256)
9002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9003 " image has more than 256 colors");
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " image has %d colors",image_colors);
9010 if (ping_preserve_colormap != MagickFalse)
9013 if (mng_info->write_png_colortype != 7) /* We won't need this info */
9015 ping_have_color=MagickFalse;
9016 ping_have_non_bw=MagickFalse;
9018 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 "incompatible colorspace");
9022 ping_have_color=MagickTrue;
9023 ping_have_non_bw=MagickTrue;
9026 if(image_colors > 256)
9028 for (y=0; y < (ssize_t) image->rows; y++)
9030 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9032 if (q == (Quantum *) NULL)
9036 for (x=0; x < (ssize_t) image->columns; x++)
9038 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9039 GetPixelRed(image,s) != GetPixelBlue(image,s))
9041 ping_have_color=MagickTrue;
9042 ping_have_non_bw=MagickTrue;
9045 s+=GetPixelChannels(image);
9048 if (ping_have_color != MagickFalse)
9051 /* Worst case is black-and-white; we are looking at every
9055 if (ping_have_non_bw == MagickFalse)
9058 for (x=0; x < (ssize_t) image->columns; x++)
9060 if (GetPixelRed(image,s) != 0 &&
9061 GetPixelRed(image,s) != QuantumRange)
9063 ping_have_non_bw=MagickTrue;
9066 s+=GetPixelChannels(image);
9073 if (image_colors < 257)
9079 * Initialize image colormap.
9082 if (logging != MagickFalse)
9083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084 " Sort the new colormap");
9086 /* Sort palette, transparent first */;
9090 for (i=0; i<number_transparent; i++)
9091 colormap[n++] = transparent[i];
9093 for (i=0; i<number_semitransparent; i++)
9094 colormap[n++] = semitransparent[i];
9096 for (i=0; i<number_opaque; i++)
9097 colormap[n++] = opaque[i];
9099 ping_background.index +=
9100 (number_transparent + number_semitransparent);
9102 /* image_colors < 257; search the colormap instead of the pixels
9103 * to get ping_have_color and ping_have_non_bw
9107 if (ping_have_color == MagickFalse)
9109 if (colormap[i].red != colormap[i].green ||
9110 colormap[i].red != colormap[i].blue)
9112 ping_have_color=MagickTrue;
9113 ping_have_non_bw=MagickTrue;
9118 if (ping_have_non_bw == MagickFalse)
9120 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9121 ping_have_non_bw=MagickTrue;
9125 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9126 (number_transparent == 0 && number_semitransparent == 0)) &&
9127 (((mng_info->write_png_colortype-1) ==
9128 PNG_COLOR_TYPE_PALETTE) ||
9129 (mng_info->write_png_colortype == 0)))
9131 if (logging != MagickFalse)
9133 if (n != (ssize_t) image_colors)
9134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135 " image_colors (%d) and n (%d) don't match",
9138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9139 " AcquireImageColormap");
9142 image->colors = image_colors;
9144 if (AcquireImageColormap(image,image_colors,exception) ==
9146 ThrowWriterException(ResourceLimitError,
9147 "MemoryAllocationFailed");
9149 for (i=0; i< (ssize_t) image_colors; i++)
9150 image->colormap[i] = colormap[i];
9152 if (logging != MagickFalse)
9154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155 " image->colors=%d (%d)",
9156 (int) image->colors, image_colors);
9158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9159 " Update the pixel indexes");
9162 /* Sync the pixel indices with the new colormap */
9164 for (y=0; y < (ssize_t) image->rows; y++)
9166 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9168 if (q == (Quantum *) NULL)
9171 for (x=0; x < (ssize_t) image->columns; x++)
9173 for (i=0; i< (ssize_t) image_colors; i++)
9175 if ((image->alpha_trait == UndefinedPixelTrait ||
9176 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9177 image->colormap[i].red == GetPixelRed(image,q) &&
9178 image->colormap[i].green == GetPixelGreen(image,q) &&
9179 image->colormap[i].blue == GetPixelBlue(image,q))
9181 SetPixelIndex(image,i,q);
9185 q+=GetPixelChannels(image);
9188 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9194 if (logging != MagickFalse)
9196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197 " image->colors=%d", (int) image->colors);
9199 if (image->colormap != NULL)
9201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9202 " i (red,green,blue,alpha)");
9204 for (i=0; i < (ssize_t) image->colors; i++)
9206 if (i < 300 || i >= (ssize_t) image->colors - 10)
9208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9209 " %d (%d,%d,%d,%d)",
9211 (int) image->colormap[i].red,
9212 (int) image->colormap[i].green,
9213 (int) image->colormap[i].blue,
9214 (int) image->colormap[i].alpha);
9219 if (number_transparent < 257)
9220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9221 " number_transparent = %d",
9222 number_transparent);
9225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9226 " number_transparent > 256");
9228 if (number_opaque < 257)
9229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9230 " number_opaque = %d",
9234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9235 " number_opaque > 256");
9237 if (number_semitransparent < 257)
9238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9239 " number_semitransparent = %d",
9240 number_semitransparent);
9243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9244 " number_semitransparent > 256");
9246 if (ping_have_non_bw == MagickFalse)
9247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9248 " All pixels and the background are black or white");
9250 else if (ping_have_color == MagickFalse)
9251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9252 " All pixels and the background are gray");
9255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256 " At least one pixel or the background is non-gray");
9258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9259 " Exit BUILD_PALETTE:");
9262 if (mng_info->write_png8 == MagickFalse)
9265 /* Make any reductions necessary for the PNG8 format */
9266 if (image_colors <= 256 &&
9267 image_colors != 0 && image->colormap != NULL &&
9268 number_semitransparent == 0 &&
9269 number_transparent <= 1)
9272 /* PNG8 can't have semitransparent colors so we threshold the
9273 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9274 * transparent color so if more than one is transparent we merge
9275 * them into image->background_color.
9277 if (number_semitransparent != 0 || number_transparent > 1)
9279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9280 " Thresholding the alpha channel to binary");
9282 for (y=0; y < (ssize_t) image->rows; y++)
9284 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9286 if (r == (Quantum *) NULL)
9289 for (x=0; x < (ssize_t) image->columns; x++)
9291 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9293 SetPixelViaPixelInfo(image,&image->background_color,r);
9294 SetPixelAlpha(image,TransparentAlpha,r);
9297 SetPixelAlpha(image,OpaqueAlpha,r);
9298 r+=GetPixelChannels(image);
9301 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9304 if (image_colors != 0 && image_colors <= 256 &&
9305 image->colormap != NULL)
9306 for (i=0; i<image_colors; i++)
9307 image->colormap[i].alpha =
9308 (image->colormap[i].alpha > TransparentAlpha/2 ?
9309 TransparentAlpha : OpaqueAlpha);
9314 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9315 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9316 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9319 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9321 if (logging != MagickFalse)
9322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9323 " Quantizing the background color to 4-4-4");
9325 tried_444 = MagickTrue;
9327 LBR04PacketRGB(image->background_color);
9329 if (logging != MagickFalse)
9330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331 " Quantizing the pixel colors to 4-4-4");
9333 if (image->colormap == NULL)
9335 for (y=0; y < (ssize_t) image->rows; y++)
9337 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9339 if (r == (Quantum *) NULL)
9342 for (x=0; x < (ssize_t) image->columns; x++)
9344 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9346 r+=GetPixelChannels(image);
9349 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9354 else /* Should not reach this; colormap already exists and
9357 if (logging != MagickFalse)
9358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359 " Quantizing the colormap to 4-4-4");
9361 for (i=0; i<image_colors; i++)
9363 LBR04PacketRGB(image->colormap[i]);
9369 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9371 if (logging != MagickFalse)
9372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373 " Quantizing the background color to 3-3-3");
9375 tried_333 = MagickTrue;
9377 LBR03PacketRGB(image->background_color);
9379 if (logging != MagickFalse)
9380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9381 " Quantizing the pixel colors to 3-3-3-1");
9383 if (image->colormap == NULL)
9385 for (y=0; y < (ssize_t) image->rows; y++)
9387 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9389 if (r == (Quantum *) NULL)
9392 for (x=0; x < (ssize_t) image->columns; x++)
9394 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9396 r+=GetPixelChannels(image);
9399 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9404 else /* Should not reach this; colormap already exists and
9407 if (logging != MagickFalse)
9408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9409 " Quantizing the colormap to 3-3-3-1");
9410 for (i=0; i<image_colors; i++)
9412 LBR03PacketRGB(image->colormap[i]);
9418 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9420 if (logging != MagickFalse)
9421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422 " Quantizing the background color to 3-3-2");
9424 tried_332 = MagickTrue;
9426 /* Red and green were already done so we only quantize the blue
9430 LBR02PacketBlue(image->background_color);
9432 if (logging != MagickFalse)
9433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9434 " Quantizing the pixel colors to 3-3-2-1");
9436 if (image->colormap == NULL)
9438 for (y=0; y < (ssize_t) image->rows; y++)
9440 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9442 if (r == (Quantum *) NULL)
9445 for (x=0; x < (ssize_t) image->columns; x++)
9447 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9449 r+=GetPixelChannels(image);
9452 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9457 else /* Should not reach this; colormap already exists and
9460 if (logging != MagickFalse)
9461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9462 " Quantizing the colormap to 3-3-2-1");
9463 for (i=0; i<image_colors; i++)
9465 LBR02PacketBlue(image->colormap[i]);
9471 if (image_colors == 0 || image_colors > 256)
9473 /* Take care of special case with 256 opaque colors + 1 transparent
9474 * color. We don't need to quantize to 2-3-2-1; we only need to
9475 * eliminate one color, so we'll merge the two darkest red
9476 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9478 if (logging != MagickFalse)
9479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9480 " Merging two dark red background colors to 3-3-2-1");
9482 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9483 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9484 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9486 image->background_color.red=ScaleCharToQuantum(0x24);
9489 if (logging != MagickFalse)
9490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9491 " Merging two dark red pixel colors to 3-3-2-1");
9493 if (image->colormap == NULL)
9495 for (y=0; y < (ssize_t) image->rows; y++)
9497 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9499 if (r == (Quantum *) NULL)
9502 for (x=0; x < (ssize_t) image->columns; x++)
9504 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9505 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9506 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9507 GetPixelAlpha(image,r) == OpaqueAlpha)
9509 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9511 r+=GetPixelChannels(image);
9514 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9522 for (i=0; i<image_colors; i++)
9524 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9525 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9526 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9528 image->colormap[i].red=ScaleCharToQuantum(0x24);
9535 /* END OF BUILD_PALETTE */
9537 /* If we are excluding the tRNS chunk and there is transparency,
9538 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9541 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9542 (number_transparent != 0 || number_semitransparent != 0))
9544 unsigned int colortype=mng_info->write_png_colortype;
9546 if (ping_have_color == MagickFalse)
9547 mng_info->write_png_colortype = 5;
9550 mng_info->write_png_colortype = 7;
9552 if (colortype != 0 &&
9553 mng_info->write_png_colortype != colortype)
9554 ping_need_colortype_warning=MagickTrue;
9558 /* See if cheap transparency is possible. It is only possible
9559 * when there is a single transparent color, no semitransparent
9560 * color, and no opaque color that has the same RGB components
9561 * as the transparent color. We only need this information if
9562 * we are writing a PNG with colortype 0 or 2, and we have not
9563 * excluded the tRNS chunk.
9565 if (number_transparent == 1 &&
9566 mng_info->write_png_colortype < 4)
9568 ping_have_cheap_transparency = MagickTrue;
9570 if (number_semitransparent != 0)
9571 ping_have_cheap_transparency = MagickFalse;
9573 else if (image_colors == 0 || image_colors > 256 ||
9574 image->colormap == NULL)
9576 register const Quantum
9579 for (y=0; y < (ssize_t) image->rows; y++)
9581 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9583 if (q == (Quantum *) NULL)
9586 for (x=0; x < (ssize_t) image->columns; x++)
9588 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9589 (unsigned short) GetPixelRed(image,q) ==
9590 ping_trans_color.red &&
9591 (unsigned short) GetPixelGreen(image,q) ==
9592 ping_trans_color.green &&
9593 (unsigned short) GetPixelBlue(image,q) ==
9594 ping_trans_color.blue)
9596 ping_have_cheap_transparency = MagickFalse;
9600 q+=GetPixelChannels(image);
9603 if (ping_have_cheap_transparency == MagickFalse)
9609 /* Assuming that image->colormap[0] is the one transparent color
9610 * and that all others are opaque.
9612 if (image_colors > 1)
9613 for (i=1; i<image_colors; i++)
9614 if (image->colormap[i].red == image->colormap[0].red &&
9615 image->colormap[i].green == image->colormap[0].green &&
9616 image->colormap[i].blue == image->colormap[0].blue)
9618 ping_have_cheap_transparency = MagickFalse;
9623 if (logging != MagickFalse)
9625 if (ping_have_cheap_transparency == MagickFalse)
9626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9627 " Cheap transparency is not possible.");
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Cheap transparency is possible.");
9635 ping_have_cheap_transparency = MagickFalse;
9637 image_depth=image->depth;
9639 quantum_info = (QuantumInfo *) NULL;
9641 image_colors=(int) image->colors;
9642 image_matte=image->alpha_trait !=
9643 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9645 if (mng_info->write_png_colortype < 5)
9646 mng_info->IsPalette=image->storage_class == PseudoClass &&
9647 image_colors <= 256 && image->colormap != NULL;
9649 mng_info->IsPalette = MagickFalse;
9651 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9652 (image->colors == 0 || image->colormap == NULL))
9654 image_info=DestroyImageInfo(image_info);
9655 image=DestroyImage(image);
9656 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9657 "Cannot write PNG8 or color-type 3; colormap is NULL",
9658 "`%s'",IMimage->filename);
9659 return(MagickFalse);
9663 Allocate the PNG structures
9665 #ifdef PNG_USER_MEM_SUPPORTED
9666 error_info.image=image;
9667 error_info.exception=exception;
9668 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9669 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9670 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9673 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9674 MagickPNGErrorHandler,MagickPNGWarningHandler);
9677 if (ping == (png_struct *) NULL)
9678 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9680 ping_info=png_create_info_struct(ping);
9682 if (ping_info == (png_info *) NULL)
9684 png_destroy_write_struct(&ping,(png_info **) NULL);
9685 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9688 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9689 pixel_info=(MemoryInfo *) NULL;
9691 if (setjmp(png_jmpbuf(ping)))
9697 if (image_info->verbose)
9698 (void) printf("PNG write has failed.\n");
9700 png_destroy_write_struct(&ping,&ping_info);
9701 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9702 UnlockSemaphoreInfo(ping_semaphore);
9705 if (pixel_info != (MemoryInfo *) NULL)
9706 pixel_info=RelinquishVirtualMemory(pixel_info);
9708 if (quantum_info != (QuantumInfo *) NULL)
9709 quantum_info=DestroyQuantumInfo(quantum_info);
9711 if (ping_have_blob != MagickFalse)
9712 (void) CloseBlob(image);
9713 image_info=DestroyImageInfo(image_info);
9714 image=DestroyImage(image);
9715 return(MagickFalse);
9718 /* { For navigation to end of SETJMP-protected block. Within this
9719 * block, use png_error() instead of Throwing an Exception, to ensure
9720 * that libpng is able to clean up, and that the semaphore is unlocked.
9723 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9724 LockSemaphoreInfo(ping_semaphore);
9727 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9728 /* Allow benign errors */
9729 png_set_benign_errors(ping, 1);
9732 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9733 /* Reject images with too many rows or columns */
9734 png_set_user_limits(ping,
9735 (png_uint_32) MagickMin(0x7fffffffL,
9736 GetMagickResourceLimit(WidthResource)),
9737 (png_uint_32) MagickMin(0x7fffffffL,
9738 GetMagickResourceLimit(HeightResource)));
9739 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9742 Prepare PNG for writing.
9745 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9746 if (mng_info->write_mng)
9748 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9749 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9750 /* Disable new libpng-1.5.10 feature when writing a MNG because
9751 * zero-length PLTE is OK
9753 png_set_check_for_invalid_index (ping, 0);
9758 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9759 if (mng_info->write_mng)
9760 png_permit_empty_plte(ping,MagickTrue);
9767 ping_width=(png_uint_32) image->columns;
9768 ping_height=(png_uint_32) image->rows;
9770 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9773 if (mng_info->write_png48 || mng_info->write_png64)
9776 if (mng_info->write_png_depth != 0)
9777 image_depth=mng_info->write_png_depth;
9779 /* Adjust requested depth to next higher valid depth if necessary */
9780 if (image_depth > 8)
9783 if ((image_depth > 4) && (image_depth < 8))
9786 if (image_depth == 3)
9789 if (logging != MagickFalse)
9791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9792 " width=%.20g",(double) ping_width);
9793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794 " height=%.20g",(double) ping_height);
9795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9796 " image_matte=%.20g",(double) image->alpha_trait);
9797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " image->depth=%.20g",(double) image->depth);
9799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9800 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9803 save_image_depth=image_depth;
9804 ping_bit_depth=(png_byte) save_image_depth;
9807 #if defined(PNG_pHYs_SUPPORTED)
9808 if (ping_exclude_pHYs == MagickFalse)
9810 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9811 (!mng_info->write_mng || !mng_info->equal_physs))
9813 if (logging != MagickFalse)
9814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9815 " Setting up pHYs chunk");
9817 if (image->units == PixelsPerInchResolution)
9819 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9820 ping_pHYs_x_resolution=
9821 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9822 ping_pHYs_y_resolution=
9823 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9826 else if (image->units == PixelsPerCentimeterResolution)
9828 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9829 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9830 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9835 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9836 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9837 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9840 if (logging != MagickFalse)
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9843 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9844 (int) ping_pHYs_unit_type);
9845 ping_have_pHYs = MagickTrue;
9850 if (ping_exclude_bKGD == MagickFalse)
9852 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9858 if (ping_bit_depth == 8)
9861 if (ping_bit_depth == 4)
9864 if (ping_bit_depth == 2)
9867 if (ping_bit_depth == 1)
9870 ping_background.red=(png_uint_16)
9871 (ScaleQuantumToShort(image->background_color.red) & mask);
9873 ping_background.green=(png_uint_16)
9874 (ScaleQuantumToShort(image->background_color.green) & mask);
9876 ping_background.blue=(png_uint_16)
9877 (ScaleQuantumToShort(image->background_color.blue) & mask);
9879 ping_background.gray=(png_uint_16) ping_background.green;
9882 if (logging != MagickFalse)
9884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9885 " Setting up bKGD chunk (1)");
9886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9887 " background_color index is %d",
9888 (int) ping_background.index);
9890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9891 " ping_bit_depth=%d",ping_bit_depth);
9894 ping_have_bKGD = MagickTrue;
9898 Select the color type.
9903 if (mng_info->IsPalette && mng_info->write_png8)
9905 /* To do: make this a function cause it's used twice, except
9906 for reducing the sample depth from 8. */
9908 number_colors=image_colors;
9910 ping_have_tRNS=MagickFalse;
9915 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9917 if (logging != MagickFalse)
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919 " Setting up PLTE chunk with %d colors (%d)",
9920 number_colors, image_colors);
9922 for (i=0; i < (ssize_t) number_colors; i++)
9924 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9925 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9926 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9927 if (logging != MagickFalse)
9928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9929 #if MAGICKCORE_QUANTUM_DEPTH == 8
9930 " %3ld (%3d,%3d,%3d)",
9932 " %5ld (%5d,%5d,%5d)",
9934 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9938 ping_have_PLTE=MagickTrue;
9939 image_depth=ping_bit_depth;
9942 if (matte != MagickFalse)
9945 Identify which colormap entry is transparent.
9947 assert(number_colors <= 256);
9948 assert(image->colormap != NULL);
9950 for (i=0; i < (ssize_t) number_transparent; i++)
9951 ping_trans_alpha[i]=0;
9954 ping_num_trans=(unsigned short) (number_transparent +
9955 number_semitransparent);
9957 if (ping_num_trans == 0)
9958 ping_have_tRNS=MagickFalse;
9961 ping_have_tRNS=MagickTrue;
9964 if (ping_exclude_bKGD == MagickFalse)
9967 * Identify which colormap entry is the background color.
9970 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9971 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9974 ping_background.index=(png_byte) i;
9976 if (logging != MagickFalse)
9978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9979 " background_color index is %d",
9980 (int) ping_background.index);
9983 } /* end of write_png8 */
9985 else if (mng_info->write_png_colortype == 1)
9987 image_matte=MagickFalse;
9988 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9991 else if (mng_info->write_png24 || mng_info->write_png48 ||
9992 mng_info->write_png_colortype == 3)
9994 image_matte=MagickFalse;
9995 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9998 else if (mng_info->write_png32 || mng_info->write_png64 ||
9999 mng_info->write_png_colortype == 7)
10001 image_matte=MagickTrue;
10002 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10005 else /* mng_info->write_pngNN not specified */
10007 image_depth=ping_bit_depth;
10009 if (mng_info->write_png_colortype != 0)
10011 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10013 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10014 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10015 image_matte=MagickTrue;
10018 image_matte=MagickFalse;
10020 if (logging != MagickFalse)
10021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10022 " PNG colortype %d was specified:",(int) ping_color_type);
10025 else /* write_png_colortype not specified */
10027 if (logging != MagickFalse)
10028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029 " Selecting PNG colortype:");
10031 ping_color_type=(png_byte) ((matte != MagickFalse)?
10032 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10034 if (image_info->type == TrueColorType)
10036 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10037 image_matte=MagickFalse;
10040 if (image_info->type == TrueColorAlphaType)
10042 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10043 image_matte=MagickTrue;
10046 if (image_info->type == PaletteType ||
10047 image_info->type == PaletteAlphaType)
10048 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10050 if (mng_info->write_png_colortype == 0 &&
10051 image_info->type == UndefinedType)
10053 if (ping_have_color == MagickFalse)
10055 if (image_matte == MagickFalse)
10057 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10058 image_matte=MagickFalse;
10063 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10064 image_matte=MagickTrue;
10069 if (image_matte == MagickFalse)
10071 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10072 image_matte=MagickFalse;
10077 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10078 image_matte=MagickTrue;
10085 if (logging != MagickFalse)
10086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10087 " Selected PNG colortype=%d",ping_color_type);
10089 if (ping_bit_depth < 8)
10091 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10092 ping_color_type == PNG_COLOR_TYPE_RGB ||
10093 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10097 old_bit_depth=ping_bit_depth;
10099 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10101 if (image->alpha_trait == UndefinedPixelTrait &&
10102 ping_have_non_bw == MagickFalse)
10106 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10111 if (image->colors == 0)
10114 png_error(ping,"image has 0 colors");
10117 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10118 ping_bit_depth <<= 1;
10121 if (logging != MagickFalse)
10123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10124 " Number of colors: %.20g",(double) image_colors);
10126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10127 " Tentative PNG bit depth: %d",ping_bit_depth);
10130 if (ping_bit_depth < (int) mng_info->write_png_depth)
10131 ping_bit_depth = mng_info->write_png_depth;
10134 image_depth=ping_bit_depth;
10136 if (logging != MagickFalse)
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10139 " Tentative PNG color type: %s (%.20g)",
10140 PngColorTypeToString(ping_color_type),
10141 (double) ping_color_type);
10143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144 " image_info->type: %.20g",(double) image_info->type);
10146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10147 " image_depth: %.20g",(double) image_depth);
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10151 " image->depth: %.20g",(double) image->depth);
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10157 if (matte != MagickFalse)
10159 if (mng_info->IsPalette)
10161 if (mng_info->write_png_colortype == 0)
10163 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10165 if (ping_have_color != MagickFalse)
10166 ping_color_type=PNG_COLOR_TYPE_RGBA;
10170 * Determine if there is any transparent color.
10172 if (number_transparent + number_semitransparent == 0)
10175 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10178 image_matte=MagickFalse;
10180 if (mng_info->write_png_colortype == 0)
10181 ping_color_type&=0x03;
10191 if (ping_bit_depth == 8)
10194 if (ping_bit_depth == 4)
10197 if (ping_bit_depth == 2)
10200 if (ping_bit_depth == 1)
10203 ping_trans_color.red=(png_uint_16)
10204 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10206 ping_trans_color.green=(png_uint_16)
10207 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10209 ping_trans_color.blue=(png_uint_16)
10210 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10212 ping_trans_color.gray=(png_uint_16)
10213 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10214 image->colormap)) & mask);
10216 ping_trans_color.index=(png_byte) 0;
10218 ping_have_tRNS=MagickTrue;
10221 if (ping_have_tRNS != MagickFalse)
10224 * Determine if there is one and only one transparent color
10225 * and if so if it is fully transparent.
10227 if (ping_have_cheap_transparency == MagickFalse)
10228 ping_have_tRNS=MagickFalse;
10231 if (ping_have_tRNS != MagickFalse)
10233 if (mng_info->write_png_colortype == 0)
10234 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10236 if (image_depth == 8)
10238 ping_trans_color.red&=0xff;
10239 ping_trans_color.green&=0xff;
10240 ping_trans_color.blue&=0xff;
10241 ping_trans_color.gray&=0xff;
10247 if (image_depth == 8)
10249 ping_trans_color.red&=0xff;
10250 ping_trans_color.green&=0xff;
10251 ping_trans_color.blue&=0xff;
10252 ping_trans_color.gray&=0xff;
10259 if (ping_have_tRNS != MagickFalse)
10260 image_matte=MagickFalse;
10262 if ((mng_info->IsPalette) &&
10263 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10264 ping_have_color == MagickFalse &&
10265 (image_matte == MagickFalse || image_depth >= 8))
10269 if (image_matte != MagickFalse)
10270 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10272 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10274 ping_color_type=PNG_COLOR_TYPE_GRAY;
10276 if (save_image_depth == 16 && image_depth == 8)
10278 if (logging != MagickFalse)
10280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10281 " Scaling ping_trans_color (0)");
10283 ping_trans_color.gray*=0x0101;
10287 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10288 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10290 if ((image_colors == 0) ||
10291 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10292 image_colors=(int) (one << image_depth);
10294 if (image_depth > 8)
10300 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10302 if(!mng_info->write_png_depth)
10306 while ((int) (one << ping_bit_depth)
10307 < (ssize_t) image_colors)
10308 ping_bit_depth <<= 1;
10312 else if (ping_color_type ==
10313 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10314 mng_info->IsPalette)
10316 /* Check if grayscale is reducible */
10319 depth_4_ok=MagickTrue,
10320 depth_2_ok=MagickTrue,
10321 depth_1_ok=MagickTrue;
10323 for (i=0; i < (ssize_t) image_colors; i++)
10328 intensity=ScaleQuantumToChar(image->colormap[i].red);
10330 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10331 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10332 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10333 depth_2_ok=depth_1_ok=MagickFalse;
10334 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10335 depth_1_ok=MagickFalse;
10338 if (depth_1_ok && mng_info->write_png_depth <= 1)
10341 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10344 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10349 image_depth=ping_bit_depth;
10354 if (mng_info->IsPalette)
10356 number_colors=image_colors;
10358 if (image_depth <= 8)
10363 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10365 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10367 for (i=0; i < (ssize_t) number_colors; i++)
10369 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10371 ScaleQuantumToChar(image->colormap[i].green);
10372 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10375 if (logging != MagickFalse)
10376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377 " Setting up PLTE chunk with %d colors",
10380 ping_have_PLTE=MagickTrue;
10383 /* color_type is PNG_COLOR_TYPE_PALETTE */
10384 if (mng_info->write_png_depth == 0)
10392 while ((one << ping_bit_depth) < (size_t) number_colors)
10393 ping_bit_depth <<= 1;
10398 if (matte != MagickFalse)
10401 * Set up trans_colors array.
10403 assert(number_colors <= 256);
10405 ping_num_trans=(unsigned short) (number_transparent +
10406 number_semitransparent);
10408 if (ping_num_trans == 0)
10409 ping_have_tRNS=MagickFalse;
10413 if (logging != MagickFalse)
10415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416 " Scaling ping_trans_color (1)");
10418 ping_have_tRNS=MagickTrue;
10420 for (i=0; i < ping_num_trans; i++)
10422 ping_trans_alpha[i]= (png_byte)
10423 ScaleQuantumToChar(image->colormap[i].alpha);
10433 if (image_depth < 8)
10436 if ((save_image_depth == 16) && (image_depth == 8))
10438 if (logging != MagickFalse)
10440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10441 " Scaling ping_trans_color from (%d,%d,%d)",
10442 (int) ping_trans_color.red,
10443 (int) ping_trans_color.green,
10444 (int) ping_trans_color.blue);
10447 ping_trans_color.red*=0x0101;
10448 ping_trans_color.green*=0x0101;
10449 ping_trans_color.blue*=0x0101;
10450 ping_trans_color.gray*=0x0101;
10452 if (logging != MagickFalse)
10454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10456 (int) ping_trans_color.red,
10457 (int) ping_trans_color.green,
10458 (int) ping_trans_color.blue);
10463 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10464 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10467 Adjust background and transparency samples in sub-8-bit grayscale files.
10469 if (ping_bit_depth < 8 && ping_color_type ==
10470 PNG_COLOR_TYPE_GRAY)
10478 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10480 if (ping_exclude_bKGD == MagickFalse)
10483 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10484 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10485 &image->background_color))) +.5)));
10487 if (logging != MagickFalse)
10488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10489 " Setting up bKGD chunk (2)");
10490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10491 " background_color index is %d",
10492 (int) ping_background.index);
10494 ping_have_bKGD = MagickTrue;
10497 if (logging != MagickFalse)
10498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10499 " Scaling ping_trans_color.gray from %d",
10500 (int)ping_trans_color.gray);
10502 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10503 ping_trans_color.gray)+.5);
10505 if (logging != MagickFalse)
10506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10507 " to %d", (int)ping_trans_color.gray);
10510 if (ping_exclude_bKGD == MagickFalse)
10512 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10515 Identify which colormap entry is the background color.
10518 number_colors=image_colors;
10520 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10521 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10524 ping_background.index=(png_byte) i;
10526 if (logging != MagickFalse)
10528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " Setting up bKGD chunk with index=%d",(int) i);
10532 if (i < (ssize_t) number_colors)
10534 ping_have_bKGD = MagickTrue;
10536 if (logging != MagickFalse)
10538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10539 " background =(%d,%d,%d)",
10540 (int) ping_background.red,
10541 (int) ping_background.green,
10542 (int) ping_background.blue);
10546 else /* Can't happen */
10548 if (logging != MagickFalse)
10549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10550 " No room in PLTE to add bKGD color");
10551 ping_have_bKGD = MagickFalse;
10556 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10561 Initialize compression level and filtering.
10563 if (logging != MagickFalse)
10565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " Setting up deflate compression");
10568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569 " Compression buffer size: 32768");
10572 png_set_compression_buffer_size(ping,32768L);
10574 if (logging != MagickFalse)
10575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576 " Compression mem level: 9");
10578 png_set_compression_mem_level(ping, 9);
10580 /* Untangle the "-quality" setting:
10582 Undefined is 0; the default is used.
10587 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10588 zlib default compression level
10590 1-9: the zlib compression level
10594 0-4: the PNG filter method
10596 5: libpng adaptive filtering if compression level > 5
10597 libpng filter type "none" if compression level <= 5
10598 or if image is grayscale or palette
10600 6: libpng adaptive filtering
10602 7: "LOCO" filtering (intrapixel differing) if writing
10603 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10604 and earlier because of a missing "else".
10606 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10607 filtering. Unused prior to IM-6.7.0-10, was same as 6
10609 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10610 Unused prior to IM-6.7.0-10, was same as 6
10612 Note that using the -quality option, not all combinations of
10613 PNG filter type, zlib compression level, and zlib compression
10614 strategy are possible. This will be addressed soon in a
10615 release that accomodates "-define png:compression-strategy", etc.
10619 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10620 image_info->quality;
10624 if (mng_info->write_png_compression_strategy == 0)
10625 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10628 else if (mng_info->write_png_compression_level == 0)
10633 level=(int) MagickMin((ssize_t) quality/10,9);
10635 mng_info->write_png_compression_level = level+1;
10638 if (mng_info->write_png_compression_strategy == 0)
10640 if ((quality %10) == 8 || (quality %10) == 9)
10641 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10642 mng_info->write_png_compression_strategy=Z_RLE+1;
10644 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10648 if (mng_info->write_png_compression_filter == 0)
10649 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10651 if (logging != MagickFalse)
10653 if (mng_info->write_png_compression_level)
10654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 " Compression level: %d",
10656 (int) mng_info->write_png_compression_level-1);
10658 if (mng_info->write_png_compression_strategy)
10659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660 " Compression strategy: %d",
10661 (int) mng_info->write_png_compression_strategy-1);
10663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10664 " Setting up filtering");
10666 if (mng_info->write_png_compression_filter == 6)
10667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10668 " Base filter method: ADAPTIVE");
10669 else if (mng_info->write_png_compression_filter == 0 ||
10670 mng_info->write_png_compression_filter == 1)
10671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672 " Base filter method: NONE");
10674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10675 " Base filter method: %d",
10676 (int) mng_info->write_png_compression_filter-1);
10679 if (mng_info->write_png_compression_level != 0)
10680 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10682 if (mng_info->write_png_compression_filter == 6)
10684 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10685 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10687 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10689 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10691 else if (mng_info->write_png_compression_filter == 7 ||
10692 mng_info->write_png_compression_filter == 10)
10693 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10695 else if (mng_info->write_png_compression_filter == 8)
10697 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10698 if (mng_info->write_mng)
10700 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10701 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10702 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10705 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10708 else if (mng_info->write_png_compression_filter == 9)
10709 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10711 else if (mng_info->write_png_compression_filter != 0)
10712 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10713 mng_info->write_png_compression_filter-1);
10715 if (mng_info->write_png_compression_strategy != 0)
10716 png_set_compression_strategy(ping,
10717 mng_info->write_png_compression_strategy-1);
10719 ping_interlace_method=image_info->interlace != NoInterlace;
10721 if (mng_info->write_mng)
10722 png_set_sig_bytes(ping,8);
10724 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10726 if (mng_info->write_png_colortype != 0)
10728 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10729 if (ping_have_color != MagickFalse)
10731 ping_color_type = PNG_COLOR_TYPE_RGB;
10733 if (ping_bit_depth < 8)
10737 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10738 if (ping_have_color != MagickFalse)
10739 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10742 if (ping_need_colortype_warning != MagickFalse ||
10743 ((mng_info->write_png_depth &&
10744 (int) mng_info->write_png_depth != ping_bit_depth) ||
10745 (mng_info->write_png_colortype &&
10746 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10747 mng_info->write_png_colortype != 7 &&
10748 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10750 if (logging != MagickFalse)
10752 if (ping_need_colortype_warning != MagickFalse)
10754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10755 " Image has transparency but tRNS chunk was excluded");
10758 if (mng_info->write_png_depth)
10760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10761 " Defined png:bit-depth=%u, Computed depth=%u",
10762 mng_info->write_png_depth,
10766 if (mng_info->write_png_colortype)
10768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10769 " Defined png:color-type=%u, Computed color type=%u",
10770 mng_info->write_png_colortype-1,
10776 "Cannot write image with defined png:bit-depth or png:color-type.");
10779 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10781 /* Add an opaque matte channel */
10782 image->alpha_trait = BlendPixelTrait;
10783 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10785 if (logging != MagickFalse)
10786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10787 " Added an opaque matte channel");
10790 if (number_transparent != 0 || number_semitransparent != 0)
10792 if (ping_color_type < 4)
10794 ping_have_tRNS=MagickTrue;
10795 if (logging != MagickFalse)
10796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10797 " Setting ping_have_tRNS=MagickTrue.");
10801 if (logging != MagickFalse)
10802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803 " Writing PNG header chunks");
10805 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10806 ping_bit_depth,ping_color_type,
10807 ping_interlace_method,ping_compression_method,
10808 ping_filter_method);
10810 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10812 png_set_PLTE(ping,ping_info,palette,number_colors);
10814 if (logging != MagickFalse)
10816 for (i=0; i< (ssize_t) number_colors; i++)
10818 if (i < ping_num_trans)
10819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10820 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10822 (int) palette[i].red,
10823 (int) palette[i].green,
10824 (int) palette[i].blue,
10826 (int) ping_trans_alpha[i]);
10828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10829 " PLTE[%d] = (%d,%d,%d)",
10831 (int) palette[i].red,
10832 (int) palette[i].green,
10833 (int) palette[i].blue);
10838 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10839 if (ping_exclude_sRGB != MagickFalse ||
10840 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10842 if ((ping_exclude_tEXt == MagickFalse ||
10843 ping_exclude_zTXt == MagickFalse) &&
10844 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10846 ResetImageProfileIterator(image);
10847 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10849 profile=GetImageProfile(image,name);
10851 if (profile != (StringInfo *) NULL)
10853 #ifdef PNG_WRITE_iCCP_SUPPORTED
10854 if ((LocaleCompare(name,"ICC") == 0) ||
10855 (LocaleCompare(name,"ICM") == 0))
10857 ping_have_iCCP = MagickTrue;
10858 if (ping_exclude_iCCP == MagickFalse)
10860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10861 " Setting up iCCP chunk");
10863 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10864 #if (PNG_LIBPNG_VER < 10500)
10865 (png_charp) GetStringInfoDatum(profile),
10867 (const png_byte *) GetStringInfoDatum(profile),
10869 (png_uint_32) GetStringInfoLength(profile));
10873 /* Do not write hex-encoded ICC chunk */
10874 name=GetNextImageProfile(image);
10878 #endif /* WRITE_iCCP */
10880 if (LocaleCompare(name,"exif") == 0)
10882 /* Do not write hex-encoded ICC chunk; we will
10883 write it later as an eXIf chunk */
10884 name=GetNextImageProfile(image);
10888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10889 " Setting up zTXt chunk with uuencoded %s profile",
10891 Magick_png_write_raw_profile(image_info,ping,ping_info,
10892 (unsigned char *) name,(unsigned char *) name,
10893 GetStringInfoDatum(profile),
10894 (png_uint_32) GetStringInfoLength(profile));
10896 name=GetNextImageProfile(image);
10901 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10902 if ((mng_info->have_write_global_srgb == 0) &&
10903 ping_have_iCCP != MagickTrue &&
10904 (ping_have_sRGB != MagickFalse ||
10905 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10907 if (ping_exclude_sRGB == MagickFalse)
10910 Note image rendering intent.
10912 if (logging != MagickFalse)
10913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10914 " Setting up sRGB chunk");
10916 (void) png_set_sRGB(ping,ping_info,(
10917 Magick_RenderingIntent_to_PNG_RenderingIntent(
10918 image->rendering_intent)));
10920 ping_have_sRGB = MagickTrue;
10924 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10927 if (ping_exclude_gAMA == MagickFalse &&
10928 ping_have_iCCP == MagickFalse &&
10929 ping_have_sRGB == MagickFalse &&
10930 (ping_exclude_sRGB == MagickFalse ||
10931 (image->gamma < .45 || image->gamma > .46)))
10933 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10937 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10939 if (logging != MagickFalse)
10940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10941 " Setting up gAMA chunk");
10943 png_set_gAMA(ping,ping_info,image->gamma);
10947 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10949 if ((mng_info->have_write_global_chrm == 0) &&
10950 (image->chromaticity.red_primary.x != 0.0))
10953 Note image chromaticity.
10954 Note: if cHRM+gAMA == sRGB write sRGB instead.
10962 wp=image->chromaticity.white_point;
10963 rp=image->chromaticity.red_primary;
10964 gp=image->chromaticity.green_primary;
10965 bp=image->chromaticity.blue_primary;
10967 if (logging != MagickFalse)
10968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10969 " Setting up cHRM chunk");
10971 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10977 if (ping_exclude_bKGD == MagickFalse)
10979 if (ping_have_bKGD != MagickFalse)
10981 png_set_bKGD(ping,ping_info,&ping_background);
10982 if (logging != MagickFalse)
10984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10985 " Setting up bKGD chunk");
10986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10987 " background color = (%d,%d,%d)",
10988 (int) ping_background.red,
10989 (int) ping_background.green,
10990 (int) ping_background.blue);
10991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10992 " index = %d, gray=%d",
10993 (int) ping_background.index,
10994 (int) ping_background.gray);
10999 if (ping_exclude_pHYs == MagickFalse)
11001 if (ping_have_pHYs != MagickFalse)
11003 png_set_pHYs(ping,ping_info,
11004 ping_pHYs_x_resolution,
11005 ping_pHYs_y_resolution,
11006 ping_pHYs_unit_type);
11008 if (logging != MagickFalse)
11010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11011 " Setting up pHYs chunk");
11012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11013 " x_resolution=%lu",
11014 (unsigned long) ping_pHYs_x_resolution);
11015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11016 " y_resolution=%lu",
11017 (unsigned long) ping_pHYs_y_resolution);
11018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11020 (unsigned long) ping_pHYs_unit_type);
11025 #if defined(PNG_tIME_SUPPORTED)
11026 if (ping_exclude_tIME == MagickFalse)
11031 if (image->taint == MagickFalse)
11033 timestamp=GetImageOption(image_info,"png:tIME");
11035 if (timestamp == (const char *) NULL)
11036 timestamp=GetImageProperty(image,"png:tIME",exception);
11041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11042 " Reset tIME in tainted image");
11044 timestamp=GetImageProperty(image,"date:modify",exception);
11047 if (timestamp != (const char *) NULL)
11048 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11052 if (mng_info->need_blob != MagickFalse)
11054 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11056 png_error(ping,"WriteBlob Failed");
11058 ping_have_blob=MagickTrue;
11061 png_write_info_before_PLTE(ping, ping_info);
11063 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11065 if (logging != MagickFalse)
11067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11071 if (ping_color_type == 3)
11072 (void) png_set_tRNS(ping, ping_info,
11079 (void) png_set_tRNS(ping, ping_info,
11082 &ping_trans_color);
11084 if (logging != MagickFalse)
11086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11087 " tRNS color =(%d,%d,%d)",
11088 (int) ping_trans_color.red,
11089 (int) ping_trans_color.green,
11090 (int) ping_trans_color.blue);
11095 png_write_info(ping,ping_info);
11097 /* write orNT if image->orientation is defined */
11098 if (image->orientation != UndefinedOrientation)
11102 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11103 PNGType(chunk,mng_orNT);
11104 LogPNGChunk(logging,mng_orNT,1L);
11105 /* PNG uses Exif orientation values */
11106 chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11107 (void) WriteBlob(image,5,chunk);
11108 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11111 ping_wrote_caNv = MagickFalse;
11113 /* write caNv chunk */
11114 if (ping_exclude_caNv == MagickFalse)
11116 if ((image->page.width != 0 && image->page.width != image->columns) ||
11117 (image->page.height != 0 && image->page.height != image->rows) ||
11118 image->page.x != 0 || image->page.y != 0)
11123 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11124 PNGType(chunk,mng_caNv);
11125 LogPNGChunk(logging,mng_caNv,16L);
11126 PNGLong(chunk+4,(png_uint_32) image->page.width);
11127 PNGLong(chunk+8,(png_uint_32) image->page.height);
11128 PNGsLong(chunk+12,(png_int_32) image->page.x);
11129 PNGsLong(chunk+16,(png_int_32) image->page.y);
11130 (void) WriteBlob(image,20,chunk);
11131 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11132 ping_wrote_caNv = MagickTrue;
11136 #if defined(PNG_oFFs_SUPPORTED)
11137 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11139 if (image->page.x || image->page.y)
11141 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11142 (png_int_32) image->page.y, 0);
11144 if (logging != MagickFalse)
11145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11146 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11147 (int) image->page.x, (int) image->page.y);
11152 #if (PNG_LIBPNG_VER == 10206)
11153 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11154 #define PNG_HAVE_IDAT 0x04
11155 ping->mode |= PNG_HAVE_IDAT;
11156 #undef PNG_HAVE_IDAT
11159 png_set_packing(ping);
11163 rowbytes=image->columns;
11164 if (image_depth > 8)
11166 switch (ping_color_type)
11168 case PNG_COLOR_TYPE_RGB:
11172 case PNG_COLOR_TYPE_GRAY_ALPHA:
11176 case PNG_COLOR_TYPE_RGBA:
11184 if (logging != MagickFalse)
11186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187 " Writing PNG image data");
11189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11192 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11193 if (pixel_info == (MemoryInfo *) NULL)
11194 png_error(ping,"Allocation of memory for pixels failed");
11195 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11198 Initialize image scanlines.
11200 quantum_info=AcquireQuantumInfo(image_info,image);
11201 if (quantum_info == (QuantumInfo *) NULL)
11202 png_error(ping,"Memory allocation for quantum_info failed");
11203 quantum_info->format=UndefinedQuantumFormat;
11204 SetQuantumDepth(image,quantum_info,image_depth);
11205 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11206 num_passes=png_set_interlace_handling(ping);
11208 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11209 !mng_info->write_png48 && !mng_info->write_png64 &&
11210 !mng_info->write_png32) &&
11211 (mng_info->IsPalette ||
11212 (image_info->type == BilevelType)) &&
11213 image_matte == MagickFalse &&
11214 ping_have_non_bw == MagickFalse)
11216 /* Palette, Bilevel, or Opaque Monochrome */
11217 register const Quantum
11220 SetQuantumDepth(image,quantum_info,8);
11221 for (pass=0; pass < num_passes; pass++)
11224 Convert PseudoClass image to a PNG monochrome image.
11226 for (y=0; y < (ssize_t) image->rows; y++)
11228 if (logging != MagickFalse && y == 0)
11229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11230 " Writing row of pixels (0)");
11232 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11234 if (p == (const Quantum *) NULL)
11237 if (mng_info->IsPalette)
11239 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11240 quantum_info,GrayQuantum,ping_pixels,exception);
11241 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11242 mng_info->write_png_depth &&
11243 mng_info->write_png_depth != old_bit_depth)
11245 /* Undo pixel scaling */
11246 for (i=0; i < (ssize_t) image->columns; i++)
11247 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11248 >> (8-old_bit_depth));
11254 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11255 quantum_info,RedQuantum,ping_pixels,exception);
11258 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11259 for (i=0; i < (ssize_t) image->columns; i++)
11260 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11263 if (logging != MagickFalse && y == 0)
11264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11265 " Writing row of pixels (1)");
11267 png_write_row(ping,ping_pixels);
11269 status=SetImageProgress(image,SaveImageTag,
11270 (MagickOffsetType) (pass * image->rows + y),
11271 num_passes * image->rows);
11273 if (status == MagickFalse)
11279 else /* Not Palette, Bilevel, or Opaque Monochrome */
11281 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11282 !mng_info->write_png48 && !mng_info->write_png64 &&
11283 !mng_info->write_png32) && (image_matte != MagickFalse ||
11284 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11285 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11287 register const Quantum
11290 for (pass=0; pass < num_passes; pass++)
11293 for (y=0; y < (ssize_t) image->rows; y++)
11295 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11297 if (p == (const Quantum *) NULL)
11300 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11302 if (mng_info->IsPalette)
11303 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11304 quantum_info,GrayQuantum,ping_pixels,exception);
11307 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11308 quantum_info,RedQuantum,ping_pixels,exception);
11310 if (logging != MagickFalse && y == 0)
11311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11312 " Writing GRAY PNG pixels (2)");
11315 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11317 if (logging != MagickFalse && y == 0)
11318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319 " Writing GRAY_ALPHA PNG pixels (2)");
11321 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11322 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11325 if (logging != MagickFalse && y == 0)
11326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11327 " Writing row of pixels (2)");
11329 png_write_row(ping,ping_pixels);
11331 status=SetImageProgress(image,SaveImageTag,
11332 (MagickOffsetType) (pass * image->rows + y),
11333 num_passes * image->rows);
11335 if (status == MagickFalse)
11343 register const Quantum
11346 for (pass=0; pass < num_passes; pass++)
11348 if ((image_depth > 8) ||
11349 mng_info->write_png24 ||
11350 mng_info->write_png32 ||
11351 mng_info->write_png48 ||
11352 mng_info->write_png64 ||
11353 (!mng_info->write_png8 && !mng_info->IsPalette))
11355 for (y=0; y < (ssize_t) image->rows; y++)
11357 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11359 if (p == (const Quantum *) NULL)
11362 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11364 if (image->storage_class == DirectClass)
11365 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11366 quantum_info,RedQuantum,ping_pixels,exception);
11369 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11370 quantum_info,GrayQuantum,ping_pixels,exception);
11373 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11375 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11376 quantum_info,GrayAlphaQuantum,ping_pixels,
11379 if (logging != MagickFalse && y == 0)
11380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11381 " Writing GRAY_ALPHA PNG pixels (3)");
11384 else if (image_matte != MagickFalse)
11385 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11386 quantum_info,RGBAQuantum,ping_pixels,exception);
11389 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11390 quantum_info,RGBQuantum,ping_pixels,exception);
11392 if (logging != MagickFalse && y == 0)
11393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394 " Writing row of pixels (3)");
11396 png_write_row(ping,ping_pixels);
11398 status=SetImageProgress(image,SaveImageTag,
11399 (MagickOffsetType) (pass * image->rows + y),
11400 num_passes * image->rows);
11402 if (status == MagickFalse)
11408 /* not ((image_depth > 8) ||
11409 mng_info->write_png24 || mng_info->write_png32 ||
11410 mng_info->write_png48 || mng_info->write_png64 ||
11411 (!mng_info->write_png8 && !mng_info->IsPalette))
11414 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11415 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11417 if (logging != MagickFalse)
11418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11419 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11421 SetQuantumDepth(image,quantum_info,8);
11425 for (y=0; y < (ssize_t) image->rows; y++)
11427 if (logging != MagickFalse && y == 0)
11428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11432 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11434 if (p == (const Quantum *) NULL)
11437 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11439 SetQuantumDepth(image,quantum_info,image->depth);
11441 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11442 quantum_info,GrayQuantum,ping_pixels,exception);
11445 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11447 if (logging != MagickFalse && y == 0)
11448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11449 " Writing GRAY_ALPHA PNG pixels (4)");
11451 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11452 quantum_info,GrayAlphaQuantum,ping_pixels,
11458 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11459 quantum_info,IndexQuantum,ping_pixels,exception);
11461 if (logging != MagickFalse && y <= 2)
11463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464 " Writing row of non-gray pixels (4)");
11466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11467 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11468 (int)ping_pixels[0],(int)ping_pixels[1]);
11471 png_write_row(ping,ping_pixels);
11473 status=SetImageProgress(image,SaveImageTag,
11474 (MagickOffsetType) (pass * image->rows + y),
11475 num_passes * image->rows);
11477 if (status == MagickFalse)
11485 if (quantum_info != (QuantumInfo *) NULL)
11486 quantum_info=DestroyQuantumInfo(quantum_info);
11488 if (logging != MagickFalse)
11490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11491 " Wrote PNG image data");
11493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11494 " Width: %.20g",(double) ping_width);
11496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11497 " Height: %.20g",(double) ping_height);
11499 if (mng_info->write_png_depth)
11501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11502 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11506 " PNG bit-depth written: %d",ping_bit_depth);
11508 if (mng_info->write_png_colortype)
11510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11511 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11515 " PNG color-type written: %d",ping_color_type);
11517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11518 " PNG Interlace method: %d",ping_interlace_method);
11521 Generate text chunks after IDAT.
11523 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11525 ResetImagePropertyIterator(image);
11526 property=GetNextImageProperty(image);
11527 while (property != (const char *) NULL)
11532 value=GetImageProperty(image,property,exception);
11534 /* Don't write any "png:" or "jpeg:" properties; those are just for
11535 * "identify" or for passing through to another JPEG
11537 if ((LocaleNCompare(property,"png:",4) != 0 &&
11538 LocaleNCompare(property,"jpeg:",5) != 0) &&
11541 /* Suppress density and units if we wrote a pHYs chunk */
11542 (ping_exclude_pHYs != MagickFalse ||
11543 LocaleCompare(property,"density") != 0 ||
11544 LocaleCompare(property,"units") != 0) &&
11546 /* Suppress the IM-generated Date:create and Date:modify */
11547 (ping_exclude_date == MagickFalse ||
11548 LocaleNCompare(property, "Date:",5) != 0))
11550 if (value != (const char *) NULL)
11553 #if PNG_LIBPNG_VER >= 10400
11554 text=(png_textp) png_malloc(ping,
11555 (png_alloc_size_t) sizeof(png_text));
11557 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11559 text[0].key=(char *) property;
11560 text[0].text=(char *) value;
11561 text[0].text_length=strlen(value);
11563 if (ping_exclude_tEXt != MagickFalse)
11564 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11566 else if (ping_exclude_zTXt != MagickFalse)
11567 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11571 text[0].compression=image_info->compression == NoCompression ||
11572 (image_info->compression == UndefinedCompression &&
11573 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11574 PNG_TEXT_COMPRESSION_zTXt ;
11577 if (logging != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580 " Setting up text chunk");
11582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11583 " keyword: '%s'",text[0].key);
11586 png_set_text(ping,ping_info,text,1);
11587 png_free(ping,text);
11590 property=GetNextImageProperty(image);
11594 /* write eXIf profile */
11595 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11600 ResetImageProfileIterator(image);
11602 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
11604 if (LocaleCompare(name,"exif") == 0)
11609 profile=GetImageProfile(image,name);
11611 if (profile != (StringInfo *) NULL)
11623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11624 " Have eXIf profile");
11626 ping_profile=CloneStringInfo(profile);
11627 data=GetStringInfoDatum(ping_profile),
11628 length=(png_uint_32) GetStringInfoLength(ping_profile);
11630 PNGType(chunk,mng_eXIf);
11633 ping_profile=DestroyStringInfo(ping_profile);
11634 break; /* otherwise crashes */
11637 /* skip the "Exif\0\0" JFIF Exif Header ID */
11640 LogPNGChunk(logging,chunk,length);
11641 (void) WriteBlobMSBULong(image,length);
11642 (void) WriteBlob(image,4,chunk);
11643 (void) WriteBlob(image,length,data+6);
11644 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11645 data+6, (uInt) length));
11646 ping_profile=DestroyStringInfo(ping_profile);
11650 name=GetNextImageProfile(image);
11654 if (logging != MagickFalse)
11655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11656 " Writing PNG end info");
11658 png_write_end(ping,ping_info);
11660 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11662 if (mng_info->page.x || mng_info->page.y ||
11663 (ping_width != mng_info->page.width) ||
11664 (ping_height != mng_info->page.height))
11670 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11672 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11673 PNGType(chunk,mng_FRAM);
11674 LogPNGChunk(logging,mng_FRAM,27L);
11676 chunk[5]=0; /* frame name separator (no name) */
11677 chunk[6]=1; /* flag for changing delay, for next frame only */
11678 chunk[7]=0; /* flag for changing frame timeout */
11679 chunk[8]=1; /* flag for changing frame clipping for next frame */
11680 chunk[9]=0; /* flag for changing frame sync_id */
11681 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11682 chunk[14]=0; /* clipping boundaries delta type */
11683 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11685 (png_uint_32) (mng_info->page.x + ping_width));
11686 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11688 (png_uint_32) (mng_info->page.y + ping_height));
11689 (void) WriteBlob(image,31,chunk);
11690 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11691 mng_info->old_framing_mode=4;
11692 mng_info->framing_mode=1;
11696 mng_info->framing_mode=3;
11698 if (mng_info->write_mng && !mng_info->need_fram &&
11699 ((int) image->dispose == 3))
11700 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11703 Free PNG resources.
11706 png_destroy_write_struct(&ping,&ping_info);
11708 pixel_info=RelinquishVirtualMemory(pixel_info);
11710 if (ping_have_blob != MagickFalse)
11711 (void) CloseBlob(image);
11713 image_info=DestroyImageInfo(image_info);
11714 image=DestroyImage(image);
11716 /* Store bit depth actually written */
11717 s[0]=(char) ping_bit_depth;
11720 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11722 if (logging != MagickFalse)
11723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 " exit WriteOnePNGImage()");
11726 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11727 UnlockSemaphoreInfo(ping_semaphore);
11730 /* } for navigation to beginning of SETJMP-protected block. Revert to
11731 * Throwing an Exception when an error occurs.
11734 return(MagickTrue);
11735 /* End write one PNG image */
11740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11744 % W r i t e P N G I m a g e %
11748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11750 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11751 % Multiple-image Network Graphics (MNG) image file.
11753 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11755 % The format of the WritePNGImage method is:
11757 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11758 % Image *image,ExceptionInfo *exception)
11760 % A description of each parameter follows:
11762 % o image_info: the image info.
11764 % o image: The image.
11766 % o exception: return any errors or warnings in this structure.
11768 % Returns MagickTrue on success, MagickFalse on failure.
11770 % Communicating with the PNG encoder:
11772 % While the datastream written is always in PNG format and normally would
11773 % be given the "png" file extension, this method also writes the following
11774 % pseudo-formats which are subsets of png:
11776 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11777 % a depth greater than 8, the depth is reduced. If transparency
11778 % is present, the tRNS chunk must only have values 0 and 255
11779 % (i.e., transparency is binary: fully opaque or fully
11780 % transparent). If other values are present they will be
11781 % 50%-thresholded to binary transparency. If more than 256
11782 % colors are present, they will be quantized to the 4-4-4-1,
11783 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11784 % of any resulting fully-transparent pixels is changed to
11785 % the image's background color.
11787 % If you want better quantization or dithering of the colors
11788 % or alpha than that, you need to do it before calling the
11789 % PNG encoder. The pixels contain 8-bit indices even if
11790 % they could be represented with 1, 2, or 4 bits. Grayscale
11791 % images will be written as indexed PNG files even though the
11792 % PNG grayscale type might be slightly more efficient. Please
11793 % note that writing to the PNG8 format may result in loss
11794 % of color and alpha data.
11796 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11797 % chunk can be present to convey binary transparency by naming
11798 % one of the colors as transparent. The only loss incurred
11799 % is reduction of sample depth to 8. If the image has more
11800 % than one transparent color, has semitransparent pixels, or
11801 % has an opaque pixel with the same RGB components as the
11802 % transparent color, an image is not written.
11804 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11805 % transparency is permitted, i.e., the alpha sample for
11806 % each pixel can have any value from 0 to 255. The alpha
11807 % channel is present even if the image is fully opaque.
11808 % The only loss in data is the reduction of the sample depth
11811 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11812 % chunk can be present to convey binary transparency by naming
11813 % one of the colors as transparent. If the image has more
11814 % than one transparent color, has semitransparent pixels, or
11815 % has an opaque pixel with the same RGB components as the
11816 % transparent color, an image is not written.
11818 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11819 % transparency is permitted, i.e., the alpha sample for
11820 % each pixel can have any value from 0 to 65535. The alpha
11821 % channel is present even if the image is fully opaque.
11823 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11824 % image, if the input was a PNG, is written. If these values
11825 % cannot be found, or if the pixels have been changed in a way
11826 % that makes this impossible, then "PNG00" falls back to the
11827 % regular "PNG" format.
11829 % o -define: For more precise control of the PNG output, you can use the
11830 % Image options "png:bit-depth" and "png:color-type". These
11831 % can be set from the commandline with "-define" and also
11832 % from the application programming interfaces. The options
11833 % are case-independent and are converted to lowercase before
11834 % being passed to this encoder.
11836 % png:color-type can be 0, 2, 3, 4, or 6.
11838 % When png:color-type is 0 (Grayscale), png:bit-depth can
11839 % be 1, 2, 4, 8, or 16.
11841 % When png:color-type is 2 (RGB), png:bit-depth can
11844 % When png:color-type is 3 (Indexed), png:bit-depth can
11845 % be 1, 2, 4, or 8. This refers to the number of bits
11846 % used to store the index. The color samples always have
11847 % bit-depth 8 in indexed PNG files.
11849 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11850 % png:bit-depth can be 8 or 16.
11852 % If the image cannot be written without loss with the
11853 % requested bit-depth and color-type, a PNG file will not
11854 % be written, a warning will be issued, and the encoder will
11855 % return MagickFalse.
11857 % Since image encoders should not be responsible for the "heavy lifting",
11858 % the user should make sure that ImageMagick has already reduced the
11859 % image depth and number of colors and limit transparency to binary
11860 % transparency prior to attempting to write the image with depth, color,
11861 % or transparency limitations.
11863 % Note that another definition, "png:bit-depth-written" exists, but it
11864 % is not intended for external use. It is only used internally by the
11865 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11867 % As of version 6.6.6 the following optimizations are always done:
11869 % o 32-bit depth is reduced to 16.
11870 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11871 % high byte and low byte are identical.
11872 % o Palette is sorted to remove unused entries and to put a
11873 % transparent color first, if BUILD_PNG_PALETTE is defined.
11874 % o Opaque matte channel is removed when writing an indexed PNG.
11875 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11876 % this can be done without loss and a larger bit depth N was not
11877 % requested via the "-define png:bit-depth=N" option.
11878 % o If matte channel is present but only one transparent color is
11879 % present, RGB+tRNS is written instead of RGBA
11880 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11881 % was requested when converting an opaque image).
11883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11885 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11886 Image *image,ExceptionInfo *exception)
11905 assert(image_info != (const ImageInfo *) NULL);
11906 assert(image_info->signature == MagickCoreSignature);
11907 assert(image != (Image *) NULL);
11908 assert(image->signature == MagickCoreSignature);
11909 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11910 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11912 Allocate a MngInfo structure.
11914 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11916 if (mng_info == (MngInfo *) NULL)
11917 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11920 Initialize members of the MngInfo structure.
11922 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11923 mng_info->image=image;
11924 mng_info->equal_backgrounds=MagickTrue;
11926 /* See if user has requested a specific PNG subformat */
11928 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11929 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11930 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11931 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11932 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11934 value=GetImageOption(image_info,"png:format");
11936 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11939 " Format=%s",value);
11941 mng_info->write_png8 = MagickFalse;
11942 mng_info->write_png24 = MagickFalse;
11943 mng_info->write_png32 = MagickFalse;
11944 mng_info->write_png48 = MagickFalse;
11945 mng_info->write_png64 = MagickFalse;
11947 if (LocaleCompare(value,"png8") == 0)
11948 mng_info->write_png8 = MagickTrue;
11950 else if (LocaleCompare(value,"png24") == 0)
11951 mng_info->write_png24 = MagickTrue;
11953 else if (LocaleCompare(value,"png32") == 0)
11954 mng_info->write_png32 = MagickTrue;
11956 else if (LocaleCompare(value,"png48") == 0)
11957 mng_info->write_png48 = MagickTrue;
11959 else if (LocaleCompare(value,"png64") == 0)
11960 mng_info->write_png64 = MagickTrue;
11962 else if ((LocaleCompare(value,"png00") == 0) ||
11963 LocaleCompare(image_info->magick,"PNG00") == 0)
11965 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11966 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11968 if (value != (char *) NULL)
11970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11971 " png00 inherited bit depth=%s",value);
11973 if (LocaleCompare(value,"1") == 0)
11974 mng_info->write_png_depth = 1;
11976 else if (LocaleCompare(value,"2") == 0)
11977 mng_info->write_png_depth = 2;
11979 else if (LocaleCompare(value,"4") == 0)
11980 mng_info->write_png_depth = 4;
11982 else if (LocaleCompare(value,"8") == 0)
11983 mng_info->write_png_depth = 8;
11985 else if (LocaleCompare(value,"16") == 0)
11986 mng_info->write_png_depth = 16;
11989 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11991 if (value != (char *) NULL)
11993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11994 " png00 inherited color type=%s",value);
11996 if (LocaleCompare(value,"0") == 0)
11997 mng_info->write_png_colortype = 1;
11999 else if (LocaleCompare(value,"2") == 0)
12000 mng_info->write_png_colortype = 3;
12002 else if (LocaleCompare(value,"3") == 0)
12003 mng_info->write_png_colortype = 4;
12005 else if (LocaleCompare(value,"4") == 0)
12006 mng_info->write_png_colortype = 5;
12008 else if (LocaleCompare(value,"6") == 0)
12009 mng_info->write_png_colortype = 7;
12014 if (mng_info->write_png8)
12016 mng_info->write_png_colortype = /* 3 */ 4;
12017 mng_info->write_png_depth = 8;
12021 if (mng_info->write_png24)
12023 mng_info->write_png_colortype = /* 2 */ 3;
12024 mng_info->write_png_depth = 8;
12027 if (image->alpha_trait != UndefinedPixelTrait)
12028 (void) SetImageType(image,TrueColorAlphaType,exception);
12031 (void) SetImageType(image,TrueColorType,exception);
12033 (void) SyncImage(image,exception);
12036 if (mng_info->write_png32)
12038 mng_info->write_png_colortype = /* 6 */ 7;
12039 mng_info->write_png_depth = 8;
12041 image->alpha_trait = BlendPixelTrait;
12043 (void) SetImageType(image,TrueColorAlphaType,exception);
12044 (void) SyncImage(image,exception);
12047 if (mng_info->write_png48)
12049 mng_info->write_png_colortype = /* 2 */ 3;
12050 mng_info->write_png_depth = 16;
12053 if (image->alpha_trait != UndefinedPixelTrait)
12054 (void) SetImageType(image,TrueColorAlphaType,exception);
12057 (void) SetImageType(image,TrueColorType,exception);
12059 (void) SyncImage(image,exception);
12062 if (mng_info->write_png64)
12064 mng_info->write_png_colortype = /* 6 */ 7;
12065 mng_info->write_png_depth = 16;
12067 image->alpha_trait = BlendPixelTrait;
12069 (void) SetImageType(image,TrueColorAlphaType,exception);
12070 (void) SyncImage(image,exception);
12073 value=GetImageOption(image_info,"png:bit-depth");
12075 if (value != (char *) NULL)
12077 if (LocaleCompare(value,"1") == 0)
12078 mng_info->write_png_depth = 1;
12080 else if (LocaleCompare(value,"2") == 0)
12081 mng_info->write_png_depth = 2;
12083 else if (LocaleCompare(value,"4") == 0)
12084 mng_info->write_png_depth = 4;
12086 else if (LocaleCompare(value,"8") == 0)
12087 mng_info->write_png_depth = 8;
12089 else if (LocaleCompare(value,"16") == 0)
12090 mng_info->write_png_depth = 16;
12093 (void) ThrowMagickException(exception,
12094 GetMagickModule(),CoderWarning,
12095 "ignoring invalid defined png:bit-depth",
12098 if (logging != MagickFalse)
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12103 value=GetImageOption(image_info,"png:color-type");
12105 if (value != (char *) NULL)
12107 /* We must store colortype+1 because 0 is a valid colortype */
12108 if (LocaleCompare(value,"0") == 0)
12109 mng_info->write_png_colortype = 1;
12111 else if (LocaleCompare(value,"1") == 0)
12112 mng_info->write_png_colortype = 2;
12114 else if (LocaleCompare(value,"2") == 0)
12115 mng_info->write_png_colortype = 3;
12117 else if (LocaleCompare(value,"3") == 0)
12118 mng_info->write_png_colortype = 4;
12120 else if (LocaleCompare(value,"4") == 0)
12121 mng_info->write_png_colortype = 5;
12123 else if (LocaleCompare(value,"6") == 0)
12124 mng_info->write_png_colortype = 7;
12127 (void) ThrowMagickException(exception,
12128 GetMagickModule(),CoderWarning,
12129 "ignoring invalid defined png:color-type",
12132 if (logging != MagickFalse)
12133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12134 " png:color-type=%d was defined.\n",
12135 mng_info->write_png_colortype-1);
12138 /* Check for chunks to be excluded:
12140 * The default is to not exclude any known chunks except for any
12141 * listed in the "unused_chunks" array, above.
12143 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12144 * define (in the image properties or in the image artifacts)
12145 * or via a mng_info member. For convenience, in addition
12146 * to or instead of a comma-separated list of chunks, the
12147 * "exclude-chunk" string can be simply "all" or "none".
12149 * Note that the "-strip" option provides a convenient way of
12150 * doing the equivalent of
12152 * -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12153 * iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12155 * The exclude-chunk define takes priority over the mng_info.
12157 * A "png:include-chunk" define takes priority over both the
12158 * mng_info and the "png:exclude-chunk" define. Like the
12159 * "exclude-chunk" string, it can define "all" or "none" as
12160 * well as a comma-separated list. Chunks that are unknown to
12161 * ImageMagick are always excluded, regardless of their "copy-safe"
12162 * status according to the PNG specification, and even if they
12163 * appear in the "include-chunk" list. Such defines appearing among
12164 * the image options take priority over those found among the image
12167 * Finally, all chunks listed in the "unused_chunks" array are
12168 * automatically excluded, regardless of the other instructions
12171 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12172 * will not be written and the gAMA chunk will only be written if it
12173 * is not between .45 and .46, or approximately (1.0/2.2).
12175 * If you exclude tRNS and the image has transparency, the colortype
12176 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12178 * The -strip option causes StripImage() to set the png:include-chunk
12179 * artifact to "none,trns,gama".
12182 mng_info->ping_exclude_bKGD=MagickFalse;
12183 mng_info->ping_exclude_caNv=MagickFalse;
12184 mng_info->ping_exclude_cHRM=MagickFalse;
12185 mng_info->ping_exclude_date=MagickFalse;
12186 mng_info->ping_exclude_eXIf=MagickFalse;
12187 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12188 mng_info->ping_exclude_gAMA=MagickFalse;
12189 mng_info->ping_exclude_iCCP=MagickFalse;
12190 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12191 mng_info->ping_exclude_oFFs=MagickFalse;
12192 mng_info->ping_exclude_pHYs=MagickFalse;
12193 mng_info->ping_exclude_sRGB=MagickFalse;
12194 mng_info->ping_exclude_tEXt=MagickFalse;
12195 mng_info->ping_exclude_tIME=MagickFalse;
12196 mng_info->ping_exclude_tRNS=MagickFalse;
12197 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12198 mng_info->ping_exclude_zTXt=MagickFalse;
12200 mng_info->ping_preserve_colormap=MagickFalse;
12202 value=GetImageOption(image_info,"png:preserve-colormap");
12204 value=GetImageArtifact(image,"png:preserve-colormap");
12206 mng_info->ping_preserve_colormap=MagickTrue;
12208 mng_info->ping_preserve_iCCP=MagickFalse;
12210 value=GetImageOption(image_info,"png:preserve-iCCP");
12212 value=GetImageArtifact(image,"png:preserve-iCCP");
12214 mng_info->ping_preserve_iCCP=MagickTrue;
12216 /* These compression-level, compression-strategy, and compression-filter
12217 * defines take precedence over values from the -quality option.
12219 value=GetImageOption(image_info,"png:compression-level");
12221 value=GetImageArtifact(image,"png:compression-level");
12224 /* We have to add 1 to everything because 0 is a valid input,
12225 * and we want to use 0 (the default) to mean undefined.
12227 if (LocaleCompare(value,"0") == 0)
12228 mng_info->write_png_compression_level = 1;
12230 else if (LocaleCompare(value,"1") == 0)
12231 mng_info->write_png_compression_level = 2;
12233 else if (LocaleCompare(value,"2") == 0)
12234 mng_info->write_png_compression_level = 3;
12236 else if (LocaleCompare(value,"3") == 0)
12237 mng_info->write_png_compression_level = 4;
12239 else if (LocaleCompare(value,"4") == 0)
12240 mng_info->write_png_compression_level = 5;
12242 else if (LocaleCompare(value,"5") == 0)
12243 mng_info->write_png_compression_level = 6;
12245 else if (LocaleCompare(value,"6") == 0)
12246 mng_info->write_png_compression_level = 7;
12248 else if (LocaleCompare(value,"7") == 0)
12249 mng_info->write_png_compression_level = 8;
12251 else if (LocaleCompare(value,"8") == 0)
12252 mng_info->write_png_compression_level = 9;
12254 else if (LocaleCompare(value,"9") == 0)
12255 mng_info->write_png_compression_level = 10;
12258 (void) ThrowMagickException(exception,
12259 GetMagickModule(),CoderWarning,
12260 "ignoring invalid defined png:compression-level",
12264 value=GetImageOption(image_info,"png:compression-strategy");
12266 value=GetImageArtifact(image,"png:compression-strategy");
12269 if (LocaleCompare(value,"0") == 0)
12270 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12272 else if (LocaleCompare(value,"1") == 0)
12273 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12275 else if (LocaleCompare(value,"2") == 0)
12276 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12278 else if (LocaleCompare(value,"3") == 0)
12279 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12280 mng_info->write_png_compression_strategy = Z_RLE+1;
12282 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12285 else if (LocaleCompare(value,"4") == 0)
12286 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12287 mng_info->write_png_compression_strategy = Z_FIXED+1;
12289 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12293 (void) ThrowMagickException(exception,
12294 GetMagickModule(),CoderWarning,
12295 "ignoring invalid defined png:compression-strategy",
12299 value=GetImageOption(image_info,"png:compression-filter");
12301 value=GetImageArtifact(image,"png:compression-filter");
12304 /* To do: combinations of filters allowed by libpng
12305 * masks 0x08 through 0xf8
12307 * Implement this as a comma-separated list of 0,1,2,3,4,5
12308 * where 5 is a special case meaning PNG_ALL_FILTERS.
12311 if (LocaleCompare(value,"0") == 0)
12312 mng_info->write_png_compression_filter = 1;
12314 else if (LocaleCompare(value,"1") == 0)
12315 mng_info->write_png_compression_filter = 2;
12317 else if (LocaleCompare(value,"2") == 0)
12318 mng_info->write_png_compression_filter = 3;
12320 else if (LocaleCompare(value,"3") == 0)
12321 mng_info->write_png_compression_filter = 4;
12323 else if (LocaleCompare(value,"4") == 0)
12324 mng_info->write_png_compression_filter = 5;
12326 else if (LocaleCompare(value,"5") == 0)
12327 mng_info->write_png_compression_filter = 6;
12330 (void) ThrowMagickException(exception,
12331 GetMagickModule(),CoderWarning,
12332 "ignoring invalid defined png:compression-filter",
12336 for (source=0; source<8; source++)
12341 value=GetImageOption(image_info,"png:exclude-chunks");
12344 value=GetImageArtifact(image,"png:exclude-chunks");
12347 value=GetImageOption(image_info,"png:exclude-chunk");
12350 value=GetImageArtifact(image,"png:exclude-chunk");
12353 value=GetImageOption(image_info,"png:include-chunks");
12356 value=GetImageArtifact(image,"png:include-chunks");
12359 value=GetImageOption(image_info,"png:include-chunk");
12362 value=GetImageArtifact(image,"png:include-chunk");
12368 excluding = MagickTrue;
12370 excluding = MagickFalse;
12372 if (logging != MagickFalse)
12374 if (source == 0 || source == 2)
12375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12376 " png:exclude-chunk=%s found in image options.\n", value);
12377 else if (source == 1 || source == 3)
12378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379 " png:exclude-chunk=%s found in image artifacts.\n", value);
12380 else if (source == 4 || source == 6)
12381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12382 " png:include-chunk=%s found in image options.\n", value);
12383 else /* if (source == 5 || source == 7) */
12384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12385 " png:include-chunk=%s found in image artifacts.\n", value);
12388 if (IsOptionMember("all",value) != MagickFalse)
12390 mng_info->ping_exclude_bKGD=excluding;
12391 mng_info->ping_exclude_caNv=excluding;
12392 mng_info->ping_exclude_cHRM=excluding;
12393 mng_info->ping_exclude_date=excluding;
12394 mng_info->ping_exclude_EXIF=excluding;
12395 mng_info->ping_exclude_eXIf=excluding;
12396 mng_info->ping_exclude_gAMA=excluding;
12397 mng_info->ping_exclude_iCCP=excluding;
12398 /* mng_info->ping_exclude_iTXt=excluding; */
12399 mng_info->ping_exclude_oFFs=excluding;
12400 mng_info->ping_exclude_pHYs=excluding;
12401 mng_info->ping_exclude_sRGB=excluding;
12402 mng_info->ping_exclude_tEXt=excluding;
12403 mng_info->ping_exclude_tIME=excluding;
12404 mng_info->ping_exclude_tRNS=excluding;
12405 mng_info->ping_exclude_zCCP=excluding;
12406 mng_info->ping_exclude_zTXt=excluding;
12409 if (IsOptionMember("none",value) != MagickFalse)
12411 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12413 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12415 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12417 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12419 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12421 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12423 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12425 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12427 /* mng_info->ping_exclude_iTXt=!excluding; */
12428 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12430 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12432 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12434 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12436 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12438 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12440 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12442 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12446 if (IsOptionMember("bkgd",value) != MagickFalse)
12447 mng_info->ping_exclude_bKGD=excluding;
12449 if (IsOptionMember("caNv",value) != MagickFalse)
12450 mng_info->ping_exclude_caNv=excluding;
12452 if (IsOptionMember("chrm",value) != MagickFalse)
12453 mng_info->ping_exclude_cHRM=excluding;
12455 if (IsOptionMember("date",value) != MagickFalse)
12456 mng_info->ping_exclude_date=excluding;
12458 if (IsOptionMember("exif",value) != MagickFalse)
12460 mng_info->ping_exclude_EXIF=excluding;
12461 mng_info->ping_exclude_eXIf=excluding;
12464 if (IsOptionMember("gama",value) != MagickFalse)
12465 mng_info->ping_exclude_gAMA=excluding;
12467 if (IsOptionMember("iccp",value) != MagickFalse)
12468 mng_info->ping_exclude_iCCP=excluding;
12471 if (IsOptionMember("itxt",value) != MagickFalse)
12472 mng_info->ping_exclude_iTXt=excluding;
12475 if (IsOptionMember("offs",value) != MagickFalse)
12476 mng_info->ping_exclude_oFFs=excluding;
12478 if (IsOptionMember("phys",value) != MagickFalse)
12479 mng_info->ping_exclude_pHYs=excluding;
12481 if (IsOptionMember("srgb",value) != MagickFalse)
12482 mng_info->ping_exclude_sRGB=excluding;
12484 if (IsOptionMember("text",value) != MagickFalse)
12485 mng_info->ping_exclude_tEXt=excluding;
12487 if (IsOptionMember("time",value) != MagickFalse)
12488 mng_info->ping_exclude_tIME=excluding;
12490 if (IsOptionMember("trns",value) != MagickFalse)
12491 mng_info->ping_exclude_tRNS=excluding;
12493 if (IsOptionMember("zccp",value) != MagickFalse)
12494 mng_info->ping_exclude_zCCP=excluding;
12496 if (IsOptionMember("ztxt",value) != MagickFalse)
12497 mng_info->ping_exclude_zTXt=excluding;
12500 if (logging != MagickFalse)
12502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12503 " Chunks to be excluded from the output png:");
12504 if (mng_info->ping_exclude_bKGD != MagickFalse)
12505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12507 if (mng_info->ping_exclude_caNv != MagickFalse)
12508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12510 if (mng_info->ping_exclude_cHRM != MagickFalse)
12511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12513 if (mng_info->ping_exclude_date != MagickFalse)
12514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12516 if (mng_info->ping_exclude_EXIF != MagickFalse)
12517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12519 if (mng_info->ping_exclude_eXIf != MagickFalse)
12520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12522 if (mng_info->ping_exclude_gAMA != MagickFalse)
12523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12525 if (mng_info->ping_exclude_iCCP != MagickFalse)
12526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12529 if (mng_info->ping_exclude_iTXt != MagickFalse)
12530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12534 if (mng_info->ping_exclude_oFFs != MagickFalse)
12535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12537 if (mng_info->ping_exclude_pHYs != MagickFalse)
12538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12540 if (mng_info->ping_exclude_sRGB != MagickFalse)
12541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12543 if (mng_info->ping_exclude_tEXt != MagickFalse)
12544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546 if (mng_info->ping_exclude_tIME != MagickFalse)
12547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12549 if (mng_info->ping_exclude_tRNS != MagickFalse)
12550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12552 if (mng_info->ping_exclude_zCCP != MagickFalse)
12553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12555 if (mng_info->ping_exclude_zTXt != MagickFalse)
12556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12560 mng_info->need_blob = MagickTrue;
12562 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12564 mng_info=MngInfoFreeStruct(mng_info);
12566 if (logging != MagickFalse)
12567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12572 #if defined(JNG_SUPPORTED)
12574 /* Write one JNG image */
12575 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12576 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12597 jng_alpha_compression_method,
12598 jng_alpha_sample_depth,
12606 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12607 " Enter WriteOneJNGImage()");
12609 blob=(unsigned char *) NULL;
12610 jpeg_image=(Image *) NULL;
12611 jpeg_image_info=(ImageInfo *) NULL;
12615 transparent=image_info->type==GrayscaleAlphaType ||
12616 image_info->type==TrueColorAlphaType ||
12617 image->alpha_trait != UndefinedPixelTrait;
12619 jng_alpha_sample_depth = 0;
12621 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12623 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12625 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12626 image_info->quality;
12628 if (jng_alpha_quality >= 1000)
12629 jng_alpha_quality /= 1000;
12633 if (transparent != 0)
12637 /* Create JPEG blob, image, and image_info */
12638 if (logging != MagickFalse)
12639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12640 " Creating jpeg_image_info for alpha.");
12642 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12644 if (jpeg_image_info == (ImageInfo *) NULL)
12645 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12647 if (logging != MagickFalse)
12648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12649 " Creating jpeg_image.");
12651 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12652 if (jpeg_image == (Image *) NULL)
12653 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12654 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12655 jpeg_image->alpha_trait=UndefinedPixelTrait;
12656 jpeg_image->quality=jng_alpha_quality;
12657 jpeg_image_info->type=GrayscaleType;
12658 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12659 (void) AcquireUniqueFilename(jpeg_image->filename);
12660 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12661 "%s",jpeg_image->filename);
12665 jng_alpha_compression_method=0;
12667 jng_alpha_sample_depth=0;
12670 /* To do: check bit depth of PNG alpha channel */
12672 /* Check if image is grayscale. */
12673 if (image_info->type != TrueColorAlphaType && image_info->type !=
12674 TrueColorType && SetImageGray(image,exception))
12677 if (logging != MagickFalse)
12679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12680 " JNG Quality = %d",(int) jng_quality);
12681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12682 " JNG Color Type = %d",jng_color_type);
12683 if (transparent != 0)
12685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12688 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12690 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12694 if (transparent != 0)
12696 if (jng_alpha_compression_method==0)
12701 /* Encode alpha as a grayscale PNG blob */
12702 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12704 if (status == MagickFalse)
12705 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12707 if (logging != MagickFalse)
12708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12709 " Creating PNG blob.");
12711 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12713 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12714 jpeg_image_info->interlace=NoInterlace;
12716 /* Exclude all ancillary chunks */
12717 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12719 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12720 &length,exception);
12722 /* Retrieve sample depth used */
12723 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12724 if (value != (char *) NULL)
12725 jng_alpha_sample_depth= (unsigned int) value[0];
12729 /* Encode alpha as a grayscale JPEG blob */
12731 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12733 if (status == MagickFalse)
12734 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12736 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12738 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12739 jpeg_image_info->interlace=NoInterlace;
12740 if (logging != MagickFalse)
12741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12742 " Creating blob.");
12743 blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12744 jpeg_image,&length,
12746 jng_alpha_sample_depth=8;
12748 if (logging != MagickFalse)
12749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12750 " Successfully read jpeg_image into a blob, length=%.20g.",
12754 /* Destroy JPEG image and image_info */
12755 jpeg_image=DestroyImage(jpeg_image);
12756 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12757 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12760 /* Write JHDR chunk */
12761 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12762 PNGType(chunk,mng_JHDR);
12763 LogPNGChunk(logging,mng_JHDR,16L);
12764 PNGLong(chunk+4,(png_uint_32) image->columns);
12765 PNGLong(chunk+8,(png_uint_32) image->rows);
12766 chunk[12]=jng_color_type;
12767 chunk[13]=8; /* sample depth */
12768 chunk[14]=8; /*jng_image_compression_method */
12769 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12770 chunk[16]=jng_alpha_sample_depth;
12771 chunk[17]=jng_alpha_compression_method;
12772 chunk[18]=0; /*jng_alpha_filter_method */
12773 chunk[19]=0; /*jng_alpha_interlace_method */
12774 (void) WriteBlob(image,20,chunk);
12775 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12776 if (logging != MagickFalse)
12778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12779 " JNG width:%15lu",(unsigned long) image->columns);
12781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12782 " JNG height:%14lu",(unsigned long) image->rows);
12784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12785 " JNG color type:%10d",jng_color_type);
12787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12788 " JNG sample depth:%8d",8);
12790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12791 " JNG compression:%9d",8);
12793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12794 " JNG interlace:%11d",0);
12796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12797 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12800 " JNG alpha compression:%3d",jng_alpha_compression_method);
12802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12803 " JNG alpha filter:%8d",0);
12805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12806 " JNG alpha interlace:%5d",0);
12810 Write leading ancillary chunks
12813 if (transparent != 0)
12816 Write JNG bKGD chunk
12827 if (jng_color_type == 8 || jng_color_type == 12)
12831 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12832 PNGType(chunk,mng_bKGD);
12833 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12834 red=ScaleQuantumToChar(image->background_color.red);
12835 green=ScaleQuantumToChar(image->background_color.green);
12836 blue=ScaleQuantumToChar(image->background_color.blue);
12843 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12844 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12847 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12850 Write JNG sRGB chunk
12852 (void) WriteBlobMSBULong(image,1L);
12853 PNGType(chunk,mng_sRGB);
12854 LogPNGChunk(logging,mng_sRGB,1L);
12856 if (image->rendering_intent != UndefinedIntent)
12857 chunk[4]=(unsigned char)
12858 Magick_RenderingIntent_to_PNG_RenderingIntent(
12859 (image->rendering_intent));
12862 chunk[4]=(unsigned char)
12863 Magick_RenderingIntent_to_PNG_RenderingIntent(
12864 (PerceptualIntent));
12866 (void) WriteBlob(image,5,chunk);
12867 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12871 if (image->gamma != 0.0)
12874 Write JNG gAMA chunk
12876 (void) WriteBlobMSBULong(image,4L);
12877 PNGType(chunk,mng_gAMA);
12878 LogPNGChunk(logging,mng_gAMA,4L);
12879 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12880 (void) WriteBlob(image,8,chunk);
12881 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12884 if ((mng_info->equal_chrms == MagickFalse) &&
12885 (image->chromaticity.red_primary.x != 0.0))
12891 Write JNG cHRM chunk
12893 (void) WriteBlobMSBULong(image,32L);
12894 PNGType(chunk,mng_cHRM);
12895 LogPNGChunk(logging,mng_cHRM,32L);
12896 primary=image->chromaticity.white_point;
12897 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12898 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12899 primary=image->chromaticity.red_primary;
12900 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12901 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12902 primary=image->chromaticity.green_primary;
12903 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12904 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12905 primary=image->chromaticity.blue_primary;
12906 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12907 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12908 (void) WriteBlob(image,36,chunk);
12909 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12913 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12916 Write JNG pHYs chunk
12918 (void) WriteBlobMSBULong(image,9L);
12919 PNGType(chunk,mng_pHYs);
12920 LogPNGChunk(logging,mng_pHYs,9L);
12921 if (image->units == PixelsPerInchResolution)
12923 PNGLong(chunk+4,(png_uint_32)
12924 (image->resolution.x*100.0/2.54+0.5));
12926 PNGLong(chunk+8,(png_uint_32)
12927 (image->resolution.y*100.0/2.54+0.5));
12934 if (image->units == PixelsPerCentimeterResolution)
12936 PNGLong(chunk+4,(png_uint_32)
12937 (image->resolution.x*100.0+0.5));
12939 PNGLong(chunk+8,(png_uint_32)
12940 (image->resolution.y*100.0+0.5));
12947 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12948 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12952 (void) WriteBlob(image,13,chunk);
12953 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12956 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12959 Write JNG oFFs chunk
12961 (void) WriteBlobMSBULong(image,9L);
12962 PNGType(chunk,mng_oFFs);
12963 LogPNGChunk(logging,mng_oFFs,9L);
12964 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12965 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12967 (void) WriteBlob(image,13,chunk);
12968 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12971 if (transparent != 0)
12973 if (jng_alpha_compression_method==0)
12981 /* Write IDAT chunk header */
12982 if (logging != MagickFalse)
12983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984 " Write IDAT chunks from blob, length=%.20g.",(double)
12987 /* Copy IDAT chunks */
12990 for (i=8; i<(ssize_t) length; i+=len+12)
12992 len=(((unsigned int) *(p ) & 0xff) << 24) +
12993 (((unsigned int) *(p + 1) & 0xff) << 16) +
12994 (((unsigned int) *(p + 2) & 0xff) << 8) +
12995 (((unsigned int) *(p + 3) & 0xff) ) ;
12998 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13000 /* Found an IDAT chunk. */
13001 (void) WriteBlobMSBULong(image,len);
13002 LogPNGChunk(logging,mng_IDAT,len);
13003 (void) WriteBlob(image,len+4,p);
13004 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13009 if (logging != MagickFalse)
13010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13011 " Skipping %c%c%c%c chunk, length=%.20g.",
13012 *(p),*(p+1),*(p+2),*(p+3),(double) len);
13017 else if (length != 0)
13019 /* Write JDAA chunk header */
13020 if (logging != MagickFalse)
13021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13022 " Write JDAA chunk, length=%.20g.",(double) length);
13023 (void) WriteBlobMSBULong(image,(size_t) length);
13024 PNGType(chunk,mng_JDAA);
13025 LogPNGChunk(logging,mng_JDAA,length);
13026 /* Write JDAT chunk(s) data */
13027 (void) WriteBlob(image,4,chunk);
13028 (void) WriteBlob(image,length,blob);
13029 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13032 blob=(unsigned char *) RelinquishMagickMemory(blob);
13035 /* Encode image as a JPEG blob */
13036 if (logging != MagickFalse)
13037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13038 " Creating jpeg_image_info.");
13039 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13040 if (jpeg_image_info == (ImageInfo *) NULL)
13041 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13043 if (logging != MagickFalse)
13044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13045 " Creating jpeg_image.");
13047 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13048 if (jpeg_image == (Image *) NULL)
13049 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13050 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13052 (void) AcquireUniqueFilename(jpeg_image->filename);
13053 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13054 jpeg_image->filename);
13056 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13059 if (logging != MagickFalse)
13060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13061 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13062 (double) jpeg_image->rows);
13064 if (status == MagickFalse)
13065 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13067 if (jng_color_type == 8 || jng_color_type == 12)
13068 jpeg_image_info->type=GrayscaleType;
13070 jpeg_image_info->quality=jng_quality;
13071 jpeg_image->quality=jng_quality;
13072 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13073 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13075 if (logging != MagickFalse)
13076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13077 " Creating blob.");
13079 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13082 if (logging != MagickFalse)
13084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13085 " Successfully read jpeg_image into a blob, length=%.20g.",
13088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13089 " Write JDAT chunk, length=%.20g.",(double) length);
13092 /* Write JDAT chunk(s) */
13093 (void) WriteBlobMSBULong(image,(size_t) length);
13094 PNGType(chunk,mng_JDAT);
13095 LogPNGChunk(logging,mng_JDAT,length);
13096 (void) WriteBlob(image,4,chunk);
13097 (void) WriteBlob(image,length,blob);
13098 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13100 jpeg_image=DestroyImage(jpeg_image);
13101 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13102 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13103 blob=(unsigned char *) RelinquishMagickMemory(blob);
13105 /* Write IEND chunk */
13106 (void) WriteBlobMSBULong(image,0L);
13107 PNGType(chunk,mng_IEND);
13108 LogPNGChunk(logging,mng_IEND,0);
13109 (void) WriteBlob(image,4,chunk);
13110 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13112 if (logging != MagickFalse)
13113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13114 " exit WriteOneJNGImage()");
13120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13124 % W r i t e J N G I m a g e %
13128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13130 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13132 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13134 % The format of the WriteJNGImage method is:
13136 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13137 % Image *image,ExceptionInfo *exception)
13139 % A description of each parameter follows:
13141 % o image_info: the image info.
13143 % o image: The image.
13145 % o exception: return any errors or warnings in this structure.
13147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13149 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13150 Image *image, ExceptionInfo *exception)
13162 assert(image_info != (const ImageInfo *) NULL);
13163 assert(image_info->signature == MagickCoreSignature);
13164 assert(image != (Image *) NULL);
13165 assert(image->signature == MagickCoreSignature);
13166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13167 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13168 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13169 if (status == MagickFalse)
13171 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13172 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13175 Allocate a MngInfo structure.
13177 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13178 if (mng_info == (MngInfo *) NULL)
13179 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13181 Initialize members of the MngInfo structure.
13183 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13184 mng_info->image=image;
13186 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13188 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13189 mng_info=MngInfoFreeStruct(mng_info);
13190 (void) CloseBlob(image);
13192 (void) CatchImageException(image);
13193 if (logging != MagickFalse)
13194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13199 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13200 Image *image, ExceptionInfo *exception)
13208 volatile MagickBooleanType
13220 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13221 defined(PNG_MNG_FEATURES_SUPPORTED)
13224 all_images_are_gray,
13234 volatile unsigned int
13245 #if (PNG_LIBPNG_VER < 10200)
13246 if (image_info->verbose)
13247 printf("Your PNG library (libpng-%s) is rather old.\n",
13248 PNG_LIBPNG_VER_STRING);
13254 assert(image_info != (const ImageInfo *) NULL);
13255 assert(image_info->signature == MagickCoreSignature);
13256 assert(image != (Image *) NULL);
13257 assert(image->signature == MagickCoreSignature);
13258 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13259 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13260 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13261 if (status == MagickFalse)
13265 Allocate a MngInfo structure.
13267 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13268 if (mng_info == (MngInfo *) NULL)
13269 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13271 Initialize members of the MngInfo structure.
13273 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13274 mng_info->image=image;
13275 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13278 * See if user has requested a specific PNG subformat to be used
13279 * for all of the PNGs in the MNG being written, e.g.,
13281 * convert *.png png8:animation.mng
13283 * To do: check -define png:bit_depth and png:color_type as well,
13284 * or perhaps use mng:bit_depth and mng:color_type instead for
13288 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13289 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13290 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13292 write_jng=MagickFalse;
13293 if (image_info->compression == JPEGCompression)
13294 write_jng=MagickTrue;
13296 mng_info->adjoin=image_info->adjoin &&
13297 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13299 if (logging != MagickFalse)
13301 /* Log some info about the input */
13305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13306 " Checking input image(s)\n"
13307 " Image_info depth: %.20g, Type: %d",
13308 (double) image_info->depth, image_info->type);
13311 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13315 " Scene: %.20g\n, Image depth: %.20g",
13316 (double) scene++, (double) p->depth);
13318 if (p->alpha_trait != UndefinedPixelTrait)
13319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13326 if (p->storage_class == PseudoClass)
13327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13328 " Storage class: PseudoClass");
13331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13332 " Storage class: DirectClass");
13335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13336 " Number of colors: %.20g",(double) p->colors);
13339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13340 " Number of colors: unspecified");
13342 if (mng_info->adjoin == MagickFalse)
13347 use_global_plte=MagickFalse;
13348 all_images_are_gray=MagickFalse;
13349 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13350 need_local_plte=MagickTrue;
13352 need_defi=MagickFalse;
13353 need_matte=MagickFalse;
13354 mng_info->framing_mode=1;
13355 mng_info->old_framing_mode=1;
13358 if (image_info->page != (char *) NULL)
13361 Determine image bounding box.
13363 SetGeometry(image,&mng_info->page);
13364 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13365 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13380 mng_info->page=image->page;
13381 need_geom=MagickTrue;
13382 if (mng_info->page.width || mng_info->page.height)
13383 need_geom=MagickFalse;
13385 Check all the scenes.
13387 initial_delay=image->delay;
13388 need_iterations=MagickFalse;
13389 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13390 mng_info->equal_physs=MagickTrue,
13391 mng_info->equal_gammas=MagickTrue;
13392 mng_info->equal_srgbs=MagickTrue;
13393 mng_info->equal_backgrounds=MagickTrue;
13395 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13396 defined(PNG_MNG_FEATURES_SUPPORTED)
13397 all_images_are_gray=MagickTrue;
13398 mng_info->equal_palettes=MagickFalse;
13399 need_local_plte=MagickFalse;
13401 for (next_image=image; next_image != (Image *) NULL; )
13405 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13406 mng_info->page.width=next_image->columns+next_image->page.x;
13408 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13409 mng_info->page.height=next_image->rows+next_image->page.y;
13412 if (next_image->page.x || next_image->page.y)
13413 need_defi=MagickTrue;
13415 if (next_image->alpha_trait != UndefinedPixelTrait)
13416 need_matte=MagickTrue;
13418 if ((int) next_image->dispose >= BackgroundDispose)
13419 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13420 next_image->page.x || next_image->page.y ||
13421 ((next_image->columns < mng_info->page.width) &&
13422 (next_image->rows < mng_info->page.height)))
13423 mng_info->need_fram=MagickTrue;
13425 if (next_image->iterations)
13426 need_iterations=MagickTrue;
13428 final_delay=next_image->delay;
13430 if (final_delay != initial_delay || final_delay > 1UL*
13431 next_image->ticks_per_second)
13432 mng_info->need_fram=1;
13434 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13435 defined(PNG_MNG_FEATURES_SUPPORTED)
13437 check for global palette possibility.
13439 if (image->alpha_trait != UndefinedPixelTrait)
13440 need_local_plte=MagickTrue;
13442 if (need_local_plte == 0)
13444 if (SetImageGray(image,exception) == MagickFalse)
13445 all_images_are_gray=MagickFalse;
13446 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13447 if (use_global_plte == 0)
13448 use_global_plte=mng_info->equal_palettes;
13449 need_local_plte=!mng_info->equal_palettes;
13452 if (GetNextImageInList(next_image) != (Image *) NULL)
13454 if (next_image->background_color.red !=
13455 next_image->next->background_color.red ||
13456 next_image->background_color.green !=
13457 next_image->next->background_color.green ||
13458 next_image->background_color.blue !=
13459 next_image->next->background_color.blue)
13460 mng_info->equal_backgrounds=MagickFalse;
13462 if (next_image->gamma != next_image->next->gamma)
13463 mng_info->equal_gammas=MagickFalse;
13465 if (next_image->rendering_intent !=
13466 next_image->next->rendering_intent)
13467 mng_info->equal_srgbs=MagickFalse;
13469 if ((next_image->units != next_image->next->units) ||
13470 (next_image->resolution.x != next_image->next->resolution.x) ||
13471 (next_image->resolution.y != next_image->next->resolution.y))
13472 mng_info->equal_physs=MagickFalse;
13474 if (mng_info->equal_chrms)
13476 if (next_image->chromaticity.red_primary.x !=
13477 next_image->next->chromaticity.red_primary.x ||
13478 next_image->chromaticity.red_primary.y !=
13479 next_image->next->chromaticity.red_primary.y ||
13480 next_image->chromaticity.green_primary.x !=
13481 next_image->next->chromaticity.green_primary.x ||
13482 next_image->chromaticity.green_primary.y !=
13483 next_image->next->chromaticity.green_primary.y ||
13484 next_image->chromaticity.blue_primary.x !=
13485 next_image->next->chromaticity.blue_primary.x ||
13486 next_image->chromaticity.blue_primary.y !=
13487 next_image->next->chromaticity.blue_primary.y ||
13488 next_image->chromaticity.white_point.x !=
13489 next_image->next->chromaticity.white_point.x ||
13490 next_image->chromaticity.white_point.y !=
13491 next_image->next->chromaticity.white_point.y)
13492 mng_info->equal_chrms=MagickFalse;
13496 next_image=GetNextImageInList(next_image);
13498 if (image_count < 2)
13500 mng_info->equal_backgrounds=MagickFalse;
13501 mng_info->equal_chrms=MagickFalse;
13502 mng_info->equal_gammas=MagickFalse;
13503 mng_info->equal_srgbs=MagickFalse;
13504 mng_info->equal_physs=MagickFalse;
13505 use_global_plte=MagickFalse;
13506 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13507 need_local_plte=MagickTrue;
13509 need_iterations=MagickFalse;
13512 if (mng_info->need_fram == MagickFalse)
13515 Only certain framing rates 100/n are exactly representable without
13516 the FRAM chunk but we'll allow some slop in VLC files
13518 if (final_delay == 0)
13520 if (need_iterations != MagickFalse)
13523 It's probably a GIF with loop; don't run it *too* fast.
13525 if (mng_info->adjoin)
13528 (void) ThrowMagickException(exception,GetMagickModule(),
13530 "input has zero delay between all frames; assuming",
13535 mng_info->ticks_per_second=0;
13537 if (final_delay != 0)
13538 mng_info->ticks_per_second=(png_uint_32)
13539 (image->ticks_per_second/final_delay);
13540 if (final_delay > 50)
13541 mng_info->ticks_per_second=2;
13543 if (final_delay > 75)
13544 mng_info->ticks_per_second=1;
13546 if (final_delay > 125)
13547 mng_info->need_fram=MagickTrue;
13549 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13550 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13551 (final_delay != 25) && (final_delay != 50) &&
13552 (final_delay != (size_t) image->ticks_per_second))
13553 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13556 if (mng_info->need_fram != MagickFalse)
13557 mng_info->ticks_per_second=image->ticks_per_second;
13559 If pseudocolor, we should also check to see if all the
13560 palettes are identical and write a global PLTE if they are.
13564 Write the MNG version 1.0 signature and MHDR chunk.
13566 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13567 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13568 PNGType(chunk,mng_MHDR);
13569 LogPNGChunk(logging,mng_MHDR,28L);
13570 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13571 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13572 PNGLong(chunk+12,mng_info->ticks_per_second);
13573 PNGLong(chunk+16,0L); /* layer count=unknown */
13574 PNGLong(chunk+20,0L); /* frame count=unknown */
13575 PNGLong(chunk+24,0L); /* play time=unknown */
13580 if (need_defi || mng_info->need_fram || use_global_plte)
13581 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13584 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13589 if (need_defi || mng_info->need_fram || use_global_plte)
13590 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13593 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13601 if (need_defi || mng_info->need_fram || use_global_plte)
13602 PNGLong(chunk+28,11L); /* simplicity=LC */
13605 PNGLong(chunk+28,9L); /* simplicity=VLC */
13610 if (need_defi || mng_info->need_fram || use_global_plte)
13611 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13614 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13617 (void) WriteBlob(image,32,chunk);
13618 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13619 option=GetImageOption(image_info,"mng:need-cacheoff");
13620 if (option != (const char *) NULL)
13625 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13627 PNGType(chunk,mng_nEED);
13628 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13629 (void) WriteBlobMSBULong(image,(size_t) length);
13630 LogPNGChunk(logging,mng_nEED,(size_t) length);
13632 (void) WriteBlob(image,length,chunk);
13633 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13635 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13636 (GetNextImageInList(image) != (Image *) NULL) &&
13637 (image->iterations != 1))
13640 Write MNG TERM chunk
13642 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13643 PNGType(chunk,mng_TERM);
13644 LogPNGChunk(logging,mng_TERM,10L);
13645 chunk[4]=3; /* repeat animation */
13646 chunk[5]=0; /* show last frame when done */
13647 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13648 final_delay/MagickMax(image->ticks_per_second,1)));
13650 if (image->iterations == 0)
13651 PNGLong(chunk+10,PNG_UINT_31_MAX);
13654 PNGLong(chunk+10,(png_uint_32) image->iterations);
13656 if (logging != MagickFalse)
13658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13659 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13660 final_delay/MagickMax(image->ticks_per_second,1)));
13662 if (image->iterations == 0)
13663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13664 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13668 " Image iterations: %.20g",(double) image->iterations);
13670 (void) WriteBlob(image,14,chunk);
13671 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13674 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13676 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13677 mng_info->equal_srgbs)
13680 Write MNG sRGB chunk
13682 (void) WriteBlobMSBULong(image,1L);
13683 PNGType(chunk,mng_sRGB);
13684 LogPNGChunk(logging,mng_sRGB,1L);
13686 if (image->rendering_intent != UndefinedIntent)
13687 chunk[4]=(unsigned char)
13688 Magick_RenderingIntent_to_PNG_RenderingIntent(
13689 (image->rendering_intent));
13692 chunk[4]=(unsigned char)
13693 Magick_RenderingIntent_to_PNG_RenderingIntent(
13694 (PerceptualIntent));
13696 (void) WriteBlob(image,5,chunk);
13697 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13698 mng_info->have_write_global_srgb=MagickTrue;
13703 if (image->gamma && mng_info->equal_gammas)
13706 Write MNG gAMA chunk
13708 (void) WriteBlobMSBULong(image,4L);
13709 PNGType(chunk,mng_gAMA);
13710 LogPNGChunk(logging,mng_gAMA,4L);
13711 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13712 (void) WriteBlob(image,8,chunk);
13713 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13714 mng_info->have_write_global_gama=MagickTrue;
13716 if (mng_info->equal_chrms)
13722 Write MNG cHRM chunk
13724 (void) WriteBlobMSBULong(image,32L);
13725 PNGType(chunk,mng_cHRM);
13726 LogPNGChunk(logging,mng_cHRM,32L);
13727 primary=image->chromaticity.white_point;
13728 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13729 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13730 primary=image->chromaticity.red_primary;
13731 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13732 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13733 primary=image->chromaticity.green_primary;
13734 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13735 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13736 primary=image->chromaticity.blue_primary;
13737 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13738 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13739 (void) WriteBlob(image,36,chunk);
13740 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13741 mng_info->have_write_global_chrm=MagickTrue;
13744 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13747 Write MNG pHYs chunk
13749 (void) WriteBlobMSBULong(image,9L);
13750 PNGType(chunk,mng_pHYs);
13751 LogPNGChunk(logging,mng_pHYs,9L);
13753 if (image->units == PixelsPerInchResolution)
13755 PNGLong(chunk+4,(png_uint_32)
13756 (image->resolution.x*100.0/2.54+0.5));
13758 PNGLong(chunk+8,(png_uint_32)
13759 (image->resolution.y*100.0/2.54+0.5));
13766 if (image->units == PixelsPerCentimeterResolution)
13768 PNGLong(chunk+4,(png_uint_32)
13769 (image->resolution.x*100.0+0.5));
13771 PNGLong(chunk+8,(png_uint_32)
13772 (image->resolution.y*100.0+0.5));
13779 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13780 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13784 (void) WriteBlob(image,13,chunk);
13785 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13788 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13789 or does not cover the entire frame.
13791 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13792 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13793 (image->page.width+image->page.x < mng_info->page.width))
13794 || (image->page.height && (image->page.height+image->page.y
13795 < mng_info->page.height))))
13797 (void) WriteBlobMSBULong(image,6L);
13798 PNGType(chunk,mng_BACK);
13799 LogPNGChunk(logging,mng_BACK,6L);
13800 red=ScaleQuantumToShort(image->background_color.red);
13801 green=ScaleQuantumToShort(image->background_color.green);
13802 blue=ScaleQuantumToShort(image->background_color.blue);
13803 PNGShort(chunk+4,red);
13804 PNGShort(chunk+6,green);
13805 PNGShort(chunk+8,blue);
13806 (void) WriteBlob(image,10,chunk);
13807 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13808 if (mng_info->equal_backgrounds)
13810 (void) WriteBlobMSBULong(image,6L);
13811 PNGType(chunk,mng_bKGD);
13812 LogPNGChunk(logging,mng_bKGD,6L);
13813 (void) WriteBlob(image,10,chunk);
13814 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13818 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13819 if ((need_local_plte == MagickFalse) &&
13820 (image->storage_class == PseudoClass) &&
13821 (all_images_are_gray == MagickFalse))
13827 Write MNG PLTE chunk
13829 data_length=3*image->colors;
13830 (void) WriteBlobMSBULong(image,data_length);
13831 PNGType(chunk,mng_PLTE);
13832 LogPNGChunk(logging,mng_PLTE,data_length);
13834 for (i=0; i < (ssize_t) image->colors; i++)
13836 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13837 image->colormap[i].red) & 0xff);
13838 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13839 image->colormap[i].green) & 0xff);
13840 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13841 image->colormap[i].blue) & 0xff);
13844 (void) WriteBlob(image,data_length+4,chunk);
13845 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13846 mng_info->have_write_global_plte=MagickTrue;
13852 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13853 defined(PNG_MNG_FEATURES_SUPPORTED)
13854 mng_info->equal_palettes=MagickFalse;
13858 if (mng_info->adjoin)
13860 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13861 defined(PNG_MNG_FEATURES_SUPPORTED)
13863 If we aren't using a global palette for the entire MNG, check to
13864 see if we can use one for two or more consecutive images.
13866 if (need_local_plte && use_global_plte && !all_images_are_gray)
13868 if (mng_info->IsPalette)
13871 When equal_palettes is true, this image has the same palette
13872 as the previous PseudoClass image
13874 mng_info->have_write_global_plte=mng_info->equal_palettes;
13875 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13876 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13879 Write MNG PLTE chunk
13884 data_length=3*image->colors;
13885 (void) WriteBlobMSBULong(image,data_length);
13886 PNGType(chunk,mng_PLTE);
13887 LogPNGChunk(logging,mng_PLTE,data_length);
13889 for (i=0; i < (ssize_t) image->colors; i++)
13891 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13892 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13893 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13896 (void) WriteBlob(image,data_length+4,chunk);
13897 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13898 (uInt) (data_length+4)));
13899 mng_info->have_write_global_plte=MagickTrue;
13903 mng_info->have_write_global_plte=MagickFalse;
13914 previous_x=mng_info->page.x;
13915 previous_y=mng_info->page.y;
13922 mng_info->page=image->page;
13923 if ((mng_info->page.x != previous_x) ||
13924 (mng_info->page.y != previous_y))
13926 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13927 PNGType(chunk,mng_DEFI);
13928 LogPNGChunk(logging,mng_DEFI,12L);
13929 chunk[4]=0; /* object 0 MSB */
13930 chunk[5]=0; /* object 0 LSB */
13931 chunk[6]=0; /* visible */
13932 chunk[7]=0; /* abstract */
13933 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13934 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13935 (void) WriteBlob(image,16,chunk);
13936 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13941 mng_info->write_mng=write_mng;
13943 if ((int) image->dispose >= 3)
13944 mng_info->framing_mode=3;
13946 if (mng_info->need_fram && mng_info->adjoin &&
13947 ((image->delay != mng_info->delay) ||
13948 (mng_info->framing_mode != mng_info->old_framing_mode)))
13950 if (image->delay == mng_info->delay)
13953 Write a MNG FRAM chunk with the new framing mode.
13955 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13956 PNGType(chunk,mng_FRAM);
13957 LogPNGChunk(logging,mng_FRAM,1L);
13958 chunk[4]=(unsigned char) mng_info->framing_mode;
13959 (void) WriteBlob(image,5,chunk);
13960 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13965 Write a MNG FRAM chunk with the delay.
13967 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13968 PNGType(chunk,mng_FRAM);
13969 LogPNGChunk(logging,mng_FRAM,10L);
13970 chunk[4]=(unsigned char) mng_info->framing_mode;
13971 chunk[5]=0; /* frame name separator (no name) */
13972 chunk[6]=2; /* flag for changing default delay */
13973 chunk[7]=0; /* flag for changing frame timeout */
13974 chunk[8]=0; /* flag for changing frame clipping */
13975 chunk[9]=0; /* flag for changing frame sync_id */
13976 PNGLong(chunk+10,(png_uint_32)
13977 ((mng_info->ticks_per_second*
13978 image->delay)/MagickMax(image->ticks_per_second,1)));
13979 (void) WriteBlob(image,14,chunk);
13980 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13981 mng_info->delay=(png_uint_32) image->delay;
13983 mng_info->old_framing_mode=mng_info->framing_mode;
13986 #if defined(JNG_SUPPORTED)
13987 if (image_info->compression == JPEGCompression)
13992 if (logging != MagickFalse)
13993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13994 " Writing JNG object.");
13995 /* To do: specify the desired alpha compression method. */
13996 write_info=CloneImageInfo(image_info);
13997 write_info->compression=UndefinedCompression;
13998 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13999 write_info=DestroyImageInfo(write_info);
14004 if (logging != MagickFalse)
14005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14006 " Writing PNG object.");
14008 mng_info->need_blob = MagickFalse;
14009 mng_info->ping_preserve_colormap = MagickFalse;
14011 /* We don't want any ancillary chunks written */
14012 mng_info->ping_exclude_bKGD=MagickTrue;
14013 mng_info->ping_exclude_caNv=MagickTrue;
14014 mng_info->ping_exclude_cHRM=MagickTrue;
14015 mng_info->ping_exclude_date=MagickTrue;
14016 mng_info->ping_exclude_EXIF=MagickTrue;
14017 mng_info->ping_exclude_gAMA=MagickTrue;
14018 mng_info->ping_exclude_iCCP=MagickTrue;
14019 /* mng_info->ping_exclude_iTXt=MagickTrue; */
14020 mng_info->ping_exclude_oFFs=MagickTrue;
14021 mng_info->ping_exclude_pHYs=MagickTrue;
14022 mng_info->ping_exclude_sRGB=MagickTrue;
14023 mng_info->ping_exclude_tEXt=MagickTrue;
14024 mng_info->ping_exclude_tRNS=MagickTrue;
14025 mng_info->ping_exclude_zCCP=MagickTrue;
14026 mng_info->ping_exclude_zTXt=MagickTrue;
14028 status=WriteOnePNGImage(mng_info,image_info,image,exception);
14031 if (status == MagickFalse)
14033 mng_info=MngInfoFreeStruct(mng_info);
14034 (void) CloseBlob(image);
14035 return(MagickFalse);
14037 (void) CatchImageException(image);
14038 if (GetNextImageInList(image) == (Image *) NULL)
14040 image=SyncNextImageInList(image);
14041 status=SetImageProgress(image,SaveImagesTag,scene++,
14042 GetImageListLength(image));
14044 if (status == MagickFalse)
14047 } while (mng_info->adjoin);
14051 while (GetPreviousImageInList(image) != (Image *) NULL)
14052 image=GetPreviousImageInList(image);
14054 Write the MEND chunk.
14056 (void) WriteBlobMSBULong(image,0x00000000L);
14057 PNGType(chunk,mng_MEND);
14058 LogPNGChunk(logging,mng_MEND,0L);
14059 (void) WriteBlob(image,4,chunk);
14060 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14063 Relinquish resources.
14065 (void) CloseBlob(image);
14066 mng_info=MngInfoFreeStruct(mng_info);
14068 if (logging != MagickFalse)
14069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14071 return(MagickTrue);
14073 #else /* PNG_LIBPNG_VER > 10011 */
14075 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14079 printf("Your PNG library is too old: You have libpng-%s\n",
14080 PNG_LIBPNG_VER_STRING);
14082 ThrowBinaryException(CoderError,"PNG library is too old",
14083 image_info->filename);
14086 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14089 return(WritePNGImage(image_info,image));
14091 #endif /* PNG_LIBPNG_VER > 10011 */