2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2013 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 % http://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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
106 /* ImageMagick differences */
107 #define first_scene scene
109 #if PNG_LIBPNG_VER > 10011
111 Optional declarations. Define or undefine them as you like.
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116 Features under construction. Define these to work on them.
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
132 /* Macros for left-bit-replication to ensure that pixels
133 * and PixelInfos all have the same image->depth, and for use
134 * in PNG8 quantization.
138 /* LBR01: Replicate top bit */
140 #define LBR01PacketRed(pixelpacket) \
141 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
144 #define LBR01PacketGreen(pixelpacket) \
145 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
148 #define LBR01PacketBlue(pixelpacket) \
149 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
152 #define LBR01PacketAlpha(pixelpacket) \
153 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
156 #define LBR01PacketRGB(pixelpacket) \
158 LBR01PacketRed((pixelpacket)); \
159 LBR01PacketGreen((pixelpacket)); \
160 LBR01PacketBlue((pixelpacket)); \
163 #define LBR01PacketRGBO(pixelpacket) \
165 LBR01PacketRGB((pixelpacket)); \
166 LBR01PacketAlpha((pixelpacket)); \
169 #define LBR01PixelRed(pixel) \
170 (SetPixelRed(image, \
171 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
172 0 : QuantumRange,(pixel)));
174 #define LBR01PixelGreen(pixel) \
175 (SetPixelGreen(image, \
176 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177 0 : QuantumRange,(pixel)));
179 #define LBR01PixelBlue(pixel) \
180 (SetPixelBlue(image, \
181 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
182 0 : QuantumRange,(pixel)));
184 #define LBR01PixelAlpha(pixel) \
185 (SetPixelAlpha(image, \
186 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
187 0 : QuantumRange,(pixel)));
189 #define LBR01PixelRGB(pixel) \
191 LBR01PixelRed((pixel)); \
192 LBR01PixelGreen((pixel)); \
193 LBR01PixelBlue((pixel)); \
196 #define LBR01PixelRGBA(pixel) \
198 LBR01PixelRGB((pixel)); \
199 LBR01PixelAlpha((pixel)); \
202 /* LBR02: Replicate top 2 bits */
204 #define LBR02PacketRed(pixelpacket) \
206 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
207 (pixelpacket).red=ScaleCharToQuantum( \
208 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 #define LBR02PacketGreen(pixelpacket) \
212 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
213 (pixelpacket).green=ScaleCharToQuantum( \
214 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 #define LBR02PacketBlue(pixelpacket) \
218 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
219 (pixelpacket).blue=ScaleCharToQuantum( \
220 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 #define LBR02PacketAlpha(pixelpacket) \
224 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
225 (pixelpacket).alpha=ScaleCharToQuantum( \
226 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
229 #define LBR02PacketRGB(pixelpacket) \
231 LBR02PacketRed((pixelpacket)); \
232 LBR02PacketGreen((pixelpacket)); \
233 LBR02PacketBlue((pixelpacket)); \
236 #define LBR02PacketRGBO(pixelpacket) \
238 LBR02PacketRGB((pixelpacket)); \
239 LBR02PacketAlpha((pixelpacket)); \
242 #define LBR02PixelRed(pixel) \
244 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
246 SetPixelRed(image, ScaleCharToQuantum( \
247 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
250 #define LBR02PixelGreen(pixel) \
252 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
254 SetPixelGreen(image, ScaleCharToQuantum( \
255 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
258 #define LBR02PixelBlue(pixel) \
260 unsigned char lbr_bits= \
261 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
262 SetPixelBlue(image, ScaleCharToQuantum( \
263 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
266 #define LBR02PixelAlpha(pixel) \
268 unsigned char lbr_bits= \
269 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
270 SetPixelAlpha(image, ScaleCharToQuantum( \
271 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
275 #define LBR02PixelRGB(pixel) \
277 LBR02PixelRed((pixel)); \
278 LBR02PixelGreen((pixel)); \
279 LBR02PixelBlue((pixel)); \
282 #define LBR02PixelRGBA(pixel) \
284 LBR02PixelRGB((pixel)); \
285 LBR02PixelAlpha((pixel)); \
288 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
289 PNG8 quantization) */
291 #define LBR03PacketRed(pixelpacket) \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
294 (pixelpacket).red=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 #define LBR03PacketGreen(pixelpacket) \
299 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
300 (pixelpacket).green=ScaleCharToQuantum( \
301 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 #define LBR03PacketBlue(pixelpacket) \
305 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
306 (pixelpacket).blue=ScaleCharToQuantum( \
307 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
310 #define LBR03PacketRGB(pixelpacket) \
312 LBR03PacketRed((pixelpacket)); \
313 LBR03PacketGreen((pixelpacket)); \
314 LBR03PacketBlue((pixelpacket)); \
317 #define LBR03PixelRed(pixel) \
319 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
321 SetPixelRed(image, ScaleCharToQuantum( \
322 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
324 #define LBR03Green(pixel) \
326 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
328 SetPixelGreen(image, ScaleCharToQuantum( \
329 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
331 #define LBR03Blue(pixel) \
333 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
335 SetPixelBlue(image, ScaleCharToQuantum( \
336 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
339 #define LBR03RGB(pixel) \
341 LBR03PixelRed((pixel)); \
342 LBR03Green((pixel)); \
343 LBR03Blue((pixel)); \
346 /* LBR04: Replicate top 4 bits */
348 #define LBR04PacketRed(pixelpacket) \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
351 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
353 #define LBR04PacketGreen(pixelpacket) \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
356 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
358 #define LBR04PacketBlue(pixelpacket) \
360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
361 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
363 #define LBR04PacketAlpha(pixelpacket) \
365 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
366 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
369 #define LBR04PacketRGB(pixelpacket) \
371 LBR04PacketRed((pixelpacket)); \
372 LBR04PacketGreen((pixelpacket)); \
373 LBR04PacketBlue((pixelpacket)); \
376 #define LBR04PacketRGBO(pixelpacket) \
378 LBR04PacketRGB((pixelpacket)); \
379 LBR04PacketAlpha((pixelpacket)); \
382 #define LBR04PixelRed(pixel) \
384 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
387 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
389 #define LBR04PixelGreen(pixel) \
391 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
393 SetPixelGreen(image,\
394 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
396 #define LBR04PixelBlue(pixel) \
398 unsigned char lbr_bits= \
399 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
401 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
403 #define LBR04PixelAlpha(pixel) \
405 unsigned char lbr_bits= \
406 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
407 SetPixelAlpha(image,\
408 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
411 #define LBR04PixelRGB(pixel) \
413 LBR04PixelRed((pixel)); \
414 LBR04PixelGreen((pixel)); \
415 LBR04PixelBlue((pixel)); \
418 #define LBR04PixelRGBA(pixel) \
420 LBR04PixelRGB((pixel)); \
421 LBR04PixelAlpha((pixel)); \
425 #if MAGICKCORE_QUANTUM_DEPTH > 8
426 /* LBR08: Replicate top 8 bits */
428 #define LBR08PacketRed(pixelpacket) \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
431 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
433 #define LBR08PacketGreen(pixelpacket) \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
436 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
438 #define LBR08PacketBlue(pixelpacket) \
440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
441 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
443 #define LBR08PacketAlpha(pixelpacket) \
445 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
446 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
449 #define LBR08PacketRGB(pixelpacket) \
451 LBR08PacketRed((pixelpacket)); \
452 LBR08PacketGreen((pixelpacket)); \
453 LBR08PacketBlue((pixelpacket)); \
456 #define LBR08PacketRGBO(pixelpacket) \
458 LBR08PacketRGB((pixelpacket)); \
459 LBR08PacketAlpha((pixelpacket)); \
462 #define LBR08PixelRed(pixel) \
464 unsigned char lbr_bits= \
465 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
467 ScaleCharToQuantum((lbr_bits)), (pixel)); \
469 #define LBR08PixelGreen(pixel) \
471 unsigned char lbr_bits= \
472 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
473 SetPixelGreen(image,\
474 ScaleCharToQuantum((lbr_bits)), (pixel)); \
476 #define LBR08PixelBlue(pixel) \
478 unsigned char lbr_bits= \
479 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
481 ScaleCharToQuantum((lbr_bits)), (pixel)); \
483 #define LBR08PixelAlpha(pixel) \
485 unsigned char lbr_bits= \
486 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
487 SetPixelAlpha(image,\
488 ScaleCharToQuantum((lbr_bits)), (pixel)); \
491 #define LBR08PixelRGB(pixel) \
493 LBR08PixelRed((pixel)); \
494 LBR08PixelGreen((pixel)); \
495 LBR08PixelBlue((pixel)); \
498 #define LBR08PixelRGBA(pixel) \
500 LBR08PixelRGB((pixel)); \
501 LBR08PixelAlpha((pixel)); \
503 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
506 #if MAGICKCORE_QUANTUM_DEPTH > 16
507 /* LBR16: Replicate top 16 bits */
509 #define LBR16PacketRed(pixelpacket) \
511 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
512 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
514 #define LBR16PacketGreen(pixelpacket) \
516 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
517 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
519 #define LBR16PacketBlue(pixelpacket) \
521 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
522 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
524 #define LBR16PacketAlpha(pixelpacket) \
526 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
527 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
530 #define LBR16PacketRGB(pixelpacket) \
532 LBR16PacketRed((pixelpacket)); \
533 LBR16PacketGreen((pixelpacket)); \
534 LBR16PacketBlue((pixelpacket)); \
537 #define LBR16PacketRGBO(pixelpacket) \
539 LBR16PacketRGB((pixelpacket)); \
540 LBR16PacketAlpha((pixelpacket)); \
543 #define LBR16PixelRed(pixel) \
545 unsigned short lbr_bits= \
546 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
550 #define LBR16PixelGreen(pixel) \
552 unsigned short lbr_bits= \
553 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
554 SetPixelGreen(image,\
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
557 #define LBR16PixelBlue(pixel) \
559 unsigned short lbr_bits= \
560 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
564 #define LBR16PixelAlpha(pixel) \
566 unsigned short lbr_bits= \
567 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
568 SetPixelAlpha(image,\
569 ScaleShortToQuantum((lbr_bits)),(pixel)); \
572 #define LBR16PixelRGB(pixel) \
574 LBR16PixelRed((pixel)); \
575 LBR16PixelGreen((pixel)); \
576 LBR16PixelBlue((pixel)); \
579 #define LBR16PixelRGBA(pixel) \
581 LBR16PixelRGB((pixel)); \
582 LBR16PixelAlpha((pixel)); \
584 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
587 Establish thread safety.
588 setjmp/longjmp is claimed to be safe on these platforms:
589 setjmp/longjmp is alleged to be unsafe on these platforms:
591 #ifndef SETJMP_IS_THREAD_SAFE
592 #define PNG_SETJMP_NOT_THREAD_SAFE
595 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
597 *ping_semaphore = (SemaphoreInfo *) NULL;
601 This temporary until I set up malloc'ed object attributes array.
602 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
605 #define MNG_MAX_OBJECTS 256
608 If this not defined, spec is interpreted strictly. If it is
609 defined, an attempt will be made to recover from some errors,
611 o global PLTE too short
616 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
617 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
618 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
619 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
620 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
621 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
622 will be enabled by default in libpng-1.2.0.
624 #ifdef PNG_MNG_FEATURES_SUPPORTED
625 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
626 # define PNG_READ_EMPTY_PLTE_SUPPORTED
628 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
629 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
634 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
635 This macro is only defined in libpng-1.0.3 and later.
636 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
638 #ifndef PNG_UINT_31_MAX
639 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
643 Constant strings for known chunk types. If you need to add a chunk,
644 add a string holding the name here. To make the code more
645 portable, we use ASCII numbers like this, not characters.
648 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
649 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
650 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
651 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
652 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
653 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
654 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
655 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
656 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
657 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
658 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
659 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
660 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
661 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
662 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
663 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
664 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
665 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
666 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
667 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
668 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
669 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
670 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
671 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
672 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
673 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
674 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
675 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
676 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
677 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
678 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
679 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
680 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
681 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
683 #if defined(JNG_SUPPORTED)
684 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
685 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
686 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
687 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
688 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
689 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
693 Other known chunks that are not yet supported by ImageMagick:
694 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
695 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
696 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
697 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
698 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
699 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
700 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
701 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
704 typedef struct _MngBox
713 typedef struct _MngPair
720 #ifdef MNG_OBJECT_BUFFERS
721 typedef struct _MngBuffer
753 typedef struct _MngInfo
756 #ifdef MNG_OBJECT_BUFFERS
758 *ob[MNG_MAX_OBJECTS];
769 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
770 bytes_in_read_buffer,
776 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
777 defined(PNG_MNG_FEATURES_SUPPORTED)
789 have_saved_bkgd_index,
790 have_write_global_chrm,
791 have_write_global_gama,
792 have_write_global_plte,
793 have_write_global_srgb,
807 x_off[MNG_MAX_OBJECTS],
808 y_off[MNG_MAX_OBJECTS];
814 object_clip[MNG_MAX_OBJECTS];
817 /* These flags could be combined into one byte */
818 exists[MNG_MAX_OBJECTS],
819 frozen[MNG_MAX_OBJECTS],
821 invisible[MNG_MAX_OBJECTS],
822 viewable[MNG_MAX_OBJECTS];
834 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
852 global_x_pixels_per_unit,
853 global_y_pixels_per_unit,
863 global_phys_unit_type,
878 write_png_compression_level,
879 write_png_compression_strategy,
880 write_png_compression_filter,
887 #ifdef MNG_BASI_SUPPORTED
895 basi_compression_method,
897 basi_interlace_method,
920 /* Added at version 6.6.6-7 */
928 /* ping_exclude_iTXt, */
935 ping_exclude_zCCP, /* hex-encoded iCCP */
937 ping_preserve_colormap;
943 Forward declarations.
945 static MagickBooleanType
946 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
948 static MagickBooleanType
949 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
951 #if defined(JNG_SUPPORTED)
952 static MagickBooleanType
953 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
956 #if PNG_LIBPNG_VER > 10011
959 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
960 static MagickBooleanType
961 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
963 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
965 * This is true if the high byte and the next highest byte of
966 * each sample of the image, the colormap, and the background color
967 * are equal to each other. We check this by seeing if the samples
968 * are unchanged when we scale them down to 8 and back up to Quantum.
970 * We don't use the method GetImageDepth() because it doesn't check
971 * background and doesn't handle PseudoClass specially.
974 #define QuantumToCharToQuantumEqQuantum(quantum) \
975 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
978 ok_to_reduce=MagickFalse;
980 if (image->depth >= 16)
987 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
988 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
989 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
990 MagickTrue : MagickFalse;
992 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
996 for (indx=0; indx < (ssize_t) image->colors; indx++)
999 QuantumToCharToQuantumEqQuantum(
1000 image->colormap[indx].red) &&
1001 QuantumToCharToQuantumEqQuantum(
1002 image->colormap[indx].green) &&
1003 QuantumToCharToQuantumEqQuantum(
1004 image->colormap[indx].blue)) ?
1005 MagickTrue : MagickFalse;
1007 if (ok_to_reduce == MagickFalse)
1012 if ((ok_to_reduce != MagickFalse) &&
1013 (image->storage_class != PseudoClass))
1021 for (y=0; y < (ssize_t) image->rows; y++)
1023 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1025 if (p == (const Quantum *) NULL)
1027 ok_to_reduce = MagickFalse;
1031 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1034 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1035 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1036 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1037 MagickTrue : MagickFalse;
1039 if (ok_to_reduce == MagickFalse)
1042 p+=GetPixelChannels(image);
1049 if (ok_to_reduce != MagickFalse)
1051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1052 " OK to reduce PNG bit depth to 8 without loss of info");
1056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1057 " Not OK to reduce PNG bit depth to 8 without loss of info");
1061 return ok_to_reduce;
1063 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1065 static const char* PngColorTypeToString(const unsigned int color_type)
1068 *result = "Unknown";
1072 case PNG_COLOR_TYPE_GRAY:
1075 case PNG_COLOR_TYPE_GRAY_ALPHA:
1076 result = "Gray+Alpha";
1078 case PNG_COLOR_TYPE_PALETTE:
1081 case PNG_COLOR_TYPE_RGB:
1084 case PNG_COLOR_TYPE_RGB_ALPHA:
1085 result = "RGB+Alpha";
1093 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1097 case PerceptualIntent:
1100 case RelativeIntent:
1103 case SaturationIntent:
1106 case AbsoluteIntent:
1114 static RenderingIntent
1115 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1117 switch (ping_intent)
1120 return PerceptualIntent;
1123 return RelativeIntent;
1126 return SaturationIntent;
1129 return AbsoluteIntent;
1132 return UndefinedIntent;
1137 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1139 switch (ping_intent)
1142 return "Perceptual Intent";
1145 return "Relative Intent";
1148 return "Saturation Intent";
1151 return "Absolute Intent";
1154 return "Undefined Intent";
1158 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1167 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1169 switch (ping_colortype)
1187 return "UndefinedColorType";
1192 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1199 #endif /* PNG_LIBPNG_VER > 10011 */
1200 #endif /* MAGICKCORE_PNG_DELEGATE */
1203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 % IsMNG() returns MagickTrue if the image format type, identified by the
1214 % magick string, is MNG.
1216 % The format of the IsMNG method is:
1218 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1220 % A description of each parameter follows:
1222 % o magick: compare image format pattern against these bytes.
1224 % o length: Specifies the length of the magick string.
1228 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1231 return(MagickFalse);
1233 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1236 return(MagickFalse);
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1250 % IsJNG() returns MagickTrue if the image format type, identified by the
1251 % magick string, is JNG.
1253 % The format of the IsJNG method is:
1255 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1257 % A description of each parameter follows:
1259 % o magick: compare image format pattern against these bytes.
1261 % o length: Specifies the length of the magick string.
1265 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1268 return(MagickFalse);
1270 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1273 return(MagickFalse);
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287 % IsPNG() returns MagickTrue if the image format type, identified by the
1288 % magick string, is PNG.
1290 % The format of the IsPNG method is:
1292 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1294 % A description of each parameter follows:
1296 % o magick: compare image format pattern against these bytes.
1298 % o length: Specifies the length of the magick string.
1301 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1304 return(MagickFalse);
1306 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1309 return(MagickFalse);
1312 #if defined(MAGICKCORE_PNG_DELEGATE)
1313 #if defined(__cplusplus) || defined(c_plusplus)
1317 #if (PNG_LIBPNG_VER > 10011)
1318 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1323 assert(image != (Image *) NULL);
1324 assert(image->signature == MagickSignature);
1325 buffer[0]=(unsigned char) (value >> 24);
1326 buffer[1]=(unsigned char) (value >> 16);
1327 buffer[2]=(unsigned char) (value >> 8);
1328 buffer[3]=(unsigned char) value;
1329 return((size_t) WriteBlob(image,4,buffer));
1332 static void PNGLong(png_bytep p,png_uint_32 value)
1334 *p++=(png_byte) ((value >> 24) & 0xff);
1335 *p++=(png_byte) ((value >> 16) & 0xff);
1336 *p++=(png_byte) ((value >> 8) & 0xff);
1337 *p++=(png_byte) (value & 0xff);
1340 #if defined(JNG_SUPPORTED)
1341 static void PNGsLong(png_bytep p,png_int_32 value)
1343 *p++=(png_byte) ((value >> 24) & 0xff);
1344 *p++=(png_byte) ((value >> 16) & 0xff);
1345 *p++=(png_byte) ((value >> 8) & 0xff);
1346 *p++=(png_byte) (value & 0xff);
1350 static void PNGShort(png_bytep p,png_uint_16 value)
1352 *p++=(png_byte) ((value >> 8) & 0xff);
1353 *p++=(png_byte) (value & 0xff);
1356 static void PNGType(png_bytep p,png_bytep type)
1358 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1361 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1364 if (logging != MagickFalse)
1365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1366 " Writing %c%c%c%c chunk, length: %.20g",
1367 type[0],type[1],type[2],type[3],(double) length);
1369 #endif /* PNG_LIBPNG_VER > 10011 */
1371 #if defined(__cplusplus) || defined(c_plusplus)
1375 #if PNG_LIBPNG_VER > 10011
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1381 % R e a d P N G I m a g e %
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1388 % Multiple-image Network Graphics (MNG) image file and returns it. It
1389 % allocates the memory necessary for the new Image structure and returns a
1390 % pointer to the new image or set of images.
1392 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1394 % The format of the ReadPNGImage method is:
1396 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1398 % A description of each parameter follows:
1400 % o image_info: the image info.
1402 % o exception: return any errors or warnings in this structure.
1404 % To do, more or less in chronological order (as of version 5.5.2,
1405 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1407 % Get 16-bit cheap transparency working.
1409 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1411 % Preserve all unknown and not-yet-handled known chunks found in input
1412 % PNG file and copy them into output PNG files according to the PNG
1415 % (At this point, PNG encoding should be in full MNG compliance)
1417 % Provide options for choice of background to use when the MNG BACK
1418 % chunk is not present or is not mandatory (i.e., leave transparent,
1419 % user specified, MNG BACK, PNG bKGD)
1421 % Implement LOOP/ENDL [done, but could do discretionary loops more
1422 % efficiently by linking in the duplicate frames.].
1424 % Decode and act on the MHDR simplicity profile (offer option to reject
1425 % files or attempt to process them anyway when the profile isn't LC or VLC).
1427 % Upgrade to full MNG without Delta-PNG.
1429 % o BACK [done a while ago except for background image ID]
1430 % o MOVE [done 15 May 1999]
1431 % o CLIP [done 15 May 1999]
1432 % o DISC [done 19 May 1999]
1433 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1434 % o SEEK [partially done 19 May 1999 (discard function only)]
1438 % o MNG-level tEXt/iTXt/zTXt
1443 % o iTXt (wait for libpng implementation).
1445 % Use the scene signature to discover when an identical scene is
1446 % being reused, and just point to the original image->exception instead
1447 % of storing another set of pixels. This not specific to MNG
1448 % but could be applied generally.
1450 % Upgrade to full MNG with Delta-PNG.
1452 % JNG tEXt/iTXt/zTXt
1454 % We will not attempt to read files containing the CgBI chunk.
1455 % They are really Xcode files meant for display on the iPhone.
1456 % These are not valid PNG files and it is impossible to recover
1457 % the original PNG from files that have been converted to Xcode-PNG,
1458 % since irretrievable loss of color data has occurred due to the
1459 % use of premultiplied alpha.
1462 #if defined(__cplusplus) || defined(c_plusplus)
1467 This the function that does the actual reading of data. It is
1468 the same as the one supplied in libpng, except that it receives the
1469 datastream from the ReadBlob() function instead of standard input.
1471 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1476 image=(Image *) png_get_io_ptr(png_ptr);
1482 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1483 if (check != length)
1488 (void) FormatLocaleString(msg,MaxTextExtent,
1489 "Expected %.20g bytes; found %.20g bytes",(double) length,
1491 png_warning(png_ptr,msg);
1492 png_error(png_ptr,"Read Exception");
1497 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1498 !defined(PNG_MNG_FEATURES_SUPPORTED)
1499 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1500 * older than libpng-1.0.3a, which was the first to allow the empty
1501 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1502 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1503 * encountered after an empty PLTE, so we have to look ahead for bKGD
1504 * chunks and remove them from the datastream that is passed to libpng,
1505 * and store their contents for later use.
1507 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1522 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1523 image=(Image *) mng_info->image;
1524 while (mng_info->bytes_in_read_buffer && length)
1526 data[i]=mng_info->read_buffer[i];
1527 mng_info->bytes_in_read_buffer--;
1533 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1535 if (check != length)
1536 png_error(png_ptr,"Read Exception");
1540 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1543 check=(png_size_t) ReadBlob(image,(size_t) length,
1544 (char *) mng_info->read_buffer);
1545 mng_info->read_buffer[4]=0;
1546 mng_info->bytes_in_read_buffer=4;
1547 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1548 mng_info->found_empty_plte=MagickTrue;
1549 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1551 mng_info->found_empty_plte=MagickFalse;
1552 mng_info->have_saved_bkgd_index=MagickFalse;
1556 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1559 check=(png_size_t) ReadBlob(image,(size_t) length,
1560 (char *) mng_info->read_buffer);
1561 mng_info->read_buffer[4]=0;
1562 mng_info->bytes_in_read_buffer=4;
1563 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1564 if (mng_info->found_empty_plte)
1567 Skip the bKGD data byte and CRC.
1570 ReadBlob(image,5,(char *) mng_info->read_buffer);
1571 check=(png_size_t) ReadBlob(image,(size_t) length,
1572 (char *) mng_info->read_buffer);
1573 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1574 mng_info->have_saved_bkgd_index=MagickTrue;
1575 mng_info->bytes_in_read_buffer=0;
1583 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1588 image=(Image *) png_get_io_ptr(png_ptr);
1594 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1596 if (check != length)
1597 png_error(png_ptr,"WriteBlob Failed");
1601 static void png_flush_data(png_structp png_ptr)
1606 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1607 static int PalettesAreEqual(Image *a,Image *b)
1612 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1613 return((int) MagickFalse);
1615 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1616 return((int) MagickFalse);
1618 if (a->colors != b->colors)
1619 return((int) MagickFalse);
1621 for (i=0; i < (ssize_t) a->colors; i++)
1623 if ((a->colormap[i].red != b->colormap[i].red) ||
1624 (a->colormap[i].green != b->colormap[i].green) ||
1625 (a->colormap[i].blue != b->colormap[i].blue))
1626 return((int) MagickFalse);
1629 return((int) MagickTrue);
1633 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1635 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1636 mng_info->exists[i] && !mng_info->frozen[i])
1638 #ifdef MNG_OBJECT_BUFFERS
1639 if (mng_info->ob[i] != (MngBuffer *) NULL)
1641 if (mng_info->ob[i]->reference_count > 0)
1642 mng_info->ob[i]->reference_count--;
1644 if (mng_info->ob[i]->reference_count == 0)
1646 if (mng_info->ob[i]->image != (Image *) NULL)
1647 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1649 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1652 mng_info->ob[i]=(MngBuffer *) NULL;
1654 mng_info->exists[i]=MagickFalse;
1655 mng_info->invisible[i]=MagickFalse;
1656 mng_info->viewable[i]=MagickFalse;
1657 mng_info->frozen[i]=MagickFalse;
1658 mng_info->x_off[i]=0;
1659 mng_info->y_off[i]=0;
1660 mng_info->object_clip[i].left=0;
1661 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1662 mng_info->object_clip[i].top=0;
1663 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1667 static void MngInfoFreeStruct(MngInfo *mng_info,
1668 MagickBooleanType *have_mng_structure)
1670 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1675 for (i=1; i < MNG_MAX_OBJECTS; i++)
1676 MngInfoDiscardObject(mng_info,i);
1678 if (mng_info->global_plte != (png_colorp) NULL)
1679 mng_info->global_plte=(png_colorp)
1680 RelinquishMagickMemory(mng_info->global_plte);
1682 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1683 *have_mng_structure=MagickFalse;
1687 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1693 if (box.left < box2.left)
1696 if (box.top < box2.top)
1699 if (box.right > box2.right)
1700 box.right=box2.right;
1702 if (box.bottom > box2.bottom)
1703 box.bottom=box2.bottom;
1708 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1714 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1716 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1717 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1718 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1719 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1720 if (delta_type != 0)
1722 box.left+=previous_box.left;
1723 box.right+=previous_box.right;
1724 box.top+=previous_box.top;
1725 box.bottom+=previous_box.bottom;
1731 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1737 Read two ssize_ts from CLON, MOVE or PAST chunk
1739 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1740 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1742 if (delta_type != 0)
1744 pair.a+=previous_pair.a;
1745 pair.b+=previous_pair.b;
1751 static long mng_get_long(unsigned char *p)
1753 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1756 typedef struct _PNGErrorInfo
1765 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1776 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1777 image=error_info->image;
1778 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1783 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1784 "`%s'",image->filename);
1786 #if (PNG_LIBPNG_VER < 10500)
1787 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1788 * are building with libpng-1.4.x and can be ignored.
1790 longjmp(ping->jmpbuf,1);
1792 png_longjmp(ping,1);
1796 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1807 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1808 png_error(ping, message);
1810 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1811 image=error_info->image;
1812 exception=error_info->exception;
1813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1816 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1817 message,"`%s'",image->filename);
1820 #ifdef PNG_USER_MEM_SUPPORTED
1821 #if PNG_LIBPNG_VER >= 14000
1822 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1824 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1828 return((png_voidp) AcquireMagickMemory((size_t) size));
1832 Free a pointer. It is removed from the list at the same time.
1834 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1837 ptr=RelinquishMagickMemory(ptr);
1838 return((png_free_ptr) NULL);
1842 #if defined(__cplusplus) || defined(c_plusplus)
1847 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1848 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1853 register unsigned char
1867 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1868 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1869 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1870 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1871 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1875 /* look for newline */
1879 /* look for length */
1880 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1883 length=(png_uint_32) StringToLong(sp);
1885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1886 " length: %lu",(unsigned long) length);
1888 while (*sp != ' ' && *sp != '\n')
1891 /* allocate space */
1894 png_warning(ping,"invalid profile length");
1895 return(MagickFalse);
1898 profile=BlobToStringInfo((const void *) NULL,length);
1900 if (profile == (StringInfo *) NULL)
1902 png_warning(ping, "unable to copy profile");
1903 return(MagickFalse);
1906 /* copy profile, skipping white space and column 1 "=" signs */
1907 dp=GetStringInfoDatum(profile);
1910 for (i=0; i < (ssize_t) nibbles; i++)
1912 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1916 png_warning(ping, "ran out of profile data");
1917 profile=DestroyStringInfo(profile);
1918 return(MagickFalse);
1924 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1927 (*dp++)+=unhex[(int) *sp++];
1930 We have already read "Raw profile type.
1932 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1933 profile=DestroyStringInfo(profile);
1935 if (image_info->verbose)
1936 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1941 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1942 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1948 /* The unknown chunk structure contains the chunk data:
1953 Note that libpng has already taken care of the CRC handling.
1957 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1958 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1959 return(0); /* Did not recognize */
1961 /* recognized vpAg */
1963 if (chunk->size != 9)
1964 return(-1); /* Error return */
1966 if (chunk->data[8] != 0)
1967 return(0); /* ImageMagick requires pixel units */
1969 image=(Image *) png_get_user_chunk_ptr(ping);
1971 image->page.width=(size_t) ((chunk->data[0] << 24) |
1972 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1974 image->page.height=(size_t) ((chunk->data[4] << 24) |
1975 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1977 /* Return one of the following: */
1978 /* return(-n); chunk had an error */
1979 /* return(0); did not recognize */
1980 /* return(n); success */
1988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1992 % R e a d O n e P N G I m a g e %
1996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1998 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1999 % (minus the 8-byte signature) and returns it. It allocates the memory
2000 % necessary for the new Image structure and returns a pointer to the new
2003 % The format of the ReadOnePNGImage method is:
2005 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2006 % ExceptionInfo *exception)
2008 % A description of each parameter follows:
2010 % o mng_info: Specifies a pointer to a MngInfo structure.
2012 % o image_info: the image info.
2014 % o exception: return any errors or warnings in this structure.
2017 static Image *ReadOnePNGImage(MngInfo *mng_info,
2018 const ImageInfo *image_info, ExceptionInfo *exception)
2020 /* Read one PNG image */
2022 /* To do: Read the tIME chunk into the date:modify property */
2023 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2029 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2039 ping_interlace_method,
2040 ping_compression_method,
2092 register unsigned char
2110 *volatile ping_pixels;
2112 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2113 png_byte unused_chunks[]=
2115 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2116 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2117 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2118 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2119 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2120 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2121 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2122 /* ignore the APNG chunks */
2123 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2124 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2125 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2130 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2131 " Enter ReadOnePNGImage()");
2133 #if (PNG_LIBPNG_VER < 10200)
2134 if (image_info->verbose)
2135 printf("Your PNG library (libpng-%s) is rather old.\n",
2136 PNG_LIBPNG_VER_STRING);
2139 #if (PNG_LIBPNG_VER >= 10400)
2140 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2141 if (image_info->verbose)
2143 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2144 PNG_LIBPNG_VER_STRING);
2145 printf("Please update it.\n");
2151 quantum_info = (QuantumInfo *) NULL;
2152 image=mng_info->image;
2154 if (logging != MagickFalse)
2156 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " image->alpha_trait=%d",(int) image->alpha_trait);
2159 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2160 " image->rendering_intent=%d",(int) image->rendering_intent);
2162 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2163 " image->colorspace=%d",(int) image->colorspace);
2165 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2167 /* Set to an out-of-range color unless tRNS chunk is present */
2168 transparent_color.red=65537;
2169 transparent_color.green=65537;
2170 transparent_color.blue=65537;
2171 transparent_color.alpha=65537;
2176 num_raw_profiles = 0;
2178 ping_found_cHRM = MagickFalse;
2179 ping_found_gAMA = MagickFalse;
2180 ping_found_iCCP = MagickFalse;
2181 ping_found_sRGB = MagickFalse;
2184 Allocate the PNG structures
2186 #ifdef PNG_USER_MEM_SUPPORTED
2187 error_info.image=image;
2188 error_info.exception=exception;
2189 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2190 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2191 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2193 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2194 MagickPNGErrorHandler,MagickPNGWarningHandler);
2196 if (ping == (png_struct *) NULL)
2197 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2199 ping_info=png_create_info_struct(ping);
2201 if (ping_info == (png_info *) NULL)
2203 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2204 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2207 end_info=png_create_info_struct(ping);
2209 if (end_info == (png_info *) NULL)
2211 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2212 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2215 ping_pixels=(unsigned char *) NULL;
2217 if (setjmp(png_jmpbuf(ping)))
2220 PNG image is corrupt.
2222 png_destroy_read_struct(&ping,&ping_info,&end_info);
2224 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2225 UnlockSemaphoreInfo(ping_semaphore);
2228 if (ping_pixels != (unsigned char *) NULL)
2229 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2231 if (logging != MagickFalse)
2232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2233 " exit ReadOnePNGImage() with error.");
2235 if (image != (Image *) NULL)
2237 InheritException(exception,exception);
2241 return(GetFirstImageInList(image));
2244 /* { For navigation to end of SETJMP-protected block. Within this
2245 * block, use png_error() instead of Throwing an Exception, to ensure
2246 * that libpng is able to clean up, and that the semaphore is unlocked.
2249 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2250 LockSemaphoreInfo(ping_semaphore);
2254 Prepare PNG for reading.
2257 mng_info->image_found++;
2258 png_set_sig_bytes(ping,8);
2260 if (LocaleCompare(image_info->magick,"MNG") == 0)
2262 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2263 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2264 png_set_read_fn(ping,image,png_get_data);
2266 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2267 png_permit_empty_plte(ping,MagickTrue);
2268 png_set_read_fn(ping,image,png_get_data);
2270 mng_info->image=image;
2271 mng_info->bytes_in_read_buffer=0;
2272 mng_info->found_empty_plte=MagickFalse;
2273 mng_info->have_saved_bkgd_index=MagickFalse;
2274 png_set_read_fn(ping,mng_info,mng_get_data);
2280 png_set_read_fn(ping,image,png_get_data);
2282 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2283 /* Ignore unused chunks and all unknown chunks except for vpAg */
2284 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2285 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2286 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2287 (int)sizeof(unused_chunks)/5);
2288 /* Callback for other unknown chunks */
2289 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2292 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2293 # if (PNG_LIBPNG_VER >= 10400)
2294 /* Limit the size of the chunk storage cache used for sPLT, text,
2295 * and unknown chunks.
2297 png_set_chunk_cache_max(ping, 32767);
2301 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2302 /* Disable new libpng-1.5.10 feature */
2303 png_set_check_for_invalid_index (ping, 0);
2306 #if (PNG_LIBPNG_VER < 10400)
2307 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2308 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2309 /* Disable thread-unsafe features of pnggccrd */
2310 if (png_access_version_number() >= 10200)
2312 png_uint_32 mmx_disable_mask=0;
2313 png_uint_32 asm_flags;
2315 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2316 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2317 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2318 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2319 asm_flags=png_get_asm_flags(ping);
2320 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2325 png_read_info(ping,ping_info);
2327 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2328 &ping_bit_depth,&ping_color_type,
2329 &ping_interlace_method,&ping_compression_method,
2330 &ping_filter_method);
2332 ping_file_depth = ping_bit_depth;
2334 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2337 (void) png_get_bKGD(ping, ping_info, &ping_background);
2339 if (ping_bit_depth < 8)
2341 png_set_packing(ping);
2345 image->depth=ping_bit_depth;
2346 image->depth=GetImageQuantumDepth(image,MagickFalse);
2347 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2349 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2350 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2352 image->rendering_intent=UndefinedIntent;
2353 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2355 (void) ResetMagickMemory(&image->chromaticity,0,
2356 sizeof(image->chromaticity));
2359 if (logging != MagickFalse)
2361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2362 " PNG width: %.20g, height: %.20g",
2363 (double) ping_width, (double) ping_height);
2365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2366 " PNG color_type: %d, bit_depth: %d",
2367 ping_color_type, ping_bit_depth);
2369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2370 " PNG compression_method: %d",
2371 ping_compression_method);
2373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2374 " PNG interlace_method: %d, filter_method: %d",
2375 ping_interlace_method,ping_filter_method);
2378 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2380 ping_found_gAMA=MagickTrue;
2381 if (logging != MagickFalse)
2382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2383 " Found PNG gAMA chunk.");
2386 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2388 ping_found_cHRM=MagickTrue;
2389 if (logging != MagickFalse)
2390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2391 " Found PNG cHRM chunk.");
2394 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2396 ping_found_iCCP=MagickTrue;
2397 if (logging != MagickFalse)
2398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2399 " Found PNG iCCP chunk.");
2402 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2404 ping_found_sRGB=MagickTrue;
2405 if (logging != MagickFalse)
2406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2407 " Found PNG sRGB chunk.");
2410 #ifdef PNG_READ_iCCP_SUPPORTED
2411 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2416 #if (PNG_LIBPNG_VER < 10500)
2430 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2433 if (profile_length != 0)
2438 if (logging != MagickFalse)
2439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2440 " Reading PNG iCCP chunk.");
2441 profile=BlobToStringInfo(info,profile_length);
2442 if (profile == (StringInfo *) NULL)
2444 png_warning(ping, "ICC profile is NULL");
2445 profile=DestroyStringInfo(profile);
2449 (void) SetImageProfile(image,"icc",profile,exception);
2450 profile=DestroyStringInfo(profile);
2455 #if defined(PNG_READ_sRGB_SUPPORTED)
2457 if (mng_info->have_global_srgb)
2459 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2460 (mng_info->global_srgb_intent);
2463 if (png_get_sRGB(ping,ping_info,&intent))
2465 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2468 if (logging != MagickFalse)
2469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2470 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2475 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2476 if (mng_info->have_global_gama)
2477 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2479 if (png_get_gAMA(ping,ping_info,&file_gamma))
2481 image->gamma=(float) file_gamma;
2482 if (logging != MagickFalse)
2483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2484 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2488 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2490 if (mng_info->have_global_chrm != MagickFalse)
2492 (void) png_set_cHRM(ping,ping_info,
2493 mng_info->global_chrm.white_point.x,
2494 mng_info->global_chrm.white_point.y,
2495 mng_info->global_chrm.red_primary.x,
2496 mng_info->global_chrm.red_primary.y,
2497 mng_info->global_chrm.green_primary.x,
2498 mng_info->global_chrm.green_primary.y,
2499 mng_info->global_chrm.blue_primary.x,
2500 mng_info->global_chrm.blue_primary.y);
2504 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2506 (void) png_get_cHRM(ping,ping_info,
2507 &image->chromaticity.white_point.x,
2508 &image->chromaticity.white_point.y,
2509 &image->chromaticity.red_primary.x,
2510 &image->chromaticity.red_primary.y,
2511 &image->chromaticity.green_primary.x,
2512 &image->chromaticity.green_primary.y,
2513 &image->chromaticity.blue_primary.x,
2514 &image->chromaticity.blue_primary.y);
2516 ping_found_cHRM=MagickTrue;
2519 if (image->rendering_intent != UndefinedIntent)
2521 if (ping_found_sRGB != MagickTrue)
2523 png_set_sRGB(ping,ping_info,
2524 Magick_RenderingIntent_to_PNG_RenderingIntent
2525 (image->rendering_intent));
2526 png_set_gAMA(ping,ping_info,1.000f/2.200f);
2527 file_gamma=1.000f/2.200f;
2528 ping_found_sRGB=MagickTrue;
2529 ping_found_cHRM=MagickTrue;
2533 #if defined(PNG_oFFs_SUPPORTED)
2534 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2536 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2537 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2539 if (logging != MagickFalse)
2540 if (image->page.x || image->page.y)
2541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2542 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2543 image->page.x,(double) image->page.y);
2546 #if defined(PNG_pHYs_SUPPORTED)
2547 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2549 if (mng_info->have_global_phys)
2551 png_set_pHYs(ping,ping_info,
2552 mng_info->global_x_pixels_per_unit,
2553 mng_info->global_y_pixels_per_unit,
2554 mng_info->global_phys_unit_type);
2558 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2561 Set image resolution.
2563 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2565 image->resolution.x=(double) x_resolution;
2566 image->resolution.y=(double) y_resolution;
2568 if (unit_type == PNG_RESOLUTION_METER)
2570 image->units=PixelsPerCentimeterResolution;
2571 image->resolution.x=(double) x_resolution/100.0;
2572 image->resolution.y=(double) y_resolution/100.0;
2575 if (logging != MagickFalse)
2576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2577 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2578 (double) x_resolution,(double) y_resolution,unit_type);
2582 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2587 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2589 if ((number_colors == 0) &&
2590 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2592 if (mng_info->global_plte_length)
2594 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2595 (int) mng_info->global_plte_length);
2597 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2599 if (mng_info->global_trns_length)
2602 "global tRNS has more entries than global PLTE");
2606 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2607 (int) mng_info->global_trns_length,NULL);
2610 #ifdef PNG_READ_bKGD_SUPPORTED
2612 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2613 mng_info->have_saved_bkgd_index ||
2615 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2620 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2621 if (mng_info->have_saved_bkgd_index)
2622 background.index=mng_info->saved_bkgd_index;
2624 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2625 background.index=ping_background->index;
2627 background.red=(png_uint_16)
2628 mng_info->global_plte[background.index].red;
2630 background.green=(png_uint_16)
2631 mng_info->global_plte[background.index].green;
2633 background.blue=(png_uint_16)
2634 mng_info->global_plte[background.index].blue;
2636 background.gray=(png_uint_16)
2637 mng_info->global_plte[background.index].green;
2639 png_set_bKGD(ping,ping_info,&background);
2644 png_error(ping,"No global PLTE in file");
2648 #ifdef PNG_READ_bKGD_SUPPORTED
2649 if (mng_info->have_global_bkgd &&
2650 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2651 image->background_color=mng_info->mng_global_bkgd;
2653 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2659 Set image background color.
2661 if (logging != MagickFalse)
2662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2663 " Reading PNG bKGD chunk.");
2665 /* Scale background components to 16-bit, then scale
2668 if (logging != MagickFalse)
2669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670 " raw ping_background=(%d,%d,%d).",ping_background->red,
2671 ping_background->green,ping_background->blue);
2675 if (ping_file_depth == 1)
2678 else if (ping_file_depth == 2)
2681 else if (ping_file_depth == 4)
2684 if (ping_file_depth <= 8)
2687 ping_background->red *= bkgd_scale;
2688 ping_background->green *= bkgd_scale;
2689 ping_background->blue *= bkgd_scale;
2691 if (logging != MagickFalse)
2693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2694 " bkgd_scale=%d.",bkgd_scale);
2696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2697 " ping_background=(%d,%d,%d).",ping_background->red,
2698 ping_background->green,ping_background->blue);
2701 image->background_color.red=
2702 ScaleShortToQuantum(ping_background->red);
2704 image->background_color.green=
2705 ScaleShortToQuantum(ping_background->green);
2707 image->background_color.blue=
2708 ScaleShortToQuantum(ping_background->blue);
2710 image->background_color.alpha=OpaqueAlpha;
2712 if (logging != MagickFalse)
2713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2714 " image->background_color=(%.20g,%.20g,%.20g).",
2715 (double) image->background_color.red,
2716 (double) image->background_color.green,
2717 (double) image->background_color.blue);
2719 #endif /* PNG_READ_bKGD_SUPPORTED */
2721 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2724 Image has a tRNS chunk.
2732 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " Reading PNG tRNS chunk.");
2736 max_sample = (int) ((one << ping_file_depth) - 1);
2738 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2739 (int)ping_trans_color->gray > max_sample) ||
2740 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2741 ((int)ping_trans_color->red > max_sample ||
2742 (int)ping_trans_color->green > max_sample ||
2743 (int)ping_trans_color->blue > max_sample)))
2745 if (logging != MagickFalse)
2746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2747 " Ignoring PNG tRNS chunk with out-of-range sample.");
2748 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2749 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2750 image->alpha_trait=UndefinedPixelTrait;
2757 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2759 /* Scale transparent_color to short */
2760 transparent_color.red= scale_to_short*ping_trans_color->red;
2761 transparent_color.green= scale_to_short*ping_trans_color->green;
2762 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2763 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2765 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2767 if (logging != MagickFalse)
2769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2770 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2773 " scaled graylevel is %.20g.",transparent_color.alpha);
2775 transparent_color.red=transparent_color.alpha;
2776 transparent_color.green=transparent_color.alpha;
2777 transparent_color.blue=transparent_color.alpha;
2781 #if defined(PNG_READ_sBIT_SUPPORTED)
2782 if (mng_info->have_global_sbit)
2784 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2785 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2788 num_passes=png_set_interlace_handling(ping);
2790 png_read_update_info(ping,ping_info);
2792 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2795 Initialize image structure.
2797 mng_info->image_box.left=0;
2798 mng_info->image_box.right=(ssize_t) ping_width;
2799 mng_info->image_box.top=0;
2800 mng_info->image_box.bottom=(ssize_t) ping_height;
2801 if (mng_info->mng_type == 0)
2803 mng_info->mng_width=ping_width;
2804 mng_info->mng_height=ping_height;
2805 mng_info->frame=mng_info->image_box;
2806 mng_info->clip=mng_info->image_box;
2811 image->page.y=mng_info->y_off[mng_info->object_id];
2814 image->compression=ZipCompression;
2815 image->columns=ping_width;
2816 image->rows=ping_height;
2818 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2819 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2821 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2822 image->gamma == 1.0) &&
2823 ping_found_cHRM != MagickTrue && ping_found_sRGB != MagickTrue)
2825 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2826 * image->colorspace to GRAY, and reset image->chromaticity.
2828 SetImageColorspace(image,GRAYColorspace,exception);
2832 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " image->colorspace=%d",(int) image->colorspace);
2835 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2836 ((int) ping_bit_depth < 16 &&
2837 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2842 image->storage_class=PseudoClass;
2844 image->colors=one << ping_file_depth;
2845 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2846 if (image->colors > 256)
2849 if (image->colors > 65536L)
2850 image->colors=65536L;
2852 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2857 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2858 image->colors=(size_t) number_colors;
2860 if (logging != MagickFalse)
2861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2862 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2866 if (image->storage_class == PseudoClass)
2869 Initialize image colormap.
2871 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2872 png_error(ping,"Memory allocation failed");
2874 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2879 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2881 for (i=0; i < (ssize_t) number_colors; i++)
2883 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2884 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2885 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2888 for ( ; i < (ssize_t) image->colors; i++)
2890 image->colormap[i].red=0;
2891 image->colormap[i].green=0;
2892 image->colormap[i].blue=0;
2901 scale=(QuantumRange/((1UL << ping_file_depth)-1));
2906 for (i=0; i < (ssize_t) image->colors; i++)
2908 image->colormap[i].red=(Quantum) (i*scale);
2909 image->colormap[i].green=(Quantum) (i*scale);
2910 image->colormap[i].blue=(Quantum) (i*scale);
2915 /* Set some properties for reporting by "identify" */
2920 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
2921 ping_interlace_method in value */
2923 (void) FormatLocaleString(msg,MaxTextExtent,
2924 "%d, %d",(int) ping_width, (int) ping_height);
2925 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2927 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
2928 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2930 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2931 (int) ping_color_type,
2932 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
2933 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2935 if (ping_interlace_method == 0)
2937 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2938 (int) ping_interlace_method);
2940 else if (ping_interlace_method == 1)
2942 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2943 (int) ping_interlace_method);
2947 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2948 (int) ping_interlace_method);
2950 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2952 if (number_colors != 0)
2954 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2955 (int) number_colors);
2956 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
2962 Read image scanlines.
2964 if (image->delay != 0)
2965 mng_info->scenes_found++;
2967 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2968 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2969 (image_info->first_scene+image_info->number_scenes))))
2971 /* This happens later in non-ping decodes */
2972 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2973 image->storage_class=DirectClass;
2975 if (logging != MagickFalse)
2976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2977 " Skipping PNG image data for scene %.20g",(double)
2978 mng_info->scenes_found-1);
2979 png_destroy_read_struct(&ping,&ping_info,&end_info);
2981 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2982 UnlockSemaphoreInfo(ping_semaphore);
2985 if (logging != MagickFalse)
2986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2987 " exit ReadOnePNGImage().");
2992 if (logging != MagickFalse)
2993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2994 " Reading PNG IDAT chunk(s)");
2997 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2998 ping_rowbytes*sizeof(*ping_pixels));
3001 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
3002 sizeof(*ping_pixels));
3004 if (ping_pixels == (unsigned char *) NULL)
3005 png_error(ping,"Memory allocation failed");
3007 if (logging != MagickFalse)
3008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3009 " Converting PNG pixels to pixel packets");
3011 Convert PNG pixels to pixel packets.
3013 quantum_info=AcquireQuantumInfo(image_info,image);
3015 if (quantum_info == (QuantumInfo *) NULL)
3016 png_error(ping,"Failed to allocate quantum_info");
3018 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3023 found_transparent_pixel;
3025 found_transparent_pixel=MagickFalse;
3027 if (image->storage_class == DirectClass)
3029 for (pass=0; pass < num_passes; pass++)
3032 Convert image to DirectClass pixel packets.
3034 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3035 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3036 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3037 BlendPixelTrait : UndefinedPixelTrait;
3039 for (y=0; y < (ssize_t) image->rows; y++)
3042 row_offset=ping_rowbytes*y;
3047 png_read_row(ping,ping_pixels+row_offset,NULL);
3049 if (pass < num_passes-1)
3052 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3054 if (q == (Quantum *) NULL)
3057 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3058 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3059 GrayQuantum,ping_pixels+row_offset,exception);
3061 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3062 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3063 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3065 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3066 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3067 RGBAQuantum,ping_pixels+row_offset,exception);
3069 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3070 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3071 IndexQuantum,ping_pixels+row_offset,exception);
3073 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3074 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3075 RGBQuantum,ping_pixels+row_offset,exception);
3077 if (found_transparent_pixel == MagickFalse)
3079 /* Is there a transparent pixel in the row? */
3080 if (y== 0 && logging != MagickFalse)
3081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3082 " Looking for cheap transparent pixel");
3084 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3086 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3087 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3088 (GetPixelAlpha(image,q) != OpaqueAlpha))
3090 if (logging != MagickFalse)
3091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3094 found_transparent_pixel = MagickTrue;
3097 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3098 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3099 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3100 transparent_color.red &&
3101 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3102 transparent_color.green &&
3103 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3104 transparent_color.blue))
3106 if (logging != MagickFalse)
3107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3109 found_transparent_pixel = MagickTrue;
3112 q+=GetPixelChannels(image);
3116 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3118 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3121 if (status == MagickFalse)
3124 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3128 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3130 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3131 if (status == MagickFalse)
3137 else /* image->storage_class != DirectClass */
3139 for (pass=0; pass < num_passes; pass++)
3148 Convert grayscale image to PseudoClass pixel packets.
3150 if (logging != MagickFalse)
3151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3152 " Converting grayscale pixels to pixel packets");
3154 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3155 BlendPixelTrait : UndefinedPixelTrait;
3157 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3158 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3159 sizeof(*quantum_scanline));
3161 if (quantum_scanline == (Quantum *) NULL)
3162 png_error(ping,"Memory allocation failed");
3164 for (y=0; y < (ssize_t) image->rows; y++)
3170 row_offset=ping_rowbytes*y;
3175 png_read_row(ping,ping_pixels+row_offset,NULL);
3177 if (pass < num_passes-1)
3180 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3182 if (q == (Quantum *) NULL)
3185 p=ping_pixels+row_offset;
3188 switch (ping_bit_depth)
3193 if (ping_color_type == 4)
3194 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3198 alpha=ScaleCharToQuantum((unsigned char)*p++);
3200 SetPixelAlpha(image,alpha,q);
3202 if (alpha != OpaqueAlpha)
3203 found_transparent_pixel = MagickTrue;
3205 q+=GetPixelChannels(image);
3209 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3217 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3219 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3223 if (image->colors > 256)
3224 quantum=((*p++) << 8);
3230 *r=ScaleShortToQuantum(quantum);
3233 if (ping_color_type == 4)
3235 if (image->colors > 256)
3236 quantum=((*p++) << 8);
3242 alpha=ScaleShortToQuantum(quantum);
3243 SetPixelAlpha(image,alpha,q);
3245 if (alpha != OpaqueAlpha)
3246 found_transparent_pixel = MagickTrue;
3248 q+=GetPixelChannels(image);
3251 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3253 p++; /* strip low byte */
3255 if (ping_color_type == 4)
3257 SetPixelAlpha(image,*p++,q);
3259 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3260 found_transparent_pixel = MagickTrue;
3263 q+=GetPixelChannels(image);
3276 Transfer image scanline.
3280 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3282 if (q == (Quantum *) NULL)
3284 for (x=0; x < (ssize_t) image->columns; x++)
3286 SetPixelIndex(image,*r++,q);
3287 q+=GetPixelChannels(image);
3290 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3293 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3295 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3298 if (status == MagickFalse)
3303 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3305 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3307 if (status == MagickFalse)
3311 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3314 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3315 UndefinedPixelTrait;
3317 if (logging != MagickFalse)
3319 if (found_transparent_pixel != MagickFalse)
3320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3321 " Found transparent pixel");
3324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3325 " No transparent pixel was found");
3327 ping_color_type&=0x03;
3332 if (quantum_info != (QuantumInfo *) NULL)
3333 quantum_info=DestroyQuantumInfo(quantum_info);
3335 if (image->storage_class == PseudoClass)
3340 alpha_trait=image->alpha_trait;
3341 image->alpha_trait=UndefinedPixelTrait;
3342 (void) SyncImage(image,exception);
3343 image->alpha_trait=alpha_trait;
3346 png_read_end(ping,end_info);
3348 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3349 (ssize_t) image_info->first_scene && image->delay != 0)
3351 png_destroy_read_struct(&ping,&ping_info,&end_info);
3352 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3354 (void) SetImageBackgroundColor(image,exception);
3355 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3356 UnlockSemaphoreInfo(ping_semaphore);
3358 if (logging != MagickFalse)
3359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3360 " exit ReadOnePNGImage() early.");
3364 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3370 Image has a transparent background.
3372 storage_class=image->storage_class;
3373 image->alpha_trait=BlendPixelTrait;
3375 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3377 if (storage_class == PseudoClass)
3379 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3381 for (x=0; x < ping_num_trans; x++)
3383 image->colormap[x].alpha_trait=BlendPixelTrait;
3384 image->colormap[x].alpha =
3385 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3389 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3391 for (x=0; x < (int) image->colors; x++)
3393 if (ScaleQuantumToShort(image->colormap[x].red) ==
3394 transparent_color.alpha)
3396 image->colormap[x].alpha_trait=BlendPixelTrait;
3397 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3401 (void) SyncImage(image,exception);
3404 #if 1 /* Should have already been done above, but glennrp problem P10
3409 for (y=0; y < (ssize_t) image->rows; y++)
3411 image->storage_class=storage_class;
3412 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3414 if (q == (Quantum *) NULL)
3418 /* Caution: on a Q8 build, this does not distinguish between
3419 * 16-bit colors that differ only in the low byte
3421 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3423 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3424 transparent_color.red &&
3425 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3426 transparent_color.green &&
3427 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3428 transparent_color.blue)
3430 SetPixelAlpha(image,TransparentAlpha,q);
3433 #if 0 /* I have not found a case where this is needed. */
3436 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3440 q+=GetPixelChannels(image);
3443 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3449 image->storage_class=DirectClass;
3452 for (j = 0; j < 2; j++)
3455 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3456 MagickTrue : MagickFalse;
3458 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3459 MagickTrue : MagickFalse;
3461 if (status != MagickFalse)
3462 for (i=0; i < (ssize_t) num_text; i++)
3464 /* Check for a profile */
3466 if (logging != MagickFalse)
3467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3468 " Reading PNG text chunk");
3470 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3472 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3482 length=text[i].text_length;
3483 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3485 if (value == (char *) NULL)
3487 png_error(ping,"Memory allocation failed");
3491 (void) ConcatenateMagickString(value,text[i].text,length+2);
3493 /* Don't save "density" or "units" property if we have a pHYs
3496 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3497 (LocaleCompare(text[i].key,"density") != 0 &&
3498 LocaleCompare(text[i].key,"units") != 0))
3499 (void) SetImageProperty(image,text[i].key,value,exception);
3501 if (logging != MagickFalse)
3503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3504 " length: %lu",(unsigned long) length);
3505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3506 " Keyword: %s",text[i].key);
3509 value=DestroyString(value);
3512 num_text_total += num_text;
3515 #ifdef MNG_OBJECT_BUFFERS
3517 Store the object if necessary.
3519 if (object_id && !mng_info->frozen[object_id])
3521 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3524 create a new object buffer.
3526 mng_info->ob[object_id]=(MngBuffer *)
3527 AcquireMagickMemory(sizeof(MngBuffer));
3529 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3531 mng_info->ob[object_id]->image=(Image *) NULL;
3532 mng_info->ob[object_id]->reference_count=1;
3536 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3537 mng_info->ob[object_id]->frozen)
3539 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3540 png_error(ping,"Memory allocation failed");
3542 if (mng_info->ob[object_id]->frozen)
3543 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3549 if (mng_info->ob[object_id]->image != (Image *) NULL)
3550 mng_info->ob[object_id]->image=DestroyImage
3551 (mng_info->ob[object_id]->image);
3553 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3556 if (mng_info->ob[object_id]->image != (Image *) NULL)
3557 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3560 png_error(ping, "Cloning image for object buffer failed");
3562 if (ping_width > 250000L || ping_height > 250000L)
3563 png_error(ping,"PNG Image dimensions are too large.");
3565 mng_info->ob[object_id]->width=ping_width;
3566 mng_info->ob[object_id]->height=ping_height;
3567 mng_info->ob[object_id]->color_type=ping_color_type;
3568 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3569 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3570 mng_info->ob[object_id]->compression_method=
3571 ping_compression_method;
3572 mng_info->ob[object_id]->filter_method=ping_filter_method;
3574 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3580 Copy the PLTE to the object buffer.
3582 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3583 mng_info->ob[object_id]->plte_length=number_colors;
3585 for (i=0; i < number_colors; i++)
3587 mng_info->ob[object_id]->plte[i]=plte[i];
3592 mng_info->ob[object_id]->plte_length=0;
3597 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3598 * alpha or if a valid tRNS chunk is present, no matter whether there
3599 * is actual transparency present.
3601 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3602 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3603 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3604 BlendPixelTrait : UndefinedPixelTrait;
3606 /* Set more properties for identify to retrieve */
3611 if (num_text_total != 0)
3613 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3614 (void) FormatLocaleString(msg,MaxTextExtent,
3615 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3616 (void) SetImageProperty(image,"png:text ",msg,
3620 if (num_raw_profiles != 0)
3622 (void) FormatLocaleString(msg,MaxTextExtent,
3623 "%d were found", num_raw_profiles);
3624 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3628 if (ping_found_cHRM != MagickFalse)
3630 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3631 "chunk was found (see Chromaticity, above)");
3632 (void) SetImageProperty(image,"png:cHRM ",msg,
3636 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3638 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3639 "chunk was found (see Background color, above)");
3640 (void) SetImageProperty(image,"png:bKGD ",msg,
3644 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3647 #if defined(PNG_iCCP_SUPPORTED)
3648 if (ping_found_iCCP != MagickFalse)
3649 (void) SetImageProperty(image,"png:iCCP ",msg,
3653 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3654 (void) SetImageProperty(image,"png:tRNS ",msg,
3657 #if defined(PNG_sRGB_SUPPORTED)
3658 if (ping_found_sRGB != MagickFalse)
3660 (void) FormatLocaleString(msg,MaxTextExtent,
3663 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3664 (void) SetImageProperty(image,"png:sRGB ",msg,
3669 if (ping_found_gAMA != MagickFalse)
3671 (void) FormatLocaleString(msg,MaxTextExtent,
3672 "gamma=%.8g (See Gamma, above)",
3674 (void) SetImageProperty(image,"png:gAMA ",msg,
3678 #if defined(PNG_pHYs_SUPPORTED)
3679 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3681 (void) FormatLocaleString(msg,MaxTextExtent,
3682 "x_res=%.10g, y_res=%.10g, units=%d",
3683 (double) x_resolution,(double) y_resolution, unit_type);
3684 (void) SetImageProperty(image,"png:pHYs ",msg,
3689 #if defined(PNG_oFFs_SUPPORTED)
3690 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3692 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3693 (double) image->page.x,(double) image->page.y);
3694 (void) SetImageProperty(image,"png:oFFs ",msg,
3699 if ((image->page.width != 0 && image->page.width != image->columns) ||
3700 (image->page.height != 0 && image->page.height != image->rows))
3702 (void) FormatLocaleString(msg,MaxTextExtent,
3703 "width=%.20g, height=%.20g",
3704 (double) image->page.width,(double) image->page.height);
3705 (void) SetImageProperty(image,"png:vpAg ",msg,
3711 Relinquish resources.
3713 png_destroy_read_struct(&ping,&ping_info,&end_info);
3715 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3717 if (logging != MagickFalse)
3718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3719 " exit ReadOnePNGImage()");
3721 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3722 UnlockSemaphoreInfo(ping_semaphore);
3725 /* } for navigation to beginning of SETJMP-protected block, revert to
3726 * Throwing an Exception when an error occurs.
3731 /* end of reading one PNG image */
3734 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3749 magic_number[MaxTextExtent];
3757 assert(image_info != (const ImageInfo *) NULL);
3758 assert(image_info->signature == MagickSignature);
3760 if (image_info->debug != MagickFalse)
3761 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3762 image_info->filename);
3764 assert(exception != (ExceptionInfo *) NULL);
3765 assert(exception->signature == MagickSignature);
3766 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3767 image=AcquireImage(image_info,exception);
3768 mng_info=(MngInfo *) NULL;
3769 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3771 if (status == MagickFalse)
3772 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3775 Verify PNG signature.
3777 count=ReadBlob(image,8,(unsigned char *) magic_number);
3779 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3780 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3783 Allocate a MngInfo structure.
3785 have_mng_structure=MagickFalse;
3786 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3788 if (mng_info == (MngInfo *) NULL)
3789 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3792 Initialize members of the MngInfo structure.
3794 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3795 mng_info->image=image;
3796 have_mng_structure=MagickTrue;
3799 image=ReadOnePNGImage(mng_info,image_info,exception);
3800 MngInfoFreeStruct(mng_info,&have_mng_structure);
3802 if (image == (Image *) NULL)
3804 if (previous != (Image *) NULL)
3806 if (previous->signature != MagickSignature)
3807 ThrowReaderException(CorruptImageError,"CorruptImage");
3809 (void) CloseBlob(previous);
3810 (void) DestroyImageList(previous);
3813 if (logging != MagickFalse)
3814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3815 "exit ReadPNGImage() with error");
3817 return((Image *) NULL);
3820 (void) CloseBlob(image);
3822 if ((image->columns == 0) || (image->rows == 0))
3824 if (logging != MagickFalse)
3825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3826 "exit ReadPNGImage() with error.");
3828 ThrowReaderException(CorruptImageError,"CorruptImage");
3831 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3832 ((image->gamma < .45) || (image->gamma > .46)))
3833 SetImageColorspace(image,RGBColorspace,exception);
3835 if ((LocaleCompare(image_info->magick,"PNG24") == 0) ||
3836 (LocaleCompare(image_info->magick,"PNG48") == 0))
3838 (void) SetImageType(image,TrueColorType,exception);
3839 image->alpha_trait=UndefinedPixelTrait;
3842 if ((LocaleCompare(image_info->magick,"PNG32") == 0) ||
3843 (LocaleCompare(image_info->magick,"PNG64") == 0))
3844 (void) SetImageType(image,TrueColorMatteType,exception);
3846 if (logging != MagickFalse)
3847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3848 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3849 (double) image->page.width,(double) image->page.height,
3850 (double) image->page.x,(double) image->page.y);
3852 if (logging != MagickFalse)
3853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3860 #if defined(JNG_SUPPORTED)
3862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3866 % R e a d O n e J N G I m a g e %
3870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3872 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3873 % (minus the 8-byte signature) and returns it. It allocates the memory
3874 % necessary for the new Image structure and returns a pointer to the new
3877 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3879 % The format of the ReadOneJNGImage method is:
3881 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3882 % ExceptionInfo *exception)
3884 % A description of each parameter follows:
3886 % o mng_info: Specifies a pointer to a MngInfo structure.
3888 % o image_info: the image info.
3890 % o exception: return any errors or warnings in this structure.
3893 static Image *ReadOneJNGImage(MngInfo *mng_info,
3894 const ImageInfo *image_info, ExceptionInfo *exception)
3921 jng_image_sample_depth,
3922 jng_image_compression_method,
3923 jng_image_interlace_method,
3924 jng_alpha_sample_depth,
3925 jng_alpha_compression_method,
3926 jng_alpha_filter_method,
3927 jng_alpha_interlace_method;
3929 register const Quantum
3939 register unsigned char
3950 jng_alpha_compression_method=0;
3951 jng_alpha_sample_depth=8;
3955 alpha_image=(Image *) NULL;
3956 color_image=(Image *) NULL;
3957 alpha_image_info=(ImageInfo *) NULL;
3958 color_image_info=(ImageInfo *) NULL;
3960 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " Enter ReadOneJNGImage()");
3963 image=mng_info->image;
3965 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3968 Allocate next image structure.
3970 if (logging != MagickFalse)
3971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3972 " AcquireNextImage()");
3974 AcquireNextImage(image_info,image,exception);
3976 if (GetNextImageInList(image) == (Image *) NULL)
3977 return((Image *) NULL);
3979 image=SyncNextImageInList(image);
3981 mng_info->image=image;
3984 Signature bytes have already been read.
3987 read_JSEP=MagickFalse;
3988 reading_idat=MagickFalse;
3989 skip_to_iend=MagickFalse;
3993 type[MaxTextExtent];
4002 Read a new JNG chunk.
4004 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4005 2*GetBlobSize(image));
4007 if (status == MagickFalse)
4011 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4012 length=ReadBlobMSBLong(image);
4013 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4015 if (logging != MagickFalse)
4016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4017 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4018 type[0],type[1],type[2],type[3],(double) length);
4020 if (length > PNG_UINT_31_MAX || count == 0)
4021 ThrowReaderException(CorruptImageError,"CorruptImage");
4024 chunk=(unsigned char *) NULL;
4028 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4030 if (chunk == (unsigned char *) NULL)
4031 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4033 for (i=0; i < (ssize_t) length; i++)
4034 chunk[i]=(unsigned char) ReadBlobByte(image);
4039 (void) ReadBlobMSBLong(image); /* read crc word */
4044 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4049 if (memcmp(type,mng_JHDR,4) == 0)
4053 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4054 (p[2] << 8) | p[3]);
4055 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4056 (p[6] << 8) | p[7]);
4057 jng_color_type=p[8];
4058 jng_image_sample_depth=p[9];
4059 jng_image_compression_method=p[10];
4060 jng_image_interlace_method=p[11];
4062 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4065 jng_alpha_sample_depth=p[12];
4066 jng_alpha_compression_method=p[13];
4067 jng_alpha_filter_method=p[14];
4068 jng_alpha_interlace_method=p[15];
4070 if (logging != MagickFalse)
4072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4073 " jng_width: %16lu",(unsigned long) jng_width);
4075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4076 " jng_width: %16lu",(unsigned long) jng_height);
4078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4079 " jng_color_type: %16d",jng_color_type);
4081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4082 " jng_image_sample_depth: %3d",
4083 jng_image_sample_depth);
4085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4086 " jng_image_compression_method:%3d",
4087 jng_image_compression_method);
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " jng_image_interlace_method: %3d",
4091 jng_image_interlace_method);
4093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4094 " jng_alpha_sample_depth: %3d",
4095 jng_alpha_sample_depth);
4097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4098 " jng_alpha_compression_method:%3d",
4099 jng_alpha_compression_method);
4101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4102 " jng_alpha_filter_method: %3d",
4103 jng_alpha_filter_method);
4105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4106 " jng_alpha_interlace_method: %3d",
4107 jng_alpha_interlace_method);
4112 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4118 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4119 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4120 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4123 o create color_image
4124 o open color_blob, attached to color_image
4125 o if (color type has alpha)
4126 open alpha_blob, attached to alpha_image
4129 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4131 if (color_image_info == (ImageInfo *) NULL)
4132 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4134 GetImageInfo(color_image_info);
4135 color_image=AcquireImage(color_image_info,exception);
4137 if (color_image == (Image *) NULL)
4138 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4140 if (logging != MagickFalse)
4141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4142 " Creating color_blob.");
4144 (void) AcquireUniqueFilename(color_image->filename);
4145 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4148 if (status == MagickFalse)
4149 return((Image *) NULL);
4151 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4153 alpha_image_info=(ImageInfo *)
4154 AcquireMagickMemory(sizeof(ImageInfo));
4156 if (alpha_image_info == (ImageInfo *) NULL)
4157 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4159 GetImageInfo(alpha_image_info);
4160 alpha_image=AcquireImage(alpha_image_info,exception);
4162 if (alpha_image == (Image *) NULL)
4164 alpha_image=DestroyImage(alpha_image);
4165 ThrowReaderException(ResourceLimitError,
4166 "MemoryAllocationFailed");
4169 if (logging != MagickFalse)
4170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4171 " Creating alpha_blob.");
4173 (void) AcquireUniqueFilename(alpha_image->filename);
4174 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4177 if (status == MagickFalse)
4178 return((Image *) NULL);
4180 if (jng_alpha_compression_method == 0)
4185 if (logging != MagickFalse)
4186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4187 " Writing IHDR chunk to alpha_blob.");
4189 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4190 "\211PNG\r\n\032\n");
4192 (void) WriteBlobMSBULong(alpha_image,13L);
4193 PNGType(data,mng_IHDR);
4194 LogPNGChunk(logging,mng_IHDR,13L);
4195 PNGLong(data+4,jng_width);
4196 PNGLong(data+8,jng_height);
4197 data[12]=jng_alpha_sample_depth;
4198 data[13]=0; /* color_type gray */
4199 data[14]=0; /* compression method 0 */
4200 data[15]=0; /* filter_method 0 */
4201 data[16]=0; /* interlace_method 0 */
4202 (void) WriteBlob(alpha_image,17,data);
4203 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4206 reading_idat=MagickTrue;
4209 if (memcmp(type,mng_JDAT,4) == 0)
4211 /* Copy chunk to color_image->blob */
4213 if (logging != MagickFalse)
4214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4215 " Copying JDAT chunk data to color_blob.");
4217 (void) WriteBlob(color_image,length,chunk);
4220 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4225 if (memcmp(type,mng_IDAT,4) == 0)
4230 /* Copy IDAT header and chunk data to alpha_image->blob */
4232 if (image_info->ping == MagickFalse)
4234 if (logging != MagickFalse)
4235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4236 " Copying IDAT chunk data to alpha_blob.");
4238 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4239 PNGType(data,mng_IDAT);
4240 LogPNGChunk(logging,mng_IDAT,length);
4241 (void) WriteBlob(alpha_image,4,data);
4242 (void) WriteBlob(alpha_image,length,chunk);
4243 (void) WriteBlobMSBULong(alpha_image,
4244 crc32(crc32(0,data,4),chunk,(uInt) length));
4248 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4253 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4255 /* Copy chunk data to alpha_image->blob */
4257 if (image_info->ping == MagickFalse)
4259 if (logging != MagickFalse)
4260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4261 " Copying JDAA chunk data to alpha_blob.");
4263 (void) WriteBlob(alpha_image,length,chunk);
4267 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4272 if (memcmp(type,mng_JSEP,4) == 0)
4274 read_JSEP=MagickTrue;
4277 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4282 if (memcmp(type,mng_bKGD,4) == 0)
4286 image->background_color.red=ScaleCharToQuantum(p[1]);
4287 image->background_color.green=image->background_color.red;
4288 image->background_color.blue=image->background_color.red;
4293 image->background_color.red=ScaleCharToQuantum(p[1]);
4294 image->background_color.green=ScaleCharToQuantum(p[3]);
4295 image->background_color.blue=ScaleCharToQuantum(p[5]);
4298 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4302 if (memcmp(type,mng_gAMA,4) == 0)
4305 image->gamma=((float) mng_get_long(p))*0.00001;
4307 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4311 if (memcmp(type,mng_cHRM,4) == 0)
4315 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4316 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4317 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4318 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4319 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4320 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4321 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4322 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4325 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4329 if (memcmp(type,mng_sRGB,4) == 0)
4333 image->rendering_intent=
4334 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4335 image->gamma=1.000f/2.200f;
4336 image->chromaticity.red_primary.x=0.6400f;
4337 image->chromaticity.red_primary.y=0.3300f;
4338 image->chromaticity.green_primary.x=0.3000f;
4339 image->chromaticity.green_primary.y=0.6000f;
4340 image->chromaticity.blue_primary.x=0.1500f;
4341 image->chromaticity.blue_primary.y=0.0600f;
4342 image->chromaticity.white_point.x=0.3127f;
4343 image->chromaticity.white_point.y=0.3290f;
4346 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4350 if (memcmp(type,mng_oFFs,4) == 0)
4354 image->page.x=(ssize_t) mng_get_long(p);
4355 image->page.y=(ssize_t) mng_get_long(&p[4]);
4357 if ((int) p[8] != 0)
4359 image->page.x/=10000;
4360 image->page.y/=10000;
4365 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4370 if (memcmp(type,mng_pHYs,4) == 0)
4374 image->resolution.x=(double) mng_get_long(p);
4375 image->resolution.y=(double) mng_get_long(&p[4]);
4376 if ((int) p[8] == PNG_RESOLUTION_METER)
4378 image->units=PixelsPerCentimeterResolution;
4379 image->resolution.x=image->resolution.x/100.0f;
4380 image->resolution.y=image->resolution.y/100.0f;
4384 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4389 if (memcmp(type,mng_iCCP,4) == 0)
4393 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4402 if (memcmp(type,mng_IEND,4))
4412 Finish up reading image data:
4414 o read main image from color_blob.
4418 o if (color_type has alpha)
4419 if alpha_encoding is PNG
4420 read secondary image from alpha_blob via ReadPNG
4421 if alpha_encoding is JPEG
4422 read secondary image from alpha_blob via ReadJPEG
4426 o copy intensity of secondary image into
4427 alpha samples of main image.
4429 o destroy the secondary image.
4432 (void) CloseBlob(color_image);
4434 if (logging != MagickFalse)
4435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4436 " Reading jng_image from color_blob.");
4438 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4439 color_image->filename);
4441 color_image_info->ping=MagickFalse; /* To do: avoid this */
4442 jng_image=ReadImage(color_image_info,exception);
4444 if (jng_image == (Image *) NULL)
4445 return((Image *) NULL);
4447 (void) RelinquishUniqueFileResource(color_image->filename);
4448 color_image=DestroyImage(color_image);
4449 color_image_info=DestroyImageInfo(color_image_info);
4451 if (jng_image == (Image *) NULL)
4452 return((Image *) NULL);
4454 if (logging != MagickFalse)
4455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4456 " Copying jng_image pixels to main image.");
4458 image->rows=jng_height;
4459 image->columns=jng_width;
4461 for (y=0; y < (ssize_t) image->rows; y++)
4463 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4464 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4465 for (x=(ssize_t) image->columns; x != 0; x--)
4467 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4468 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4469 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4470 q+=GetPixelChannels(image);
4471 s+=GetPixelChannels(jng_image);
4474 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4478 jng_image=DestroyImage(jng_image);
4480 if (image_info->ping == MagickFalse)
4482 if (jng_color_type >= 12)
4484 if (jng_alpha_compression_method == 0)
4488 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4489 PNGType(data,mng_IEND);
4490 LogPNGChunk(logging,mng_IEND,0L);
4491 (void) WriteBlob(alpha_image,4,data);
4492 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4495 (void) CloseBlob(alpha_image);
4497 if (logging != MagickFalse)
4498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4499 " Reading alpha from alpha_blob.");
4501 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4502 "%s",alpha_image->filename);
4504 jng_image=ReadImage(alpha_image_info,exception);
4506 if (jng_image != (Image *) NULL)
4507 for (y=0; y < (ssize_t) image->rows; y++)
4509 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4511 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4513 if (image->alpha_trait == BlendPixelTrait)
4514 for (x=(ssize_t) image->columns; x != 0; x--)
4516 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4517 q+=GetPixelChannels(image);
4518 s+=GetPixelChannels(jng_image);
4522 for (x=(ssize_t) image->columns; x != 0; x--)
4524 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4525 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4526 image->alpha_trait=BlendPixelTrait;
4527 q+=GetPixelChannels(image);
4528 s+=GetPixelChannels(jng_image);
4531 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4534 (void) RelinquishUniqueFileResource(alpha_image->filename);
4535 alpha_image=DestroyImage(alpha_image);
4536 alpha_image_info=DestroyImageInfo(alpha_image_info);
4537 if (jng_image != (Image *) NULL)
4538 jng_image=DestroyImage(jng_image);
4542 /* Read the JNG image. */
4544 if (mng_info->mng_type == 0)
4546 mng_info->mng_width=jng_width;
4547 mng_info->mng_height=jng_height;
4550 if (image->page.width == 0 && image->page.height == 0)
4552 image->page.width=jng_width;
4553 image->page.height=jng_height;
4556 if (image->page.x == 0 && image->page.y == 0)
4558 image->page.x=mng_info->x_off[mng_info->object_id];
4559 image->page.y=mng_info->y_off[mng_info->object_id];
4564 image->page.y=mng_info->y_off[mng_info->object_id];
4567 mng_info->image_found++;
4568 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4569 2*GetBlobSize(image));
4571 if (logging != MagickFalse)
4572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4573 " exit ReadOneJNGImage()");
4579 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4583 % R e a d J N G I m a g e %
4587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4589 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4590 % (including the 8-byte signature) and returns it. It allocates the memory
4591 % necessary for the new Image structure and returns a pointer to the new
4594 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4596 % The format of the ReadJNGImage method is:
4598 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4601 % A description of each parameter follows:
4603 % o image_info: the image info.
4605 % o exception: return any errors or warnings in this structure.
4609 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4624 magic_number[MaxTextExtent];
4632 assert(image_info != (const ImageInfo *) NULL);
4633 assert(image_info->signature == MagickSignature);
4634 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4635 assert(exception != (ExceptionInfo *) NULL);
4636 assert(exception->signature == MagickSignature);
4637 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4638 image=AcquireImage(image_info,exception);
4639 mng_info=(MngInfo *) NULL;
4640 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4642 if (status == MagickFalse)
4643 return((Image *) NULL);
4645 if (LocaleCompare(image_info->magick,"JNG") != 0)
4646 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4648 /* Verify JNG signature. */
4650 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4652 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4653 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4655 /* Allocate a MngInfo structure. */
4657 have_mng_structure=MagickFalse;
4658 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4660 if (mng_info == (MngInfo *) NULL)
4661 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4663 /* Initialize members of the MngInfo structure. */
4665 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4666 have_mng_structure=MagickTrue;
4668 mng_info->image=image;
4670 image=ReadOneJNGImage(mng_info,image_info,exception);
4671 MngInfoFreeStruct(mng_info,&have_mng_structure);
4673 if (image == (Image *) NULL)
4675 if (IsImageObject(previous) != MagickFalse)
4677 (void) CloseBlob(previous);
4678 (void) DestroyImageList(previous);
4681 if (logging != MagickFalse)
4682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4683 "exit ReadJNGImage() with error");
4685 return((Image *) NULL);
4687 (void) CloseBlob(image);
4689 if (image->columns == 0 || image->rows == 0)
4691 if (logging != MagickFalse)
4692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4693 "exit ReadJNGImage() with error");
4695 ThrowReaderException(CorruptImageError,"CorruptImage");
4698 if (logging != MagickFalse)
4699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4705 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4708 page_geometry[MaxTextExtent];
4741 #if defined(MNG_INSERT_LAYERS)
4743 mng_background_color;
4746 register unsigned char
4761 #if defined(MNG_INSERT_LAYERS)
4766 volatile unsigned int
4767 #ifdef MNG_OBJECT_BUFFERS
4768 mng_background_object=0,
4770 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4773 default_frame_timeout,
4775 #if defined(MNG_INSERT_LAYERS)
4781 /* These delays are all measured in image ticks_per_second,
4782 * not in MNG ticks_per_second
4785 default_frame_delay,
4789 #if defined(MNG_INSERT_LAYERS)
4798 previous_fb.bottom=0;
4800 previous_fb.right=0;
4802 default_fb.bottom=0;
4806 /* Open image file. */
4808 assert(image_info != (const ImageInfo *) NULL);
4809 assert(image_info->signature == MagickSignature);
4810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4811 assert(exception != (ExceptionInfo *) NULL);
4812 assert(exception->signature == MagickSignature);
4813 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4814 image=AcquireImage(image_info,exception);
4815 mng_info=(MngInfo *) NULL;
4816 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4818 if (status == MagickFalse)
4819 return((Image *) NULL);
4821 first_mng_object=MagickFalse;
4823 have_mng_structure=MagickFalse;
4825 /* Allocate a MngInfo structure. */
4827 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4829 if (mng_info == (MngInfo *) NULL)
4830 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4832 /* Initialize members of the MngInfo structure. */
4834 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4835 mng_info->image=image;
4836 have_mng_structure=MagickTrue;
4838 if (LocaleCompare(image_info->magick,"MNG") == 0)
4841 magic_number[MaxTextExtent];
4843 /* Verify MNG signature. */
4844 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4845 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4846 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4848 /* Initialize some nonzero members of the MngInfo structure. */
4849 for (i=0; i < MNG_MAX_OBJECTS; i++)
4851 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4852 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4854 mng_info->exists[0]=MagickTrue;
4857 first_mng_object=MagickTrue;
4859 #if defined(MNG_INSERT_LAYERS)
4860 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4862 default_frame_delay=0;
4863 default_frame_timeout=0;
4866 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4868 skip_to_iend=MagickFalse;
4869 term_chunk_found=MagickFalse;
4870 mng_info->framing_mode=1;
4871 #if defined(MNG_INSERT_LAYERS)
4872 mandatory_back=MagickFalse;
4874 #if defined(MNG_INSERT_LAYERS)
4875 mng_background_color=image->background_color;
4877 default_fb=mng_info->frame;
4878 previous_fb=mng_info->frame;
4882 type[MaxTextExtent];
4884 if (LocaleCompare(image_info->magick,"MNG") == 0)
4893 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4894 length=ReadBlobMSBLong(image);
4895 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4897 if (logging != MagickFalse)
4898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4899 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4900 type[0],type[1],type[2],type[3],(double) length);
4902 if (length > PNG_UINT_31_MAX)
4906 ThrowReaderException(CorruptImageError,"CorruptImage");
4909 chunk=(unsigned char *) NULL;
4913 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4915 if (chunk == (unsigned char *) NULL)
4916 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4918 for (i=0; i < (ssize_t) length; i++)
4919 chunk[i]=(unsigned char) ReadBlobByte(image);
4924 (void) ReadBlobMSBLong(image); /* read crc word */
4926 #if !defined(JNG_SUPPORTED)
4927 if (memcmp(type,mng_JHDR,4) == 0)
4929 skip_to_iend=MagickTrue;
4931 if (mng_info->jhdr_warning == 0)
4932 (void) ThrowMagickException(exception,GetMagickModule(),
4933 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4935 mng_info->jhdr_warning++;
4938 if (memcmp(type,mng_DHDR,4) == 0)
4940 skip_to_iend=MagickTrue;
4942 if (mng_info->dhdr_warning == 0)
4943 (void) ThrowMagickException(exception,GetMagickModule(),
4944 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4946 mng_info->dhdr_warning++;
4948 if (memcmp(type,mng_MEND,4) == 0)
4953 if (memcmp(type,mng_IEND,4) == 0)
4954 skip_to_iend=MagickFalse;
4957 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4959 if (logging != MagickFalse)
4960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4966 if (memcmp(type,mng_MHDR,4) == 0)
4968 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4969 (p[2] << 8) | p[3]);
4971 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4972 (p[6] << 8) | p[7]);
4974 if (logging != MagickFalse)
4976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4977 " MNG width: %.20g",(double) mng_info->mng_width);
4978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4979 " MNG height: %.20g",(double) mng_info->mng_height);
4983 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4985 if (mng_info->ticks_per_second == 0)
4986 default_frame_delay=0;
4989 default_frame_delay=1UL*image->ticks_per_second/
4990 mng_info->ticks_per_second;
4992 frame_delay=default_frame_delay;
4998 simplicity=(size_t) mng_get_long(p);
5001 mng_type=1; /* Full MNG */
5003 if ((simplicity != 0) && ((simplicity | 11) == 11))
5004 mng_type=2; /* LC */
5006 if ((simplicity != 0) && ((simplicity | 9) == 9))
5007 mng_type=3; /* VLC */
5009 #if defined(MNG_INSERT_LAYERS)
5011 insert_layers=MagickTrue;
5013 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5015 /* Allocate next image structure. */
5016 AcquireNextImage(image_info,image,exception);
5018 if (GetNextImageInList(image) == (Image *) NULL)
5019 return((Image *) NULL);
5021 image=SyncNextImageInList(image);
5022 mng_info->image=image;
5025 if ((mng_info->mng_width > 65535L) ||
5026 (mng_info->mng_height > 65535L))
5027 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5029 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5030 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5031 mng_info->mng_height);
5033 mng_info->frame.left=0;
5034 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5035 mng_info->frame.top=0;
5036 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5037 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5039 for (i=0; i < MNG_MAX_OBJECTS; i++)
5040 mng_info->object_clip[i]=mng_info->frame;
5042 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5046 if (memcmp(type,mng_TERM,4) == 0)
5057 final_delay=(png_uint_32) mng_get_long(&p[2]);
5058 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5060 if (mng_iterations == PNG_UINT_31_MAX)
5063 image->iterations=mng_iterations;
5064 term_chunk_found=MagickTrue;
5067 if (logging != MagickFalse)
5069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5070 " repeat=%d",repeat);
5072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5073 " final_delay=%.20g",(double) final_delay);
5075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5076 " image->iterations=%.20g",(double) image->iterations);
5079 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5082 if (memcmp(type,mng_DEFI,4) == 0)
5085 (void) ThrowMagickException(exception,GetMagickModule(),
5086 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5089 object_id=(p[0] << 8) | p[1];
5091 if (mng_type == 2 && object_id != 0)
5092 (void) ThrowMagickException(exception,GetMagickModule(),
5093 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5096 if (object_id > MNG_MAX_OBJECTS)
5099 Instead of using a warning we should allocate a larger
5100 MngInfo structure and continue.
5102 (void) ThrowMagickException(exception,GetMagickModule(),
5103 CoderError,"object id too large","`%s'",image->filename);
5104 object_id=MNG_MAX_OBJECTS;
5107 if (mng_info->exists[object_id])
5108 if (mng_info->frozen[object_id])
5110 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5111 (void) ThrowMagickException(exception,
5112 GetMagickModule(),CoderError,
5113 "DEFI cannot redefine a frozen MNG object","`%s'",
5118 mng_info->exists[object_id]=MagickTrue;
5121 mng_info->invisible[object_id]=p[2];
5124 Extract object offset info.
5128 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5129 (p[5] << 16) | (p[6] << 8) | p[7]);
5131 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5132 (p[9] << 16) | (p[10] << 8) | p[11]);
5134 if (logging != MagickFalse)
5136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5137 " x_off[%d]: %.20g",object_id,(double)
5138 mng_info->x_off[object_id]);
5140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5141 " y_off[%d]: %.20g",object_id,(double)
5142 mng_info->y_off[object_id]);
5147 Extract object clipping info.
5150 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5153 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5156 if (memcmp(type,mng_bKGD,4) == 0)
5158 mng_info->have_global_bkgd=MagickFalse;
5162 mng_info->mng_global_bkgd.red=
5163 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5165 mng_info->mng_global_bkgd.green=
5166 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5168 mng_info->mng_global_bkgd.blue=
5169 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5171 mng_info->have_global_bkgd=MagickTrue;
5174 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5177 if (memcmp(type,mng_BACK,4) == 0)
5179 #if defined(MNG_INSERT_LAYERS)
5181 mandatory_back=p[6];
5186 if (mandatory_back && length > 5)
5188 mng_background_color.red=
5189 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5191 mng_background_color.green=
5192 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5194 mng_background_color.blue=
5195 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5197 mng_background_color.alpha=OpaqueAlpha;
5200 #ifdef MNG_OBJECT_BUFFERS
5202 mng_background_object=(p[7] << 8) | p[8];
5205 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5209 if (memcmp(type,mng_PLTE,4) == 0)
5211 /* Read global PLTE. */
5213 if (length && (length < 769))
5215 if (mng_info->global_plte == (png_colorp) NULL)
5216 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5217 sizeof(*mng_info->global_plte));
5219 for (i=0; i < (ssize_t) (length/3); i++)
5221 mng_info->global_plte[i].red=p[3*i];
5222 mng_info->global_plte[i].green=p[3*i+1];
5223 mng_info->global_plte[i].blue=p[3*i+2];
5226 mng_info->global_plte_length=(unsigned int) (length/3);
5229 for ( ; i < 256; i++)
5231 mng_info->global_plte[i].red=i;
5232 mng_info->global_plte[i].green=i;
5233 mng_info->global_plte[i].blue=i;
5237 mng_info->global_plte_length=256;
5240 mng_info->global_plte_length=0;
5242 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5246 if (memcmp(type,mng_tRNS,4) == 0)
5248 /* read global tRNS */
5251 for (i=0; i < (ssize_t) length; i++)
5252 mng_info->global_trns[i]=p[i];
5255 for ( ; i < 256; i++)
5256 mng_info->global_trns[i]=255;
5258 mng_info->global_trns_length=(unsigned int) length;
5259 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5262 if (memcmp(type,mng_gAMA,4) == 0)
5269 igamma=mng_get_long(p);
5270 mng_info->global_gamma=((float) igamma)*0.00001;
5271 mng_info->have_global_gama=MagickTrue;
5275 mng_info->have_global_gama=MagickFalse;
5277 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5281 if (memcmp(type,mng_cHRM,4) == 0)
5283 /* Read global cHRM */
5287 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5288 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5289 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5290 mng_info->global_chrm.red_primary.y=0.00001*
5291 mng_get_long(&p[12]);
5292 mng_info->global_chrm.green_primary.x=0.00001*
5293 mng_get_long(&p[16]);
5294 mng_info->global_chrm.green_primary.y=0.00001*
5295 mng_get_long(&p[20]);
5296 mng_info->global_chrm.blue_primary.x=0.00001*
5297 mng_get_long(&p[24]);
5298 mng_info->global_chrm.blue_primary.y=0.00001*
5299 mng_get_long(&p[28]);
5300 mng_info->have_global_chrm=MagickTrue;
5303 mng_info->have_global_chrm=MagickFalse;
5305 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5309 if (memcmp(type,mng_sRGB,4) == 0)
5316 mng_info->global_srgb_intent=
5317 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5318 mng_info->have_global_srgb=MagickTrue;
5321 mng_info->have_global_srgb=MagickFalse;
5323 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5327 if (memcmp(type,mng_iCCP,4) == 0)
5335 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5340 if (memcmp(type,mng_FRAM,4) == 0)
5343 (void) ThrowMagickException(exception,GetMagickModule(),
5344 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5347 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5348 image->delay=frame_delay;
5350 frame_delay=default_frame_delay;
5351 frame_timeout=default_frame_timeout;
5356 mng_info->framing_mode=p[0];
5358 if (logging != MagickFalse)
5359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5360 " Framing_mode=%d",mng_info->framing_mode);
5364 /* Note the delay and frame clipping boundaries. */
5366 p++; /* framing mode */
5368 while (*p && ((p-chunk) < (ssize_t) length))
5369 p++; /* frame name */
5371 p++; /* frame name terminator */
5373 if ((p-chunk) < (ssize_t) (length-4))
5380 change_delay=(*p++);
5381 change_timeout=(*p++);
5382 change_clipping=(*p++);
5383 p++; /* change_sync */
5387 frame_delay=1UL*image->ticks_per_second*
5390 if (mng_info->ticks_per_second != 0)
5391 frame_delay/=mng_info->ticks_per_second;
5394 frame_delay=PNG_UINT_31_MAX;
5396 if (change_delay == 2)
5397 default_frame_delay=frame_delay;
5401 if (logging != MagickFalse)
5402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5403 " Framing_delay=%.20g",(double) frame_delay);
5408 frame_timeout=1UL*image->ticks_per_second*
5411 if (mng_info->ticks_per_second != 0)
5412 frame_timeout/=mng_info->ticks_per_second;
5415 frame_timeout=PNG_UINT_31_MAX;
5417 if (change_delay == 2)
5418 default_frame_timeout=frame_timeout;
5422 if (logging != MagickFalse)
5423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5424 " Framing_timeout=%.20g",(double) frame_timeout);
5427 if (change_clipping)
5429 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5433 if (logging != MagickFalse)
5434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5435 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5436 (double) fb.left,(double) fb.right,(double) fb.top,
5437 (double) fb.bottom);
5439 if (change_clipping == 2)
5445 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5447 subframe_width=(size_t) (mng_info->clip.right
5448 -mng_info->clip.left);
5450 subframe_height=(size_t) (mng_info->clip.bottom
5451 -mng_info->clip.top);
5453 Insert a background layer behind the frame if framing_mode is 4.
5455 #if defined(MNG_INSERT_LAYERS)
5456 if (logging != MagickFalse)
5457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5458 " subframe_width=%.20g, subframe_height=%.20g",(double)
5459 subframe_width,(double) subframe_height);
5461 if (insert_layers && (mng_info->framing_mode == 4) &&
5462 (subframe_width) && (subframe_height))
5464 /* Allocate next image structure. */
5465 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5467 AcquireNextImage(image_info,image,exception);
5469 if (GetNextImageInList(image) == (Image *) NULL)
5471 image=DestroyImageList(image);
5472 MngInfoFreeStruct(mng_info,&have_mng_structure);
5473 return((Image *) NULL);
5476 image=SyncNextImageInList(image);
5479 mng_info->image=image;
5481 if (term_chunk_found)
5483 image->start_loop=MagickTrue;
5484 image->iterations=mng_iterations;
5485 term_chunk_found=MagickFalse;
5489 image->start_loop=MagickFalse;
5491 image->columns=subframe_width;
5492 image->rows=subframe_height;
5493 image->page.width=subframe_width;
5494 image->page.height=subframe_height;
5495 image->page.x=mng_info->clip.left;
5496 image->page.y=mng_info->clip.top;
5497 image->background_color=mng_background_color;
5498 image->alpha_trait=UndefinedPixelTrait;
5500 (void) SetImageBackgroundColor(image,exception);
5502 if (logging != MagickFalse)
5503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5504 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5505 (double) mng_info->clip.left,(double) mng_info->clip.right,
5506 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5512 if (memcmp(type,mng_CLIP,4) == 0)
5521 first_object=(p[0] << 8) | p[1];
5522 last_object=(p[2] << 8) | p[3];
5524 for (i=(int) first_object; i <= (int) last_object; i++)
5526 if (mng_info->exists[i] && !mng_info->frozen[i])
5531 box=mng_info->object_clip[i];
5532 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5536 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539 if (memcmp(type,mng_SAVE,4) == 0)
5541 for (i=1; i < MNG_MAX_OBJECTS; i++)
5542 if (mng_info->exists[i])
5544 mng_info->frozen[i]=MagickTrue;
5545 #ifdef MNG_OBJECT_BUFFERS
5546 if (mng_info->ob[i] != (MngBuffer *) NULL)
5547 mng_info->ob[i]->frozen=MagickTrue;
5552 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5557 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5559 /* Read DISC or SEEK. */
5561 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5563 for (i=1; i < MNG_MAX_OBJECTS; i++)
5564 MngInfoDiscardObject(mng_info,i);
5572 for (j=0; j < (ssize_t) length; j+=2)
5574 i=p[j] << 8 | p[j+1];
5575 MngInfoDiscardObject(mng_info,i);
5580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5585 if (memcmp(type,mng_MOVE,4) == 0)
5593 first_object=(p[0] << 8) | p[1];
5594 last_object=(p[2] << 8) | p[3];
5595 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5597 if (mng_info->exists[i] && !mng_info->frozen[i])
5605 old_pair.a=mng_info->x_off[i];
5606 old_pair.b=mng_info->y_off[i];
5607 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5608 mng_info->x_off[i]=new_pair.a;
5609 mng_info->y_off[i]=new_pair.b;
5613 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5617 if (memcmp(type,mng_LOOP,4) == 0)
5619 ssize_t loop_iters=1;
5620 loop_level=chunk[0];
5621 mng_info->loop_active[loop_level]=1; /* mark loop active */
5623 /* Record starting point. */
5624 loop_iters=mng_get_long(&chunk[1]);
5626 if (logging != MagickFalse)
5627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5628 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5629 (double) loop_iters);
5631 if (loop_iters == 0)
5632 skipping_loop=loop_level;
5636 mng_info->loop_jump[loop_level]=TellBlob(image);
5637 mng_info->loop_count[loop_level]=loop_iters;
5640 mng_info->loop_iteration[loop_level]=0;
5641 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5645 if (memcmp(type,mng_ENDL,4) == 0)
5647 loop_level=chunk[0];
5649 if (skipping_loop > 0)
5651 if (skipping_loop == loop_level)
5654 Found end of zero-iteration loop.
5657 mng_info->loop_active[loop_level]=0;
5663 if (mng_info->loop_active[loop_level] == 1)
5665 mng_info->loop_count[loop_level]--;
5666 mng_info->loop_iteration[loop_level]++;
5668 if (logging != MagickFalse)
5669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5670 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5671 (double) loop_level,(double)
5672 mng_info->loop_count[loop_level]);
5674 if (mng_info->loop_count[loop_level] != 0)
5676 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5680 ThrowReaderException(CorruptImageError,
5681 "ImproperImageHeader");
5692 mng_info->loop_active[loop_level]=0;
5694 for (i=0; i < loop_level; i++)
5695 if (mng_info->loop_active[i] == 1)
5696 last_level=(short) i;
5697 loop_level=last_level;
5702 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5706 if (memcmp(type,mng_CLON,4) == 0)
5708 if (mng_info->clon_warning == 0)
5709 (void) ThrowMagickException(exception,GetMagickModule(),
5710 CoderError,"CLON is not implemented yet","`%s'",
5713 mng_info->clon_warning++;
5716 if (memcmp(type,mng_MAGN,4) == 0)
5731 magn_first=(p[0] << 8) | p[1];
5737 magn_last=(p[2] << 8) | p[3];
5740 magn_last=magn_first;
5741 #ifndef MNG_OBJECT_BUFFERS
5742 if (magn_first || magn_last)
5743 if (mng_info->magn_warning == 0)
5745 (void) ThrowMagickException(exception,
5746 GetMagickModule(),CoderError,
5747 "MAGN is not implemented yet for nonzero objects",
5748 "`%s'",image->filename);
5750 mng_info->magn_warning++;
5760 magn_mx=(p[5] << 8) | p[6];
5769 magn_my=(p[7] << 8) | p[8];
5778 magn_ml=(p[9] << 8) | p[10];
5787 magn_mr=(p[11] << 8) | p[12];
5796 magn_mt=(p[13] << 8) | p[14];
5805 magn_mb=(p[15] << 8) | p[16];
5817 magn_methy=magn_methx;
5820 if (magn_methx > 5 || magn_methy > 5)
5821 if (mng_info->magn_warning == 0)
5823 (void) ThrowMagickException(exception,
5824 GetMagickModule(),CoderError,
5825 "Unknown MAGN method in MNG datastream","`%s'",
5828 mng_info->magn_warning++;
5830 #ifdef MNG_OBJECT_BUFFERS
5831 /* Magnify existing objects in the range magn_first to magn_last */
5833 if (magn_first == 0 || magn_last == 0)
5835 /* Save the magnification factors for object 0 */
5836 mng_info->magn_mb=magn_mb;
5837 mng_info->magn_ml=magn_ml;
5838 mng_info->magn_mr=magn_mr;
5839 mng_info->magn_mt=magn_mt;
5840 mng_info->magn_mx=magn_mx;
5841 mng_info->magn_my=magn_my;
5842 mng_info->magn_methx=magn_methx;
5843 mng_info->magn_methy=magn_methy;
5847 if (memcmp(type,mng_PAST,4) == 0)
5849 if (mng_info->past_warning == 0)
5850 (void) ThrowMagickException(exception,GetMagickModule(),
5851 CoderError,"PAST is not implemented yet","`%s'",
5854 mng_info->past_warning++;
5857 if (memcmp(type,mng_SHOW,4) == 0)
5859 if (mng_info->show_warning == 0)
5860 (void) ThrowMagickException(exception,GetMagickModule(),
5861 CoderError,"SHOW is not implemented yet","`%s'",
5864 mng_info->show_warning++;
5867 if (memcmp(type,mng_sBIT,4) == 0)
5870 mng_info->have_global_sbit=MagickFalse;
5874 mng_info->global_sbit.gray=p[0];
5875 mng_info->global_sbit.red=p[0];
5876 mng_info->global_sbit.green=p[1];
5877 mng_info->global_sbit.blue=p[2];
5878 mng_info->global_sbit.alpha=p[3];
5879 mng_info->have_global_sbit=MagickTrue;
5882 if (memcmp(type,mng_pHYs,4) == 0)
5886 mng_info->global_x_pixels_per_unit=
5887 (size_t) mng_get_long(p);
5888 mng_info->global_y_pixels_per_unit=
5889 (size_t) mng_get_long(&p[4]);
5890 mng_info->global_phys_unit_type=p[8];
5891 mng_info->have_global_phys=MagickTrue;
5895 mng_info->have_global_phys=MagickFalse;
5897 if (memcmp(type,mng_pHYg,4) == 0)
5899 if (mng_info->phyg_warning == 0)
5900 (void) ThrowMagickException(exception,GetMagickModule(),
5901 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5903 mng_info->phyg_warning++;
5905 if (memcmp(type,mng_BASI,4) == 0)
5907 skip_to_iend=MagickTrue;
5909 if (mng_info->basi_warning == 0)
5910 (void) ThrowMagickException(exception,GetMagickModule(),
5911 CoderError,"BASI is not implemented yet","`%s'",
5914 mng_info->basi_warning++;
5915 #ifdef MNG_BASI_SUPPORTED
5916 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5917 (p[2] << 8) | p[3]);
5918 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5919 (p[6] << 8) | p[7]);
5920 basi_color_type=p[8];
5921 basi_compression_method=p[9];
5922 basi_filter_type=p[10];
5923 basi_interlace_method=p[11];
5925 basi_red=(p[12] << 8) & p[13];
5931 basi_green=(p[14] << 8) & p[15];
5937 basi_blue=(p[16] << 8) & p[17];
5943 basi_alpha=(p[18] << 8) & p[19];
5947 if (basi_sample_depth == 16)
5954 basi_viewable=p[20];
5960 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5964 if (memcmp(type,mng_IHDR,4)
5965 #if defined(JNG_SUPPORTED)
5966 && memcmp(type,mng_JHDR,4)
5970 /* Not an IHDR or JHDR chunk */
5972 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5977 if (logging != MagickFalse)
5978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5979 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5981 mng_info->exists[object_id]=MagickTrue;
5982 mng_info->viewable[object_id]=MagickTrue;
5984 if (mng_info->invisible[object_id])
5986 if (logging != MagickFalse)
5987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5988 " Skipping invisible object");
5990 skip_to_iend=MagickTrue;
5991 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5994 #if defined(MNG_INSERT_LAYERS)
5996 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5998 image_width=(size_t) mng_get_long(p);
5999 image_height=(size_t) mng_get_long(&p[4]);
6001 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6004 Insert a transparent background layer behind the entire animation
6005 if it is not full screen.
6007 #if defined(MNG_INSERT_LAYERS)
6008 if (insert_layers && mng_type && first_mng_object)
6010 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6011 (image_width < mng_info->mng_width) ||
6012 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6013 (image_height < mng_info->mng_height) ||
6014 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6016 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6019 Allocate next image structure.
6021 AcquireNextImage(image_info,image,exception);
6023 if (GetNextImageInList(image) == (Image *) NULL)
6025 image=DestroyImageList(image);
6026 MngInfoFreeStruct(mng_info,&have_mng_structure);
6027 return((Image *) NULL);
6030 image=SyncNextImageInList(image);
6032 mng_info->image=image;
6034 if (term_chunk_found)
6036 image->start_loop=MagickTrue;
6037 image->iterations=mng_iterations;
6038 term_chunk_found=MagickFalse;
6042 image->start_loop=MagickFalse;
6044 /* Make a background rectangle. */
6047 image->columns=mng_info->mng_width;
6048 image->rows=mng_info->mng_height;
6049 image->page.width=mng_info->mng_width;
6050 image->page.height=mng_info->mng_height;
6053 image->background_color=mng_background_color;
6054 (void) SetImageBackgroundColor(image,exception);
6055 if (logging != MagickFalse)
6056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6057 " Inserted transparent background layer, W=%.20g, H=%.20g",
6058 (double) mng_info->mng_width,(double) mng_info->mng_height);
6062 Insert a background layer behind the upcoming image if
6063 framing_mode is 3, and we haven't already inserted one.
6065 if (insert_layers && (mng_info->framing_mode == 3) &&
6066 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6067 (simplicity & 0x08)))
6069 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6072 Allocate next image structure.
6074 AcquireNextImage(image_info,image,exception);
6076 if (GetNextImageInList(image) == (Image *) NULL)
6078 image=DestroyImageList(image);
6079 MngInfoFreeStruct(mng_info,&have_mng_structure);
6080 return((Image *) NULL);
6083 image=SyncNextImageInList(image);
6086 mng_info->image=image;
6088 if (term_chunk_found)
6090 image->start_loop=MagickTrue;
6091 image->iterations=mng_iterations;
6092 term_chunk_found=MagickFalse;
6096 image->start_loop=MagickFalse;
6099 image->columns=subframe_width;
6100 image->rows=subframe_height;
6101 image->page.width=subframe_width;
6102 image->page.height=subframe_height;
6103 image->page.x=mng_info->clip.left;
6104 image->page.y=mng_info->clip.top;
6105 image->background_color=mng_background_color;
6106 image->alpha_trait=UndefinedPixelTrait;
6107 (void) SetImageBackgroundColor(image,exception);
6109 if (logging != MagickFalse)
6110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6111 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6112 (double) mng_info->clip.left,(double) mng_info->clip.right,
6113 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6115 #endif /* MNG_INSERT_LAYERS */
6116 first_mng_object=MagickFalse;
6118 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6121 Allocate next image structure.
6123 AcquireNextImage(image_info,image,exception);
6125 if (GetNextImageInList(image) == (Image *) NULL)
6127 image=DestroyImageList(image);
6128 MngInfoFreeStruct(mng_info,&have_mng_structure);
6129 return((Image *) NULL);
6132 image=SyncNextImageInList(image);
6134 mng_info->image=image;
6135 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6136 GetBlobSize(image));
6138 if (status == MagickFalse)
6141 if (term_chunk_found)
6143 image->start_loop=MagickTrue;
6144 term_chunk_found=MagickFalse;
6148 image->start_loop=MagickFalse;
6150 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6152 image->delay=frame_delay;
6153 frame_delay=default_frame_delay;
6159 image->page.width=mng_info->mng_width;
6160 image->page.height=mng_info->mng_height;
6161 image->page.x=mng_info->x_off[object_id];
6162 image->page.y=mng_info->y_off[object_id];
6163 image->iterations=mng_iterations;
6166 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6169 if (logging != MagickFalse)
6170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6171 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6174 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6177 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6181 mng_info->image=image;
6182 mng_info->mng_type=mng_type;
6183 mng_info->object_id=object_id;
6185 if (memcmp(type,mng_IHDR,4) == 0)
6186 image=ReadOnePNGImage(mng_info,image_info,exception);
6188 #if defined(JNG_SUPPORTED)
6190 image=ReadOneJNGImage(mng_info,image_info,exception);
6193 if (image == (Image *) NULL)
6195 if (IsImageObject(previous) != MagickFalse)
6197 (void) DestroyImageList(previous);
6198 (void) CloseBlob(previous);
6201 MngInfoFreeStruct(mng_info,&have_mng_structure);
6202 return((Image *) NULL);
6205 if (image->columns == 0 || image->rows == 0)
6207 (void) CloseBlob(image);
6208 image=DestroyImageList(image);
6209 MngInfoFreeStruct(mng_info,&have_mng_structure);
6210 return((Image *) NULL);
6213 mng_info->image=image;
6220 if (mng_info->magn_methx || mng_info->magn_methy)
6226 if (logging != MagickFalse)
6227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6228 " Processing MNG MAGN chunk");
6230 if (mng_info->magn_methx == 1)
6232 magnified_width=mng_info->magn_ml;
6234 if (image->columns > 1)
6235 magnified_width += mng_info->magn_mr;
6237 if (image->columns > 2)
6238 magnified_width += (png_uint_32)
6239 ((image->columns-2)*(mng_info->magn_mx));
6244 magnified_width=(png_uint_32) image->columns;
6246 if (image->columns > 1)
6247 magnified_width += mng_info->magn_ml-1;
6249 if (image->columns > 2)
6250 magnified_width += mng_info->magn_mr-1;
6252 if (image->columns > 3)
6253 magnified_width += (png_uint_32)
6254 ((image->columns-3)*(mng_info->magn_mx-1));
6257 if (mng_info->magn_methy == 1)
6259 magnified_height=mng_info->magn_mt;
6261 if (image->rows > 1)
6262 magnified_height += mng_info->magn_mb;
6264 if (image->rows > 2)
6265 magnified_height += (png_uint_32)
6266 ((image->rows-2)*(mng_info->magn_my));
6271 magnified_height=(png_uint_32) image->rows;
6273 if (image->rows > 1)
6274 magnified_height += mng_info->magn_mt-1;
6276 if (image->rows > 2)
6277 magnified_height += mng_info->magn_mb-1;
6279 if (image->rows > 3)
6280 magnified_height += (png_uint_32)
6281 ((image->rows-3)*(mng_info->magn_my-1));
6284 if (magnified_height > image->rows ||
6285 magnified_width > image->columns)
6312 /* Allocate next image structure. */
6314 if (logging != MagickFalse)
6315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6316 " Allocate magnified image");
6318 AcquireNextImage(image_info,image,exception);
6320 if (GetNextImageInList(image) == (Image *) NULL)
6322 image=DestroyImageList(image);
6323 MngInfoFreeStruct(mng_info,&have_mng_structure);
6324 return((Image *) NULL);
6327 large_image=SyncNextImageInList(image);
6329 large_image->columns=magnified_width;
6330 large_image->rows=magnified_height;
6332 magn_methx=mng_info->magn_methx;
6333 magn_methy=mng_info->magn_methy;
6335 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6336 #define QM unsigned short
6337 if (magn_methx != 1 || magn_methy != 1)
6340 Scale pixels to unsigned shorts to prevent
6341 overflow of intermediate values of interpolations
6343 for (y=0; y < (ssize_t) image->rows; y++)
6345 q=GetAuthenticPixels(image,0,y,image->columns,1,
6348 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6350 SetPixelRed(image,ScaleQuantumToShort(
6351 GetPixelRed(image,q)),q);
6352 SetPixelGreen(image,ScaleQuantumToShort(
6353 GetPixelGreen(image,q)),q);
6354 SetPixelBlue(image,ScaleQuantumToShort(
6355 GetPixelBlue(image,q)),q);
6356 SetPixelAlpha(image,ScaleQuantumToShort(
6357 GetPixelAlpha(image,q)),q);
6358 q+=GetPixelChannels(image);
6361 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6369 if (image->alpha_trait == BlendPixelTrait)
6370 (void) SetImageBackgroundColor(large_image,exception);
6374 large_image->background_color.alpha=OpaqueAlpha;
6375 (void) SetImageBackgroundColor(large_image,exception);
6377 if (magn_methx == 4)
6380 if (magn_methx == 5)
6383 if (magn_methy == 4)
6386 if (magn_methy == 5)
6390 /* magnify the rows into the right side of the large image */
6392 if (logging != MagickFalse)
6393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6394 " Magnify the rows to %.20g",(double) large_image->rows);
6395 m=(ssize_t) mng_info->magn_mt;
6397 length=(size_t) image->columns*GetPixelChannels(image);
6398 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6399 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6401 if ((prev == (Quantum *) NULL) ||
6402 (next == (Quantum *) NULL))
6404 image=DestroyImageList(image);
6405 MngInfoFreeStruct(mng_info,&have_mng_structure);
6406 ThrowReaderException(ResourceLimitError,
6407 "MemoryAllocationFailed");
6410 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6411 (void) CopyMagickMemory(next,n,length);
6413 for (y=0; y < (ssize_t) image->rows; y++)
6416 m=(ssize_t) mng_info->magn_mt;
6418 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6419 m=(ssize_t) mng_info->magn_mb;
6421 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6422 m=(ssize_t) mng_info->magn_mb;
6424 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6428 m=(ssize_t) mng_info->magn_my;
6434 if (y < (ssize_t) image->rows-1)
6436 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6438 (void) CopyMagickMemory(next,n,length);
6441 for (i=0; i < m; i++, yy++)
6446 assert(yy < (ssize_t) large_image->rows);
6449 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6451 q+=(large_image->columns-image->columns)*
6452 GetPixelChannels(large_image);
6454 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6456 /* To do: get color as function of indexes[x] */
6458 if (image->storage_class == PseudoClass)
6463 if (magn_methy <= 1)
6465 /* replicate previous */
6466 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6467 SetPixelGreen(large_image,GetPixelGreen(image,
6469 SetPixelBlue(large_image,GetPixelBlue(image,
6471 SetPixelAlpha(large_image,GetPixelAlpha(image,
6475 else if (magn_methy == 2 || magn_methy == 4)
6479 SetPixelRed(large_image,GetPixelRed(image,
6481 SetPixelGreen(large_image,GetPixelGreen(image,
6483 SetPixelBlue(large_image,GetPixelBlue(image,
6485 SetPixelAlpha(large_image,GetPixelAlpha(image,
6492 SetPixelRed(large_image,((QM) (((ssize_t)
6493 (2*i*(GetPixelRed(image,n)
6494 -GetPixelRed(image,pixels)+m))/
6496 +GetPixelRed(image,pixels)))),q);
6497 SetPixelGreen(large_image,((QM) (((ssize_t)
6498 (2*i*(GetPixelGreen(image,n)
6499 -GetPixelGreen(image,pixels)+m))/
6501 +GetPixelGreen(image,pixels)))),q);
6502 SetPixelBlue(large_image,((QM) (((ssize_t)
6503 (2*i*(GetPixelBlue(image,n)
6504 -GetPixelBlue(image,pixels)+m))/
6506 +GetPixelBlue(image,pixels)))),q);
6508 if (image->alpha_trait == BlendPixelTrait)
6509 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6510 (2*i*(GetPixelAlpha(image,n)
6511 -GetPixelAlpha(image,pixels)+m))
6513 GetPixelAlpha(image,pixels)))),q);
6516 if (magn_methy == 4)
6518 /* Replicate nearest */
6519 if (i <= ((m+1) << 1))
6520 SetPixelAlpha(large_image,GetPixelAlpha(image,
6523 SetPixelAlpha(large_image,GetPixelAlpha(image,
6528 else /* if (magn_methy == 3 || magn_methy == 5) */
6530 /* Replicate nearest */
6531 if (i <= ((m+1) << 1))
6533 SetPixelRed(large_image,GetPixelRed(image,
6535 SetPixelGreen(large_image,GetPixelGreen(image,
6537 SetPixelBlue(large_image,GetPixelBlue(image,
6539 SetPixelAlpha(large_image,GetPixelAlpha(image,
6545 SetPixelRed(large_image,GetPixelRed(image,n),q);
6546 SetPixelGreen(large_image,GetPixelGreen(image,n),
6548 SetPixelBlue(large_image,GetPixelBlue(image,n),
6550 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6554 if (magn_methy == 5)
6556 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6557 (GetPixelAlpha(image,n)
6558 -GetPixelAlpha(image,pixels))
6559 +m))/((ssize_t) (m*2))
6560 +GetPixelAlpha(image,pixels)),q);
6563 n+=GetPixelChannels(image);
6564 q+=GetPixelChannels(large_image);
6565 pixels+=GetPixelChannels(image);
6568 if (SyncAuthenticPixels(large_image,exception) == 0)
6574 prev=(Quantum *) RelinquishMagickMemory(prev);
6575 next=(Quantum *) RelinquishMagickMemory(next);
6577 length=image->columns;
6579 if (logging != MagickFalse)
6580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6581 " Delete original image");
6583 DeleteImageFromList(&image);
6587 mng_info->image=image;
6589 /* magnify the columns */
6590 if (logging != MagickFalse)
6591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6592 " Magnify the columns to %.20g",(double) image->columns);
6594 for (y=0; y < (ssize_t) image->rows; y++)
6599 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6600 pixels=q+(image->columns-length)*GetPixelChannels(image);
6601 n=pixels+GetPixelChannels(image);
6603 for (x=(ssize_t) (image->columns-length);
6604 x < (ssize_t) image->columns; x++)
6606 /* To do: Rewrite using Get/Set***PixelChannel() */
6608 if (x == (ssize_t) (image->columns-length))
6609 m=(ssize_t) mng_info->magn_ml;
6611 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6612 m=(ssize_t) mng_info->magn_mr;
6614 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6615 m=(ssize_t) mng_info->magn_mr;
6617 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6621 m=(ssize_t) mng_info->magn_mx;
6623 for (i=0; i < m; i++)
6625 if (magn_methx <= 1)
6627 /* replicate previous */
6628 SetPixelRed(image,GetPixelRed(image,pixels),q);
6629 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6630 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6631 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6634 else if (magn_methx == 2 || magn_methx == 4)
6638 SetPixelRed(image,GetPixelRed(image,pixels),q);
6639 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6640 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6641 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6644 /* To do: Rewrite using Get/Set***PixelChannel() */
6648 SetPixelRed(image,(QM) ((2*i*(
6649 GetPixelRed(image,n)
6650 -GetPixelRed(image,pixels))+m)
6652 GetPixelRed(image,pixels)),q);
6654 SetPixelGreen(image,(QM) ((2*i*(
6655 GetPixelGreen(image,n)
6656 -GetPixelGreen(image,pixels))+m)
6658 GetPixelGreen(image,pixels)),q);
6660 SetPixelBlue(image,(QM) ((2*i*(
6661 GetPixelBlue(image,n)
6662 -GetPixelBlue(image,pixels))+m)
6664 GetPixelBlue(image,pixels)),q);
6665 if (image->alpha_trait == BlendPixelTrait)
6666 SetPixelAlpha(image,(QM) ((2*i*(
6667 GetPixelAlpha(image,n)
6668 -GetPixelAlpha(image,pixels))+m)
6670 GetPixelAlpha(image,pixels)),q);
6673 if (magn_methx == 4)
6675 /* Replicate nearest */
6676 if (i <= ((m+1) << 1))
6678 SetPixelAlpha(image,
6679 GetPixelAlpha(image,pixels)+0,q);
6683 SetPixelAlpha(image,
6684 GetPixelAlpha(image,n)+0,q);
6689 else /* if (magn_methx == 3 || magn_methx == 5) */
6691 /* Replicate nearest */
6692 if (i <= ((m+1) << 1))
6694 SetPixelRed(image,GetPixelRed(image,pixels),q);
6695 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6696 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6697 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6702 SetPixelRed(image,GetPixelRed(image,n),q);
6703 SetPixelGreen(image,GetPixelGreen(image,n),q);
6704 SetPixelBlue(image,GetPixelBlue(image,n),q);
6705 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6708 if (magn_methx == 5)
6711 SetPixelAlpha(image,
6712 (QM) ((2*i*( GetPixelAlpha(image,n)
6713 -GetPixelAlpha(image,pixels))+m)/
6715 +GetPixelAlpha(image,pixels)),q);
6718 q+=GetPixelChannels(image);
6720 n+=GetPixelChannels(image);
6721 p+=GetPixelChannels(image);
6724 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6727 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6728 if (magn_methx != 1 || magn_methy != 1)
6731 Rescale pixels to Quantum
6733 for (y=0; y < (ssize_t) image->rows; y++)
6735 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6737 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6739 SetPixelRed(image,ScaleShortToQuantum(
6740 GetPixelRed(image,q)),q);
6741 SetPixelGreen(image,ScaleShortToQuantum(
6742 GetPixelGreen(image,q)),q);
6743 SetPixelBlue(image,ScaleShortToQuantum(
6744 GetPixelBlue(image,q)),q);
6745 SetPixelAlpha(image,ScaleShortToQuantum(
6746 GetPixelAlpha(image,q)),q);
6747 q+=GetPixelChannels(image);
6750 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6755 if (logging != MagickFalse)
6756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6757 " Finished MAGN processing");
6762 Crop_box is with respect to the upper left corner of the MNG.
6764 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6765 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6766 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6767 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6768 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6769 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6770 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6771 if ((crop_box.left != (mng_info->image_box.left
6772 +mng_info->x_off[object_id])) ||
6773 (crop_box.right != (mng_info->image_box.right
6774 +mng_info->x_off[object_id])) ||
6775 (crop_box.top != (mng_info->image_box.top
6776 +mng_info->y_off[object_id])) ||
6777 (crop_box.bottom != (mng_info->image_box.bottom
6778 +mng_info->y_off[object_id])))
6780 if (logging != MagickFalse)
6781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6782 " Crop the PNG image");
6784 if ((crop_box.left < crop_box.right) &&
6785 (crop_box.top < crop_box.bottom))
6794 Crop_info is with respect to the upper left corner of
6797 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6798 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6799 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6800 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6801 image->page.width=image->columns;
6802 image->page.height=image->rows;
6805 im=CropImage(image,&crop_info,exception);
6807 if (im != (Image *) NULL)
6809 image->columns=im->columns;
6810 image->rows=im->rows;
6811 im=DestroyImage(im);
6812 image->page.width=image->columns;
6813 image->page.height=image->rows;
6814 image->page.x=crop_box.left;
6815 image->page.y=crop_box.top;
6822 No pixels in crop area. The MNG spec still requires
6823 a layer, though, so make a single transparent pixel in
6824 the top left corner.
6829 (void) SetImageBackgroundColor(image,exception);
6830 image->page.width=1;
6831 image->page.height=1;
6836 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6837 image=mng_info->image;
6841 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6842 /* PNG does not handle depths greater than 16 so reduce it even
6845 if (image->depth > 16)
6849 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6850 if (image->depth > 8)
6852 /* To do: fill low byte properly */
6856 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6860 if (image_info->number_scenes != 0)
6862 if (mng_info->scenes_found >
6863 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6867 if (logging != MagickFalse)
6868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6869 " Finished reading image datastream.");
6871 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6873 (void) CloseBlob(image);
6875 if (logging != MagickFalse)
6876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6877 " Finished reading all image datastreams.");
6879 #if defined(MNG_INSERT_LAYERS)
6880 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6881 (mng_info->mng_height))
6884 Insert a background layer if nothing else was found.
6886 if (logging != MagickFalse)
6887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6888 " No images found. Inserting a background layer.");
6890 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6893 Allocate next image structure.
6895 AcquireNextImage(image_info,image,exception);
6896 if (GetNextImageInList(image) == (Image *) NULL)
6898 image=DestroyImageList(image);
6899 MngInfoFreeStruct(mng_info,&have_mng_structure);
6901 if (logging != MagickFalse)
6902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6903 " Allocation failed, returning NULL.");
6905 return((Image *) NULL);
6907 image=SyncNextImageInList(image);
6909 image->columns=mng_info->mng_width;
6910 image->rows=mng_info->mng_height;
6911 image->page.width=mng_info->mng_width;
6912 image->page.height=mng_info->mng_height;
6915 image->background_color=mng_background_color;
6916 image->alpha_trait=UndefinedPixelTrait;
6918 if (image_info->ping == MagickFalse)
6919 (void) SetImageBackgroundColor(image,exception);
6921 mng_info->image_found++;
6924 image->iterations=mng_iterations;
6926 if (mng_iterations == 1)
6927 image->start_loop=MagickTrue;
6929 while (GetPreviousImageInList(image) != (Image *) NULL)
6932 if (image_count > 10*mng_info->image_found)
6934 if (logging != MagickFalse)
6935 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6937 (void) ThrowMagickException(exception,GetMagickModule(),
6938 CoderError,"Linked list is corrupted, beginning of list not found",
6939 "`%s'",image_info->filename);
6941 return((Image *) NULL);
6944 image=GetPreviousImageInList(image);
6946 if (GetNextImageInList(image) == (Image *) NULL)
6948 if (logging != MagickFalse)
6949 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6951 (void) ThrowMagickException(exception,GetMagickModule(),
6952 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6953 image_info->filename);
6957 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6958 GetNextImageInList(image) ==
6961 if (logging != MagickFalse)
6962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6963 " First image null");
6965 (void) ThrowMagickException(exception,GetMagickModule(),
6966 CoderError,"image->next for first image is NULL but shouldn't be.",
6967 "`%s'",image_info->filename);
6970 if (mng_info->image_found == 0)
6972 if (logging != MagickFalse)
6973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6974 " No visible images found.");
6976 (void) ThrowMagickException(exception,GetMagickModule(),
6977 CoderError,"No visible images in file","`%s'",image_info->filename);
6979 if (image != (Image *) NULL)
6980 image=DestroyImageList(image);
6982 MngInfoFreeStruct(mng_info,&have_mng_structure);
6983 return((Image *) NULL);
6986 if (mng_info->ticks_per_second)
6987 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6988 final_delay/mng_info->ticks_per_second;
6991 image->start_loop=MagickTrue;
6993 /* Find final nonzero image delay */
6994 final_image_delay=0;
6996 while (GetNextImageInList(image) != (Image *) NULL)
6999 final_image_delay=image->delay;
7001 image=GetNextImageInList(image);
7004 if (final_delay < final_image_delay)
7005 final_delay=final_image_delay;
7007 image->delay=final_delay;
7009 if (logging != MagickFalse)
7010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7011 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7012 (double) final_delay);
7014 if (logging != MagickFalse)
7020 image=GetFirstImageInList(image);
7022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7023 " Before coalesce:");
7025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7026 " scene 0 delay=%.20g",(double) image->delay);
7028 while (GetNextImageInList(image) != (Image *) NULL)
7030 image=GetNextImageInList(image);
7031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7032 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7036 image=GetFirstImageInList(image);
7037 #ifdef MNG_COALESCE_LAYERS
7047 if (logging != MagickFalse)
7048 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7051 next_image=CoalesceImages(image,exception);
7053 if (next_image == (Image *) NULL)
7054 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7056 image=DestroyImageList(image);
7059 for (next=image; next != (Image *) NULL; next=next_image)
7061 next->page.width=mng_info->mng_width;
7062 next->page.height=mng_info->mng_height;
7065 next->scene=scene++;
7066 next_image=GetNextImageInList(next);
7068 if (next_image == (Image *) NULL)
7071 if (next->delay == 0)
7074 next_image->previous=GetPreviousImageInList(next);
7075 if (GetPreviousImageInList(next) == (Image *) NULL)
7078 next->previous->next=next_image;
7079 next=DestroyImage(next);
7085 while (GetNextImageInList(image) != (Image *) NULL)
7086 image=GetNextImageInList(image);
7088 image->dispose=BackgroundDispose;
7090 if (logging != MagickFalse)
7096 image=GetFirstImageInList(image);
7098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7099 " After coalesce:");
7101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7102 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7103 (double) image->dispose);
7105 while (GetNextImageInList(image) != (Image *) NULL)
7107 image=GetNextImageInList(image);
7109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7110 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7111 (double) image->delay,(double) image->dispose);
7115 image=GetFirstImageInList(image);
7116 MngInfoFreeStruct(mng_info,&have_mng_structure);
7117 have_mng_structure=MagickFalse;
7119 if (logging != MagickFalse)
7120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7122 return(GetFirstImageInList(image));
7124 #else /* PNG_LIBPNG_VER > 10011 */
7125 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7127 printf("Your PNG library is too old: You have libpng-%s\n",
7128 PNG_LIBPNG_VER_STRING);
7130 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7131 "PNG library is too old","`%s'",image_info->filename);
7133 return(Image *) NULL;
7136 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7138 return(ReadPNGImage(image_info,exception));
7140 #endif /* PNG_LIBPNG_VER > 10011 */
7144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7148 % R e g i s t e r P N G I m a g e %
7152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7154 % RegisterPNGImage() adds properties for the PNG image format to
7155 % the list of supported formats. The properties include the image format
7156 % tag, a method to read and/or write the format, whether the format
7157 % supports the saving of more than one frame to the same file or blob,
7158 % whether the format supports native in-memory I/O, and a brief
7159 % description of the format.
7161 % The format of the RegisterPNGImage method is:
7163 % size_t RegisterPNGImage(void)
7166 ModuleExport size_t RegisterPNGImage(void)
7169 version[MaxTextExtent];
7177 "See http://www.libpng.org/ for details about the PNG format."
7182 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7188 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7194 #if defined(PNG_LIBPNG_VER_STRING)
7195 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7196 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7198 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7200 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7201 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7206 entry=SetMagickInfo("MNG");
7207 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7209 #if defined(MAGICKCORE_PNG_DELEGATE)
7210 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7211 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7214 entry->magick=(IsImageFormatHandler *) IsMNG;
7215 entry->description=ConstantString("Multiple-image Network Graphics");
7217 if (*version != '\0')
7218 entry->version=ConstantString(version);
7220 entry->module=ConstantString("PNG");
7221 entry->note=ConstantString(MNGNote);
7222 (void) RegisterMagickInfo(entry);
7224 entry=SetMagickInfo("PNG");
7226 #if defined(MAGICKCORE_PNG_DELEGATE)
7227 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7228 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7231 entry->magick=(IsImageFormatHandler *) IsPNG;
7232 entry->adjoin=MagickFalse;
7233 entry->description=ConstantString("Portable Network Graphics");
7234 entry->module=ConstantString("PNG");
7236 if (*version != '\0')
7237 entry->version=ConstantString(version);
7239 entry->note=ConstantString(PNGNote);
7240 (void) RegisterMagickInfo(entry);
7242 entry=SetMagickInfo("PNG8");
7244 #if defined(MAGICKCORE_PNG_DELEGATE)
7245 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7246 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7249 entry->magick=(IsImageFormatHandler *) IsPNG;
7250 entry->adjoin=MagickFalse;
7251 entry->description=ConstantString(
7252 "8-bit indexed with optional binary transparency");
7253 entry->module=ConstantString("PNG");
7254 (void) RegisterMagickInfo(entry);
7256 entry=SetMagickInfo("PNG24");
7259 #if defined(ZLIB_VERSION)
7260 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7261 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7263 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7265 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7266 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7270 if (*version != '\0')
7271 entry->version=ConstantString(version);
7273 #if defined(MAGICKCORE_PNG_DELEGATE)
7274 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7275 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7278 entry->magick=(IsImageFormatHandler *) IsPNG;
7279 entry->adjoin=MagickFalse;
7280 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7281 entry->module=ConstantString("PNG");
7282 (void) RegisterMagickInfo(entry);
7284 entry=SetMagickInfo("PNG32");
7286 #if defined(MAGICKCORE_PNG_DELEGATE)
7287 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7288 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7291 entry->magick=(IsImageFormatHandler *) IsPNG;
7292 entry->adjoin=MagickFalse;
7293 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7294 entry->module=ConstantString("PNG");
7295 (void) RegisterMagickInfo(entry);
7297 entry=SetMagickInfo("PNG48");
7299 #if defined(MAGICKCORE_PNG_DELEGATE)
7300 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7301 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7304 entry->magick=(IsImageFormatHandler *) IsPNG;
7305 entry->adjoin=MagickFalse;
7306 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7307 entry->module=ConstantString("PNG");
7308 (void) RegisterMagickInfo(entry);
7310 entry=SetMagickInfo("PNG64");
7312 #if defined(MAGICKCORE_PNG_DELEGATE)
7313 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7314 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7317 entry->magick=(IsImageFormatHandler *) IsPNG;
7318 entry->adjoin=MagickFalse;
7319 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7320 entry->module=ConstantString("PNG");
7321 (void) RegisterMagickInfo(entry);
7323 entry=SetMagickInfo("JNG");
7325 #if defined(JNG_SUPPORTED)
7326 #if defined(MAGICKCORE_PNG_DELEGATE)
7327 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7328 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7332 entry->magick=(IsImageFormatHandler *) IsJNG;
7333 entry->adjoin=MagickFalse;
7334 entry->description=ConstantString("JPEG Network Graphics");
7335 entry->module=ConstantString("PNG");
7336 entry->note=ConstantString(JNGNote);
7337 (void) RegisterMagickInfo(entry);
7339 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7340 ping_semaphore=AllocateSemaphoreInfo();
7343 return(MagickImageCoderSignature);
7347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7351 % U n r e g i s t e r P N G I m a g e %
7355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7357 % UnregisterPNGImage() removes format registrations made by the
7358 % PNG module from the list of supported formats.
7360 % The format of the UnregisterPNGImage method is:
7362 % UnregisterPNGImage(void)
7365 ModuleExport void UnregisterPNGImage(void)
7367 (void) UnregisterMagickInfo("MNG");
7368 (void) UnregisterMagickInfo("PNG");
7369 (void) UnregisterMagickInfo("PNG8");
7370 (void) UnregisterMagickInfo("PNG24");
7371 (void) UnregisterMagickInfo("PNG32");
7372 (void) UnregisterMagickInfo("PNG48");
7373 (void) UnregisterMagickInfo("PNG64");
7374 (void) UnregisterMagickInfo("JNG");
7376 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7377 if (ping_semaphore != (SemaphoreInfo *) NULL)
7378 DestroySemaphoreInfo(&ping_semaphore);
7382 #if defined(MAGICKCORE_PNG_DELEGATE)
7383 #if PNG_LIBPNG_VER > 10011
7385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7389 % W r i t e M N G I m a g e %
7393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7395 % WriteMNGImage() writes an image in the Portable Network Graphics
7396 % Group's "Multiple-image Network Graphics" encoded image format.
7398 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7400 % The format of the WriteMNGImage method is:
7402 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7403 % Image *image,ExceptionInfo *exception)
7405 % A description of each parameter follows.
7407 % o image_info: the image info.
7409 % o image: The image.
7411 % o exception: return any errors or warnings in this structure.
7413 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7414 % "To do" under ReadPNGImage):
7416 % Preserve all unknown and not-yet-handled known chunks found in input
7417 % PNG file and copy them into output PNG files according to the PNG
7420 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7422 % Improve selection of color type (use indexed-colour or indexed-colour
7423 % with tRNS when 256 or fewer unique RGBA values are present).
7425 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7426 % This will be complicated if we limit ourselves to generating MNG-LC
7427 % files. For now we ignore disposal method 3 and simply overlay the next
7430 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7431 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7432 % [mostly done 15 June 1999 but still need to take care of tRNS]
7434 % Check for identical sRGB and replace with a global sRGB (and remove
7435 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7436 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7437 % local gAMA/cHRM with local sRGB if appropriate).
7439 % Check for identical sBIT chunks and write global ones.
7441 % Provide option to skip writing the signature tEXt chunks.
7443 % Use signatures to detect identical objects and reuse the first
7444 % instance of such objects instead of writing duplicate objects.
7446 % Use a smaller-than-32k value of compression window size when
7449 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7450 % ancillary text chunks and save profiles.
7452 % Provide an option to force LC files (to ensure exact framing rate)
7455 % Provide an option to force VLC files instead of LC, even when offsets
7456 % are present. This will involve expanding the embedded images with a
7457 % transparent region at the top and/or left.
7461 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7462 png_info *ping_info, unsigned char *profile_type, unsigned char
7463 *profile_description, unsigned char *profile_data, png_uint_32 length)
7482 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7484 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7487 if (image_info->verbose)
7489 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7490 (char *) profile_type, (double) length);
7493 #if PNG_LIBPNG_VER >= 14000
7494 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7496 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7498 description_length=(png_uint_32) strlen((const char *) profile_description);
7499 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7500 + description_length);
7501 #if PNG_LIBPNG_VER >= 14000
7502 text[0].text=(png_charp) png_malloc(ping,
7503 (png_alloc_size_t) allocated_length);
7504 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7506 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7507 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7509 text[0].key[0]='\0';
7510 (void) ConcatenateMagickString(text[0].key,
7511 "Raw profile type ",MaxTextExtent);
7512 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7516 (void) CopyMagickString(dp,(const char *) profile_description,
7518 dp+=description_length;
7520 (void) FormatLocaleString(dp,allocated_length-
7521 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7524 for (i=0; i < (ssize_t) length; i++)
7528 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7529 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7534 text[0].text_length=(png_size_t) (dp-text[0].text);
7535 text[0].compression=image_info->compression == NoCompression ||
7536 (image_info->compression == UndefinedCompression &&
7537 text[0].text_length < 128) ? -1 : 0;
7539 if (text[0].text_length <= allocated_length)
7540 png_set_text(ping,ping_info,text,1);
7542 png_free(ping,text[0].text);
7543 png_free(ping,text[0].key);
7544 png_free(ping,text);
7547 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7548 const char *string, MagickBooleanType logging)
7561 ResetImageProfileIterator(image);
7563 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7565 profile=GetImageProfile(image,name);
7567 if (profile != (const StringInfo *) NULL)
7572 if (LocaleNCompare(name,string,11) == 0)
7574 if (logging != MagickFalse)
7575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7576 " Found %s profile",name);
7578 ping_profile=CloneStringInfo(profile);
7579 data=GetStringInfoDatum(ping_profile),
7580 length=(png_uint_32) GetStringInfoLength(ping_profile);
7585 (void) WriteBlobMSBULong(image,length-5); /* data length */
7586 (void) WriteBlob(image,length-1,data+1);
7587 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7588 ping_profile=DestroyStringInfo(ping_profile);
7592 name=GetNextImageProfile(image);
7599 /* Write one PNG image */
7600 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7601 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7625 ping_trans_alpha[256];
7653 ping_have_cheap_transparency,
7664 /* ping_exclude_EXIF, */
7667 /* ping_exclude_iTXt, */
7672 /* ping_exclude_tRNS, */
7674 ping_exclude_zCCP, /* hex-encoded iCCP */
7677 ping_preserve_colormap,
7678 ping_need_colortype_warning,
7696 *volatile ping_pixels;
7702 ping_interlace_method,
7703 ping_compression_method,
7720 number_semitransparent,
7722 ping_pHYs_unit_type;
7725 ping_pHYs_x_resolution,
7726 ping_pHYs_y_resolution;
7728 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7729 " Enter WriteOnePNGImage()");
7731 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7732 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7733 if (image_info == (ImageInfo *) NULL)
7734 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7736 /* Initialize some stuff */
7739 ping_interlace_method=0,
7740 ping_compression_method=0,
7741 ping_filter_method=0,
7744 ping_background.red = 0;
7745 ping_background.green = 0;
7746 ping_background.blue = 0;
7747 ping_background.gray = 0;
7748 ping_background.index = 0;
7750 ping_trans_color.red=0;
7751 ping_trans_color.green=0;
7752 ping_trans_color.blue=0;
7753 ping_trans_color.gray=0;
7755 ping_pHYs_unit_type = 0;
7756 ping_pHYs_x_resolution = 0;
7757 ping_pHYs_y_resolution = 0;
7759 ping_have_blob=MagickFalse;
7760 ping_have_color=MagickTrue;
7761 ping_have_non_bw=MagickTrue;
7762 ping_have_PLTE=MagickFalse;
7763 ping_have_bKGD=MagickFalse;
7764 ping_have_pHYs=MagickFalse;
7765 ping_have_tRNS=MagickFalse;
7767 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7768 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7769 ping_exclude_date=mng_info->ping_exclude_date;
7770 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7771 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7772 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7773 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7774 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7775 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7776 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7777 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7778 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7779 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7780 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7781 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7783 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7784 ping_need_colortype_warning = MagickFalse;
7786 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7787 * i.e., eliminate the ICC profile and set image->rendering_intent.
7788 * Note that this will not involve any changes to the actual pixels
7789 * but merely passes information to applications that read the resulting
7792 if (ping_exclude_sRGB == MagickFalse)
7800 ResetImageProfileIterator(image);
7801 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7803 profile=GetImageProfile(image,name);
7805 if (profile != (StringInfo *) NULL)
7807 if ((LocaleCompare(name,"ICC") == 0) ||
7808 (LocaleCompare(name,"ICM") == 0))
7813 /* 0: not a known sRGB profile
7814 * 1: HP-Microsoft sRGB v2
7815 * 2: ICC sRGB v4 perceptual
7816 * 3: ICC sRGB v2 perceptual no black-compensation
7819 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7820 check_len[4] = {0, 3144, 60960, 3052};
7829 length=(png_uint_32) GetStringInfoLength(profile);
7831 for (icheck=3; icheck > 0; icheck--)
7833 if (length == check_len[icheck])
7835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7836 " Got a %lu-byte ICC profile (potentially sRGB)",
7837 (unsigned long) length);
7839 data=GetStringInfoDatum(profile);
7840 profile_crc=crc32(0,data,length);
7842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7843 " with crc=%8x",(unsigned int) profile_crc);
7845 if (profile_crc == check_crc[icheck])
7847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7849 if (image->rendering_intent==UndefinedIntent)
7850 image->rendering_intent=PerceptualIntent;
7856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7857 " Got a %lu-byte ICC profile",
7858 (unsigned long) length);
7861 name=GetNextImageProfile(image);
7866 number_semitransparent = 0;
7867 number_transparent = 0;
7869 if (logging != MagickFalse)
7871 if (image->storage_class == UndefinedClass)
7872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7873 " storage_class=UndefinedClass");
7874 if (image->storage_class == DirectClass)
7875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7876 " storage_class=DirectClass");
7877 if (image->storage_class == PseudoClass)
7878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7879 " storage_class=PseudoClass");
7882 if (image->storage_class == PseudoClass &&
7883 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7884 mng_info->write_png48 || mng_info->write_png64 ||
7885 (mng_info->write_png_colortype != 1 &&
7886 mng_info->write_png_colortype != 5)))
7888 (void) SyncImage(image,exception);
7889 image->storage_class = DirectClass;
7892 if (ping_preserve_colormap == MagickFalse)
7894 if (image->storage_class != PseudoClass && image->colormap != NULL)
7896 /* Free the bogus colormap; it can cause trouble later */
7897 if (logging != MagickFalse)
7898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7899 " Freeing bogus colormap");
7900 (void) RelinquishMagickMemory(image->colormap);
7901 image->colormap=NULL;
7905 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
7906 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7909 Sometimes we get PseudoClass images whose RGB values don't match
7910 the colors in the colormap. This code syncs the RGB values.
7912 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7913 (void) SyncImage(image,exception);
7915 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7916 if (image->depth > 8)
7918 if (logging != MagickFalse)
7919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7920 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7926 /* Respect the -depth option */
7927 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7932 if (image->depth > 8)
7934 #if MAGICKCORE_QUANTUM_DEPTH > 16
7935 /* Scale to 16-bit */
7936 LBR16PacketRGBO(image->background_color);
7938 for (y=0; y < (ssize_t) image->rows; y++)
7940 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7942 if (r == (Quantum *) NULL)
7945 for (x=0; x < (ssize_t) image->columns; x++)
7948 r+=GetPixelChannels(image);
7951 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7955 if (image->storage_class == PseudoClass && image->colormap != NULL)
7957 for (i=0; i < (ssize_t) image->colors; i++)
7959 LBR16PacketRGBO(image->colormap[i]);
7962 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7965 else if (image->depth > 4)
7967 #if MAGICKCORE_QUANTUM_DEPTH > 8
7968 /* Scale to 8-bit */
7969 LBR08PacketRGBO(image->background_color);
7971 for (y=0; y < (ssize_t) image->rows; y++)
7973 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7975 if (r == (Quantum *) NULL)
7978 for (x=0; x < (ssize_t) image->columns; x++)
7981 r+=GetPixelChannels(image);
7984 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7988 if (image->storage_class == PseudoClass && image->colormap != NULL)
7990 for (i=0; i < (ssize_t) image->colors; i++)
7992 LBR08PacketRGBO(image->colormap[i]);
7995 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7998 if (image->depth > 2)
8000 /* Scale to 4-bit */
8001 LBR04PacketRGBO(image->background_color);
8003 for (y=0; y < (ssize_t) image->rows; y++)
8005 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8007 if (r == (Quantum *) NULL)
8010 for (x=0; x < (ssize_t) image->columns; x++)
8013 r+=GetPixelChannels(image);
8016 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8020 if (image->storage_class == PseudoClass && image->colormap != NULL)
8022 for (i=0; i < (ssize_t) image->colors; i++)
8024 LBR04PacketRGBO(image->colormap[i]);
8029 else if (image->depth > 1)
8031 /* Scale to 2-bit */
8032 LBR02PacketRGBO(image->background_color);
8034 for (y=0; y < (ssize_t) image->rows; y++)
8036 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8038 if (r == (Quantum *) NULL)
8041 for (x=0; x < (ssize_t) image->columns; x++)
8044 r+=GetPixelChannels(image);
8047 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8051 if (image->storage_class == PseudoClass && image->colormap != NULL)
8053 for (i=0; i < (ssize_t) image->colors; i++)
8055 LBR02PacketRGBO(image->colormap[i]);
8061 /* Scale to 1-bit */
8062 LBR01PacketRGBO(image->background_color);
8064 for (y=0; y < (ssize_t) image->rows; y++)
8066 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8068 if (r == (Quantum *) NULL)
8071 for (x=0; x < (ssize_t) image->columns; x++)
8074 r+=GetPixelChannels(image);
8077 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8081 if (image->storage_class == PseudoClass && image->colormap != NULL)
8083 for (i=0; i < (ssize_t) image->colors; i++)
8085 LBR01PacketRGBO(image->colormap[i]);
8091 /* To do: set to next higher multiple of 8 */
8092 if (image->depth < 8)
8095 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8096 /* PNG does not handle depths greater than 16 so reduce it even
8099 if (image->depth > 8)
8103 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8104 if (image->depth > 8)
8106 /* To do: fill low byte properly */
8110 if (image->depth == 16 && mng_info->write_png_depth != 16)
8111 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8115 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8116 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8117 mng_info->write_png_colortype < 4 &&
8118 image->alpha_trait != BlendPixelTrait)))
8120 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8121 * are not going to need the result.
8123 image_colors = (int) image->colors;
8124 number_opaque = (int) image->colors;
8125 if (mng_info->write_png_colortype == 1 ||
8126 mng_info->write_png_colortype == 5)
8127 ping_have_color=MagickFalse;
8129 ping_have_color=MagickTrue;
8130 ping_have_non_bw=MagickFalse;
8132 if (image->alpha_trait == BlendPixelTrait)
8134 number_transparent = 2;
8135 number_semitransparent = 1;
8140 number_transparent = 0;
8141 number_semitransparent = 0;
8149 * Normally we run this just once, but in the case of writing PNG8
8150 * we reduce the transparency to binary and run again, then if there
8151 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8152 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8153 * palette. Then (To do) we take care of a final reduction that is only
8154 * needed if there are still 256 colors present and one of them has both
8155 * transparent and opaque instances.
8158 tried_332 = MagickFalse;
8159 tried_333 = MagickFalse;
8160 tried_444 = MagickFalse;
8165 * Sometimes we get DirectClass images that have 256 colors or fewer.
8166 * This code will build a colormap.
8168 * Also, sometimes we get PseudoClass images with an out-of-date
8169 * colormap. This code will replace the colormap with a new one.
8170 * Sometimes we get PseudoClass images that have more than 256 colors.
8171 * This code will delete the colormap and change the image to
8174 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8175 * even though it sometimes contains left-over non-opaque values.
8177 * Also we gather some information (number of opaque, transparent,
8178 * and semitransparent pixels, and whether the image has any non-gray
8179 * pixels or only black-and-white pixels) that we might need later.
8181 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8182 * we need to check for bogus non-opaque values, at least.
8190 semitransparent[260],
8193 register const Quantum
8200 if (logging != MagickFalse)
8201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202 " Enter BUILD_PALETTE:");
8204 if (logging != MagickFalse)
8206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 " image->columns=%.20g",(double) image->columns);
8208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8209 " image->rows=%.20g",(double) image->rows);
8210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8211 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " image->depth=%.20g",(double) image->depth);
8215 if (image->storage_class == PseudoClass && image->colormap != NULL)
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " Original colormap:");
8219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8220 " i (red,green,blue,alpha)");
8222 for (i=0; i < 256; i++)
8224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8225 " %d (%d,%d,%d,%d)",
8227 (int) image->colormap[i].red,
8228 (int) image->colormap[i].green,
8229 (int) image->colormap[i].blue,
8230 (int) image->colormap[i].alpha);
8233 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8238 " %d (%d,%d,%d,%d)",
8240 (int) image->colormap[i].red,
8241 (int) image->colormap[i].green,
8242 (int) image->colormap[i].blue,
8243 (int) image->colormap[i].alpha);
8248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8249 " image->colors=%d",(int) image->colors);
8251 if (image->colors == 0)
8252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8253 " (zero means unknown)");
8255 if (ping_preserve_colormap == MagickFalse)
8256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8257 " Regenerate the colormap");
8262 number_semitransparent = 0;
8263 number_transparent = 0;
8265 for (y=0; y < (ssize_t) image->rows; y++)
8267 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8269 if (q == (Quantum *) NULL)
8272 for (x=0; x < (ssize_t) image->columns; x++)
8274 if (image->alpha_trait != BlendPixelTrait ||
8275 GetPixelAlpha(image,q) == OpaqueAlpha)
8277 if (number_opaque < 259)
8279 if (number_opaque == 0)
8281 GetPixelInfoPixel(image, q, opaque);
8282 opaque[0].alpha=OpaqueAlpha;
8286 for (i=0; i< (ssize_t) number_opaque; i++)
8288 if (IsPixelEquivalent(image,q, opaque+i))
8292 if (i == (ssize_t) number_opaque && number_opaque < 259)
8295 GetPixelInfoPixel(image, q, opaque+i);
8296 opaque[i].alpha=OpaqueAlpha;
8300 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8302 if (number_transparent < 259)
8304 if (number_transparent == 0)
8306 GetPixelInfoPixel(image, q, transparent);
8307 ping_trans_color.red=(unsigned short)
8308 GetPixelRed(image,q);
8309 ping_trans_color.green=(unsigned short)
8310 GetPixelGreen(image,q);
8311 ping_trans_color.blue=(unsigned short)
8312 GetPixelBlue(image,q);
8313 ping_trans_color.gray=(unsigned short)
8314 GetPixelGray(image,q);
8315 number_transparent = 1;
8318 for (i=0; i< (ssize_t) number_transparent; i++)
8320 if (IsPixelEquivalent(image,q, transparent+i))
8324 if (i == (ssize_t) number_transparent &&
8325 number_transparent < 259)
8327 number_transparent++;
8328 GetPixelInfoPixel(image,q,transparent+i);
8334 if (number_semitransparent < 259)
8336 if (number_semitransparent == 0)
8338 GetPixelInfoPixel(image,q,semitransparent);
8339 number_semitransparent = 1;
8342 for (i=0; i< (ssize_t) number_semitransparent; i++)
8344 if (IsPixelEquivalent(image,q, semitransparent+i)
8345 && GetPixelAlpha(image,q) ==
8346 semitransparent[i].alpha)
8350 if (i == (ssize_t) number_semitransparent &&
8351 number_semitransparent < 259)
8353 number_semitransparent++;
8354 GetPixelInfoPixel(image, q, semitransparent+i);
8358 q+=GetPixelChannels(image);
8362 if (mng_info->write_png8 == MagickFalse &&
8363 ping_exclude_bKGD == MagickFalse)
8365 /* Add the background color to the palette, if it
8366 * isn't already there.
8368 if (logging != MagickFalse)
8370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8371 " Check colormap for background (%d,%d,%d)",
8372 (int) image->background_color.red,
8373 (int) image->background_color.green,
8374 (int) image->background_color.blue);
8376 for (i=0; i<number_opaque; i++)
8378 if (opaque[i].red == image->background_color.red &&
8379 opaque[i].green == image->background_color.green &&
8380 opaque[i].blue == image->background_color.blue)
8383 if (number_opaque < 259 && i == number_opaque)
8385 opaque[i] = image->background_color;
8386 ping_background.index = i;
8388 if (logging != MagickFalse)
8390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8391 " background_color index is %d",(int) i);
8395 else if (logging != MagickFalse)
8396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " No room in the colormap to add background color");
8400 image_colors=number_opaque+number_transparent+number_semitransparent;
8402 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8404 /* No room for the background color; remove it. */
8409 if (logging != MagickFalse)
8411 if (image_colors > 256)
8412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8413 " image has more than 256 colors");
8416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8417 " image has %d colors",image_colors);
8420 if (ping_preserve_colormap != MagickFalse)
8423 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8425 ping_have_color=MagickFalse;
8426 ping_have_non_bw=MagickFalse;
8428 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8429 (IssRGBColorspace(image->colorspace) != MagickFalse))
8431 ping_have_color=MagickTrue;
8432 ping_have_non_bw=MagickTrue;
8435 if(image_colors > 256)
8437 for (y=0; y < (ssize_t) image->rows; y++)
8439 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8441 if (q == (Quantum *) NULL)
8445 for (x=0; x < (ssize_t) image->columns; x++)
8447 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8448 GetPixelRed(image,s) != GetPixelBlue(image,s))
8450 ping_have_color=MagickTrue;
8451 ping_have_non_bw=MagickTrue;
8454 s+=GetPixelChannels(image);
8457 if (ping_have_color != MagickFalse)
8460 /* Worst case is black-and-white; we are looking at every
8464 if (ping_have_non_bw == MagickFalse)
8467 for (x=0; x < (ssize_t) image->columns; x++)
8469 if (GetPixelRed(image,s) != 0 &&
8470 GetPixelRed(image,s) != QuantumRange)
8472 ping_have_non_bw=MagickTrue;
8475 s+=GetPixelChannels(image);
8482 if (image_colors < 257)
8488 * Initialize image colormap.
8491 if (logging != MagickFalse)
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493 " Sort the new colormap");
8495 /* Sort palette, transparent first */;
8499 for (i=0; i<number_transparent; i++)
8500 colormap[n++] = transparent[i];
8502 for (i=0; i<number_semitransparent; i++)
8503 colormap[n++] = semitransparent[i];
8505 for (i=0; i<number_opaque; i++)
8506 colormap[n++] = opaque[i];
8508 ping_background.index +=
8509 (number_transparent + number_semitransparent);
8511 /* image_colors < 257; search the colormap instead of the pixels
8512 * to get ping_have_color and ping_have_non_bw
8516 if (ping_have_color == MagickFalse)
8518 if (colormap[i].red != colormap[i].green ||
8519 colormap[i].red != colormap[i].blue)
8521 ping_have_color=MagickTrue;
8522 ping_have_non_bw=MagickTrue;
8527 if (ping_have_non_bw == MagickFalse)
8529 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8530 ping_have_non_bw=MagickTrue;
8534 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8535 (number_transparent == 0 && number_semitransparent == 0)) &&
8536 (((mng_info->write_png_colortype-1) ==
8537 PNG_COLOR_TYPE_PALETTE) ||
8538 (mng_info->write_png_colortype == 0)))
8540 if (logging != MagickFalse)
8542 if (n != (ssize_t) image_colors)
8543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8544 " image_colors (%d) and n (%d) don't match",
8547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8548 " AcquireImageColormap");
8551 image->colors = image_colors;
8553 if (AcquireImageColormap(image,image_colors,exception) ==
8555 ThrowWriterException(ResourceLimitError,
8556 "MemoryAllocationFailed");
8558 for (i=0; i< (ssize_t) image_colors; i++)
8559 image->colormap[i] = colormap[i];
8561 if (logging != MagickFalse)
8563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8564 " image->colors=%d (%d)",
8565 (int) image->colors, image_colors);
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Update the pixel indexes");
8571 /* Sync the pixel indices with the new colormap */
8573 for (y=0; y < (ssize_t) image->rows; y++)
8575 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8577 if (q == (Quantum *) NULL)
8580 for (x=0; x < (ssize_t) image->columns; x++)
8582 for (i=0; i< (ssize_t) image_colors; i++)
8584 if ((image->alpha_trait != BlendPixelTrait ||
8585 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8586 image->colormap[i].red == GetPixelRed(image,q) &&
8587 image->colormap[i].green == GetPixelGreen(image,q) &&
8588 image->colormap[i].blue == GetPixelBlue(image,q))
8590 SetPixelIndex(image,i,q);
8594 q+=GetPixelChannels(image);
8597 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8603 if (logging != MagickFalse)
8605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8606 " image->colors=%d", (int) image->colors);
8608 if (image->colormap != NULL)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " i (red,green,blue,alpha)");
8613 for (i=0; i < (ssize_t) image->colors; i++)
8615 if (i < 300 || i >= (ssize_t) image->colors - 10)
8617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8618 " %d (%d,%d,%d,%d)",
8620 (int) image->colormap[i].red,
8621 (int) image->colormap[i].green,
8622 (int) image->colormap[i].blue,
8623 (int) image->colormap[i].alpha);
8628 if (number_transparent < 257)
8629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8630 " number_transparent = %d",
8631 number_transparent);
8634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8635 " number_transparent > 256");
8637 if (number_opaque < 257)
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " number_opaque = %d",
8643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8644 " number_opaque > 256");
8646 if (number_semitransparent < 257)
8647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8648 " number_semitransparent = %d",
8649 number_semitransparent);
8652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8653 " number_semitransparent > 256");
8655 if (ping_have_non_bw == MagickFalse)
8656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8657 " All pixels and the background are black or white");
8659 else if (ping_have_color == MagickFalse)
8660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8661 " All pixels and the background are gray");
8664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8665 " At least one pixel or the background is non-gray");
8667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8668 " Exit BUILD_PALETTE:");
8671 if (mng_info->write_png8 == MagickFalse)
8674 /* Make any reductions necessary for the PNG8 format */
8675 if (image_colors <= 256 &&
8676 image_colors != 0 && image->colormap != NULL &&
8677 number_semitransparent == 0 &&
8678 number_transparent <= 1)
8681 /* PNG8 can't have semitransparent colors so we threshold the
8682 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8683 * transparent color so if more than one is transparent we merge
8684 * them into image->background_color.
8686 if (number_semitransparent != 0 || number_transparent > 1)
8688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8689 " Thresholding the alpha channel to binary");
8691 for (y=0; y < (ssize_t) image->rows; y++)
8693 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8695 if (r == (Quantum *) NULL)
8698 for (x=0; x < (ssize_t) image->columns; x++)
8700 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8702 SetPixelInfoPixel(image,&image->background_color,r);
8703 SetPixelAlpha(image,TransparentAlpha,r);
8706 SetPixelAlpha(image,OpaqueAlpha,r);
8707 r+=GetPixelChannels(image);
8710 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8713 if (image_colors != 0 && image_colors <= 256 &&
8714 image->colormap != NULL)
8715 for (i=0; i<image_colors; i++)
8716 image->colormap[i].alpha =
8717 (image->colormap[i].alpha > TransparentAlpha/2 ?
8718 TransparentAlpha : OpaqueAlpha);
8723 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8724 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8725 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8728 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8730 if (logging != MagickFalse)
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " Quantizing the background color to 4-4-4");
8734 tried_444 = MagickTrue;
8736 LBR04PacketRGB(image->background_color);
8738 if (logging != MagickFalse)
8739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740 " Quantizing the pixel colors to 4-4-4");
8742 if (image->colormap == NULL)
8744 for (y=0; y < (ssize_t) image->rows; y++)
8746 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8748 if (r == (Quantum *) NULL)
8751 for (x=0; x < (ssize_t) image->columns; x++)
8753 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8755 r+=GetPixelChannels(image);
8758 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8763 else /* Should not reach this; colormap already exists and
8766 if (logging != MagickFalse)
8767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8768 " Quantizing the colormap to 4-4-4");
8770 for (i=0; i<image_colors; i++)
8772 LBR04PacketRGB(image->colormap[i]);
8778 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8780 if (logging != MagickFalse)
8781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8782 " Quantizing the background color to 3-3-3");
8784 tried_333 = MagickTrue;
8786 LBR03PacketRGB(image->background_color);
8788 if (logging != MagickFalse)
8789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8790 " Quantizing the pixel colors to 3-3-3-1");
8792 if (image->colormap == NULL)
8794 for (y=0; y < (ssize_t) image->rows; y++)
8796 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8798 if (r == (Quantum *) NULL)
8801 for (x=0; x < (ssize_t) image->columns; x++)
8803 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8805 r+=GetPixelChannels(image);
8808 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8813 else /* Should not reach this; colormap already exists and
8816 if (logging != MagickFalse)
8817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8818 " Quantizing the colormap to 3-3-3-1");
8819 for (i=0; i<image_colors; i++)
8821 LBR03PacketRGB(image->colormap[i]);
8827 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8829 if (logging != MagickFalse)
8830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8831 " Quantizing the background color to 3-3-2");
8833 tried_332 = MagickTrue;
8835 /* Red and green were already done so we only quantize the blue
8839 LBR02PacketBlue(image->background_color);
8841 if (logging != MagickFalse)
8842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8843 " Quantizing the pixel colors to 3-3-2-1");
8845 if (image->colormap == NULL)
8847 for (y=0; y < (ssize_t) image->rows; y++)
8849 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8851 if (r == (Quantum *) NULL)
8854 for (x=0; x < (ssize_t) image->columns; x++)
8856 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8858 r+=GetPixelChannels(image);
8861 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8866 else /* Should not reach this; colormap already exists and
8869 if (logging != MagickFalse)
8870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8871 " Quantizing the colormap to 3-3-2-1");
8872 for (i=0; i<image_colors; i++)
8874 LBR02PacketBlue(image->colormap[i]);
8881 if (image_colors == 0 || image_colors > 256)
8883 /* Take care of special case with 256 colors + 1 transparent
8884 * color. We don't need to quantize to 2-3-2-1; we only need to
8885 * eliminate one color, so we'll merge the two darkest red
8886 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8888 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8889 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8890 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8892 image->background_color.red=ScaleCharToQuantum(0x24);
8895 if (image->colormap == NULL)
8897 for (y=0; y < (ssize_t) image->rows; y++)
8899 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8901 if (r == (Quantum *) NULL)
8904 for (x=0; x < (ssize_t) image->columns; x++)
8906 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8907 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8908 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8909 GetPixelAlpha(image,r) == OpaqueAlpha)
8911 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8913 r+=GetPixelChannels(image);
8916 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8924 for (i=0; i<image_colors; i++)
8926 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8927 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8928 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8930 image->colormap[i].red=ScaleCharToQuantum(0x24);
8937 /* END OF BUILD_PALETTE */
8939 /* If we are excluding the tRNS chunk and there is transparency,
8940 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8943 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8944 (number_transparent != 0 || number_semitransparent != 0))
8946 unsigned int colortype=mng_info->write_png_colortype;
8948 if (ping_have_color == MagickFalse)
8949 mng_info->write_png_colortype = 5;
8952 mng_info->write_png_colortype = 7;
8954 if (colortype != 0 &&
8955 mng_info->write_png_colortype != colortype)
8956 ping_need_colortype_warning=MagickTrue;
8960 /* See if cheap transparency is possible. It is only possible
8961 * when there is a single transparent color, no semitransparent
8962 * color, and no opaque color that has the same RGB components
8963 * as the transparent color. We only need this information if
8964 * we are writing a PNG with colortype 0 or 2, and we have not
8965 * excluded the tRNS chunk.
8967 if (number_transparent == 1 &&
8968 mng_info->write_png_colortype < 4)
8970 ping_have_cheap_transparency = MagickTrue;
8972 if (number_semitransparent != 0)
8973 ping_have_cheap_transparency = MagickFalse;
8975 else if (image_colors == 0 || image_colors > 256 ||
8976 image->colormap == NULL)
8978 register const Quantum
8981 for (y=0; y < (ssize_t) image->rows; y++)
8983 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8985 if (q == (Quantum *) NULL)
8988 for (x=0; x < (ssize_t) image->columns; x++)
8990 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8991 (unsigned short) GetPixelRed(image,q) ==
8992 ping_trans_color.red &&
8993 (unsigned short) GetPixelGreen(image,q) ==
8994 ping_trans_color.green &&
8995 (unsigned short) GetPixelBlue(image,q) ==
8996 ping_trans_color.blue)
8998 ping_have_cheap_transparency = MagickFalse;
9002 q+=GetPixelChannels(image);
9005 if (ping_have_cheap_transparency == MagickFalse)
9011 /* Assuming that image->colormap[0] is the one transparent color
9012 * and that all others are opaque.
9014 if (image_colors > 1)
9015 for (i=1; i<image_colors; i++)
9016 if (image->colormap[i].red == image->colormap[0].red &&
9017 image->colormap[i].green == image->colormap[0].green &&
9018 image->colormap[i].blue == image->colormap[0].blue)
9020 ping_have_cheap_transparency = MagickFalse;
9025 if (logging != MagickFalse)
9027 if (ping_have_cheap_transparency == MagickFalse)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " Cheap transparency is not possible.");
9032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9033 " Cheap transparency is possible.");
9037 ping_have_cheap_transparency = MagickFalse;
9039 image_depth=image->depth;
9041 quantum_info = (QuantumInfo *) NULL;
9043 image_colors=(int) image->colors;
9044 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9046 mng_info->IsPalette=image->storage_class == PseudoClass &&
9047 image_colors <= 256 && image->colormap != NULL;
9049 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9050 (image->colors == 0 || image->colormap == NULL))
9052 image_info=DestroyImageInfo(image_info);
9053 image=DestroyImage(image);
9054 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9055 "Cannot write PNG8 or color-type 3; colormap is NULL",
9056 "`%s'",IMimage->filename);
9057 return(MagickFalse);
9061 Allocate the PNG structures
9063 #ifdef PNG_USER_MEM_SUPPORTED
9064 error_info.image=image;
9065 error_info.exception=exception;
9066 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9067 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9068 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9071 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9072 MagickPNGErrorHandler,MagickPNGWarningHandler);
9075 if (ping == (png_struct *) NULL)
9076 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9078 ping_info=png_create_info_struct(ping);
9080 if (ping_info == (png_info *) NULL)
9082 png_destroy_write_struct(&ping,(png_info **) NULL);
9083 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9086 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9087 ping_pixels=(unsigned char *) NULL;
9089 if (setjmp(png_jmpbuf(ping)))
9095 if (image_info->verbose)
9096 (void) printf("PNG write has failed.\n");
9098 png_destroy_write_struct(&ping,&ping_info);
9099 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9100 UnlockSemaphoreInfo(ping_semaphore);
9103 if (ping_pixels != (unsigned char *) NULL)
9104 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9106 if (quantum_info != (QuantumInfo *) NULL)
9107 quantum_info=DestroyQuantumInfo(quantum_info);
9109 if (ping_have_blob != MagickFalse)
9110 (void) CloseBlob(image);
9111 image_info=DestroyImageInfo(image_info);
9112 image=DestroyImage(image);
9113 return(MagickFalse);
9116 /* { For navigation to end of SETJMP-protected block. Within this
9117 * block, use png_error() instead of Throwing an Exception, to ensure
9118 * that libpng is able to clean up, and that the semaphore is unlocked.
9121 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9122 LockSemaphoreInfo(ping_semaphore);
9126 Prepare PNG for writing.
9129 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9130 if (mng_info->write_mng)
9132 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9133 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9134 /* Disable new libpng-1.5.10 feature when writing a MNG because
9135 * zero-length PLTE is OK
9137 png_set_check_for_invalid_index (ping, 0);
9142 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9143 if (mng_info->write_mng)
9144 png_permit_empty_plte(ping,MagickTrue);
9151 ping_width=(png_uint_32) image->columns;
9152 ping_height=(png_uint_32) image->rows;
9154 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9157 if (mng_info->write_png48 || mng_info->write_png64)
9160 if (mng_info->write_png_depth != 0)
9161 image_depth=mng_info->write_png_depth;
9163 /* Adjust requested depth to next higher valid depth if necessary */
9164 if (image_depth > 8)
9167 if ((image_depth > 4) && (image_depth < 8))
9170 if (image_depth == 3)
9173 if (logging != MagickFalse)
9175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9176 " width=%.20g",(double) ping_width);
9177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9178 " height=%.20g",(double) ping_height);
9179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9180 " image_matte=%.20g",(double) image->alpha_trait);
9181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9182 " image->depth=%.20g",(double) image->depth);
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9187 save_image_depth=image_depth;
9188 ping_bit_depth=(png_byte) save_image_depth;
9191 #if defined(PNG_pHYs_SUPPORTED)
9192 if (ping_exclude_pHYs == MagickFalse)
9194 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9195 (!mng_info->write_mng || !mng_info->equal_physs))
9197 if (logging != MagickFalse)
9198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9199 " Setting up pHYs chunk");
9201 if (image->units == PixelsPerInchResolution)
9203 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9204 ping_pHYs_x_resolution=
9205 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9206 ping_pHYs_y_resolution=
9207 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9210 else if (image->units == PixelsPerCentimeterResolution)
9212 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9213 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9214 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9219 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9220 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9221 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9224 if (logging != MagickFalse)
9225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9226 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9227 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9228 (int) ping_pHYs_unit_type);
9229 ping_have_pHYs = MagickTrue;
9234 if (ping_exclude_bKGD == MagickFalse)
9236 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9242 if (ping_bit_depth == 8)
9245 if (ping_bit_depth == 4)
9248 if (ping_bit_depth == 2)
9251 if (ping_bit_depth == 1)
9254 ping_background.red=(png_uint_16)
9255 (ScaleQuantumToShort(image->background_color.red) & mask);
9257 ping_background.green=(png_uint_16)
9258 (ScaleQuantumToShort(image->background_color.green) & mask);
9260 ping_background.blue=(png_uint_16)
9261 (ScaleQuantumToShort(image->background_color.blue) & mask);
9263 ping_background.gray=(png_uint_16) ping_background.green;
9266 if (logging != MagickFalse)
9268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9269 " Setting up bKGD chunk (1)");
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271 " background_color index is %d",
9272 (int) ping_background.index);
9274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9275 " ping_bit_depth=%d",ping_bit_depth);
9278 ping_have_bKGD = MagickTrue;
9282 Select the color type.
9287 if (mng_info->IsPalette && mng_info->write_png8)
9290 /* To do: make this a function cause it's used twice, except
9291 for reducing the sample depth from 8. */
9293 number_colors=image_colors;
9295 ping_have_tRNS=MagickFalse;
9300 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9302 if (logging != MagickFalse)
9303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9304 " Setting up PLTE chunk with %d colors (%d)",
9305 number_colors, image_colors);
9307 for (i=0; i < (ssize_t) number_colors; i++)
9309 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9310 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9311 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9312 if (logging != MagickFalse)
9313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9314 #if MAGICKCORE_QUANTUM_DEPTH == 8
9315 " %3ld (%3d,%3d,%3d)",
9317 " %5ld (%5d,%5d,%5d)",
9319 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9323 ping_have_PLTE=MagickTrue;
9324 image_depth=ping_bit_depth;
9327 if (matte != MagickFalse)
9330 Identify which colormap entry is transparent.
9332 assert(number_colors <= 256);
9333 assert(image->colormap != NULL);
9335 for (i=0; i < (ssize_t) number_transparent; i++)
9336 ping_trans_alpha[i]=0;
9339 ping_num_trans=(unsigned short) (number_transparent +
9340 number_semitransparent);
9342 if (ping_num_trans == 0)
9343 ping_have_tRNS=MagickFalse;
9346 ping_have_tRNS=MagickTrue;
9349 if (ping_exclude_bKGD == MagickFalse)
9352 * Identify which colormap entry is the background color.
9355 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9356 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9359 ping_background.index=(png_byte) i;
9361 if (logging != MagickFalse)
9363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9364 " background_color index is %d",
9365 (int) ping_background.index);
9368 } /* end of write_png8 */
9370 else if (mng_info->write_png_colortype == 1)
9372 image_matte=MagickFalse;
9373 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9376 else if (mng_info->write_png24 || mng_info->write_png48 ||
9377 mng_info->write_png_colortype == 3)
9379 image_matte=MagickFalse;
9380 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9383 else if (mng_info->write_png32 || mng_info->write_png64 ||
9384 mng_info->write_png_colortype == 7)
9386 image_matte=MagickTrue;
9387 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9390 else /* mng_info->write_pngNN not specified */
9392 image_depth=ping_bit_depth;
9394 if (mng_info->write_png_colortype != 0)
9396 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9398 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9399 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9400 image_matte=MagickTrue;
9403 image_matte=MagickFalse;
9405 if (logging != MagickFalse)
9406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9407 " PNG colortype %d was specified:",(int) ping_color_type);
9410 else /* write_png_colortype not specified */
9412 if (logging != MagickFalse)
9413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9414 " Selecting PNG colortype:");
9416 ping_color_type=(png_byte) ((matte != MagickFalse)?
9417 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9419 if (image_info->type == TrueColorType)
9421 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9422 image_matte=MagickFalse;
9425 if (image_info->type == TrueColorMatteType)
9427 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9428 image_matte=MagickTrue;
9431 if (image_info->type == PaletteType ||
9432 image_info->type == PaletteMatteType)
9433 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9435 if (mng_info->write_png_colortype == 0 &&
9436 (image_info->type == UndefinedType ||
9437 image_info->type == OptimizeType))
9439 if (ping_have_color == MagickFalse)
9441 if (image_matte == MagickFalse)
9443 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9444 image_matte=MagickFalse;
9449 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9450 image_matte=MagickTrue;
9455 if (image_matte == MagickFalse)
9457 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9458 image_matte=MagickFalse;
9463 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9464 image_matte=MagickTrue;
9471 if (logging != MagickFalse)
9472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9473 " Selected PNG colortype=%d",ping_color_type);
9475 if (ping_bit_depth < 8)
9477 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9478 ping_color_type == PNG_COLOR_TYPE_RGB ||
9479 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9483 old_bit_depth=ping_bit_depth;
9485 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9487 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9491 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9496 if (image->colors == 0)
9499 png_error(ping,"image has 0 colors");
9502 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9503 ping_bit_depth <<= 1;
9506 if (logging != MagickFalse)
9508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9509 " Number of colors: %.20g",(double) image_colors);
9511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9512 " Tentative PNG bit depth: %d",ping_bit_depth);
9515 if (ping_bit_depth < (int) mng_info->write_png_depth)
9516 ping_bit_depth = mng_info->write_png_depth;
9519 image_depth=ping_bit_depth;
9521 if (logging != MagickFalse)
9523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9524 " Tentative PNG color type: %s (%.20g)",
9525 PngColorTypeToString(ping_color_type),
9526 (double) ping_color_type);
9528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9529 " image_info->type: %.20g",(double) image_info->type);
9531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9532 " image_depth: %.20g",(double) image_depth);
9534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9536 " image->depth: %.20g",(double) image->depth);
9538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9539 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9542 if (matte != MagickFalse)
9544 if (mng_info->IsPalette)
9546 if (mng_info->write_png_colortype == 0)
9548 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9550 if (ping_have_color != MagickFalse)
9551 ping_color_type=PNG_COLOR_TYPE_RGBA;
9555 * Determine if there is any transparent color.
9557 if (number_transparent + number_semitransparent == 0)
9560 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9563 image_matte=MagickFalse;
9565 if (mng_info->write_png_colortype == 0)
9566 ping_color_type&=0x03;
9576 if (ping_bit_depth == 8)
9579 if (ping_bit_depth == 4)
9582 if (ping_bit_depth == 2)
9585 if (ping_bit_depth == 1)
9588 ping_trans_color.red=(png_uint_16)
9589 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9591 ping_trans_color.green=(png_uint_16)
9592 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9594 ping_trans_color.blue=(png_uint_16)
9595 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9597 ping_trans_color.gray=(png_uint_16)
9598 (ScaleQuantumToShort(GetPixelInfoIntensity(
9599 image->colormap)) & mask);
9601 ping_trans_color.index=(png_byte) 0;
9603 ping_have_tRNS=MagickTrue;
9606 if (ping_have_tRNS != MagickFalse)
9609 * Determine if there is one and only one transparent color
9610 * and if so if it is fully transparent.
9612 if (ping_have_cheap_transparency == MagickFalse)
9613 ping_have_tRNS=MagickFalse;
9616 if (ping_have_tRNS != MagickFalse)
9618 if (mng_info->write_png_colortype == 0)
9619 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9621 if (image_depth == 8)
9623 ping_trans_color.red&=0xff;
9624 ping_trans_color.green&=0xff;
9625 ping_trans_color.blue&=0xff;
9626 ping_trans_color.gray&=0xff;
9632 if (image_depth == 8)
9634 ping_trans_color.red&=0xff;
9635 ping_trans_color.green&=0xff;
9636 ping_trans_color.blue&=0xff;
9637 ping_trans_color.gray&=0xff;
9644 if (ping_have_tRNS != MagickFalse)
9645 image_matte=MagickFalse;
9647 if ((mng_info->IsPalette) &&
9648 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9649 ping_have_color == MagickFalse &&
9650 (image_matte == MagickFalse || image_depth >= 8))
9654 if (image_matte != MagickFalse)
9655 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9657 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9659 ping_color_type=PNG_COLOR_TYPE_GRAY;
9661 if (save_image_depth == 16 && image_depth == 8)
9663 if (logging != MagickFalse)
9665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9666 " Scaling ping_trans_color (0)");
9668 ping_trans_color.gray*=0x0101;
9672 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9673 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9675 if ((image_colors == 0) ||
9676 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9677 image_colors=(int) (one << image_depth);
9679 if (image_depth > 8)
9685 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9687 if(!mng_info->write_png_depth)
9691 while ((int) (one << ping_bit_depth)
9692 < (ssize_t) image_colors)
9693 ping_bit_depth <<= 1;
9697 else if (ping_color_type ==
9698 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9699 mng_info->IsPalette)
9701 /* Check if grayscale is reducible */
9704 depth_4_ok=MagickTrue,
9705 depth_2_ok=MagickTrue,
9706 depth_1_ok=MagickTrue;
9708 for (i=0; i < (ssize_t) image_colors; i++)
9713 intensity=ScaleQuantumToChar(image->colormap[i].red);
9715 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9716 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9717 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9718 depth_2_ok=depth_1_ok=MagickFalse;
9719 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9720 depth_1_ok=MagickFalse;
9723 if (depth_1_ok && mng_info->write_png_depth <= 1)
9726 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9729 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9734 image_depth=ping_bit_depth;
9739 if (mng_info->IsPalette)
9741 number_colors=image_colors;
9743 if (image_depth <= 8)
9748 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9750 if (mng_info->have_write_global_plte && matte == MagickFalse)
9752 png_set_PLTE(ping,ping_info,NULL,0);
9754 if (logging != MagickFalse)
9755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9756 " Setting up empty PLTE chunk");
9761 for (i=0; i < (ssize_t) number_colors; i++)
9763 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9764 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9765 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9768 if (logging != MagickFalse)
9769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9770 " Setting up PLTE chunk with %d colors",
9773 ping_have_PLTE=MagickTrue;
9776 /* color_type is PNG_COLOR_TYPE_PALETTE */
9777 if (mng_info->write_png_depth == 0)
9785 while ((one << ping_bit_depth) < (size_t) number_colors)
9786 ping_bit_depth <<= 1;
9791 if (matte != MagickFalse)
9794 * Set up trans_colors array.
9796 assert(number_colors <= 256);
9798 ping_num_trans=(unsigned short) (number_transparent +
9799 number_semitransparent);
9801 if (ping_num_trans == 0)
9802 ping_have_tRNS=MagickFalse;
9806 if (logging != MagickFalse)
9808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9809 " Scaling ping_trans_color (1)");
9811 ping_have_tRNS=MagickTrue;
9813 for (i=0; i < ping_num_trans; i++)
9815 ping_trans_alpha[i]= (png_byte)
9816 ScaleQuantumToChar(image->colormap[i].alpha);
9826 if (image_depth < 8)
9829 if ((save_image_depth == 16) && (image_depth == 8))
9831 if (logging != MagickFalse)
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Scaling ping_trans_color from (%d,%d,%d)",
9835 (int) ping_trans_color.red,
9836 (int) ping_trans_color.green,
9837 (int) ping_trans_color.blue);
9840 ping_trans_color.red*=0x0101;
9841 ping_trans_color.green*=0x0101;
9842 ping_trans_color.blue*=0x0101;
9843 ping_trans_color.gray*=0x0101;
9845 if (logging != MagickFalse)
9847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9849 (int) ping_trans_color.red,
9850 (int) ping_trans_color.green,
9851 (int) ping_trans_color.blue);
9856 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9857 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9860 Adjust background and transparency samples in sub-8-bit grayscale files.
9862 if (ping_bit_depth < 8 && ping_color_type ==
9863 PNG_COLOR_TYPE_GRAY)
9871 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9873 if (ping_exclude_bKGD == MagickFalse)
9876 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9877 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9878 &image->background_color))) +.5)));
9880 if (logging != MagickFalse)
9881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9882 " Setting up bKGD chunk (2)");
9883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9884 " background_color index is %d",
9885 (int) ping_background.index);
9887 ping_have_bKGD = MagickTrue;
9890 if (logging != MagickFalse)
9891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9892 " Scaling ping_trans_color.gray from %d",
9893 (int)ping_trans_color.gray);
9895 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9896 ping_trans_color.gray)+.5);
9898 if (logging != MagickFalse)
9899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9900 " to %d", (int)ping_trans_color.gray);
9903 if (ping_exclude_bKGD == MagickFalse)
9905 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9908 Identify which colormap entry is the background color.
9911 number_colors=image_colors;
9913 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9914 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9917 ping_background.index=(png_byte) i;
9919 if (logging != MagickFalse)
9921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9922 " Setting up bKGD chunk with index=%d",(int) i);
9925 if (i < (ssize_t) number_colors)
9927 ping_have_bKGD = MagickTrue;
9929 if (logging != MagickFalse)
9931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9932 " background =(%d,%d,%d)",
9933 (int) ping_background.red,
9934 (int) ping_background.green,
9935 (int) ping_background.blue);
9939 else /* Can't happen */
9941 if (logging != MagickFalse)
9942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9943 " No room in PLTE to add bKGD color");
9944 ping_have_bKGD = MagickFalse;
9949 if (logging != MagickFalse)
9950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9951 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9954 Initialize compression level and filtering.
9956 if (logging != MagickFalse)
9958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9959 " Setting up deflate compression");
9961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962 " Compression buffer size: 32768");
9965 png_set_compression_buffer_size(ping,32768L);
9967 if (logging != MagickFalse)
9968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9969 " Compression mem level: 9");
9971 png_set_compression_mem_level(ping, 9);
9973 /* Untangle the "-quality" setting:
9975 Undefined is 0; the default is used.
9980 0: Use Z_HUFFMAN_ONLY strategy with the
9981 zlib default compression level
9983 1-9: the zlib compression level
9987 0-4: the PNG filter method
9989 5: libpng adaptive filtering if compression level > 5
9990 libpng filter type "none" if compression level <= 5
9991 or if image is grayscale or palette
9993 6: libpng adaptive filtering
9995 7: "LOCO" filtering (intrapixel differing) if writing
9996 a MNG, othewise "none". Did not work in IM-6.7.0-9
9997 and earlier because of a missing "else".
9999 8: Z_RLE strategy, all filters
10000 Unused prior to IM-6.7.0-10, was same as 6
10002 9: Z_RLE strategy, no PNG filters
10003 Unused prior to IM-6.7.0-10, was same as 6
10005 Note that using the -quality option, not all combinations of
10006 PNG filter type, zlib compression level, and zlib compression
10007 strategy are possible. This will be addressed soon in a
10008 release that accomodates "-define png:compression-strategy", etc.
10012 quality=image->quality == UndefinedCompressionQuality ? 75UL :
10017 if (mng_info->write_png_compression_strategy == 0)
10018 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10021 else if (mng_info->write_png_compression_level == 0)
10026 level=(int) MagickMin((ssize_t) quality/10,9);
10028 mng_info->write_png_compression_level = level+1;
10031 if (mng_info->write_png_compression_strategy == 0)
10033 if ((quality %10) == 8 || (quality %10) == 9)
10034 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10035 mng_info->write_png_compression_strategy=Z_RLE+1;
10037 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10041 if (mng_info->write_png_compression_filter == 0)
10042 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10044 if (logging != MagickFalse)
10046 if (mng_info->write_png_compression_level)
10047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10048 " Compression level: %d",
10049 (int) mng_info->write_png_compression_level-1);
10051 if (mng_info->write_png_compression_strategy)
10052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10053 " Compression strategy: %d",
10054 (int) mng_info->write_png_compression_strategy-1);
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Setting up filtering");
10059 if (mng_info->write_png_compression_filter == 6)
10060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10061 " Base filter method: ADAPTIVE");
10062 else if (mng_info->write_png_compression_filter == 0 ||
10063 mng_info->write_png_compression_filter == 1)
10064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10065 " Base filter method: NONE");
10067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10068 " Base filter method: %d",
10069 (int) mng_info->write_png_compression_filter-1);
10072 if (mng_info->write_png_compression_level != 0)
10073 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10075 if (mng_info->write_png_compression_filter == 6)
10077 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10078 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10080 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10082 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10084 else if (mng_info->write_png_compression_filter == 7 ||
10085 mng_info->write_png_compression_filter == 10)
10086 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10088 else if (mng_info->write_png_compression_filter == 8)
10090 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10091 if (mng_info->write_mng)
10093 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10094 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10095 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10098 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10101 else if (mng_info->write_png_compression_filter == 9)
10102 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10104 else if (mng_info->write_png_compression_filter != 0)
10105 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10106 mng_info->write_png_compression_filter-1);
10108 if (mng_info->write_png_compression_strategy != 0)
10109 png_set_compression_strategy(ping,
10110 mng_info->write_png_compression_strategy-1);
10112 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10113 if (ping_exclude_sRGB != MagickFalse ||
10114 (image->rendering_intent == UndefinedIntent))
10116 if ((ping_exclude_tEXt == MagickFalse ||
10117 ping_exclude_zTXt == MagickFalse) &&
10118 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10120 ResetImageProfileIterator(image);
10121 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10123 profile=GetImageProfile(image,name);
10125 if (profile != (StringInfo *) NULL)
10127 #ifdef PNG_WRITE_iCCP_SUPPORTED
10128 if ((LocaleCompare(name,"ICC") == 0) ||
10129 (LocaleCompare(name,"ICM") == 0))
10132 if (ping_exclude_iCCP == MagickFalse)
10134 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10135 #if (PNG_LIBPNG_VER < 10500)
10136 (png_charp) GetStringInfoDatum(profile),
10138 (png_const_bytep) GetStringInfoDatum(profile),
10140 (png_uint_32) GetStringInfoLength(profile));
10146 if (ping_exclude_zCCP == MagickFalse)
10148 Magick_png_write_raw_profile(image_info,ping,ping_info,
10149 (unsigned char *) name,(unsigned char *) name,
10150 GetStringInfoDatum(profile),
10151 (png_uint_32) GetStringInfoLength(profile));
10155 if (logging != MagickFalse)
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " Setting up text chunk with %s profile",name);
10159 name=GetNextImageProfile(image);
10164 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10165 if ((mng_info->have_write_global_srgb == 0) &&
10166 (image->rendering_intent != UndefinedIntent))
10168 if (ping_exclude_sRGB == MagickFalse)
10171 Note image rendering intent.
10173 if (logging != MagickFalse)
10174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10175 " Setting up sRGB chunk");
10177 (void) png_set_sRGB(ping,ping_info,(
10178 Magick_RenderingIntent_to_PNG_RenderingIntent(
10179 image->rendering_intent)));
10183 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10186 if (ping_exclude_gAMA == MagickFalse &&
10187 (ping_exclude_sRGB == MagickFalse ||
10188 (image->gamma < .45 || image->gamma > .46)))
10190 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10194 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10196 if (logging != MagickFalse)
10197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " Setting up gAMA chunk");
10200 png_set_gAMA(ping,ping_info,image->gamma);
10204 if (ping_exclude_cHRM == MagickFalse)
10206 if ((mng_info->have_write_global_chrm == 0) &&
10207 (image->chromaticity.red_primary.x != 0.0))
10210 Note image chromaticity.
10211 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10219 wp=image->chromaticity.white_point;
10220 rp=image->chromaticity.red_primary;
10221 gp=image->chromaticity.green_primary;
10222 bp=image->chromaticity.blue_primary;
10224 if (logging != MagickFalse)
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " Setting up cHRM chunk");
10228 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10234 ping_interlace_method=image_info->interlace != NoInterlace;
10236 if (mng_info->write_mng)
10237 png_set_sig_bytes(ping,8);
10239 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10241 if (mng_info->write_png_colortype != 0)
10243 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10244 if (ping_have_color != MagickFalse)
10246 ping_color_type = PNG_COLOR_TYPE_RGB;
10248 if (ping_bit_depth < 8)
10252 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10253 if (ping_have_color != MagickFalse)
10254 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10257 if (ping_need_colortype_warning != MagickFalse ||
10258 ((mng_info->write_png_depth &&
10259 (int) mng_info->write_png_depth != ping_bit_depth) ||
10260 (mng_info->write_png_colortype &&
10261 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10262 mng_info->write_png_colortype != 7 &&
10263 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10265 if (logging != MagickFalse)
10267 if (ping_need_colortype_warning != MagickFalse)
10269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10270 " Image has transparency but tRNS chunk was excluded");
10273 if (mng_info->write_png_depth)
10275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10276 " Defined png:bit-depth=%u, Computed depth=%u",
10277 mng_info->write_png_depth,
10281 if (mng_info->write_png_colortype)
10283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10284 " Defined png:color-type=%u, Computed color type=%u",
10285 mng_info->write_png_colortype-1,
10291 "Cannot write image with defined png:bit-depth or png:color-type.");
10294 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10296 /* Add an opaque matte channel */
10297 image->alpha_trait = BlendPixelTrait;
10298 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10300 if (logging != MagickFalse)
10301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302 " Added an opaque matte channel");
10305 if (number_transparent != 0 || number_semitransparent != 0)
10307 if (ping_color_type < 4)
10309 ping_have_tRNS=MagickTrue;
10310 if (logging != MagickFalse)
10311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10312 " Setting ping_have_tRNS=MagickTrue.");
10316 if (logging != MagickFalse)
10317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10318 " Writing PNG header chunks");
10320 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10321 ping_bit_depth,ping_color_type,
10322 ping_interlace_method,ping_compression_method,
10323 ping_filter_method);
10325 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10327 png_set_PLTE(ping,ping_info,palette,number_colors);
10329 if (logging != MagickFalse)
10331 for (i=0; i< (ssize_t) number_colors; i++)
10333 if (i < ping_num_trans)
10334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10335 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10337 (int) palette[i].red,
10338 (int) palette[i].green,
10339 (int) palette[i].blue,
10341 (int) ping_trans_alpha[i]);
10343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10344 " PLTE[%d] = (%d,%d,%d)",
10346 (int) palette[i].red,
10347 (int) palette[i].green,
10348 (int) palette[i].blue);
10353 if (ping_exclude_bKGD == MagickFalse)
10355 if (ping_have_bKGD != MagickFalse)
10357 png_set_bKGD(ping,ping_info,&ping_background);
10360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10361 " Setting up bKGD chunk");
10362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363 " background color = (%d,%d,%d)",
10364 (int) ping_background.red,
10365 (int) ping_background.green,
10366 (int) ping_background.blue);
10367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10368 " index = %d, gray=%d",
10369 (int) ping_background.index,
10370 (int) ping_background.gray);
10375 if (ping_exclude_pHYs == MagickFalse)
10377 if (ping_have_pHYs != MagickFalse)
10379 png_set_pHYs(ping,ping_info,
10380 ping_pHYs_x_resolution,
10381 ping_pHYs_y_resolution,
10382 ping_pHYs_unit_type);
10386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387 " Setting up pHYs chunk");
10388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10389 " x_resolution=%lu",
10390 (unsigned long) ping_pHYs_x_resolution);
10391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392 " y_resolution=%lu",
10393 (unsigned long) ping_pHYs_y_resolution);
10394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396 (unsigned long) ping_pHYs_unit_type);
10401 #if defined(PNG_oFFs_SUPPORTED)
10402 if (ping_exclude_oFFs == MagickFalse)
10404 if (image->page.x || image->page.y)
10406 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10407 (png_int_32) image->page.y, 0);
10409 if (logging != MagickFalse)
10410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10411 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10412 (int) image->page.x, (int) image->page.y);
10417 if (mng_info->need_blob != MagickFalse)
10419 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10421 png_error(ping,"WriteBlob Failed");
10423 ping_have_blob=MagickTrue;
10426 png_write_info_before_PLTE(ping, ping_info);
10428 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10430 if (logging != MagickFalse)
10432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10433 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10436 if (ping_color_type == 3)
10437 (void) png_set_tRNS(ping, ping_info,
10444 (void) png_set_tRNS(ping, ping_info,
10447 &ping_trans_color);
10449 if (logging != MagickFalse)
10451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452 " tRNS color =(%d,%d,%d)",
10453 (int) ping_trans_color.red,
10454 (int) ping_trans_color.green,
10455 (int) ping_trans_color.blue);
10460 /* write any png-chunk-b profiles */
10461 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10463 png_write_info(ping,ping_info);
10465 /* write any PNG-chunk-m profiles */
10466 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10468 if (ping_exclude_vpAg == MagickFalse)
10470 if ((image->page.width != 0 && image->page.width != image->columns) ||
10471 (image->page.height != 0 && image->page.height != image->rows))
10476 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10477 PNGType(chunk,mng_vpAg);
10478 LogPNGChunk(logging,mng_vpAg,9L);
10479 PNGLong(chunk+4,(png_uint_32) image->page.width);
10480 PNGLong(chunk+8,(png_uint_32) image->page.height);
10481 chunk[12]=0; /* unit = pixels */
10482 (void) WriteBlob(image,13,chunk);
10483 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10487 #if (PNG_LIBPNG_VER == 10206)
10488 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10489 #define PNG_HAVE_IDAT 0x04
10490 ping->mode |= PNG_HAVE_IDAT;
10491 #undef PNG_HAVE_IDAT
10494 png_set_packing(ping);
10498 rowbytes=image->columns;
10499 if (image_depth > 8)
10501 switch (ping_color_type)
10503 case PNG_COLOR_TYPE_RGB:
10507 case PNG_COLOR_TYPE_GRAY_ALPHA:
10511 case PNG_COLOR_TYPE_RGBA:
10519 if (logging != MagickFalse)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Writing PNG image data");
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10527 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10528 sizeof(*ping_pixels));
10530 if (ping_pixels == (unsigned char *) NULL)
10531 png_error(ping,"Allocation of memory for pixels failed");
10534 Initialize image scanlines.
10536 quantum_info=AcquireQuantumInfo(image_info,image);
10537 if (quantum_info == (QuantumInfo *) NULL)
10538 png_error(ping,"Memory allocation for quantum_info failed");
10539 quantum_info->format=UndefinedQuantumFormat;
10540 quantum_info->depth=image_depth;
10541 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10542 num_passes=png_set_interlace_handling(ping);
10544 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10545 !mng_info->write_png48 && !mng_info->write_png64 &&
10546 !mng_info->write_png32) &&
10547 (mng_info->IsPalette ||
10548 (image_info->type == BilevelType)) &&
10549 image_matte == MagickFalse &&
10550 ping_have_non_bw == MagickFalse)
10552 /* Palette, Bilevel, or Opaque Monochrome */
10553 register const Quantum
10556 quantum_info->depth=8;
10557 for (pass=0; pass < num_passes; pass++)
10560 Convert PseudoClass image to a PNG monochrome image.
10562 for (y=0; y < (ssize_t) image->rows; y++)
10564 if (logging != MagickFalse && y == 0)
10565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " Writing row of pixels (0)");
10568 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10570 if (p == (const Quantum *) NULL)
10573 if (mng_info->IsPalette)
10575 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10576 quantum_info,GrayQuantum,ping_pixels,exception);
10577 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10578 mng_info->write_png_depth &&
10579 mng_info->write_png_depth != old_bit_depth)
10581 /* Undo pixel scaling */
10582 for (i=0; i < (ssize_t) image->columns; i++)
10583 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10584 >> (8-old_bit_depth));
10590 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10591 quantum_info,RedQuantum,ping_pixels,exception);
10594 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10595 for (i=0; i < (ssize_t) image->columns; i++)
10596 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10599 if (logging != MagickFalse && y == 0)
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 " Writing row of pixels (1)");
10603 png_write_row(ping,ping_pixels);
10605 if (image->previous == (Image *) NULL)
10607 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10608 if (status == MagickFalse)
10614 else /* Not Palette, Bilevel, or Opaque Monochrome */
10616 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10617 !mng_info->write_png48 && !mng_info->write_png64 &&
10618 !mng_info->write_png32) && (image_matte != MagickFalse ||
10619 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10620 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10622 register const Quantum
10625 for (pass=0; pass < num_passes; pass++)
10628 for (y=0; y < (ssize_t) image->rows; y++)
10630 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10632 if (p == (const Quantum *) NULL)
10635 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10637 if (mng_info->IsPalette)
10638 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10639 quantum_info,GrayQuantum,ping_pixels,exception);
10642 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10643 quantum_info,RedQuantum,ping_pixels,exception);
10645 if (logging != MagickFalse && y == 0)
10646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10647 " Writing GRAY PNG pixels (2)");
10650 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10652 if (logging != MagickFalse && y == 0)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " Writing GRAY_ALPHA PNG pixels (2)");
10656 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10657 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10660 if (logging != MagickFalse && y == 0)
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " Writing row of pixels (2)");
10664 png_write_row(ping,ping_pixels);
10667 if (image->previous == (Image *) NULL)
10669 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10670 if (status == MagickFalse)
10678 register const Quantum
10681 for (pass=0; pass < num_passes; pass++)
10683 if ((image_depth > 8) ||
10684 mng_info->write_png24 ||
10685 mng_info->write_png32 ||
10686 mng_info->write_png48 ||
10687 mng_info->write_png64 ||
10688 (!mng_info->write_png8 && !mng_info->IsPalette))
10690 for (y=0; y < (ssize_t) image->rows; y++)
10692 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10694 if (p == (const Quantum *) NULL)
10697 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10699 if (image->storage_class == DirectClass)
10700 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10701 quantum_info,RedQuantum,ping_pixels,exception);
10704 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10705 quantum_info,GrayQuantum,ping_pixels,exception);
10708 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10710 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10711 quantum_info,GrayAlphaQuantum,ping_pixels,
10714 if (logging != MagickFalse && y == 0)
10715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10716 " Writing GRAY_ALPHA PNG pixels (3)");
10719 else if (image_matte != MagickFalse)
10720 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10721 quantum_info,RGBAQuantum,ping_pixels,exception);
10724 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10725 quantum_info,RGBQuantum,ping_pixels,exception);
10727 if (logging != MagickFalse && y == 0)
10728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729 " Writing row of pixels (3)");
10731 png_write_row(ping,ping_pixels);
10736 /* not ((image_depth > 8) ||
10737 mng_info->write_png24 || mng_info->write_png32 ||
10738 mng_info->write_png48 || mng_info->write_png64 ||
10739 (!mng_info->write_png8 && !mng_info->IsPalette))
10742 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10743 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10745 if (logging != MagickFalse)
10746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10747 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10749 quantum_info->depth=8;
10753 for (y=0; y < (ssize_t) image->rows; y++)
10755 if (logging != MagickFalse && y == 0)
10756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10757 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10759 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10761 if (p == (const Quantum *) NULL)
10764 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10766 quantum_info->depth=image->depth;
10768 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10769 quantum_info,GrayQuantum,ping_pixels,exception);
10772 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10774 if (logging != MagickFalse && y == 0)
10775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10776 " Writing GRAY_ALPHA PNG pixels (4)");
10778 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10779 quantum_info,GrayAlphaQuantum,ping_pixels,
10785 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10786 quantum_info,IndexQuantum,ping_pixels,exception);
10788 if (logging != MagickFalse && y <= 2)
10790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10791 " Writing row of non-gray pixels (4)");
10793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10794 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10795 (int)ping_pixels[0],(int)ping_pixels[1]);
10798 png_write_row(ping,ping_pixels);
10802 if (image->previous == (Image *) NULL)
10804 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10805 if (status == MagickFalse)
10812 if (quantum_info != (QuantumInfo *) NULL)
10813 quantum_info=DestroyQuantumInfo(quantum_info);
10815 if (logging != MagickFalse)
10817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10818 " Wrote PNG image data");
10820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10821 " Width: %.20g",(double) ping_width);
10823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10824 " Height: %.20g",(double) ping_height);
10826 if (mng_info->write_png_depth)
10828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10829 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10833 " PNG bit-depth written: %d",ping_bit_depth);
10835 if (mng_info->write_png_colortype)
10837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10838 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10842 " PNG color-type written: %d",ping_color_type);
10844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845 " PNG Interlace method: %d",ping_interlace_method);
10848 Generate text chunks after IDAT.
10850 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10852 ResetImagePropertyIterator(image);
10853 property=GetNextImageProperty(image);
10854 while (property != (const char *) NULL)
10859 value=GetImageProperty(image,property,exception);
10861 /* Don't write any "png:" properties; those are just for "identify" */
10862 if (LocaleNCompare(property,"png:",4) != 0 &&
10864 /* Suppress density and units if we wrote a pHYs chunk */
10865 (ping_exclude_pHYs != MagickFalse ||
10866 LocaleCompare(property,"density") != 0 ||
10867 LocaleCompare(property,"units") != 0) &&
10869 /* Suppress the IM-generated Date:create and Date:modify */
10870 (ping_exclude_date == MagickFalse ||
10871 LocaleNCompare(property, "Date:",5) != 0))
10873 if (value != (const char *) NULL)
10876 #if PNG_LIBPNG_VER >= 14000
10877 text=(png_textp) png_malloc(ping,
10878 (png_alloc_size_t) sizeof(png_text));
10880 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10882 text[0].key=(char *) property;
10883 text[0].text=(char *) value;
10884 text[0].text_length=strlen(value);
10886 if (ping_exclude_tEXt != MagickFalse)
10887 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10889 else if (ping_exclude_zTXt != MagickFalse)
10890 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10894 text[0].compression=image_info->compression == NoCompression ||
10895 (image_info->compression == UndefinedCompression &&
10896 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10897 PNG_TEXT_COMPRESSION_zTXt ;
10900 if (logging != MagickFalse)
10902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10903 " Setting up text chunk");
10905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10906 " keyword: %s",text[0].key);
10909 png_set_text(ping,ping_info,text,1);
10910 png_free(ping,text);
10913 property=GetNextImageProperty(image);
10917 /* write any PNG-chunk-e profiles */
10918 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10920 if (logging != MagickFalse)
10921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10922 " Writing PNG end info");
10924 png_write_end(ping,ping_info);
10926 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10928 if (mng_info->page.x || mng_info->page.y ||
10929 (ping_width != mng_info->page.width) ||
10930 (ping_height != mng_info->page.height))
10936 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10938 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10939 PNGType(chunk,mng_FRAM);
10940 LogPNGChunk(logging,mng_FRAM,27L);
10942 chunk[5]=0; /* frame name separator (no name) */
10943 chunk[6]=1; /* flag for changing delay, for next frame only */
10944 chunk[7]=0; /* flag for changing frame timeout */
10945 chunk[8]=1; /* flag for changing frame clipping for next frame */
10946 chunk[9]=0; /* flag for changing frame sync_id */
10947 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10948 chunk[14]=0; /* clipping boundaries delta type */
10949 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10951 (png_uint_32) (mng_info->page.x + ping_width));
10952 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10954 (png_uint_32) (mng_info->page.y + ping_height));
10955 (void) WriteBlob(image,31,chunk);
10956 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10957 mng_info->old_framing_mode=4;
10958 mng_info->framing_mode=1;
10962 mng_info->framing_mode=3;
10964 if (mng_info->write_mng && !mng_info->need_fram &&
10965 ((int) image->dispose == 3))
10966 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10969 Free PNG resources.
10972 png_destroy_write_struct(&ping,&ping_info);
10974 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10976 if (ping_have_blob != MagickFalse)
10977 (void) CloseBlob(image);
10979 image_info=DestroyImageInfo(image_info);
10980 image=DestroyImage(image);
10982 /* Store bit depth actually written */
10983 s[0]=(char) ping_bit_depth;
10986 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10988 if (logging != MagickFalse)
10989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10990 " exit WriteOnePNGImage()");
10992 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10993 UnlockSemaphoreInfo(ping_semaphore);
10996 /* } for navigation to beginning of SETJMP-protected block. Revert to
10997 * Throwing an Exception when an error occurs.
11000 return(MagickTrue);
11001 /* End write one PNG image */
11006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11010 % W r i t e P N G I m a g e %
11014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11016 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11017 % Multiple-image Network Graphics (MNG) image file.
11019 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11021 % The format of the WritePNGImage method is:
11023 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11024 % Image *image,ExceptionInfo *exception)
11026 % A description of each parameter follows:
11028 % o image_info: the image info.
11030 % o image: The image.
11032 % o exception: return any errors or warnings in this structure.
11034 % Returns MagickTrue on success, MagickFalse on failure.
11036 % Communicating with the PNG encoder:
11038 % While the datastream written is always in PNG format and normally would
11039 % be given the "png" file extension, this method also writes the following
11040 % pseudo-formats which are subsets of png:
11042 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11043 % a depth greater than 8, the depth is reduced. If transparency
11044 % is present, the tRNS chunk must only have values 0 and 255
11045 % (i.e., transparency is binary: fully opaque or fully
11046 % transparent). If other values are present they will be
11047 % 50%-thresholded to binary transparency. If more than 256
11048 % colors are present, they will be quantized to the 4-4-4-1,
11049 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11050 % of any resulting fully-transparent pixels is changed to
11051 % the image's background color.
11053 % If you want better quantization or dithering of the colors
11054 % or alpha than that, you need to do it before calling the
11055 % PNG encoder. The pixels contain 8-bit indices even if
11056 % they could be represented with 1, 2, or 4 bits. Grayscale
11057 % images will be written as indexed PNG files even though the
11058 % PNG grayscale type might be slightly more efficient. Please
11059 % note that writing to the PNG8 format may result in loss
11060 % of color and alpha data.
11062 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11063 % chunk can be present to convey binary transparency by naming
11064 % one of the colors as transparent. The only loss incurred
11065 % is reduction of sample depth to 8. If the image has more
11066 % than one transparent color, has semitransparent pixels, or
11067 % has an opaque pixel with the same RGB components as the
11068 % transparent color, an image is not written.
11070 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11071 % transparency is permitted, i.e., the alpha sample for
11072 % each pixel can have any value from 0 to 255. The alpha
11073 % channel is present even if the image is fully opaque.
11074 % The only loss in data is the reduction of the sample depth
11077 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11078 % chunk can be present to convey binary transparency by naming
11079 % one of the colors as transparent. If the image has more
11080 % than one transparent color, has semitransparent pixels, or
11081 % has an opaque pixel with the same RGB components as the
11082 % transparent color, an image is not written.
11084 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11085 % transparency is permitted, i.e., the alpha sample for
11086 % each pixel can have any value from 0 to 65535. The alpha
11087 % channel is present even if the image is fully opaque.
11089 % o -define: For more precise control of the PNG output, you can use the
11090 % Image options "png:bit-depth" and "png:color-type". These
11091 % can be set from the commandline with "-define" and also
11092 % from the application programming interfaces. The options
11093 % are case-independent and are converted to lowercase before
11094 % being passed to this encoder.
11096 % png:color-type can be 0, 2, 3, 4, or 6.
11098 % When png:color-type is 0 (Grayscale), png:bit-depth can
11099 % be 1, 2, 4, 8, or 16.
11101 % When png:color-type is 2 (RGB), png:bit-depth can
11104 % When png:color-type is 3 (Indexed), png:bit-depth can
11105 % be 1, 2, 4, or 8. This refers to the number of bits
11106 % used to store the index. The color samples always have
11107 % bit-depth 8 in indexed PNG files.
11109 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11110 % png:bit-depth can be 8 or 16.
11112 % If the image cannot be written without loss with the
11113 % requested bit-depth and color-type, a PNG file will not
11114 % be written, a warning will be issued, and the encoder will
11115 % return MagickFalse.
11117 % Since image encoders should not be responsible for the "heavy lifting",
11118 % the user should make sure that ImageMagick has already reduced the
11119 % image depth and number of colors and limit transparency to binary
11120 % transparency prior to attempting to write the image with depth, color,
11121 % or transparency limitations.
11123 % Note that another definition, "png:bit-depth-written" exists, but it
11124 % is not intended for external use. It is only used internally by the
11125 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11127 % It is possible to request that the PNG encoder write previously-formatted
11128 % ancillary chunks in the output PNG file, using the "-profile" commandline
11129 % option as shown below or by setting the profile via a programming
11132 % -profile PNG-chunk-x:<file>
11134 % where x is a location flag and <file> is a file containing the chunk
11135 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11136 % This encoder will compute the chunk length and CRC, so those must not
11137 % be included in the file.
11139 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11140 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11141 % of the same type, then add a short unique string after the "x" to prevent
11142 % subsequent profiles from overwriting the preceding ones, e.g.,
11144 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11146 % As of version 6.6.6 the following optimizations are always done:
11148 % o 32-bit depth is reduced to 16.
11149 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11150 % high byte and low byte are identical.
11151 % o Palette is sorted to remove unused entries and to put a
11152 % transparent color first, if BUILD_PNG_PALETTE is defined.
11153 % o Opaque matte channel is removed when writing an indexed PNG.
11154 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11155 % this can be done without loss and a larger bit depth N was not
11156 % requested via the "-define png:bit-depth=N" option.
11157 % o If matte channel is present but only one transparent color is
11158 % present, RGB+tRNS is written instead of RGBA
11159 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11160 % was requested when converting an opaque image).
11162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11164 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11165 Image *image,ExceptionInfo *exception)
11170 have_mng_structure,
11186 assert(image_info != (const ImageInfo *) NULL);
11187 assert(image_info->signature == MagickSignature);
11188 assert(image != (Image *) NULL);
11189 assert(image->signature == MagickSignature);
11190 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11191 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11193 Allocate a MngInfo structure.
11195 have_mng_structure=MagickFalse;
11196 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11198 if (mng_info == (MngInfo *) NULL)
11199 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11202 Initialize members of the MngInfo structure.
11204 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11205 mng_info->image=image;
11206 mng_info->equal_backgrounds=MagickTrue;
11207 have_mng_structure=MagickTrue;
11209 /* See if user has requested a specific PNG subformat */
11211 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11212 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11213 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11214 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11215 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11217 value=GetImageOption(image_info,"png:format");
11219 if (value != (char *) NULL)
11221 mng_info->write_png8 = MagickFalse;
11222 mng_info->write_png24 = MagickFalse;
11223 mng_info->write_png32 = MagickFalse;
11224 mng_info->write_png48 = MagickFalse;
11225 mng_info->write_png64 = MagickFalse;
11227 if (LocaleCompare(value,"png8") == 0)
11228 mng_info->write_png8 = MagickTrue;
11230 else if (LocaleCompare(value,"png24") == 0)
11231 mng_info->write_png24 = MagickTrue;
11233 else if (LocaleCompare(value,"png32") == 0)
11234 mng_info->write_png32 = MagickTrue;
11236 else if (LocaleCompare(value,"png48") == 0)
11237 mng_info->write_png48 = MagickTrue;
11239 else if (LocaleCompare(value,"png64") == 0)
11240 mng_info->write_png64 = MagickTrue;
11242 if (mng_info->write_png8)
11244 mng_info->write_png_colortype = /* 3 */ 4;
11245 mng_info->write_png_depth = 8;
11249 if (mng_info->write_png24)
11251 mng_info->write_png_colortype = /* 2 */ 3;
11252 mng_info->write_png_depth = 8;
11255 if (image->alpha_trait == BlendPixelTrait)
11256 (void) SetImageType(image,TrueColorMatteType,exception);
11259 (void) SetImageType(image,TrueColorType,exception);
11261 (void) SyncImage(image,exception);
11264 if (mng_info->write_png32)
11266 mng_info->write_png_colortype = /* 6 */ 7;
11267 mng_info->write_png_depth = 8;
11270 if (image->alpha_trait == BlendPixelTrait)
11271 (void) SetImageType(image,TrueColorMatteType,exception);
11274 (void) SetImageType(image,TrueColorType,exception);
11276 (void) SyncImage(image,exception);
11279 if (mng_info->write_png48)
11281 mng_info->write_png_colortype = /* 2 */ 3;
11282 mng_info->write_png_depth = 16;
11285 if (image->alpha_trait == BlendPixelTrait)
11286 (void) SetImageType(image,TrueColorMatteType,exception);
11289 (void) SetImageType(image,TrueColorType,exception);
11291 (void) SyncImage(image,exception);
11294 if (mng_info->write_png64)
11296 mng_info->write_png_colortype = /* 6 */ 7;
11297 mng_info->write_png_depth = 16;
11300 if (image->alpha_trait == BlendPixelTrait)
11301 (void) SetImageType(image,TrueColorMatteType,exception);
11304 (void) SetImageType(image,TrueColorType,exception);
11306 (void) SyncImage(image,exception);
11309 value=GetImageOption(image_info,"png:bit-depth");
11311 if (value != (char *) NULL)
11313 if (LocaleCompare(value,"1") == 0)
11314 mng_info->write_png_depth = 1;
11316 else if (LocaleCompare(value,"2") == 0)
11317 mng_info->write_png_depth = 2;
11319 else if (LocaleCompare(value,"4") == 0)
11320 mng_info->write_png_depth = 4;
11322 else if (LocaleCompare(value,"8") == 0)
11323 mng_info->write_png_depth = 8;
11325 else if (LocaleCompare(value,"16") == 0)
11326 mng_info->write_png_depth = 16;
11329 (void) ThrowMagickException(exception,
11330 GetMagickModule(),CoderWarning,
11331 "ignoring invalid defined png:bit-depth",
11334 if (logging != MagickFalse)
11335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11336 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11339 value=GetImageOption(image_info,"png:color-type");
11341 if (value != (char *) NULL)
11343 /* We must store colortype+1 because 0 is a valid colortype */
11344 if (LocaleCompare(value,"0") == 0)
11345 mng_info->write_png_colortype = 1;
11347 else if (LocaleCompare(value,"1") == 0)
11348 mng_info->write_png_colortype = 2;
11350 else if (LocaleCompare(value,"2") == 0)
11351 mng_info->write_png_colortype = 3;
11353 else if (LocaleCompare(value,"3") == 0)
11354 mng_info->write_png_colortype = 4;
11356 else if (LocaleCompare(value,"4") == 0)
11357 mng_info->write_png_colortype = 5;
11359 else if (LocaleCompare(value,"6") == 0)
11360 mng_info->write_png_colortype = 7;
11363 (void) ThrowMagickException(exception,
11364 GetMagickModule(),CoderWarning,
11365 "ignoring invalid defined png:color-type",
11368 if (logging != MagickFalse)
11369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11373 /* Check for chunks to be excluded:
11375 * The default is to not exclude any known chunks except for any
11376 * listed in the "unused_chunks" array, above.
11378 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11379 * define (in the image properties or in the image artifacts)
11380 * or via a mng_info member. For convenience, in addition
11381 * to or instead of a comma-separated list of chunks, the
11382 * "exclude-chunk" string can be simply "all" or "none".
11384 * The exclude-chunk define takes priority over the mng_info.
11386 * A "png:include-chunk" define takes priority over both the
11387 * mng_info and the "png:exclude-chunk" define. Like the
11388 * "exclude-chunk" string, it can define "all" or "none" as
11389 * well as a comma-separated list. Chunks that are unknown to
11390 * ImageMagick are always excluded, regardless of their "copy-safe"
11391 * status according to the PNG specification, and even if they
11392 * appear in the "include-chunk" list. Such defines appearing among
11393 * the image options take priority over those found among the image
11396 * Finally, all chunks listed in the "unused_chunks" array are
11397 * automatically excluded, regardless of the other instructions
11400 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11401 * will not be written and the gAMA chunk will only be written if it
11402 * is not between .45 and .46, or approximately (1.0/2.2).
11404 * If you exclude tRNS and the image has transparency, the colortype
11405 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11407 * The -strip option causes StripImage() to set the png:include-chunk
11408 * artifact to "none,trns,gama".
11411 mng_info->ping_exclude_bKGD=MagickFalse;
11412 mng_info->ping_exclude_cHRM=MagickFalse;
11413 mng_info->ping_exclude_date=MagickFalse;
11414 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11415 mng_info->ping_exclude_gAMA=MagickFalse;
11416 mng_info->ping_exclude_iCCP=MagickFalse;
11417 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11418 mng_info->ping_exclude_oFFs=MagickFalse;
11419 mng_info->ping_exclude_pHYs=MagickFalse;
11420 mng_info->ping_exclude_sRGB=MagickFalse;
11421 mng_info->ping_exclude_tEXt=MagickFalse;
11422 mng_info->ping_exclude_tRNS=MagickFalse;
11423 mng_info->ping_exclude_vpAg=MagickFalse;
11424 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11425 mng_info->ping_exclude_zTXt=MagickFalse;
11427 mng_info->ping_preserve_colormap=MagickFalse;
11429 value=GetImageArtifact(image,"png:preserve-colormap");
11431 value=GetImageOption(image_info,"png:preserve-colormap");
11433 mng_info->ping_preserve_colormap=MagickTrue;
11435 /* Thes compression-level, compression-strategy, and compression-filter
11436 * defines take precedence over values from the -quality option.
11438 value=GetImageArtifact(image,"png:compression-level");
11440 value=GetImageOption(image_info,"png:compression-level");
11443 /* We have to add 1 to everything because 0 is a valid input,
11444 * and we want to use 0 (the default) to mean undefined.
11446 if (LocaleCompare(value,"0") == 0)
11447 mng_info->write_png_compression_level = 1;
11449 else if (LocaleCompare(value,"1") == 0)
11450 mng_info->write_png_compression_level = 2;
11452 else if (LocaleCompare(value,"2") == 0)
11453 mng_info->write_png_compression_level = 3;
11455 else if (LocaleCompare(value,"3") == 0)
11456 mng_info->write_png_compression_level = 4;
11458 else if (LocaleCompare(value,"4") == 0)
11459 mng_info->write_png_compression_level = 5;
11461 else if (LocaleCompare(value,"5") == 0)
11462 mng_info->write_png_compression_level = 6;
11464 else if (LocaleCompare(value,"6") == 0)
11465 mng_info->write_png_compression_level = 7;
11467 else if (LocaleCompare(value,"7") == 0)
11468 mng_info->write_png_compression_level = 8;
11470 else if (LocaleCompare(value,"8") == 0)
11471 mng_info->write_png_compression_level = 9;
11473 else if (LocaleCompare(value,"9") == 0)
11474 mng_info->write_png_compression_level = 10;
11477 (void) ThrowMagickException(exception,
11478 GetMagickModule(),CoderWarning,
11479 "ignoring invalid defined png:compression-level",
11483 value=GetImageArtifact(image,"png:compression-strategy");
11485 value=GetImageOption(image_info,"png:compression-strategy");
11489 if (LocaleCompare(value,"0") == 0)
11490 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11492 else if (LocaleCompare(value,"1") == 0)
11493 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11495 else if (LocaleCompare(value,"2") == 0)
11496 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11498 else if (LocaleCompare(value,"3") == 0)
11499 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11500 mng_info->write_png_compression_strategy = Z_RLE+1;
11502 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11505 else if (LocaleCompare(value,"4") == 0)
11506 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11507 mng_info->write_png_compression_strategy = Z_FIXED+1;
11509 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11513 (void) ThrowMagickException(exception,
11514 GetMagickModule(),CoderWarning,
11515 "ignoring invalid defined png:compression-strategy",
11519 value=GetImageArtifact(image,"png:compression-filter");
11521 value=GetImageOption(image_info,"png:compression-filter");
11525 /* To do: combinations of filters allowed by libpng
11526 * masks 0x08 through 0xf8
11528 * Implement this as a comma-separated list of 0,1,2,3,4,5
11529 * where 5 is a special case meaning PNG_ALL_FILTERS.
11532 if (LocaleCompare(value,"0") == 0)
11533 mng_info->write_png_compression_filter = 1;
11535 else if (LocaleCompare(value,"1") == 0)
11536 mng_info->write_png_compression_filter = 2;
11538 else if (LocaleCompare(value,"2") == 0)
11539 mng_info->write_png_compression_filter = 3;
11541 else if (LocaleCompare(value,"3") == 0)
11542 mng_info->write_png_compression_filter = 4;
11544 else if (LocaleCompare(value,"4") == 0)
11545 mng_info->write_png_compression_filter = 5;
11547 else if (LocaleCompare(value,"5") == 0)
11548 mng_info->write_png_compression_filter = 6;
11551 (void) ThrowMagickException(exception,
11552 GetMagickModule(),CoderWarning,
11553 "ignoring invalid defined png:compression-filter",
11557 excluding=MagickFalse;
11559 for (source=0; source<1; source++)
11563 value=GetImageArtifact(image,"png:exclude-chunk");
11566 value=GetImageArtifact(image,"png:exclude-chunks");
11570 value=GetImageOption(image_info,"png:exclude-chunk");
11573 value=GetImageOption(image_info,"png:exclude-chunks");
11582 excluding=MagickTrue;
11584 if (logging != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11588 " png:exclude-chunk=%s found in image artifacts.\n", value);
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591 " png:exclude-chunk=%s found in image properties.\n", value);
11594 last=strlen(value);
11596 for (i=0; i<(int) last; i+=5)
11599 if (LocaleNCompare(value+i,"all",3) == 0)
11601 mng_info->ping_exclude_bKGD=MagickTrue;
11602 mng_info->ping_exclude_cHRM=MagickTrue;
11603 mng_info->ping_exclude_date=MagickTrue;
11604 mng_info->ping_exclude_EXIF=MagickTrue;
11605 mng_info->ping_exclude_gAMA=MagickTrue;
11606 mng_info->ping_exclude_iCCP=MagickTrue;
11607 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11608 mng_info->ping_exclude_oFFs=MagickTrue;
11609 mng_info->ping_exclude_pHYs=MagickTrue;
11610 mng_info->ping_exclude_sRGB=MagickTrue;
11611 mng_info->ping_exclude_tEXt=MagickTrue;
11612 mng_info->ping_exclude_tRNS=MagickTrue;
11613 mng_info->ping_exclude_vpAg=MagickTrue;
11614 mng_info->ping_exclude_zCCP=MagickTrue;
11615 mng_info->ping_exclude_zTXt=MagickTrue;
11619 if (LocaleNCompare(value+i,"none",4) == 0)
11621 mng_info->ping_exclude_bKGD=MagickFalse;
11622 mng_info->ping_exclude_cHRM=MagickFalse;
11623 mng_info->ping_exclude_date=MagickFalse;
11624 mng_info->ping_exclude_EXIF=MagickFalse;
11625 mng_info->ping_exclude_gAMA=MagickFalse;
11626 mng_info->ping_exclude_iCCP=MagickFalse;
11627 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11628 mng_info->ping_exclude_oFFs=MagickFalse;
11629 mng_info->ping_exclude_pHYs=MagickFalse;
11630 mng_info->ping_exclude_sRGB=MagickFalse;
11631 mng_info->ping_exclude_tEXt=MagickFalse;
11632 mng_info->ping_exclude_tRNS=MagickFalse;
11633 mng_info->ping_exclude_vpAg=MagickFalse;
11634 mng_info->ping_exclude_zCCP=MagickFalse;
11635 mng_info->ping_exclude_zTXt=MagickFalse;
11638 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11639 mng_info->ping_exclude_bKGD=MagickTrue;
11641 if (LocaleNCompare(value+i,"chrm",4) == 0)
11642 mng_info->ping_exclude_cHRM=MagickTrue;
11644 if (LocaleNCompare(value+i,"date",4) == 0)
11645 mng_info->ping_exclude_date=MagickTrue;
11647 if (LocaleNCompare(value+i,"exif",4) == 0)
11648 mng_info->ping_exclude_EXIF=MagickTrue;
11650 if (LocaleNCompare(value+i,"gama",4) == 0)
11651 mng_info->ping_exclude_gAMA=MagickTrue;
11653 if (LocaleNCompare(value+i,"iccp",4) == 0)
11654 mng_info->ping_exclude_iCCP=MagickTrue;
11657 if (LocaleNCompare(value+i,"itxt",4) == 0)
11658 mng_info->ping_exclude_iTXt=MagickTrue;
11661 if (LocaleNCompare(value+i,"gama",4) == 0)
11662 mng_info->ping_exclude_gAMA=MagickTrue;
11664 if (LocaleNCompare(value+i,"offs",4) == 0)
11665 mng_info->ping_exclude_oFFs=MagickTrue;
11667 if (LocaleNCompare(value+i,"phys",4) == 0)
11668 mng_info->ping_exclude_pHYs=MagickTrue;
11670 if (LocaleNCompare(value+i,"srgb",4) == 0)
11671 mng_info->ping_exclude_sRGB=MagickTrue;
11673 if (LocaleNCompare(value+i,"text",4) == 0)
11674 mng_info->ping_exclude_tEXt=MagickTrue;
11676 if (LocaleNCompare(value+i,"trns",4) == 0)
11677 mng_info->ping_exclude_tRNS=MagickTrue;
11679 if (LocaleNCompare(value+i,"vpag",4) == 0)
11680 mng_info->ping_exclude_vpAg=MagickTrue;
11682 if (LocaleNCompare(value+i,"zccp",4) == 0)
11683 mng_info->ping_exclude_zCCP=MagickTrue;
11685 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11686 mng_info->ping_exclude_zTXt=MagickTrue;
11692 for (source=0; source<1; source++)
11696 value=GetImageArtifact(image,"png:include-chunk");
11699 value=GetImageArtifact(image,"png:include-chunks");
11703 value=GetImageOption(image_info,"png:include-chunk");
11706 value=GetImageOption(image_info,"png:include-chunks");
11714 excluding=MagickTrue;
11716 if (logging != MagickFalse)
11719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11720 " png:include-chunk=%s found in image artifacts.\n", value);
11722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 " png:include-chunk=%s found in image properties.\n", value);
11726 last=strlen(value);
11728 for (i=0; i<(int) last; i+=5)
11730 if (LocaleNCompare(value+i,"all",3) == 0)
11732 mng_info->ping_exclude_bKGD=MagickFalse;
11733 mng_info->ping_exclude_cHRM=MagickFalse;
11734 mng_info->ping_exclude_date=MagickFalse;
11735 mng_info->ping_exclude_EXIF=MagickFalse;
11736 mng_info->ping_exclude_gAMA=MagickFalse;
11737 mng_info->ping_exclude_iCCP=MagickFalse;
11738 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11739 mng_info->ping_exclude_oFFs=MagickFalse;
11740 mng_info->ping_exclude_pHYs=MagickFalse;
11741 mng_info->ping_exclude_sRGB=MagickFalse;
11742 mng_info->ping_exclude_tEXt=MagickFalse;
11743 mng_info->ping_exclude_tRNS=MagickFalse;
11744 mng_info->ping_exclude_vpAg=MagickFalse;
11745 mng_info->ping_exclude_zCCP=MagickFalse;
11746 mng_info->ping_exclude_zTXt=MagickFalse;
11750 if (LocaleNCompare(value+i,"none",4) == 0)
11752 mng_info->ping_exclude_bKGD=MagickTrue;
11753 mng_info->ping_exclude_cHRM=MagickTrue;
11754 mng_info->ping_exclude_date=MagickTrue;
11755 mng_info->ping_exclude_EXIF=MagickTrue;
11756 mng_info->ping_exclude_gAMA=MagickTrue;
11757 mng_info->ping_exclude_iCCP=MagickTrue;
11758 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11759 mng_info->ping_exclude_oFFs=MagickTrue;
11760 mng_info->ping_exclude_pHYs=MagickTrue;
11761 mng_info->ping_exclude_sRGB=MagickTrue;
11762 mng_info->ping_exclude_tEXt=MagickTrue;
11763 mng_info->ping_exclude_tRNS=MagickTrue;
11764 mng_info->ping_exclude_vpAg=MagickTrue;
11765 mng_info->ping_exclude_zCCP=MagickTrue;
11766 mng_info->ping_exclude_zTXt=MagickTrue;
11769 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11770 mng_info->ping_exclude_bKGD=MagickFalse;
11772 if (LocaleNCompare(value+i,"chrm",4) == 0)
11773 mng_info->ping_exclude_cHRM=MagickFalse;
11775 if (LocaleNCompare(value+i,"date",4) == 0)
11776 mng_info->ping_exclude_date=MagickFalse;
11778 if (LocaleNCompare(value+i,"exif",4) == 0)
11779 mng_info->ping_exclude_EXIF=MagickFalse;
11781 if (LocaleNCompare(value+i,"gama",4) == 0)
11782 mng_info->ping_exclude_gAMA=MagickFalse;
11784 if (LocaleNCompare(value+i,"iccp",4) == 0)
11785 mng_info->ping_exclude_iCCP=MagickFalse;
11788 if (LocaleNCompare(value+i,"itxt",4) == 0)
11789 mng_info->ping_exclude_iTXt=MagickFalse;
11792 if (LocaleNCompare(value+i,"gama",4) == 0)
11793 mng_info->ping_exclude_gAMA=MagickFalse;
11795 if (LocaleNCompare(value+i,"offs",4) == 0)
11796 mng_info->ping_exclude_oFFs=MagickFalse;
11798 if (LocaleNCompare(value+i,"phys",4) == 0)
11799 mng_info->ping_exclude_pHYs=MagickFalse;
11801 if (LocaleNCompare(value+i,"srgb",4) == 0)
11802 mng_info->ping_exclude_sRGB=MagickFalse;
11804 if (LocaleNCompare(value+i,"text",4) == 0)
11805 mng_info->ping_exclude_tEXt=MagickFalse;
11807 if (LocaleNCompare(value+i,"trns",4) == 0)
11808 mng_info->ping_exclude_tRNS=MagickFalse;
11810 if (LocaleNCompare(value+i,"vpag",4) == 0)
11811 mng_info->ping_exclude_vpAg=MagickFalse;
11813 if (LocaleNCompare(value+i,"zccp",4) == 0)
11814 mng_info->ping_exclude_zCCP=MagickFalse;
11816 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11817 mng_info->ping_exclude_zTXt=MagickFalse;
11823 if (excluding != MagickFalse && logging != MagickFalse)
11825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11826 " Chunks to be excluded from the output png:");
11827 if (mng_info->ping_exclude_bKGD != MagickFalse)
11828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11830 if (mng_info->ping_exclude_cHRM != MagickFalse)
11831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11833 if (mng_info->ping_exclude_date != MagickFalse)
11834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836 if (mng_info->ping_exclude_EXIF != MagickFalse)
11837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11839 if (mng_info->ping_exclude_gAMA != MagickFalse)
11840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842 if (mng_info->ping_exclude_iCCP != MagickFalse)
11843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11846 if (mng_info->ping_exclude_iTXt != MagickFalse)
11847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11850 if (mng_info->ping_exclude_oFFs != MagickFalse)
11851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11853 if (mng_info->ping_exclude_pHYs != MagickFalse)
11854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11856 if (mng_info->ping_exclude_sRGB != MagickFalse)
11857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11859 if (mng_info->ping_exclude_tEXt != MagickFalse)
11860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11862 if (mng_info->ping_exclude_tRNS != MagickFalse)
11863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11865 if (mng_info->ping_exclude_vpAg != MagickFalse)
11866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11868 if (mng_info->ping_exclude_zCCP != MagickFalse)
11869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11871 if (mng_info->ping_exclude_zTXt != MagickFalse)
11872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11876 mng_info->need_blob = MagickTrue;
11878 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11880 MngInfoFreeStruct(mng_info,&have_mng_structure);
11882 if (logging != MagickFalse)
11883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11888 #if defined(JNG_SUPPORTED)
11890 /* Write one JNG image */
11891 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11892 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11913 jng_alpha_compression_method,
11914 jng_alpha_sample_depth,
11922 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11923 " Enter WriteOneJNGImage()");
11925 blob=(unsigned char *) NULL;
11926 jpeg_image=(Image *) NULL;
11927 jpeg_image_info=(ImageInfo *) NULL;
11930 transparent=image_info->type==GrayscaleMatteType ||
11931 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
11933 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11935 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11937 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11938 image_info->quality;
11940 if (jng_alpha_quality >= 1000)
11941 jng_alpha_quality /= 1000;
11947 /* Create JPEG blob, image, and image_info */
11948 if (logging != MagickFalse)
11949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11950 " Creating jpeg_image_info for alpha.");
11952 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11954 if (jpeg_image_info == (ImageInfo *) NULL)
11955 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11957 if (logging != MagickFalse)
11958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11959 " Creating jpeg_image.");
11961 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11962 if (jpeg_image == (Image *) NULL)
11963 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11964 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11965 jpeg_image->alpha_trait=UndefinedPixelTrait;
11966 jpeg_image->quality=jng_alpha_quality;
11967 jpeg_image_info->type=GrayscaleType;
11968 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11969 (void) AcquireUniqueFilename(jpeg_image->filename);
11970 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11971 "%s",jpeg_image->filename);
11975 jng_alpha_compression_method=0;
11977 jng_alpha_sample_depth=0;
11980 /* To do: check bit depth of PNG alpha channel */
11982 /* Check if image is grayscale. */
11983 if (image_info->type != TrueColorMatteType && image_info->type !=
11984 TrueColorType && IsImageGray(image,exception))
11987 if (logging != MagickFalse)
11989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11990 " JNG Quality = %d",(int) jng_quality);
11991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11992 " JNG Color Type = %d",jng_color_type);
11995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11996 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11998 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12000 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12006 if (jng_alpha_compression_method==0)
12011 /* Encode alpha as a grayscale PNG blob */
12012 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12014 if (logging != MagickFalse)
12015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12016 " Creating PNG blob.");
12019 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12020 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12021 jpeg_image_info->interlace=NoInterlace;
12023 /* Exclude all ancillary chunks */
12024 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12026 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12029 /* Retrieve sample depth used */
12030 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12031 if (value != (char *) NULL)
12032 jng_alpha_sample_depth= (unsigned int) value[0];
12036 /* Encode alpha as a grayscale JPEG blob */
12038 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12041 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12042 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12043 jpeg_image_info->interlace=NoInterlace;
12044 if (logging != MagickFalse)
12045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12046 " Creating blob.");
12047 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12049 jng_alpha_sample_depth=8;
12051 if (logging != MagickFalse)
12052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12053 " Successfully read jpeg_image into a blob, length=%.20g.",
12057 /* Destroy JPEG image and image_info */
12058 jpeg_image=DestroyImage(jpeg_image);
12059 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12060 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12063 /* Write JHDR chunk */
12064 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12065 PNGType(chunk,mng_JHDR);
12066 LogPNGChunk(logging,mng_JHDR,16L);
12067 PNGLong(chunk+4,(png_uint_32) image->columns);
12068 PNGLong(chunk+8,(png_uint_32) image->rows);
12069 chunk[12]=jng_color_type;
12070 chunk[13]=8; /* sample depth */
12071 chunk[14]=8; /*jng_image_compression_method */
12072 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12073 chunk[16]=jng_alpha_sample_depth;
12074 chunk[17]=jng_alpha_compression_method;
12075 chunk[18]=0; /*jng_alpha_filter_method */
12076 chunk[19]=0; /*jng_alpha_interlace_method */
12077 (void) WriteBlob(image,20,chunk);
12078 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12079 if (logging != MagickFalse)
12081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12082 " JNG width:%15lu",(unsigned long) image->columns);
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 " JNG height:%14lu",(unsigned long) image->rows);
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12088 " JNG color type:%10d",jng_color_type);
12090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12091 " JNG sample depth:%8d",8);
12093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12094 " JNG compression:%9d",8);
12096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12097 " JNG interlace:%11d",0);
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12103 " JNG alpha compression:%3d",jng_alpha_compression_method);
12105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12106 " JNG alpha filter:%8d",0);
12108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12109 " JNG alpha interlace:%5d",0);
12112 /* Write any JNG-chunk-b profiles */
12113 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12116 Write leading ancillary chunks
12122 Write JNG bKGD chunk
12133 if (jng_color_type == 8 || jng_color_type == 12)
12137 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12138 PNGType(chunk,mng_bKGD);
12139 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12140 red=ScaleQuantumToChar(image->background_color.red);
12141 green=ScaleQuantumToChar(image->background_color.green);
12142 blue=ScaleQuantumToChar(image->background_color.blue);
12149 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12150 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12153 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12156 Write JNG sRGB chunk
12158 (void) WriteBlobMSBULong(image,1L);
12159 PNGType(chunk,mng_sRGB);
12160 LogPNGChunk(logging,mng_sRGB,1L);
12162 if (image->rendering_intent != UndefinedIntent)
12163 chunk[4]=(unsigned char)
12164 Magick_RenderingIntent_to_PNG_RenderingIntent(
12165 (image->rendering_intent));
12168 chunk[4]=(unsigned char)
12169 Magick_RenderingIntent_to_PNG_RenderingIntent(
12170 (PerceptualIntent));
12172 (void) WriteBlob(image,5,chunk);
12173 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12177 if (image->gamma != 0.0)
12180 Write JNG gAMA chunk
12182 (void) WriteBlobMSBULong(image,4L);
12183 PNGType(chunk,mng_gAMA);
12184 LogPNGChunk(logging,mng_gAMA,4L);
12185 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12186 (void) WriteBlob(image,8,chunk);
12187 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12190 if ((mng_info->equal_chrms == MagickFalse) &&
12191 (image->chromaticity.red_primary.x != 0.0))
12197 Write JNG cHRM chunk
12199 (void) WriteBlobMSBULong(image,32L);
12200 PNGType(chunk,mng_cHRM);
12201 LogPNGChunk(logging,mng_cHRM,32L);
12202 primary=image->chromaticity.white_point;
12203 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12204 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12205 primary=image->chromaticity.red_primary;
12206 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12207 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12208 primary=image->chromaticity.green_primary;
12209 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12210 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12211 primary=image->chromaticity.blue_primary;
12212 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12213 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12214 (void) WriteBlob(image,36,chunk);
12215 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12219 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12222 Write JNG pHYs chunk
12224 (void) WriteBlobMSBULong(image,9L);
12225 PNGType(chunk,mng_pHYs);
12226 LogPNGChunk(logging,mng_pHYs,9L);
12227 if (image->units == PixelsPerInchResolution)
12229 PNGLong(chunk+4,(png_uint_32)
12230 (image->resolution.x*100.0/2.54+0.5));
12232 PNGLong(chunk+8,(png_uint_32)
12233 (image->resolution.y*100.0/2.54+0.5));
12240 if (image->units == PixelsPerCentimeterResolution)
12242 PNGLong(chunk+4,(png_uint_32)
12243 (image->resolution.x*100.0+0.5));
12245 PNGLong(chunk+8,(png_uint_32)
12246 (image->resolution.y*100.0+0.5));
12253 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12254 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12258 (void) WriteBlob(image,13,chunk);
12259 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12262 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12265 Write JNG oFFs chunk
12267 (void) WriteBlobMSBULong(image,9L);
12268 PNGType(chunk,mng_oFFs);
12269 LogPNGChunk(logging,mng_oFFs,9L);
12270 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12271 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12273 (void) WriteBlob(image,13,chunk);
12274 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12276 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12278 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12279 PNGType(chunk,mng_vpAg);
12280 LogPNGChunk(logging,mng_vpAg,9L);
12281 PNGLong(chunk+4,(png_uint_32) image->page.width);
12282 PNGLong(chunk+8,(png_uint_32) image->page.height);
12283 chunk[12]=0; /* unit = pixels */
12284 (void) WriteBlob(image,13,chunk);
12285 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12291 if (jng_alpha_compression_method==0)
12299 /* Write IDAT chunk header */
12300 if (logging != MagickFalse)
12301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12302 " Write IDAT chunks from blob, length=%.20g.",(double)
12305 /* Copy IDAT chunks */
12308 for (i=8; i<(ssize_t) length; i+=len+12)
12310 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12313 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12315 /* Found an IDAT chunk. */
12316 (void) WriteBlobMSBULong(image,(size_t) len);
12317 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12318 (void) WriteBlob(image,(size_t) len+4,p);
12319 (void) WriteBlobMSBULong(image,
12320 crc32(0,p,(uInt) len+4));
12325 if (logging != MagickFalse)
12326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12327 " Skipping %c%c%c%c chunk, length=%.20g.",
12328 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12335 /* Write JDAA chunk header */
12336 if (logging != MagickFalse)
12337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " Write JDAA chunk, length=%.20g.",(double) length);
12339 (void) WriteBlobMSBULong(image,(size_t) length);
12340 PNGType(chunk,mng_JDAA);
12341 LogPNGChunk(logging,mng_JDAA,length);
12342 /* Write JDAT chunk(s) data */
12343 (void) WriteBlob(image,4,chunk);
12344 (void) WriteBlob(image,length,blob);
12345 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12348 blob=(unsigned char *) RelinquishMagickMemory(blob);
12351 /* Encode image as a JPEG blob */
12352 if (logging != MagickFalse)
12353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354 " Creating jpeg_image_info.");
12355 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12356 if (jpeg_image_info == (ImageInfo *) NULL)
12357 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12359 if (logging != MagickFalse)
12360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 " Creating jpeg_image.");
12363 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12364 if (jpeg_image == (Image *) NULL)
12365 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12366 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12368 (void) AcquireUniqueFilename(jpeg_image->filename);
12369 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12370 jpeg_image->filename);
12372 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12375 if (logging != MagickFalse)
12376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12377 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12378 (double) jpeg_image->rows);
12380 if (jng_color_type == 8 || jng_color_type == 12)
12381 jpeg_image_info->type=GrayscaleType;
12383 jpeg_image_info->quality=jng_quality;
12384 jpeg_image->quality=jng_quality;
12385 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12386 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12388 if (logging != MagickFalse)
12389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12390 " Creating blob.");
12392 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12394 if (logging != MagickFalse)
12396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12397 " Successfully read jpeg_image into a blob, length=%.20g.",
12400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12401 " Write JDAT chunk, length=%.20g.",(double) length);
12404 /* Write JDAT chunk(s) */
12405 (void) WriteBlobMSBULong(image,(size_t) length);
12406 PNGType(chunk,mng_JDAT);
12407 LogPNGChunk(logging,mng_JDAT,length);
12408 (void) WriteBlob(image,4,chunk);
12409 (void) WriteBlob(image,length,blob);
12410 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12412 jpeg_image=DestroyImage(jpeg_image);
12413 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12414 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12415 blob=(unsigned char *) RelinquishMagickMemory(blob);
12417 /* Write any JNG-chunk-e profiles */
12418 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12420 /* Write IEND chunk */
12421 (void) WriteBlobMSBULong(image,0L);
12422 PNGType(chunk,mng_IEND);
12423 LogPNGChunk(logging,mng_IEND,0);
12424 (void) WriteBlob(image,4,chunk);
12425 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12427 if (logging != MagickFalse)
12428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12429 " exit WriteOneJNGImage()");
12436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12440 % W r i t e J N G I m a g e %
12444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12446 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12448 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12450 % The format of the WriteJNGImage method is:
12452 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12453 % Image *image,ExceptionInfo *exception)
12455 % A description of each parameter follows:
12457 % o image_info: the image info.
12459 % o image: The image.
12461 % o exception: return any errors or warnings in this structure.
12463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12465 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12466 ExceptionInfo *exception)
12469 have_mng_structure,
12479 assert(image_info != (const ImageInfo *) NULL);
12480 assert(image_info->signature == MagickSignature);
12481 assert(image != (Image *) NULL);
12482 assert(image->signature == MagickSignature);
12483 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12484 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12485 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12486 if (status == MagickFalse)
12490 Allocate a MngInfo structure.
12492 have_mng_structure=MagickFalse;
12493 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12494 if (mng_info == (MngInfo *) NULL)
12495 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12497 Initialize members of the MngInfo structure.
12499 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12500 mng_info->image=image;
12501 have_mng_structure=MagickTrue;
12503 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12505 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12506 (void) CloseBlob(image);
12508 (void) CatchImageException(image);
12509 MngInfoFreeStruct(mng_info,&have_mng_structure);
12510 if (logging != MagickFalse)
12511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12516 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12517 ExceptionInfo *exception)
12526 have_mng_structure,
12529 volatile MagickBooleanType
12541 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12542 defined(PNG_MNG_FEATURES_SUPPORTED)
12545 all_images_are_gray,
12555 volatile unsigned int
12566 #if (PNG_LIBPNG_VER < 10200)
12567 if (image_info->verbose)
12568 printf("Your PNG library (libpng-%s) is rather old.\n",
12569 PNG_LIBPNG_VER_STRING);
12575 assert(image_info != (const ImageInfo *) NULL);
12576 assert(image_info->signature == MagickSignature);
12577 assert(image != (Image *) NULL);
12578 assert(image->signature == MagickSignature);
12579 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12580 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12581 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12582 if (status == MagickFalse)
12586 Allocate a MngInfo structure.
12588 have_mng_structure=MagickFalse;
12589 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12590 if (mng_info == (MngInfo *) NULL)
12591 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12593 Initialize members of the MngInfo structure.
12595 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12596 mng_info->image=image;
12597 have_mng_structure=MagickTrue;
12598 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12601 * See if user has requested a specific PNG subformat to be used
12602 * for all of the PNGs in the MNG being written, e.g.,
12604 * convert *.png png8:animation.mng
12606 * To do: check -define png:bit_depth and png:color_type as well,
12607 * or perhaps use mng:bit_depth and mng:color_type instead for
12611 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12612 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12613 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12615 write_jng=MagickFalse;
12616 if (image_info->compression == JPEGCompression)
12617 write_jng=MagickTrue;
12619 mng_info->adjoin=image_info->adjoin &&
12620 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12622 if (logging != MagickFalse)
12624 /* Log some info about the input */
12628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12629 " Checking input image(s)");
12631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12632 " Image_info depth: %.20g",(double) image_info->depth);
12634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12635 " Type: %d",image_info->type);
12638 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12641 " Scene: %.20g",(double) scene++);
12643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12644 " Image depth: %.20g",(double) p->depth);
12646 if (p->alpha_trait)
12647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12654 if (p->storage_class == PseudoClass)
12655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12656 " Storage class: PseudoClass");
12659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12660 " Storage class: DirectClass");
12663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12664 " Number of colors: %.20g",(double) p->colors);
12667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12668 " Number of colors: unspecified");
12670 if (mng_info->adjoin == MagickFalse)
12675 use_global_plte=MagickFalse;
12676 all_images_are_gray=MagickFalse;
12677 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12678 need_local_plte=MagickTrue;
12680 need_defi=MagickFalse;
12681 need_matte=MagickFalse;
12682 mng_info->framing_mode=1;
12683 mng_info->old_framing_mode=1;
12686 if (image_info->page != (char *) NULL)
12689 Determine image bounding box.
12691 SetGeometry(image,&mng_info->page);
12692 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12693 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12705 mng_info->page=image->page;
12706 need_geom=MagickTrue;
12707 if (mng_info->page.width || mng_info->page.height)
12708 need_geom=MagickFalse;
12710 Check all the scenes.
12712 initial_delay=image->delay;
12713 need_iterations=MagickFalse;
12714 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12715 mng_info->equal_physs=MagickTrue,
12716 mng_info->equal_gammas=MagickTrue;
12717 mng_info->equal_srgbs=MagickTrue;
12718 mng_info->equal_backgrounds=MagickTrue;
12720 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12721 defined(PNG_MNG_FEATURES_SUPPORTED)
12722 all_images_are_gray=MagickTrue;
12723 mng_info->equal_palettes=MagickFalse;
12724 need_local_plte=MagickFalse;
12726 for (next_image=image; next_image != (Image *) NULL; )
12730 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12731 mng_info->page.width=next_image->columns+next_image->page.x;
12733 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12734 mng_info->page.height=next_image->rows+next_image->page.y;
12737 if (next_image->page.x || next_image->page.y)
12738 need_defi=MagickTrue;
12740 if (next_image->alpha_trait)
12741 need_matte=MagickTrue;
12743 if ((int) next_image->dispose >= BackgroundDispose)
12744 if (next_image->alpha_trait || next_image->page.x || next_image->page.y ||
12745 ((next_image->columns < mng_info->page.width) &&
12746 (next_image->rows < mng_info->page.height)))
12747 mng_info->need_fram=MagickTrue;
12749 if (next_image->iterations)
12750 need_iterations=MagickTrue;
12752 final_delay=next_image->delay;
12754 if (final_delay != initial_delay || final_delay > 1UL*
12755 next_image->ticks_per_second)
12756 mng_info->need_fram=1;
12758 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12759 defined(PNG_MNG_FEATURES_SUPPORTED)
12761 check for global palette possibility.
12763 if (image->alpha_trait == BlendPixelTrait)
12764 need_local_plte=MagickTrue;
12766 if (need_local_plte == 0)
12768 if (IsImageGray(image,exception) == MagickFalse)
12769 all_images_are_gray=MagickFalse;
12770 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12771 if (use_global_plte == 0)
12772 use_global_plte=mng_info->equal_palettes;
12773 need_local_plte=!mng_info->equal_palettes;
12776 if (GetNextImageInList(next_image) != (Image *) NULL)
12778 if (next_image->background_color.red !=
12779 next_image->next->background_color.red ||
12780 next_image->background_color.green !=
12781 next_image->next->background_color.green ||
12782 next_image->background_color.blue !=
12783 next_image->next->background_color.blue)
12784 mng_info->equal_backgrounds=MagickFalse;
12786 if (next_image->gamma != next_image->next->gamma)
12787 mng_info->equal_gammas=MagickFalse;
12789 if (next_image->rendering_intent !=
12790 next_image->next->rendering_intent)
12791 mng_info->equal_srgbs=MagickFalse;
12793 if ((next_image->units != next_image->next->units) ||
12794 (next_image->resolution.x != next_image->next->resolution.x) ||
12795 (next_image->resolution.y != next_image->next->resolution.y))
12796 mng_info->equal_physs=MagickFalse;
12798 if (mng_info->equal_chrms)
12800 if (next_image->chromaticity.red_primary.x !=
12801 next_image->next->chromaticity.red_primary.x ||
12802 next_image->chromaticity.red_primary.y !=
12803 next_image->next->chromaticity.red_primary.y ||
12804 next_image->chromaticity.green_primary.x !=
12805 next_image->next->chromaticity.green_primary.x ||
12806 next_image->chromaticity.green_primary.y !=
12807 next_image->next->chromaticity.green_primary.y ||
12808 next_image->chromaticity.blue_primary.x !=
12809 next_image->next->chromaticity.blue_primary.x ||
12810 next_image->chromaticity.blue_primary.y !=
12811 next_image->next->chromaticity.blue_primary.y ||
12812 next_image->chromaticity.white_point.x !=
12813 next_image->next->chromaticity.white_point.x ||
12814 next_image->chromaticity.white_point.y !=
12815 next_image->next->chromaticity.white_point.y)
12816 mng_info->equal_chrms=MagickFalse;
12820 next_image=GetNextImageInList(next_image);
12822 if (image_count < 2)
12824 mng_info->equal_backgrounds=MagickFalse;
12825 mng_info->equal_chrms=MagickFalse;
12826 mng_info->equal_gammas=MagickFalse;
12827 mng_info->equal_srgbs=MagickFalse;
12828 mng_info->equal_physs=MagickFalse;
12829 use_global_plte=MagickFalse;
12830 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12831 need_local_plte=MagickTrue;
12833 need_iterations=MagickFalse;
12836 if (mng_info->need_fram == MagickFalse)
12839 Only certain framing rates 100/n are exactly representable without
12840 the FRAM chunk but we'll allow some slop in VLC files
12842 if (final_delay == 0)
12844 if (need_iterations != MagickFalse)
12847 It's probably a GIF with loop; don't run it *too* fast.
12849 if (mng_info->adjoin)
12852 (void) ThrowMagickException(exception,GetMagickModule(),
12854 "input has zero delay between all frames; assuming",
12859 mng_info->ticks_per_second=0;
12861 if (final_delay != 0)
12862 mng_info->ticks_per_second=(png_uint_32)
12863 (image->ticks_per_second/final_delay);
12864 if (final_delay > 50)
12865 mng_info->ticks_per_second=2;
12867 if (final_delay > 75)
12868 mng_info->ticks_per_second=1;
12870 if (final_delay > 125)
12871 mng_info->need_fram=MagickTrue;
12873 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12874 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12875 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12876 1UL*image->ticks_per_second))
12877 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12880 if (mng_info->need_fram != MagickFalse)
12881 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12883 If pseudocolor, we should also check to see if all the
12884 palettes are identical and write a global PLTE if they are.
12888 Write the MNG version 1.0 signature and MHDR chunk.
12890 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12891 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12892 PNGType(chunk,mng_MHDR);
12893 LogPNGChunk(logging,mng_MHDR,28L);
12894 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12895 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12896 PNGLong(chunk+12,mng_info->ticks_per_second);
12897 PNGLong(chunk+16,0L); /* layer count=unknown */
12898 PNGLong(chunk+20,0L); /* frame count=unknown */
12899 PNGLong(chunk+24,0L); /* play time=unknown */
12904 if (need_defi || mng_info->need_fram || use_global_plte)
12905 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12908 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12913 if (need_defi || mng_info->need_fram || use_global_plte)
12914 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12917 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12925 if (need_defi || mng_info->need_fram || use_global_plte)
12926 PNGLong(chunk+28,11L); /* simplicity=LC */
12929 PNGLong(chunk+28,9L); /* simplicity=VLC */
12934 if (need_defi || mng_info->need_fram || use_global_plte)
12935 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12938 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12941 (void) WriteBlob(image,32,chunk);
12942 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12943 option=GetImageOption(image_info,"mng:need-cacheoff");
12944 if (option != (const char *) NULL)
12950 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12952 PNGType(chunk,mng_nEED);
12953 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12954 (void) WriteBlobMSBULong(image,(size_t) length);
12955 LogPNGChunk(logging,mng_nEED,(size_t) length);
12957 (void) WriteBlob(image,length,chunk);
12958 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12960 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12961 (GetNextImageInList(image) != (Image *) NULL) &&
12962 (image->iterations != 1))
12965 Write MNG TERM chunk
12967 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12968 PNGType(chunk,mng_TERM);
12969 LogPNGChunk(logging,mng_TERM,10L);
12970 chunk[4]=3; /* repeat animation */
12971 chunk[5]=0; /* show last frame when done */
12972 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12973 final_delay/MagickMax(image->ticks_per_second,1)));
12975 if (image->iterations == 0)
12976 PNGLong(chunk+10,PNG_UINT_31_MAX);
12979 PNGLong(chunk+10,(png_uint_32) image->iterations);
12981 if (logging != MagickFalse)
12983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12985 final_delay/MagickMax(image->ticks_per_second,1)));
12987 if (image->iterations == 0)
12988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12989 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12993 " Image iterations: %.20g",(double) image->iterations);
12995 (void) WriteBlob(image,14,chunk);
12996 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12999 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13001 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13002 mng_info->equal_srgbs)
13005 Write MNG sRGB chunk
13007 (void) WriteBlobMSBULong(image,1L);
13008 PNGType(chunk,mng_sRGB);
13009 LogPNGChunk(logging,mng_sRGB,1L);
13011 if (image->rendering_intent != UndefinedIntent)
13012 chunk[4]=(unsigned char)
13013 Magick_RenderingIntent_to_PNG_RenderingIntent(
13014 (image->rendering_intent));
13017 chunk[4]=(unsigned char)
13018 Magick_RenderingIntent_to_PNG_RenderingIntent(
13019 (PerceptualIntent));
13021 (void) WriteBlob(image,5,chunk);
13022 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13023 mng_info->have_write_global_srgb=MagickTrue;
13028 if (image->gamma && mng_info->equal_gammas)
13031 Write MNG gAMA chunk
13033 (void) WriteBlobMSBULong(image,4L);
13034 PNGType(chunk,mng_gAMA);
13035 LogPNGChunk(logging,mng_gAMA,4L);
13036 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13037 (void) WriteBlob(image,8,chunk);
13038 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13039 mng_info->have_write_global_gama=MagickTrue;
13041 if (mng_info->equal_chrms)
13047 Write MNG cHRM chunk
13049 (void) WriteBlobMSBULong(image,32L);
13050 PNGType(chunk,mng_cHRM);
13051 LogPNGChunk(logging,mng_cHRM,32L);
13052 primary=image->chromaticity.white_point;
13053 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13054 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13055 primary=image->chromaticity.red_primary;
13056 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13057 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13058 primary=image->chromaticity.green_primary;
13059 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13060 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13061 primary=image->chromaticity.blue_primary;
13062 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13063 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13064 (void) WriteBlob(image,36,chunk);
13065 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13066 mng_info->have_write_global_chrm=MagickTrue;
13069 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13072 Write MNG pHYs chunk
13074 (void) WriteBlobMSBULong(image,9L);
13075 PNGType(chunk,mng_pHYs);
13076 LogPNGChunk(logging,mng_pHYs,9L);
13078 if (image->units == PixelsPerInchResolution)
13080 PNGLong(chunk+4,(png_uint_32)
13081 (image->resolution.x*100.0/2.54+0.5));
13083 PNGLong(chunk+8,(png_uint_32)
13084 (image->resolution.y*100.0/2.54+0.5));
13091 if (image->units == PixelsPerCentimeterResolution)
13093 PNGLong(chunk+4,(png_uint_32)
13094 (image->resolution.x*100.0+0.5));
13096 PNGLong(chunk+8,(png_uint_32)
13097 (image->resolution.y*100.0+0.5));
13104 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13105 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13109 (void) WriteBlob(image,13,chunk);
13110 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13113 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13114 or does not cover the entire frame.
13116 if (write_mng && (image->alpha_trait || image->page.x > 0 ||
13117 image->page.y > 0 || (image->page.width &&
13118 (image->page.width+image->page.x < mng_info->page.width))
13119 || (image->page.height && (image->page.height+image->page.y
13120 < mng_info->page.height))))
13122 (void) WriteBlobMSBULong(image,6L);
13123 PNGType(chunk,mng_BACK);
13124 LogPNGChunk(logging,mng_BACK,6L);
13125 red=ScaleQuantumToShort(image->background_color.red);
13126 green=ScaleQuantumToShort(image->background_color.green);
13127 blue=ScaleQuantumToShort(image->background_color.blue);
13128 PNGShort(chunk+4,red);
13129 PNGShort(chunk+6,green);
13130 PNGShort(chunk+8,blue);
13131 (void) WriteBlob(image,10,chunk);
13132 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13133 if (mng_info->equal_backgrounds)
13135 (void) WriteBlobMSBULong(image,6L);
13136 PNGType(chunk,mng_bKGD);
13137 LogPNGChunk(logging,mng_bKGD,6L);
13138 (void) WriteBlob(image,10,chunk);
13139 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13143 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13144 if ((need_local_plte == MagickFalse) &&
13145 (image->storage_class == PseudoClass) &&
13146 (all_images_are_gray == MagickFalse))
13152 Write MNG PLTE chunk
13154 data_length=3*image->colors;
13155 (void) WriteBlobMSBULong(image,data_length);
13156 PNGType(chunk,mng_PLTE);
13157 LogPNGChunk(logging,mng_PLTE,data_length);
13159 for (i=0; i < (ssize_t) image->colors; i++)
13161 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13162 image->colormap[i].red) & 0xff);
13163 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13164 image->colormap[i].green) & 0xff);
13165 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13166 image->colormap[i].blue) & 0xff);
13169 (void) WriteBlob(image,data_length+4,chunk);
13170 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13171 mng_info->have_write_global_plte=MagickTrue;
13177 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13178 defined(PNG_MNG_FEATURES_SUPPORTED)
13179 mng_info->equal_palettes=MagickFalse;
13183 if (mng_info->adjoin)
13185 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13186 defined(PNG_MNG_FEATURES_SUPPORTED)
13188 If we aren't using a global palette for the entire MNG, check to
13189 see if we can use one for two or more consecutive images.
13191 if (need_local_plte && use_global_plte && !all_images_are_gray)
13193 if (mng_info->IsPalette)
13196 When equal_palettes is true, this image has the same palette
13197 as the previous PseudoClass image
13199 mng_info->have_write_global_plte=mng_info->equal_palettes;
13200 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13201 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13204 Write MNG PLTE chunk
13209 data_length=3*image->colors;
13210 (void) WriteBlobMSBULong(image,data_length);
13211 PNGType(chunk,mng_PLTE);
13212 LogPNGChunk(logging,mng_PLTE,data_length);
13214 for (i=0; i < (ssize_t) image->colors; i++)
13216 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13217 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13218 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13221 (void) WriteBlob(image,data_length+4,chunk);
13222 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13223 (uInt) (data_length+4)));
13224 mng_info->have_write_global_plte=MagickTrue;
13228 mng_info->have_write_global_plte=MagickFalse;
13239 previous_x=mng_info->page.x;
13240 previous_y=mng_info->page.y;
13247 mng_info->page=image->page;
13248 if ((mng_info->page.x != previous_x) ||
13249 (mng_info->page.y != previous_y))
13251 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13252 PNGType(chunk,mng_DEFI);
13253 LogPNGChunk(logging,mng_DEFI,12L);
13254 chunk[4]=0; /* object 0 MSB */
13255 chunk[5]=0; /* object 0 LSB */
13256 chunk[6]=0; /* visible */
13257 chunk[7]=0; /* abstract */
13258 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13259 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13260 (void) WriteBlob(image,16,chunk);
13261 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13266 mng_info->write_mng=write_mng;
13268 if ((int) image->dispose >= 3)
13269 mng_info->framing_mode=3;
13271 if (mng_info->need_fram && mng_info->adjoin &&
13272 ((image->delay != mng_info->delay) ||
13273 (mng_info->framing_mode != mng_info->old_framing_mode)))
13275 if (image->delay == mng_info->delay)
13278 Write a MNG FRAM chunk with the new framing mode.
13280 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13281 PNGType(chunk,mng_FRAM);
13282 LogPNGChunk(logging,mng_FRAM,1L);
13283 chunk[4]=(unsigned char) mng_info->framing_mode;
13284 (void) WriteBlob(image,5,chunk);
13285 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13290 Write a MNG FRAM chunk with the delay.
13292 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13293 PNGType(chunk,mng_FRAM);
13294 LogPNGChunk(logging,mng_FRAM,10L);
13295 chunk[4]=(unsigned char) mng_info->framing_mode;
13296 chunk[5]=0; /* frame name separator (no name) */
13297 chunk[6]=2; /* flag for changing default delay */
13298 chunk[7]=0; /* flag for changing frame timeout */
13299 chunk[8]=0; /* flag for changing frame clipping */
13300 chunk[9]=0; /* flag for changing frame sync_id */
13301 PNGLong(chunk+10,(png_uint_32)
13302 ((mng_info->ticks_per_second*
13303 image->delay)/MagickMax(image->ticks_per_second,1)));
13304 (void) WriteBlob(image,14,chunk);
13305 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13306 mng_info->delay=(png_uint_32) image->delay;
13308 mng_info->old_framing_mode=mng_info->framing_mode;
13311 #if defined(JNG_SUPPORTED)
13312 if (image_info->compression == JPEGCompression)
13317 if (logging != MagickFalse)
13318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13319 " Writing JNG object.");
13320 /* To do: specify the desired alpha compression method. */
13321 write_info=CloneImageInfo(image_info);
13322 write_info->compression=UndefinedCompression;
13323 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13324 write_info=DestroyImageInfo(write_info);
13329 if (logging != MagickFalse)
13330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13331 " Writing PNG object.");
13333 mng_info->need_blob = MagickFalse;
13334 mng_info->ping_preserve_colormap = MagickFalse;
13336 /* We don't want any ancillary chunks written */
13337 mng_info->ping_exclude_bKGD=MagickTrue;
13338 mng_info->ping_exclude_cHRM=MagickTrue;
13339 mng_info->ping_exclude_date=MagickTrue;
13340 mng_info->ping_exclude_EXIF=MagickTrue;
13341 mng_info->ping_exclude_gAMA=MagickTrue;
13342 mng_info->ping_exclude_iCCP=MagickTrue;
13343 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13344 mng_info->ping_exclude_oFFs=MagickTrue;
13345 mng_info->ping_exclude_pHYs=MagickTrue;
13346 mng_info->ping_exclude_sRGB=MagickTrue;
13347 mng_info->ping_exclude_tEXt=MagickTrue;
13348 mng_info->ping_exclude_tRNS=MagickTrue;
13349 mng_info->ping_exclude_vpAg=MagickTrue;
13350 mng_info->ping_exclude_zCCP=MagickTrue;
13351 mng_info->ping_exclude_zTXt=MagickTrue;
13353 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13356 if (status == MagickFalse)
13358 MngInfoFreeStruct(mng_info,&have_mng_structure);
13359 (void) CloseBlob(image);
13360 return(MagickFalse);
13362 (void) CatchImageException(image);
13363 if (GetNextImageInList(image) == (Image *) NULL)
13365 image=SyncNextImageInList(image);
13366 status=SetImageProgress(image,SaveImagesTag,scene++,
13367 GetImageListLength(image));
13369 if (status == MagickFalse)
13372 } while (mng_info->adjoin);
13376 while (GetPreviousImageInList(image) != (Image *) NULL)
13377 image=GetPreviousImageInList(image);
13379 Write the MEND chunk.
13381 (void) WriteBlobMSBULong(image,0x00000000L);
13382 PNGType(chunk,mng_MEND);
13383 LogPNGChunk(logging,mng_MEND,0L);
13384 (void) WriteBlob(image,4,chunk);
13385 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13388 Relinquish resources.
13390 (void) CloseBlob(image);
13391 MngInfoFreeStruct(mng_info,&have_mng_structure);
13393 if (logging != MagickFalse)
13394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13396 return(MagickTrue);
13398 #else /* PNG_LIBPNG_VER > 10011 */
13400 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13403 printf("Your PNG library is too old: You have libpng-%s\n",
13404 PNG_LIBPNG_VER_STRING);
13406 ThrowBinaryException(CoderError,"PNG library is too old",
13407 image_info->filename);
13410 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13412 return(WritePNGImage(image_info,image));
13414 #endif /* PNG_LIBPNG_VER > 10011 */