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++)
3167 row_offset=ping_rowbytes*y;
3172 png_read_row(ping,ping_pixels+row_offset,NULL);
3174 if (pass < num_passes-1)
3177 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3179 if (q == (Quantum *) NULL)
3182 p=ping_pixels+row_offset;
3185 switch (ping_bit_depth)
3189 if (ping_color_type == 4)
3190 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3193 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3194 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3195 found_transparent_pixel = MagickTrue;
3196 q+=GetPixelChannels(image);
3200 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3208 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3210 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3214 if (image->colors > 256)
3215 quantum=((*p++) << 8);
3221 *r=ScaleShortToQuantum(quantum);
3224 if (ping_color_type == 4)
3226 if (image->colors > 256)
3227 quantum=((*p++) << 8);
3232 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3233 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3234 found_transparent_pixel = MagickTrue;
3235 q+=GetPixelChannels(image);
3238 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3240 p++; /* strip low byte */
3242 if (ping_color_type == 4)
3244 SetPixelAlpha(image,*p++,q);
3245 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3246 found_transparent_pixel = MagickTrue;
3248 q+=GetPixelChannels(image);
3261 Transfer image scanline.
3265 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3267 if (q == (Quantum *) NULL)
3269 for (x=0; x < (ssize_t) image->columns; x++)
3271 SetPixelIndex(image,*r++,q);
3272 q+=GetPixelChannels(image);
3275 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3278 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3280 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3283 if (status == MagickFalse)
3288 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3290 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3292 if (status == MagickFalse)
3296 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3299 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3300 UndefinedPixelTrait;
3302 if (logging != MagickFalse)
3304 if (found_transparent_pixel != MagickFalse)
3305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3306 " Found transparent pixel");
3309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3310 " No transparent pixel was found");
3312 ping_color_type&=0x03;
3317 if (quantum_info != (QuantumInfo *) NULL)
3318 quantum_info=DestroyQuantumInfo(quantum_info);
3320 if (image->storage_class == PseudoClass)
3325 alpha_trait=image->alpha_trait;
3326 image->alpha_trait=UndefinedPixelTrait;
3327 (void) SyncImage(image,exception);
3328 image->alpha_trait=alpha_trait;
3331 png_read_end(ping,end_info);
3333 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3334 (ssize_t) image_info->first_scene && image->delay != 0)
3336 png_destroy_read_struct(&ping,&ping_info,&end_info);
3337 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3339 (void) SetImageBackgroundColor(image,exception);
3340 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3341 UnlockSemaphoreInfo(ping_semaphore);
3343 if (logging != MagickFalse)
3344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3345 " exit ReadOnePNGImage() early.");
3349 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3355 Image has a transparent background.
3357 storage_class=image->storage_class;
3358 image->alpha_trait=BlendPixelTrait;
3360 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3362 if (storage_class == PseudoClass)
3364 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3366 for (x=0; x < ping_num_trans; x++)
3368 image->colormap[x].alpha_trait=BlendPixelTrait;
3369 image->colormap[x].alpha =
3370 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3374 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3376 for (x=0; x < (int) image->colors; x++)
3378 if (ScaleQuantumToShort(image->colormap[x].red) ==
3379 transparent_color.alpha)
3381 image->colormap[x].alpha_trait=BlendPixelTrait;
3382 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3386 (void) SyncImage(image,exception);
3389 #if 1 /* Should have already been done above, but glennrp problem P10
3394 for (y=0; y < (ssize_t) image->rows; y++)
3396 image->storage_class=storage_class;
3397 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3399 if (q == (Quantum *) NULL)
3403 /* Caution: on a Q8 build, this does not distinguish between
3404 * 16-bit colors that differ only in the low byte
3406 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3408 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3409 transparent_color.red &&
3410 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3411 transparent_color.green &&
3412 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3413 transparent_color.blue)
3415 SetPixelAlpha(image,TransparentAlpha,q);
3418 #if 0 /* I have not found a case where this is needed. */
3421 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3425 q+=GetPixelChannels(image);
3428 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3434 image->storage_class=DirectClass;
3437 for (j = 0; j < 2; j++)
3440 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3441 MagickTrue : MagickFalse;
3443 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3444 MagickTrue : MagickFalse;
3446 if (status != MagickFalse)
3447 for (i=0; i < (ssize_t) num_text; i++)
3449 /* Check for a profile */
3451 if (logging != MagickFalse)
3452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3453 " Reading PNG text chunk");
3455 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3457 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3467 length=text[i].text_length;
3468 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3470 if (value == (char *) NULL)
3472 png_error(ping,"Memory allocation failed");
3476 (void) ConcatenateMagickString(value,text[i].text,length+2);
3478 /* Don't save "density" or "units" property if we have a pHYs
3481 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3482 (LocaleCompare(text[i].key,"density") != 0 &&
3483 LocaleCompare(text[i].key,"units") != 0))
3484 (void) SetImageProperty(image,text[i].key,value,exception);
3486 if (logging != MagickFalse)
3488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3489 " length: %lu",(unsigned long) length);
3490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3491 " Keyword: %s",text[i].key);
3494 value=DestroyString(value);
3497 num_text_total += num_text;
3500 #ifdef MNG_OBJECT_BUFFERS
3502 Store the object if necessary.
3504 if (object_id && !mng_info->frozen[object_id])
3506 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3509 create a new object buffer.
3511 mng_info->ob[object_id]=(MngBuffer *)
3512 AcquireMagickMemory(sizeof(MngBuffer));
3514 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3516 mng_info->ob[object_id]->image=(Image *) NULL;
3517 mng_info->ob[object_id]->reference_count=1;
3521 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3522 mng_info->ob[object_id]->frozen)
3524 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3525 png_error(ping,"Memory allocation failed");
3527 if (mng_info->ob[object_id]->frozen)
3528 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3534 if (mng_info->ob[object_id]->image != (Image *) NULL)
3535 mng_info->ob[object_id]->image=DestroyImage
3536 (mng_info->ob[object_id]->image);
3538 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3541 if (mng_info->ob[object_id]->image != (Image *) NULL)
3542 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3545 png_error(ping, "Cloning image for object buffer failed");
3547 if (ping_width > 250000L || ping_height > 250000L)
3548 png_error(ping,"PNG Image dimensions are too large.");
3550 mng_info->ob[object_id]->width=ping_width;
3551 mng_info->ob[object_id]->height=ping_height;
3552 mng_info->ob[object_id]->color_type=ping_color_type;
3553 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3554 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3555 mng_info->ob[object_id]->compression_method=
3556 ping_compression_method;
3557 mng_info->ob[object_id]->filter_method=ping_filter_method;
3559 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3565 Copy the PLTE to the object buffer.
3567 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3568 mng_info->ob[object_id]->plte_length=number_colors;
3570 for (i=0; i < number_colors; i++)
3572 mng_info->ob[object_id]->plte[i]=plte[i];
3577 mng_info->ob[object_id]->plte_length=0;
3582 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3583 * alpha or if a valid tRNS chunk is present, no matter whether there
3584 * is actual transparency present.
3586 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3587 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3588 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3589 BlendPixelTrait : UndefinedPixelTrait;
3591 /* Set more properties for identify to retrieve */
3596 if (num_text_total != 0)
3598 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3599 (void) FormatLocaleString(msg,MaxTextExtent,
3600 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3601 (void) SetImageProperty(image,"png:text ",msg,
3605 if (num_raw_profiles != 0)
3607 (void) FormatLocaleString(msg,MaxTextExtent,
3608 "%d were found", num_raw_profiles);
3609 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3613 if (ping_found_cHRM != MagickFalse)
3615 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3616 "chunk was found (see Chromaticity, above)");
3617 (void) SetImageProperty(image,"png:cHRM ",msg,
3621 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3623 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3624 "chunk was found (see Background color, above)");
3625 (void) SetImageProperty(image,"png:bKGD ",msg,
3629 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3632 #if defined(PNG_iCCP_SUPPORTED)
3633 if (ping_found_iCCP != MagickFalse)
3634 (void) SetImageProperty(image,"png:iCCP ",msg,
3638 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3639 (void) SetImageProperty(image,"png:tRNS ",msg,
3642 #if defined(PNG_sRGB_SUPPORTED)
3643 if (ping_found_sRGB != MagickFalse)
3645 (void) FormatLocaleString(msg,MaxTextExtent,
3648 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3649 (void) SetImageProperty(image,"png:sRGB ",msg,
3654 if (ping_found_gAMA != MagickFalse)
3656 (void) FormatLocaleString(msg,MaxTextExtent,
3657 "gamma=%.8g (See Gamma, above)",
3659 (void) SetImageProperty(image,"png:gAMA ",msg,
3663 #if defined(PNG_pHYs_SUPPORTED)
3664 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3666 (void) FormatLocaleString(msg,MaxTextExtent,
3667 "x_res=%.10g, y_res=%.10g, units=%d",
3668 (double) x_resolution,(double) y_resolution, unit_type);
3669 (void) SetImageProperty(image,"png:pHYs ",msg,
3674 #if defined(PNG_oFFs_SUPPORTED)
3675 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3677 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3678 (double) image->page.x,(double) image->page.y);
3679 (void) SetImageProperty(image,"png:oFFs ",msg,
3684 if ((image->page.width != 0 && image->page.width != image->columns) ||
3685 (image->page.height != 0 && image->page.height != image->rows))
3687 (void) FormatLocaleString(msg,MaxTextExtent,
3688 "width=%.20g, height=%.20g",
3689 (double) image->page.width,(double) image->page.height);
3690 (void) SetImageProperty(image,"png:vpAg ",msg,
3696 Relinquish resources.
3698 png_destroy_read_struct(&ping,&ping_info,&end_info);
3700 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3702 if (logging != MagickFalse)
3703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3704 " exit ReadOnePNGImage()");
3706 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3707 UnlockSemaphoreInfo(ping_semaphore);
3710 /* } for navigation to beginning of SETJMP-protected block, revert to
3711 * Throwing an Exception when an error occurs.
3716 /* end of reading one PNG image */
3719 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3734 magic_number[MaxTextExtent];
3742 assert(image_info != (const ImageInfo *) NULL);
3743 assert(image_info->signature == MagickSignature);
3745 if (image_info->debug != MagickFalse)
3746 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3747 image_info->filename);
3749 assert(exception != (ExceptionInfo *) NULL);
3750 assert(exception->signature == MagickSignature);
3751 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3752 image=AcquireImage(image_info,exception);
3753 mng_info=(MngInfo *) NULL;
3754 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3756 if (status == MagickFalse)
3757 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3760 Verify PNG signature.
3762 count=ReadBlob(image,8,(unsigned char *) magic_number);
3764 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3765 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3768 Allocate a MngInfo structure.
3770 have_mng_structure=MagickFalse;
3771 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3773 if (mng_info == (MngInfo *) NULL)
3774 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3777 Initialize members of the MngInfo structure.
3779 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3780 mng_info->image=image;
3781 have_mng_structure=MagickTrue;
3784 image=ReadOnePNGImage(mng_info,image_info,exception);
3785 MngInfoFreeStruct(mng_info,&have_mng_structure);
3787 if (image == (Image *) NULL)
3789 if (previous != (Image *) NULL)
3791 if (previous->signature != MagickSignature)
3792 ThrowReaderException(CorruptImageError,"CorruptImage");
3794 (void) CloseBlob(previous);
3795 (void) DestroyImageList(previous);
3798 if (logging != MagickFalse)
3799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3800 "exit ReadPNGImage() with error");
3802 return((Image *) NULL);
3805 (void) CloseBlob(image);
3807 if ((image->columns == 0) || (image->rows == 0))
3809 if (logging != MagickFalse)
3810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3811 "exit ReadPNGImage() with error.");
3813 ThrowReaderException(CorruptImageError,"CorruptImage");
3816 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3817 ((image->gamma < .45) || (image->gamma > .46)))
3818 SetImageColorspace(image,RGBColorspace,exception);
3820 if ((LocaleCompare(image_info->magick,"PNG24") == 0) ||
3821 (LocaleCompare(image_info->magick,"PNG48") == 0))
3823 (void) SetImageType(image,TrueColorType,exception);
3824 image->alpha_trait=UndefinedPixelTrait;
3827 if ((LocaleCompare(image_info->magick,"PNG32") == 0) ||
3828 (LocaleCompare(image_info->magick,"PNG64") == 0))
3829 (void) SetImageType(image,TrueColorMatteType,exception);
3831 if (logging != MagickFalse)
3832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3833 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3834 (double) image->page.width,(double) image->page.height,
3835 (double) image->page.x,(double) image->page.y);
3837 if (logging != MagickFalse)
3838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3845 #if defined(JNG_SUPPORTED)
3847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851 % R e a d O n e J N G I m a g e %
3855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3857 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3858 % (minus the 8-byte signature) and returns it. It allocates the memory
3859 % necessary for the new Image structure and returns a pointer to the new
3862 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3864 % The format of the ReadOneJNGImage method is:
3866 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3867 % ExceptionInfo *exception)
3869 % A description of each parameter follows:
3871 % o mng_info: Specifies a pointer to a MngInfo structure.
3873 % o image_info: the image info.
3875 % o exception: return any errors or warnings in this structure.
3878 static Image *ReadOneJNGImage(MngInfo *mng_info,
3879 const ImageInfo *image_info, ExceptionInfo *exception)
3906 jng_image_sample_depth,
3907 jng_image_compression_method,
3908 jng_image_interlace_method,
3909 jng_alpha_sample_depth,
3910 jng_alpha_compression_method,
3911 jng_alpha_filter_method,
3912 jng_alpha_interlace_method;
3914 register const Quantum
3924 register unsigned char
3935 jng_alpha_compression_method=0;
3936 jng_alpha_sample_depth=8;
3940 alpha_image=(Image *) NULL;
3941 color_image=(Image *) NULL;
3942 alpha_image_info=(ImageInfo *) NULL;
3943 color_image_info=(ImageInfo *) NULL;
3945 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3946 " Enter ReadOneJNGImage()");
3948 image=mng_info->image;
3950 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3953 Allocate next image structure.
3955 if (logging != MagickFalse)
3956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " AcquireNextImage()");
3959 AcquireNextImage(image_info,image,exception);
3961 if (GetNextImageInList(image) == (Image *) NULL)
3962 return((Image *) NULL);
3964 image=SyncNextImageInList(image);
3966 mng_info->image=image;
3969 Signature bytes have already been read.
3972 read_JSEP=MagickFalse;
3973 reading_idat=MagickFalse;
3974 skip_to_iend=MagickFalse;
3978 type[MaxTextExtent];
3987 Read a new JNG chunk.
3989 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3990 2*GetBlobSize(image));
3992 if (status == MagickFalse)
3996 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3997 length=ReadBlobMSBLong(image);
3998 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4000 if (logging != MagickFalse)
4001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4003 type[0],type[1],type[2],type[3],(double) length);
4005 if (length > PNG_UINT_31_MAX || count == 0)
4006 ThrowReaderException(CorruptImageError,"CorruptImage");
4009 chunk=(unsigned char *) NULL;
4013 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4015 if (chunk == (unsigned char *) NULL)
4016 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4018 for (i=0; i < (ssize_t) length; i++)
4019 chunk[i]=(unsigned char) ReadBlobByte(image);
4024 (void) ReadBlobMSBLong(image); /* read crc word */
4029 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4034 if (memcmp(type,mng_JHDR,4) == 0)
4038 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4039 (p[2] << 8) | p[3]);
4040 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4041 (p[6] << 8) | p[7]);
4042 jng_color_type=p[8];
4043 jng_image_sample_depth=p[9];
4044 jng_image_compression_method=p[10];
4045 jng_image_interlace_method=p[11];
4047 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4050 jng_alpha_sample_depth=p[12];
4051 jng_alpha_compression_method=p[13];
4052 jng_alpha_filter_method=p[14];
4053 jng_alpha_interlace_method=p[15];
4055 if (logging != MagickFalse)
4057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4058 " jng_width: %16lu",(unsigned long) jng_width);
4060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061 " jng_width: %16lu",(unsigned long) jng_height);
4063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4064 " jng_color_type: %16d",jng_color_type);
4066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4067 " jng_image_sample_depth: %3d",
4068 jng_image_sample_depth);
4070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4071 " jng_image_compression_method:%3d",
4072 jng_image_compression_method);
4074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4075 " jng_image_interlace_method: %3d",
4076 jng_image_interlace_method);
4078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4079 " jng_alpha_sample_depth: %3d",
4080 jng_alpha_sample_depth);
4082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4083 " jng_alpha_compression_method:%3d",
4084 jng_alpha_compression_method);
4086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4087 " jng_alpha_filter_method: %3d",
4088 jng_alpha_filter_method);
4090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4091 " jng_alpha_interlace_method: %3d",
4092 jng_alpha_interlace_method);
4097 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4103 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4104 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4105 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4108 o create color_image
4109 o open color_blob, attached to color_image
4110 o if (color type has alpha)
4111 open alpha_blob, attached to alpha_image
4114 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4116 if (color_image_info == (ImageInfo *) NULL)
4117 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4119 GetImageInfo(color_image_info);
4120 color_image=AcquireImage(color_image_info,exception);
4122 if (color_image == (Image *) NULL)
4123 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4125 if (logging != MagickFalse)
4126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4127 " Creating color_blob.");
4129 (void) AcquireUniqueFilename(color_image->filename);
4130 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4133 if (status == MagickFalse)
4134 return((Image *) NULL);
4136 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4138 alpha_image_info=(ImageInfo *)
4139 AcquireMagickMemory(sizeof(ImageInfo));
4141 if (alpha_image_info == (ImageInfo *) NULL)
4142 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4144 GetImageInfo(alpha_image_info);
4145 alpha_image=AcquireImage(alpha_image_info,exception);
4147 if (alpha_image == (Image *) NULL)
4149 alpha_image=DestroyImage(alpha_image);
4150 ThrowReaderException(ResourceLimitError,
4151 "MemoryAllocationFailed");
4154 if (logging != MagickFalse)
4155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4156 " Creating alpha_blob.");
4158 (void) AcquireUniqueFilename(alpha_image->filename);
4159 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4162 if (status == MagickFalse)
4163 return((Image *) NULL);
4165 if (jng_alpha_compression_method == 0)
4170 if (logging != MagickFalse)
4171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4172 " Writing IHDR chunk to alpha_blob.");
4174 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4175 "\211PNG\r\n\032\n");
4177 (void) WriteBlobMSBULong(alpha_image,13L);
4178 PNGType(data,mng_IHDR);
4179 LogPNGChunk(logging,mng_IHDR,13L);
4180 PNGLong(data+4,jng_width);
4181 PNGLong(data+8,jng_height);
4182 data[12]=jng_alpha_sample_depth;
4183 data[13]=0; /* color_type gray */
4184 data[14]=0; /* compression method 0 */
4185 data[15]=0; /* filter_method 0 */
4186 data[16]=0; /* interlace_method 0 */
4187 (void) WriteBlob(alpha_image,17,data);
4188 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4191 reading_idat=MagickTrue;
4194 if (memcmp(type,mng_JDAT,4) == 0)
4196 /* Copy chunk to color_image->blob */
4198 if (logging != MagickFalse)
4199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4200 " Copying JDAT chunk data to color_blob.");
4202 (void) WriteBlob(color_image,length,chunk);
4205 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4210 if (memcmp(type,mng_IDAT,4) == 0)
4215 /* Copy IDAT header and chunk data to alpha_image->blob */
4217 if (image_info->ping == MagickFalse)
4219 if (logging != MagickFalse)
4220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4221 " Copying IDAT chunk data to alpha_blob.");
4223 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4224 PNGType(data,mng_IDAT);
4225 LogPNGChunk(logging,mng_IDAT,length);
4226 (void) WriteBlob(alpha_image,4,data);
4227 (void) WriteBlob(alpha_image,length,chunk);
4228 (void) WriteBlobMSBULong(alpha_image,
4229 crc32(crc32(0,data,4),chunk,(uInt) length));
4233 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4238 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4240 /* Copy chunk data to alpha_image->blob */
4242 if (image_info->ping == MagickFalse)
4244 if (logging != MagickFalse)
4245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4246 " Copying JDAA chunk data to alpha_blob.");
4248 (void) WriteBlob(alpha_image,length,chunk);
4252 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4257 if (memcmp(type,mng_JSEP,4) == 0)
4259 read_JSEP=MagickTrue;
4262 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4267 if (memcmp(type,mng_bKGD,4) == 0)
4271 image->background_color.red=ScaleCharToQuantum(p[1]);
4272 image->background_color.green=image->background_color.red;
4273 image->background_color.blue=image->background_color.red;
4278 image->background_color.red=ScaleCharToQuantum(p[1]);
4279 image->background_color.green=ScaleCharToQuantum(p[3]);
4280 image->background_color.blue=ScaleCharToQuantum(p[5]);
4283 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4287 if (memcmp(type,mng_gAMA,4) == 0)
4290 image->gamma=((float) mng_get_long(p))*0.00001;
4292 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4296 if (memcmp(type,mng_cHRM,4) == 0)
4300 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4301 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4302 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4303 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4304 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4305 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4306 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4307 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4310 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4314 if (memcmp(type,mng_sRGB,4) == 0)
4318 image->rendering_intent=
4319 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4320 image->gamma=1.000f/2.200f;
4321 image->chromaticity.red_primary.x=0.6400f;
4322 image->chromaticity.red_primary.y=0.3300f;
4323 image->chromaticity.green_primary.x=0.3000f;
4324 image->chromaticity.green_primary.y=0.6000f;
4325 image->chromaticity.blue_primary.x=0.1500f;
4326 image->chromaticity.blue_primary.y=0.0600f;
4327 image->chromaticity.white_point.x=0.3127f;
4328 image->chromaticity.white_point.y=0.3290f;
4331 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4335 if (memcmp(type,mng_oFFs,4) == 0)
4339 image->page.x=(ssize_t) mng_get_long(p);
4340 image->page.y=(ssize_t) mng_get_long(&p[4]);
4342 if ((int) p[8] != 0)
4344 image->page.x/=10000;
4345 image->page.y/=10000;
4350 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4355 if (memcmp(type,mng_pHYs,4) == 0)
4359 image->resolution.x=(double) mng_get_long(p);
4360 image->resolution.y=(double) mng_get_long(&p[4]);
4361 if ((int) p[8] == PNG_RESOLUTION_METER)
4363 image->units=PixelsPerCentimeterResolution;
4364 image->resolution.x=image->resolution.x/100.0f;
4365 image->resolution.y=image->resolution.y/100.0f;
4369 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4374 if (memcmp(type,mng_iCCP,4) == 0)
4378 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4385 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4387 if (memcmp(type,mng_IEND,4))
4397 Finish up reading image data:
4399 o read main image from color_blob.
4403 o if (color_type has alpha)
4404 if alpha_encoding is PNG
4405 read secondary image from alpha_blob via ReadPNG
4406 if alpha_encoding is JPEG
4407 read secondary image from alpha_blob via ReadJPEG
4411 o copy intensity of secondary image into
4412 alpha samples of main image.
4414 o destroy the secondary image.
4417 (void) CloseBlob(color_image);
4419 if (logging != MagickFalse)
4420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4421 " Reading jng_image from color_blob.");
4423 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4424 color_image->filename);
4426 color_image_info->ping=MagickFalse; /* To do: avoid this */
4427 jng_image=ReadImage(color_image_info,exception);
4429 if (jng_image == (Image *) NULL)
4430 return((Image *) NULL);
4432 (void) RelinquishUniqueFileResource(color_image->filename);
4433 color_image=DestroyImage(color_image);
4434 color_image_info=DestroyImageInfo(color_image_info);
4436 if (jng_image == (Image *) NULL)
4437 return((Image *) NULL);
4439 if (logging != MagickFalse)
4440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4441 " Copying jng_image pixels to main image.");
4443 image->rows=jng_height;
4444 image->columns=jng_width;
4446 for (y=0; y < (ssize_t) image->rows; y++)
4448 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4449 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4450 for (x=(ssize_t) image->columns; x != 0; x--)
4452 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4453 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4454 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4455 q+=GetPixelChannels(image);
4456 s+=GetPixelChannels(jng_image);
4459 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4463 jng_image=DestroyImage(jng_image);
4465 if (image_info->ping == MagickFalse)
4467 if (jng_color_type >= 12)
4469 if (jng_alpha_compression_method == 0)
4473 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4474 PNGType(data,mng_IEND);
4475 LogPNGChunk(logging,mng_IEND,0L);
4476 (void) WriteBlob(alpha_image,4,data);
4477 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4480 (void) CloseBlob(alpha_image);
4482 if (logging != MagickFalse)
4483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4484 " Reading alpha from alpha_blob.");
4486 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4487 "%s",alpha_image->filename);
4489 jng_image=ReadImage(alpha_image_info,exception);
4491 if (jng_image != (Image *) NULL)
4492 for (y=0; y < (ssize_t) image->rows; y++)
4494 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4496 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4498 if (image->alpha_trait == BlendPixelTrait)
4499 for (x=(ssize_t) image->columns; x != 0; x--)
4501 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4502 q+=GetPixelChannels(image);
4503 s+=GetPixelChannels(jng_image);
4507 for (x=(ssize_t) image->columns; x != 0; x--)
4509 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4510 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4511 image->alpha_trait=BlendPixelTrait;
4512 q+=GetPixelChannels(image);
4513 s+=GetPixelChannels(jng_image);
4516 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4519 (void) RelinquishUniqueFileResource(alpha_image->filename);
4520 alpha_image=DestroyImage(alpha_image);
4521 alpha_image_info=DestroyImageInfo(alpha_image_info);
4522 if (jng_image != (Image *) NULL)
4523 jng_image=DestroyImage(jng_image);
4527 /* Read the JNG image. */
4529 if (mng_info->mng_type == 0)
4531 mng_info->mng_width=jng_width;
4532 mng_info->mng_height=jng_height;
4535 if (image->page.width == 0 && image->page.height == 0)
4537 image->page.width=jng_width;
4538 image->page.height=jng_height;
4541 if (image->page.x == 0 && image->page.y == 0)
4543 image->page.x=mng_info->x_off[mng_info->object_id];
4544 image->page.y=mng_info->y_off[mng_info->object_id];
4549 image->page.y=mng_info->y_off[mng_info->object_id];
4552 mng_info->image_found++;
4553 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4554 2*GetBlobSize(image));
4556 if (logging != MagickFalse)
4557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4558 " exit ReadOneJNGImage()");
4564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4568 % R e a d J N G I m a g e %
4572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4574 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4575 % (including the 8-byte signature) and returns it. It allocates the memory
4576 % necessary for the new Image structure and returns a pointer to the new
4579 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4581 % The format of the ReadJNGImage method is:
4583 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4586 % A description of each parameter follows:
4588 % o image_info: the image info.
4590 % o exception: return any errors or warnings in this structure.
4594 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4609 magic_number[MaxTextExtent];
4617 assert(image_info != (const ImageInfo *) NULL);
4618 assert(image_info->signature == MagickSignature);
4619 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4620 assert(exception != (ExceptionInfo *) NULL);
4621 assert(exception->signature == MagickSignature);
4622 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4623 image=AcquireImage(image_info,exception);
4624 mng_info=(MngInfo *) NULL;
4625 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4627 if (status == MagickFalse)
4628 return((Image *) NULL);
4630 if (LocaleCompare(image_info->magick,"JNG") != 0)
4631 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4633 /* Verify JNG signature. */
4635 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4637 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4638 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4640 /* Allocate a MngInfo structure. */
4642 have_mng_structure=MagickFalse;
4643 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4645 if (mng_info == (MngInfo *) NULL)
4646 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4648 /* Initialize members of the MngInfo structure. */
4650 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4651 have_mng_structure=MagickTrue;
4653 mng_info->image=image;
4655 image=ReadOneJNGImage(mng_info,image_info,exception);
4656 MngInfoFreeStruct(mng_info,&have_mng_structure);
4658 if (image == (Image *) NULL)
4660 if (IsImageObject(previous) != MagickFalse)
4662 (void) CloseBlob(previous);
4663 (void) DestroyImageList(previous);
4666 if (logging != MagickFalse)
4667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4668 "exit ReadJNGImage() with error");
4670 return((Image *) NULL);
4672 (void) CloseBlob(image);
4674 if (image->columns == 0 || image->rows == 0)
4676 if (logging != MagickFalse)
4677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4678 "exit ReadJNGImage() with error");
4680 ThrowReaderException(CorruptImageError,"CorruptImage");
4683 if (logging != MagickFalse)
4684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4690 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4693 page_geometry[MaxTextExtent];
4726 #if defined(MNG_INSERT_LAYERS)
4728 mng_background_color;
4731 register unsigned char
4746 #if defined(MNG_INSERT_LAYERS)
4751 volatile unsigned int
4752 #ifdef MNG_OBJECT_BUFFERS
4753 mng_background_object=0,
4755 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4758 default_frame_timeout,
4760 #if defined(MNG_INSERT_LAYERS)
4766 /* These delays are all measured in image ticks_per_second,
4767 * not in MNG ticks_per_second
4770 default_frame_delay,
4774 #if defined(MNG_INSERT_LAYERS)
4783 previous_fb.bottom=0;
4785 previous_fb.right=0;
4787 default_fb.bottom=0;
4791 /* Open image file. */
4793 assert(image_info != (const ImageInfo *) NULL);
4794 assert(image_info->signature == MagickSignature);
4795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4796 assert(exception != (ExceptionInfo *) NULL);
4797 assert(exception->signature == MagickSignature);
4798 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4799 image=AcquireImage(image_info,exception);
4800 mng_info=(MngInfo *) NULL;
4801 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4803 if (status == MagickFalse)
4804 return((Image *) NULL);
4806 first_mng_object=MagickFalse;
4808 have_mng_structure=MagickFalse;
4810 /* Allocate a MngInfo structure. */
4812 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4814 if (mng_info == (MngInfo *) NULL)
4815 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4817 /* Initialize members of the MngInfo structure. */
4819 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4820 mng_info->image=image;
4821 have_mng_structure=MagickTrue;
4823 if (LocaleCompare(image_info->magick,"MNG") == 0)
4826 magic_number[MaxTextExtent];
4828 /* Verify MNG signature. */
4829 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4830 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4831 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4833 /* Initialize some nonzero members of the MngInfo structure. */
4834 for (i=0; i < MNG_MAX_OBJECTS; i++)
4836 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4837 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4839 mng_info->exists[0]=MagickTrue;
4842 first_mng_object=MagickTrue;
4844 #if defined(MNG_INSERT_LAYERS)
4845 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4847 default_frame_delay=0;
4848 default_frame_timeout=0;
4851 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4853 skip_to_iend=MagickFalse;
4854 term_chunk_found=MagickFalse;
4855 mng_info->framing_mode=1;
4856 #if defined(MNG_INSERT_LAYERS)
4857 mandatory_back=MagickFalse;
4859 #if defined(MNG_INSERT_LAYERS)
4860 mng_background_color=image->background_color;
4862 default_fb=mng_info->frame;
4863 previous_fb=mng_info->frame;
4867 type[MaxTextExtent];
4869 if (LocaleCompare(image_info->magick,"MNG") == 0)
4878 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4879 length=ReadBlobMSBLong(image);
4880 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4882 if (logging != MagickFalse)
4883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4884 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4885 type[0],type[1],type[2],type[3],(double) length);
4887 if (length > PNG_UINT_31_MAX)
4891 ThrowReaderException(CorruptImageError,"CorruptImage");
4894 chunk=(unsigned char *) NULL;
4898 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4900 if (chunk == (unsigned char *) NULL)
4901 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4903 for (i=0; i < (ssize_t) length; i++)
4904 chunk[i]=(unsigned char) ReadBlobByte(image);
4909 (void) ReadBlobMSBLong(image); /* read crc word */
4911 #if !defined(JNG_SUPPORTED)
4912 if (memcmp(type,mng_JHDR,4) == 0)
4914 skip_to_iend=MagickTrue;
4916 if (mng_info->jhdr_warning == 0)
4917 (void) ThrowMagickException(exception,GetMagickModule(),
4918 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4920 mng_info->jhdr_warning++;
4923 if (memcmp(type,mng_DHDR,4) == 0)
4925 skip_to_iend=MagickTrue;
4927 if (mng_info->dhdr_warning == 0)
4928 (void) ThrowMagickException(exception,GetMagickModule(),
4929 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4931 mng_info->dhdr_warning++;
4933 if (memcmp(type,mng_MEND,4) == 0)
4938 if (memcmp(type,mng_IEND,4) == 0)
4939 skip_to_iend=MagickFalse;
4942 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4944 if (logging != MagickFalse)
4945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4951 if (memcmp(type,mng_MHDR,4) == 0)
4953 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4954 (p[2] << 8) | p[3]);
4956 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4957 (p[6] << 8) | p[7]);
4959 if (logging != MagickFalse)
4961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4962 " MNG width: %.20g",(double) mng_info->mng_width);
4963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4964 " MNG height: %.20g",(double) mng_info->mng_height);
4968 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4970 if (mng_info->ticks_per_second == 0)
4971 default_frame_delay=0;
4974 default_frame_delay=1UL*image->ticks_per_second/
4975 mng_info->ticks_per_second;
4977 frame_delay=default_frame_delay;
4983 simplicity=(size_t) mng_get_long(p);
4986 mng_type=1; /* Full MNG */
4988 if ((simplicity != 0) && ((simplicity | 11) == 11))
4989 mng_type=2; /* LC */
4991 if ((simplicity != 0) && ((simplicity | 9) == 9))
4992 mng_type=3; /* VLC */
4994 #if defined(MNG_INSERT_LAYERS)
4996 insert_layers=MagickTrue;
4998 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5000 /* Allocate next image structure. */
5001 AcquireNextImage(image_info,image,exception);
5003 if (GetNextImageInList(image) == (Image *) NULL)
5004 return((Image *) NULL);
5006 image=SyncNextImageInList(image);
5007 mng_info->image=image;
5010 if ((mng_info->mng_width > 65535L) ||
5011 (mng_info->mng_height > 65535L))
5012 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5014 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5015 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5016 mng_info->mng_height);
5018 mng_info->frame.left=0;
5019 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5020 mng_info->frame.top=0;
5021 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5022 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5024 for (i=0; i < MNG_MAX_OBJECTS; i++)
5025 mng_info->object_clip[i]=mng_info->frame;
5027 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5031 if (memcmp(type,mng_TERM,4) == 0)
5042 final_delay=(png_uint_32) mng_get_long(&p[2]);
5043 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5045 if (mng_iterations == PNG_UINT_31_MAX)
5048 image->iterations=mng_iterations;
5049 term_chunk_found=MagickTrue;
5052 if (logging != MagickFalse)
5054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5055 " repeat=%d",repeat);
5057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5058 " final_delay=%.20g",(double) final_delay);
5060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5061 " image->iterations=%.20g",(double) image->iterations);
5064 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5067 if (memcmp(type,mng_DEFI,4) == 0)
5070 (void) ThrowMagickException(exception,GetMagickModule(),
5071 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5074 object_id=(p[0] << 8) | p[1];
5076 if (mng_type == 2 && object_id != 0)
5077 (void) ThrowMagickException(exception,GetMagickModule(),
5078 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5081 if (object_id > MNG_MAX_OBJECTS)
5084 Instead of using a warning we should allocate a larger
5085 MngInfo structure and continue.
5087 (void) ThrowMagickException(exception,GetMagickModule(),
5088 CoderError,"object id too large","`%s'",image->filename);
5089 object_id=MNG_MAX_OBJECTS;
5092 if (mng_info->exists[object_id])
5093 if (mng_info->frozen[object_id])
5095 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5096 (void) ThrowMagickException(exception,
5097 GetMagickModule(),CoderError,
5098 "DEFI cannot redefine a frozen MNG object","`%s'",
5103 mng_info->exists[object_id]=MagickTrue;
5106 mng_info->invisible[object_id]=p[2];
5109 Extract object offset info.
5113 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5114 (p[5] << 16) | (p[6] << 8) | p[7]);
5116 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5117 (p[9] << 16) | (p[10] << 8) | p[11]);
5119 if (logging != MagickFalse)
5121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5122 " x_off[%d]: %.20g",object_id,(double)
5123 mng_info->x_off[object_id]);
5125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5126 " y_off[%d]: %.20g",object_id,(double)
5127 mng_info->y_off[object_id]);
5132 Extract object clipping info.
5135 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5141 if (memcmp(type,mng_bKGD,4) == 0)
5143 mng_info->have_global_bkgd=MagickFalse;
5147 mng_info->mng_global_bkgd.red=
5148 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5150 mng_info->mng_global_bkgd.green=
5151 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5153 mng_info->mng_global_bkgd.blue=
5154 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5156 mng_info->have_global_bkgd=MagickTrue;
5159 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5162 if (memcmp(type,mng_BACK,4) == 0)
5164 #if defined(MNG_INSERT_LAYERS)
5166 mandatory_back=p[6];
5171 if (mandatory_back && length > 5)
5173 mng_background_color.red=
5174 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5176 mng_background_color.green=
5177 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5179 mng_background_color.blue=
5180 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5182 mng_background_color.alpha=OpaqueAlpha;
5185 #ifdef MNG_OBJECT_BUFFERS
5187 mng_background_object=(p[7] << 8) | p[8];
5190 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5194 if (memcmp(type,mng_PLTE,4) == 0)
5196 /* Read global PLTE. */
5198 if (length && (length < 769))
5200 if (mng_info->global_plte == (png_colorp) NULL)
5201 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5202 sizeof(*mng_info->global_plte));
5204 for (i=0; i < (ssize_t) (length/3); i++)
5206 mng_info->global_plte[i].red=p[3*i];
5207 mng_info->global_plte[i].green=p[3*i+1];
5208 mng_info->global_plte[i].blue=p[3*i+2];
5211 mng_info->global_plte_length=(unsigned int) (length/3);
5214 for ( ; i < 256; i++)
5216 mng_info->global_plte[i].red=i;
5217 mng_info->global_plte[i].green=i;
5218 mng_info->global_plte[i].blue=i;
5222 mng_info->global_plte_length=256;
5225 mng_info->global_plte_length=0;
5227 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5231 if (memcmp(type,mng_tRNS,4) == 0)
5233 /* read global tRNS */
5236 for (i=0; i < (ssize_t) length; i++)
5237 mng_info->global_trns[i]=p[i];
5240 for ( ; i < 256; i++)
5241 mng_info->global_trns[i]=255;
5243 mng_info->global_trns_length=(unsigned int) length;
5244 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5247 if (memcmp(type,mng_gAMA,4) == 0)
5254 igamma=mng_get_long(p);
5255 mng_info->global_gamma=((float) igamma)*0.00001;
5256 mng_info->have_global_gama=MagickTrue;
5260 mng_info->have_global_gama=MagickFalse;
5262 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5266 if (memcmp(type,mng_cHRM,4) == 0)
5268 /* Read global cHRM */
5272 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5273 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5274 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5275 mng_info->global_chrm.red_primary.y=0.00001*
5276 mng_get_long(&p[12]);
5277 mng_info->global_chrm.green_primary.x=0.00001*
5278 mng_get_long(&p[16]);
5279 mng_info->global_chrm.green_primary.y=0.00001*
5280 mng_get_long(&p[20]);
5281 mng_info->global_chrm.blue_primary.x=0.00001*
5282 mng_get_long(&p[24]);
5283 mng_info->global_chrm.blue_primary.y=0.00001*
5284 mng_get_long(&p[28]);
5285 mng_info->have_global_chrm=MagickTrue;
5288 mng_info->have_global_chrm=MagickFalse;
5290 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5294 if (memcmp(type,mng_sRGB,4) == 0)
5301 mng_info->global_srgb_intent=
5302 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5303 mng_info->have_global_srgb=MagickTrue;
5306 mng_info->have_global_srgb=MagickFalse;
5308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5312 if (memcmp(type,mng_iCCP,4) == 0)
5320 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5325 if (memcmp(type,mng_FRAM,4) == 0)
5328 (void) ThrowMagickException(exception,GetMagickModule(),
5329 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5332 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5333 image->delay=frame_delay;
5335 frame_delay=default_frame_delay;
5336 frame_timeout=default_frame_timeout;
5341 mng_info->framing_mode=p[0];
5343 if (logging != MagickFalse)
5344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5345 " Framing_mode=%d",mng_info->framing_mode);
5349 /* Note the delay and frame clipping boundaries. */
5351 p++; /* framing mode */
5353 while (*p && ((p-chunk) < (ssize_t) length))
5354 p++; /* frame name */
5356 p++; /* frame name terminator */
5358 if ((p-chunk) < (ssize_t) (length-4))
5365 change_delay=(*p++);
5366 change_timeout=(*p++);
5367 change_clipping=(*p++);
5368 p++; /* change_sync */
5372 frame_delay=1UL*image->ticks_per_second*
5375 if (mng_info->ticks_per_second != 0)
5376 frame_delay/=mng_info->ticks_per_second;
5379 frame_delay=PNG_UINT_31_MAX;
5381 if (change_delay == 2)
5382 default_frame_delay=frame_delay;
5386 if (logging != MagickFalse)
5387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5388 " Framing_delay=%.20g",(double) frame_delay);
5393 frame_timeout=1UL*image->ticks_per_second*
5396 if (mng_info->ticks_per_second != 0)
5397 frame_timeout/=mng_info->ticks_per_second;
5400 frame_timeout=PNG_UINT_31_MAX;
5402 if (change_delay == 2)
5403 default_frame_timeout=frame_timeout;
5407 if (logging != MagickFalse)
5408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5409 " Framing_timeout=%.20g",(double) frame_timeout);
5412 if (change_clipping)
5414 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5418 if (logging != MagickFalse)
5419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5420 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5421 (double) fb.left,(double) fb.right,(double) fb.top,
5422 (double) fb.bottom);
5424 if (change_clipping == 2)
5430 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5432 subframe_width=(size_t) (mng_info->clip.right
5433 -mng_info->clip.left);
5435 subframe_height=(size_t) (mng_info->clip.bottom
5436 -mng_info->clip.top);
5438 Insert a background layer behind the frame if framing_mode is 4.
5440 #if defined(MNG_INSERT_LAYERS)
5441 if (logging != MagickFalse)
5442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5443 " subframe_width=%.20g, subframe_height=%.20g",(double)
5444 subframe_width,(double) subframe_height);
5446 if (insert_layers && (mng_info->framing_mode == 4) &&
5447 (subframe_width) && (subframe_height))
5449 /* Allocate next image structure. */
5450 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5452 AcquireNextImage(image_info,image,exception);
5454 if (GetNextImageInList(image) == (Image *) NULL)
5456 image=DestroyImageList(image);
5457 MngInfoFreeStruct(mng_info,&have_mng_structure);
5458 return((Image *) NULL);
5461 image=SyncNextImageInList(image);
5464 mng_info->image=image;
5466 if (term_chunk_found)
5468 image->start_loop=MagickTrue;
5469 image->iterations=mng_iterations;
5470 term_chunk_found=MagickFalse;
5474 image->start_loop=MagickFalse;
5476 image->columns=subframe_width;
5477 image->rows=subframe_height;
5478 image->page.width=subframe_width;
5479 image->page.height=subframe_height;
5480 image->page.x=mng_info->clip.left;
5481 image->page.y=mng_info->clip.top;
5482 image->background_color=mng_background_color;
5483 image->alpha_trait=UndefinedPixelTrait;
5485 (void) SetImageBackgroundColor(image,exception);
5487 if (logging != MagickFalse)
5488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5489 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5490 (double) mng_info->clip.left,(double) mng_info->clip.right,
5491 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5494 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5497 if (memcmp(type,mng_CLIP,4) == 0)
5506 first_object=(p[0] << 8) | p[1];
5507 last_object=(p[2] << 8) | p[3];
5509 for (i=(int) first_object; i <= (int) last_object; i++)
5511 if (mng_info->exists[i] && !mng_info->frozen[i])
5516 box=mng_info->object_clip[i];
5517 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5524 if (memcmp(type,mng_SAVE,4) == 0)
5526 for (i=1; i < MNG_MAX_OBJECTS; i++)
5527 if (mng_info->exists[i])
5529 mng_info->frozen[i]=MagickTrue;
5530 #ifdef MNG_OBJECT_BUFFERS
5531 if (mng_info->ob[i] != (MngBuffer *) NULL)
5532 mng_info->ob[i]->frozen=MagickTrue;
5537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5542 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5544 /* Read DISC or SEEK. */
5546 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5548 for (i=1; i < MNG_MAX_OBJECTS; i++)
5549 MngInfoDiscardObject(mng_info,i);
5557 for (j=0; j < (ssize_t) length; j+=2)
5559 i=p[j] << 8 | p[j+1];
5560 MngInfoDiscardObject(mng_info,i);
5565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5570 if (memcmp(type,mng_MOVE,4) == 0)
5578 first_object=(p[0] << 8) | p[1];
5579 last_object=(p[2] << 8) | p[3];
5580 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5582 if (mng_info->exists[i] && !mng_info->frozen[i])
5590 old_pair.a=mng_info->x_off[i];
5591 old_pair.b=mng_info->y_off[i];
5592 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5593 mng_info->x_off[i]=new_pair.a;
5594 mng_info->y_off[i]=new_pair.b;
5598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5602 if (memcmp(type,mng_LOOP,4) == 0)
5604 ssize_t loop_iters=1;
5605 loop_level=chunk[0];
5606 mng_info->loop_active[loop_level]=1; /* mark loop active */
5608 /* Record starting point. */
5609 loop_iters=mng_get_long(&chunk[1]);
5611 if (logging != MagickFalse)
5612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5613 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5614 (double) loop_iters);
5616 if (loop_iters == 0)
5617 skipping_loop=loop_level;
5621 mng_info->loop_jump[loop_level]=TellBlob(image);
5622 mng_info->loop_count[loop_level]=loop_iters;
5625 mng_info->loop_iteration[loop_level]=0;
5626 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5630 if (memcmp(type,mng_ENDL,4) == 0)
5632 loop_level=chunk[0];
5634 if (skipping_loop > 0)
5636 if (skipping_loop == loop_level)
5639 Found end of zero-iteration loop.
5642 mng_info->loop_active[loop_level]=0;
5648 if (mng_info->loop_active[loop_level] == 1)
5650 mng_info->loop_count[loop_level]--;
5651 mng_info->loop_iteration[loop_level]++;
5653 if (logging != MagickFalse)
5654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5655 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5656 (double) loop_level,(double)
5657 mng_info->loop_count[loop_level]);
5659 if (mng_info->loop_count[loop_level] != 0)
5661 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5665 ThrowReaderException(CorruptImageError,
5666 "ImproperImageHeader");
5677 mng_info->loop_active[loop_level]=0;
5679 for (i=0; i < loop_level; i++)
5680 if (mng_info->loop_active[i] == 1)
5681 last_level=(short) i;
5682 loop_level=last_level;
5687 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5691 if (memcmp(type,mng_CLON,4) == 0)
5693 if (mng_info->clon_warning == 0)
5694 (void) ThrowMagickException(exception,GetMagickModule(),
5695 CoderError,"CLON is not implemented yet","`%s'",
5698 mng_info->clon_warning++;
5701 if (memcmp(type,mng_MAGN,4) == 0)
5716 magn_first=(p[0] << 8) | p[1];
5722 magn_last=(p[2] << 8) | p[3];
5725 magn_last=magn_first;
5726 #ifndef MNG_OBJECT_BUFFERS
5727 if (magn_first || magn_last)
5728 if (mng_info->magn_warning == 0)
5730 (void) ThrowMagickException(exception,
5731 GetMagickModule(),CoderError,
5732 "MAGN is not implemented yet for nonzero objects",
5733 "`%s'",image->filename);
5735 mng_info->magn_warning++;
5745 magn_mx=(p[5] << 8) | p[6];
5754 magn_my=(p[7] << 8) | p[8];
5763 magn_ml=(p[9] << 8) | p[10];
5772 magn_mr=(p[11] << 8) | p[12];
5781 magn_mt=(p[13] << 8) | p[14];
5790 magn_mb=(p[15] << 8) | p[16];
5802 magn_methy=magn_methx;
5805 if (magn_methx > 5 || magn_methy > 5)
5806 if (mng_info->magn_warning == 0)
5808 (void) ThrowMagickException(exception,
5809 GetMagickModule(),CoderError,
5810 "Unknown MAGN method in MNG datastream","`%s'",
5813 mng_info->magn_warning++;
5815 #ifdef MNG_OBJECT_BUFFERS
5816 /* Magnify existing objects in the range magn_first to magn_last */
5818 if (magn_first == 0 || magn_last == 0)
5820 /* Save the magnification factors for object 0 */
5821 mng_info->magn_mb=magn_mb;
5822 mng_info->magn_ml=magn_ml;
5823 mng_info->magn_mr=magn_mr;
5824 mng_info->magn_mt=magn_mt;
5825 mng_info->magn_mx=magn_mx;
5826 mng_info->magn_my=magn_my;
5827 mng_info->magn_methx=magn_methx;
5828 mng_info->magn_methy=magn_methy;
5832 if (memcmp(type,mng_PAST,4) == 0)
5834 if (mng_info->past_warning == 0)
5835 (void) ThrowMagickException(exception,GetMagickModule(),
5836 CoderError,"PAST is not implemented yet","`%s'",
5839 mng_info->past_warning++;
5842 if (memcmp(type,mng_SHOW,4) == 0)
5844 if (mng_info->show_warning == 0)
5845 (void) ThrowMagickException(exception,GetMagickModule(),
5846 CoderError,"SHOW is not implemented yet","`%s'",
5849 mng_info->show_warning++;
5852 if (memcmp(type,mng_sBIT,4) == 0)
5855 mng_info->have_global_sbit=MagickFalse;
5859 mng_info->global_sbit.gray=p[0];
5860 mng_info->global_sbit.red=p[0];
5861 mng_info->global_sbit.green=p[1];
5862 mng_info->global_sbit.blue=p[2];
5863 mng_info->global_sbit.alpha=p[3];
5864 mng_info->have_global_sbit=MagickTrue;
5867 if (memcmp(type,mng_pHYs,4) == 0)
5871 mng_info->global_x_pixels_per_unit=
5872 (size_t) mng_get_long(p);
5873 mng_info->global_y_pixels_per_unit=
5874 (size_t) mng_get_long(&p[4]);
5875 mng_info->global_phys_unit_type=p[8];
5876 mng_info->have_global_phys=MagickTrue;
5880 mng_info->have_global_phys=MagickFalse;
5882 if (memcmp(type,mng_pHYg,4) == 0)
5884 if (mng_info->phyg_warning == 0)
5885 (void) ThrowMagickException(exception,GetMagickModule(),
5886 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5888 mng_info->phyg_warning++;
5890 if (memcmp(type,mng_BASI,4) == 0)
5892 skip_to_iend=MagickTrue;
5894 if (mng_info->basi_warning == 0)
5895 (void) ThrowMagickException(exception,GetMagickModule(),
5896 CoderError,"BASI is not implemented yet","`%s'",
5899 mng_info->basi_warning++;
5900 #ifdef MNG_BASI_SUPPORTED
5901 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5902 (p[2] << 8) | p[3]);
5903 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5904 (p[6] << 8) | p[7]);
5905 basi_color_type=p[8];
5906 basi_compression_method=p[9];
5907 basi_filter_type=p[10];
5908 basi_interlace_method=p[11];
5910 basi_red=(p[12] << 8) & p[13];
5916 basi_green=(p[14] << 8) & p[15];
5922 basi_blue=(p[16] << 8) & p[17];
5928 basi_alpha=(p[18] << 8) & p[19];
5932 if (basi_sample_depth == 16)
5939 basi_viewable=p[20];
5945 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5949 if (memcmp(type,mng_IHDR,4)
5950 #if defined(JNG_SUPPORTED)
5951 && memcmp(type,mng_JHDR,4)
5955 /* Not an IHDR or JHDR chunk */
5957 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5962 if (logging != MagickFalse)
5963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5964 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5966 mng_info->exists[object_id]=MagickTrue;
5967 mng_info->viewable[object_id]=MagickTrue;
5969 if (mng_info->invisible[object_id])
5971 if (logging != MagickFalse)
5972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5973 " Skipping invisible object");
5975 skip_to_iend=MagickTrue;
5976 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5979 #if defined(MNG_INSERT_LAYERS)
5981 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5983 image_width=(size_t) mng_get_long(p);
5984 image_height=(size_t) mng_get_long(&p[4]);
5986 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5989 Insert a transparent background layer behind the entire animation
5990 if it is not full screen.
5992 #if defined(MNG_INSERT_LAYERS)
5993 if (insert_layers && mng_type && first_mng_object)
5995 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5996 (image_width < mng_info->mng_width) ||
5997 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5998 (image_height < mng_info->mng_height) ||
5999 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6001 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6004 Allocate next image structure.
6006 AcquireNextImage(image_info,image,exception);
6008 if (GetNextImageInList(image) == (Image *) NULL)
6010 image=DestroyImageList(image);
6011 MngInfoFreeStruct(mng_info,&have_mng_structure);
6012 return((Image *) NULL);
6015 image=SyncNextImageInList(image);
6017 mng_info->image=image;
6019 if (term_chunk_found)
6021 image->start_loop=MagickTrue;
6022 image->iterations=mng_iterations;
6023 term_chunk_found=MagickFalse;
6027 image->start_loop=MagickFalse;
6029 /* Make a background rectangle. */
6032 image->columns=mng_info->mng_width;
6033 image->rows=mng_info->mng_height;
6034 image->page.width=mng_info->mng_width;
6035 image->page.height=mng_info->mng_height;
6038 image->background_color=mng_background_color;
6039 (void) SetImageBackgroundColor(image,exception);
6040 if (logging != MagickFalse)
6041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6042 " Inserted transparent background layer, W=%.20g, H=%.20g",
6043 (double) mng_info->mng_width,(double) mng_info->mng_height);
6047 Insert a background layer behind the upcoming image if
6048 framing_mode is 3, and we haven't already inserted one.
6050 if (insert_layers && (mng_info->framing_mode == 3) &&
6051 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6052 (simplicity & 0x08)))
6054 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6057 Allocate next image structure.
6059 AcquireNextImage(image_info,image,exception);
6061 if (GetNextImageInList(image) == (Image *) NULL)
6063 image=DestroyImageList(image);
6064 MngInfoFreeStruct(mng_info,&have_mng_structure);
6065 return((Image *) NULL);
6068 image=SyncNextImageInList(image);
6071 mng_info->image=image;
6073 if (term_chunk_found)
6075 image->start_loop=MagickTrue;
6076 image->iterations=mng_iterations;
6077 term_chunk_found=MagickFalse;
6081 image->start_loop=MagickFalse;
6084 image->columns=subframe_width;
6085 image->rows=subframe_height;
6086 image->page.width=subframe_width;
6087 image->page.height=subframe_height;
6088 image->page.x=mng_info->clip.left;
6089 image->page.y=mng_info->clip.top;
6090 image->background_color=mng_background_color;
6091 image->alpha_trait=UndefinedPixelTrait;
6092 (void) SetImageBackgroundColor(image,exception);
6094 if (logging != MagickFalse)
6095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6096 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6097 (double) mng_info->clip.left,(double) mng_info->clip.right,
6098 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6100 #endif /* MNG_INSERT_LAYERS */
6101 first_mng_object=MagickFalse;
6103 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6106 Allocate next image structure.
6108 AcquireNextImage(image_info,image,exception);
6110 if (GetNextImageInList(image) == (Image *) NULL)
6112 image=DestroyImageList(image);
6113 MngInfoFreeStruct(mng_info,&have_mng_structure);
6114 return((Image *) NULL);
6117 image=SyncNextImageInList(image);
6119 mng_info->image=image;
6120 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6121 GetBlobSize(image));
6123 if (status == MagickFalse)
6126 if (term_chunk_found)
6128 image->start_loop=MagickTrue;
6129 term_chunk_found=MagickFalse;
6133 image->start_loop=MagickFalse;
6135 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6137 image->delay=frame_delay;
6138 frame_delay=default_frame_delay;
6144 image->page.width=mng_info->mng_width;
6145 image->page.height=mng_info->mng_height;
6146 image->page.x=mng_info->x_off[object_id];
6147 image->page.y=mng_info->y_off[object_id];
6148 image->iterations=mng_iterations;
6151 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6154 if (logging != MagickFalse)
6155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6156 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6159 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6162 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6166 mng_info->image=image;
6167 mng_info->mng_type=mng_type;
6168 mng_info->object_id=object_id;
6170 if (memcmp(type,mng_IHDR,4) == 0)
6171 image=ReadOnePNGImage(mng_info,image_info,exception);
6173 #if defined(JNG_SUPPORTED)
6175 image=ReadOneJNGImage(mng_info,image_info,exception);
6178 if (image == (Image *) NULL)
6180 if (IsImageObject(previous) != MagickFalse)
6182 (void) DestroyImageList(previous);
6183 (void) CloseBlob(previous);
6186 MngInfoFreeStruct(mng_info,&have_mng_structure);
6187 return((Image *) NULL);
6190 if (image->columns == 0 || image->rows == 0)
6192 (void) CloseBlob(image);
6193 image=DestroyImageList(image);
6194 MngInfoFreeStruct(mng_info,&have_mng_structure);
6195 return((Image *) NULL);
6198 mng_info->image=image;
6205 if (mng_info->magn_methx || mng_info->magn_methy)
6211 if (logging != MagickFalse)
6212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6213 " Processing MNG MAGN chunk");
6215 if (mng_info->magn_methx == 1)
6217 magnified_width=mng_info->magn_ml;
6219 if (image->columns > 1)
6220 magnified_width += mng_info->magn_mr;
6222 if (image->columns > 2)
6223 magnified_width += (png_uint_32)
6224 ((image->columns-2)*(mng_info->magn_mx));
6229 magnified_width=(png_uint_32) image->columns;
6231 if (image->columns > 1)
6232 magnified_width += mng_info->magn_ml-1;
6234 if (image->columns > 2)
6235 magnified_width += mng_info->magn_mr-1;
6237 if (image->columns > 3)
6238 magnified_width += (png_uint_32)
6239 ((image->columns-3)*(mng_info->magn_mx-1));
6242 if (mng_info->magn_methy == 1)
6244 magnified_height=mng_info->magn_mt;
6246 if (image->rows > 1)
6247 magnified_height += mng_info->magn_mb;
6249 if (image->rows > 2)
6250 magnified_height += (png_uint_32)
6251 ((image->rows-2)*(mng_info->magn_my));
6256 magnified_height=(png_uint_32) image->rows;
6258 if (image->rows > 1)
6259 magnified_height += mng_info->magn_mt-1;
6261 if (image->rows > 2)
6262 magnified_height += mng_info->magn_mb-1;
6264 if (image->rows > 3)
6265 magnified_height += (png_uint_32)
6266 ((image->rows-3)*(mng_info->magn_my-1));
6269 if (magnified_height > image->rows ||
6270 magnified_width > image->columns)
6297 /* Allocate next image structure. */
6299 if (logging != MagickFalse)
6300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6301 " Allocate magnified image");
6303 AcquireNextImage(image_info,image,exception);
6305 if (GetNextImageInList(image) == (Image *) NULL)
6307 image=DestroyImageList(image);
6308 MngInfoFreeStruct(mng_info,&have_mng_structure);
6309 return((Image *) NULL);
6312 large_image=SyncNextImageInList(image);
6314 large_image->columns=magnified_width;
6315 large_image->rows=magnified_height;
6317 magn_methx=mng_info->magn_methx;
6318 magn_methy=mng_info->magn_methy;
6320 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6321 #define QM unsigned short
6322 if (magn_methx != 1 || magn_methy != 1)
6325 Scale pixels to unsigned shorts to prevent
6326 overflow of intermediate values of interpolations
6328 for (y=0; y < (ssize_t) image->rows; y++)
6330 q=GetAuthenticPixels(image,0,y,image->columns,1,
6333 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6335 SetPixelRed(image,ScaleQuantumToShort(
6336 GetPixelRed(image,q)),q);
6337 SetPixelGreen(image,ScaleQuantumToShort(
6338 GetPixelGreen(image,q)),q);
6339 SetPixelBlue(image,ScaleQuantumToShort(
6340 GetPixelBlue(image,q)),q);
6341 SetPixelAlpha(image,ScaleQuantumToShort(
6342 GetPixelAlpha(image,q)),q);
6343 q+=GetPixelChannels(image);
6346 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6354 if (image->alpha_trait == BlendPixelTrait)
6355 (void) SetImageBackgroundColor(large_image,exception);
6359 large_image->background_color.alpha=OpaqueAlpha;
6360 (void) SetImageBackgroundColor(large_image,exception);
6362 if (magn_methx == 4)
6365 if (magn_methx == 5)
6368 if (magn_methy == 4)
6371 if (magn_methy == 5)
6375 /* magnify the rows into the right side of the large image */
6377 if (logging != MagickFalse)
6378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6379 " Magnify the rows to %.20g",(double) large_image->rows);
6380 m=(ssize_t) mng_info->magn_mt;
6382 length=(size_t) image->columns*GetPixelChannels(image);
6383 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6384 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6386 if ((prev == (Quantum *) NULL) ||
6387 (next == (Quantum *) NULL))
6389 image=DestroyImageList(image);
6390 MngInfoFreeStruct(mng_info,&have_mng_structure);
6391 ThrowReaderException(ResourceLimitError,
6392 "MemoryAllocationFailed");
6395 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6396 (void) CopyMagickMemory(next,n,length);
6398 for (y=0; y < (ssize_t) image->rows; y++)
6401 m=(ssize_t) mng_info->magn_mt;
6403 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6404 m=(ssize_t) mng_info->magn_mb;
6406 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6407 m=(ssize_t) mng_info->magn_mb;
6409 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6413 m=(ssize_t) mng_info->magn_my;
6419 if (y < (ssize_t) image->rows-1)
6421 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6423 (void) CopyMagickMemory(next,n,length);
6426 for (i=0; i < m; i++, yy++)
6431 assert(yy < (ssize_t) large_image->rows);
6434 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6436 q+=(large_image->columns-image->columns)*
6437 GetPixelChannels(large_image);
6439 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6441 /* To do: get color as function of indexes[x] */
6443 if (image->storage_class == PseudoClass)
6448 if (magn_methy <= 1)
6450 /* replicate previous */
6451 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6452 SetPixelGreen(large_image,GetPixelGreen(image,
6454 SetPixelBlue(large_image,GetPixelBlue(image,
6456 SetPixelAlpha(large_image,GetPixelAlpha(image,
6460 else if (magn_methy == 2 || magn_methy == 4)
6464 SetPixelRed(large_image,GetPixelRed(image,
6466 SetPixelGreen(large_image,GetPixelGreen(image,
6468 SetPixelBlue(large_image,GetPixelBlue(image,
6470 SetPixelAlpha(large_image,GetPixelAlpha(image,
6477 SetPixelRed(large_image,((QM) (((ssize_t)
6478 (2*i*(GetPixelRed(image,n)
6479 -GetPixelRed(image,pixels)+m))/
6481 +GetPixelRed(image,pixels)))),q);
6482 SetPixelGreen(large_image,((QM) (((ssize_t)
6483 (2*i*(GetPixelGreen(image,n)
6484 -GetPixelGreen(image,pixels)+m))/
6486 +GetPixelGreen(image,pixels)))),q);
6487 SetPixelBlue(large_image,((QM) (((ssize_t)
6488 (2*i*(GetPixelBlue(image,n)
6489 -GetPixelBlue(image,pixels)+m))/
6491 +GetPixelBlue(image,pixels)))),q);
6493 if (image->alpha_trait == BlendPixelTrait)
6494 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6495 (2*i*(GetPixelAlpha(image,n)
6496 -GetPixelAlpha(image,pixels)+m))
6498 GetPixelAlpha(image,pixels)))),q);
6501 if (magn_methy == 4)
6503 /* Replicate nearest */
6504 if (i <= ((m+1) << 1))
6505 SetPixelAlpha(large_image,GetPixelAlpha(image,
6508 SetPixelAlpha(large_image,GetPixelAlpha(image,
6513 else /* if (magn_methy == 3 || magn_methy == 5) */
6515 /* Replicate nearest */
6516 if (i <= ((m+1) << 1))
6518 SetPixelRed(large_image,GetPixelRed(image,
6520 SetPixelGreen(large_image,GetPixelGreen(image,
6522 SetPixelBlue(large_image,GetPixelBlue(image,
6524 SetPixelAlpha(large_image,GetPixelAlpha(image,
6530 SetPixelRed(large_image,GetPixelRed(image,n),q);
6531 SetPixelGreen(large_image,GetPixelGreen(image,n),
6533 SetPixelBlue(large_image,GetPixelBlue(image,n),
6535 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6539 if (magn_methy == 5)
6541 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6542 (GetPixelAlpha(image,n)
6543 -GetPixelAlpha(image,pixels))
6544 +m))/((ssize_t) (m*2))
6545 +GetPixelAlpha(image,pixels)),q);
6548 n+=GetPixelChannels(image);
6549 q+=GetPixelChannels(large_image);
6550 pixels+=GetPixelChannels(image);
6553 if (SyncAuthenticPixels(large_image,exception) == 0)
6559 prev=(Quantum *) RelinquishMagickMemory(prev);
6560 next=(Quantum *) RelinquishMagickMemory(next);
6562 length=image->columns;
6564 if (logging != MagickFalse)
6565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6566 " Delete original image");
6568 DeleteImageFromList(&image);
6572 mng_info->image=image;
6574 /* magnify the columns */
6575 if (logging != MagickFalse)
6576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6577 " Magnify the columns to %.20g",(double) image->columns);
6579 for (y=0; y < (ssize_t) image->rows; y++)
6584 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6585 pixels=q+(image->columns-length)*GetPixelChannels(image);
6586 n=pixels+GetPixelChannels(image);
6588 for (x=(ssize_t) (image->columns-length);
6589 x < (ssize_t) image->columns; x++)
6591 /* To do: Rewrite using Get/Set***PixelChannel() */
6593 if (x == (ssize_t) (image->columns-length))
6594 m=(ssize_t) mng_info->magn_ml;
6596 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6597 m=(ssize_t) mng_info->magn_mr;
6599 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6600 m=(ssize_t) mng_info->magn_mr;
6602 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6606 m=(ssize_t) mng_info->magn_mx;
6608 for (i=0; i < m; i++)
6610 if (magn_methx <= 1)
6612 /* replicate previous */
6613 SetPixelRed(image,GetPixelRed(image,pixels),q);
6614 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6615 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6616 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6619 else if (magn_methx == 2 || magn_methx == 4)
6623 SetPixelRed(image,GetPixelRed(image,pixels),q);
6624 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6625 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6626 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6629 /* To do: Rewrite using Get/Set***PixelChannel() */
6633 SetPixelRed(image,(QM) ((2*i*(
6634 GetPixelRed(image,n)
6635 -GetPixelRed(image,pixels))+m)
6637 GetPixelRed(image,pixels)),q);
6639 SetPixelGreen(image,(QM) ((2*i*(
6640 GetPixelGreen(image,n)
6641 -GetPixelGreen(image,pixels))+m)
6643 GetPixelGreen(image,pixels)),q);
6645 SetPixelBlue(image,(QM) ((2*i*(
6646 GetPixelBlue(image,n)
6647 -GetPixelBlue(image,pixels))+m)
6649 GetPixelBlue(image,pixels)),q);
6650 if (image->alpha_trait == BlendPixelTrait)
6651 SetPixelAlpha(image,(QM) ((2*i*(
6652 GetPixelAlpha(image,n)
6653 -GetPixelAlpha(image,pixels))+m)
6655 GetPixelAlpha(image,pixels)),q);
6658 if (magn_methx == 4)
6660 /* Replicate nearest */
6661 if (i <= ((m+1) << 1))
6663 SetPixelAlpha(image,
6664 GetPixelAlpha(image,pixels)+0,q);
6668 SetPixelAlpha(image,
6669 GetPixelAlpha(image,n)+0,q);
6674 else /* if (magn_methx == 3 || magn_methx == 5) */
6676 /* Replicate nearest */
6677 if (i <= ((m+1) << 1))
6679 SetPixelRed(image,GetPixelRed(image,pixels),q);
6680 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6681 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6682 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6687 SetPixelRed(image,GetPixelRed(image,n),q);
6688 SetPixelGreen(image,GetPixelGreen(image,n),q);
6689 SetPixelBlue(image,GetPixelBlue(image,n),q);
6690 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6693 if (magn_methx == 5)
6696 SetPixelAlpha(image,
6697 (QM) ((2*i*( GetPixelAlpha(image,n)
6698 -GetPixelAlpha(image,pixels))+m)/
6700 +GetPixelAlpha(image,pixels)),q);
6703 q+=GetPixelChannels(image);
6705 n+=GetPixelChannels(image);
6706 p+=GetPixelChannels(image);
6709 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6712 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6713 if (magn_methx != 1 || magn_methy != 1)
6716 Rescale pixels to Quantum
6718 for (y=0; y < (ssize_t) image->rows; y++)
6720 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6722 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6724 SetPixelRed(image,ScaleShortToQuantum(
6725 GetPixelRed(image,q)),q);
6726 SetPixelGreen(image,ScaleShortToQuantum(
6727 GetPixelGreen(image,q)),q);
6728 SetPixelBlue(image,ScaleShortToQuantum(
6729 GetPixelBlue(image,q)),q);
6730 SetPixelAlpha(image,ScaleShortToQuantum(
6731 GetPixelAlpha(image,q)),q);
6732 q+=GetPixelChannels(image);
6735 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6740 if (logging != MagickFalse)
6741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6742 " Finished MAGN processing");
6747 Crop_box is with respect to the upper left corner of the MNG.
6749 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6750 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6751 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6752 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6753 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6754 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6755 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6756 if ((crop_box.left != (mng_info->image_box.left
6757 +mng_info->x_off[object_id])) ||
6758 (crop_box.right != (mng_info->image_box.right
6759 +mng_info->x_off[object_id])) ||
6760 (crop_box.top != (mng_info->image_box.top
6761 +mng_info->y_off[object_id])) ||
6762 (crop_box.bottom != (mng_info->image_box.bottom
6763 +mng_info->y_off[object_id])))
6765 if (logging != MagickFalse)
6766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6767 " Crop the PNG image");
6769 if ((crop_box.left < crop_box.right) &&
6770 (crop_box.top < crop_box.bottom))
6779 Crop_info is with respect to the upper left corner of
6782 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6783 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6784 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6785 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6786 image->page.width=image->columns;
6787 image->page.height=image->rows;
6790 im=CropImage(image,&crop_info,exception);
6792 if (im != (Image *) NULL)
6794 image->columns=im->columns;
6795 image->rows=im->rows;
6796 im=DestroyImage(im);
6797 image->page.width=image->columns;
6798 image->page.height=image->rows;
6799 image->page.x=crop_box.left;
6800 image->page.y=crop_box.top;
6807 No pixels in crop area. The MNG spec still requires
6808 a layer, though, so make a single transparent pixel in
6809 the top left corner.
6814 (void) SetImageBackgroundColor(image,exception);
6815 image->page.width=1;
6816 image->page.height=1;
6821 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6822 image=mng_info->image;
6826 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6827 /* PNG does not handle depths greater than 16 so reduce it even
6830 if (image->depth > 16)
6834 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6835 if (image->depth > 8)
6837 /* To do: fill low byte properly */
6841 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6845 if (image_info->number_scenes != 0)
6847 if (mng_info->scenes_found >
6848 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6852 if (logging != MagickFalse)
6853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6854 " Finished reading image datastream.");
6856 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6858 (void) CloseBlob(image);
6860 if (logging != MagickFalse)
6861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6862 " Finished reading all image datastreams.");
6864 #if defined(MNG_INSERT_LAYERS)
6865 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6866 (mng_info->mng_height))
6869 Insert a background layer if nothing else was found.
6871 if (logging != MagickFalse)
6872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6873 " No images found. Inserting a background layer.");
6875 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6878 Allocate next image structure.
6880 AcquireNextImage(image_info,image,exception);
6881 if (GetNextImageInList(image) == (Image *) NULL)
6883 image=DestroyImageList(image);
6884 MngInfoFreeStruct(mng_info,&have_mng_structure);
6886 if (logging != MagickFalse)
6887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6888 " Allocation failed, returning NULL.");
6890 return((Image *) NULL);
6892 image=SyncNextImageInList(image);
6894 image->columns=mng_info->mng_width;
6895 image->rows=mng_info->mng_height;
6896 image->page.width=mng_info->mng_width;
6897 image->page.height=mng_info->mng_height;
6900 image->background_color=mng_background_color;
6901 image->alpha_trait=UndefinedPixelTrait;
6903 if (image_info->ping == MagickFalse)
6904 (void) SetImageBackgroundColor(image,exception);
6906 mng_info->image_found++;
6909 image->iterations=mng_iterations;
6911 if (mng_iterations == 1)
6912 image->start_loop=MagickTrue;
6914 while (GetPreviousImageInList(image) != (Image *) NULL)
6917 if (image_count > 10*mng_info->image_found)
6919 if (logging != MagickFalse)
6920 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6922 (void) ThrowMagickException(exception,GetMagickModule(),
6923 CoderError,"Linked list is corrupted, beginning of list not found",
6924 "`%s'",image_info->filename);
6926 return((Image *) NULL);
6929 image=GetPreviousImageInList(image);
6931 if (GetNextImageInList(image) == (Image *) NULL)
6933 if (logging != MagickFalse)
6934 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6936 (void) ThrowMagickException(exception,GetMagickModule(),
6937 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6938 image_info->filename);
6942 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6943 GetNextImageInList(image) ==
6946 if (logging != MagickFalse)
6947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6948 " First image null");
6950 (void) ThrowMagickException(exception,GetMagickModule(),
6951 CoderError,"image->next for first image is NULL but shouldn't be.",
6952 "`%s'",image_info->filename);
6955 if (mng_info->image_found == 0)
6957 if (logging != MagickFalse)
6958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6959 " No visible images found.");
6961 (void) ThrowMagickException(exception,GetMagickModule(),
6962 CoderError,"No visible images in file","`%s'",image_info->filename);
6964 if (image != (Image *) NULL)
6965 image=DestroyImageList(image);
6967 MngInfoFreeStruct(mng_info,&have_mng_structure);
6968 return((Image *) NULL);
6971 if (mng_info->ticks_per_second)
6972 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6973 final_delay/mng_info->ticks_per_second;
6976 image->start_loop=MagickTrue;
6978 /* Find final nonzero image delay */
6979 final_image_delay=0;
6981 while (GetNextImageInList(image) != (Image *) NULL)
6984 final_image_delay=image->delay;
6986 image=GetNextImageInList(image);
6989 if (final_delay < final_image_delay)
6990 final_delay=final_image_delay;
6992 image->delay=final_delay;
6994 if (logging != MagickFalse)
6995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6997 (double) final_delay);
6999 if (logging != MagickFalse)
7005 image=GetFirstImageInList(image);
7007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7008 " Before coalesce:");
7010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7011 " scene 0 delay=%.20g",(double) image->delay);
7013 while (GetNextImageInList(image) != (Image *) NULL)
7015 image=GetNextImageInList(image);
7016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7017 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7021 image=GetFirstImageInList(image);
7022 #ifdef MNG_COALESCE_LAYERS
7032 if (logging != MagickFalse)
7033 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7036 next_image=CoalesceImages(image,exception);
7038 if (next_image == (Image *) NULL)
7039 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7041 image=DestroyImageList(image);
7044 for (next=image; next != (Image *) NULL; next=next_image)
7046 next->page.width=mng_info->mng_width;
7047 next->page.height=mng_info->mng_height;
7050 next->scene=scene++;
7051 next_image=GetNextImageInList(next);
7053 if (next_image == (Image *) NULL)
7056 if (next->delay == 0)
7059 next_image->previous=GetPreviousImageInList(next);
7060 if (GetPreviousImageInList(next) == (Image *) NULL)
7063 next->previous->next=next_image;
7064 next=DestroyImage(next);
7070 while (GetNextImageInList(image) != (Image *) NULL)
7071 image=GetNextImageInList(image);
7073 image->dispose=BackgroundDispose;
7075 if (logging != MagickFalse)
7081 image=GetFirstImageInList(image);
7083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7084 " After coalesce:");
7086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7087 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7088 (double) image->dispose);
7090 while (GetNextImageInList(image) != (Image *) NULL)
7092 image=GetNextImageInList(image);
7094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7095 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7096 (double) image->delay,(double) image->dispose);
7100 image=GetFirstImageInList(image);
7101 MngInfoFreeStruct(mng_info,&have_mng_structure);
7102 have_mng_structure=MagickFalse;
7104 if (logging != MagickFalse)
7105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7107 return(GetFirstImageInList(image));
7109 #else /* PNG_LIBPNG_VER > 10011 */
7110 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7112 printf("Your PNG library is too old: You have libpng-%s\n",
7113 PNG_LIBPNG_VER_STRING);
7115 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7116 "PNG library is too old","`%s'",image_info->filename);
7118 return(Image *) NULL;
7121 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7123 return(ReadPNGImage(image_info,exception));
7125 #endif /* PNG_LIBPNG_VER > 10011 */
7129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7133 % R e g i s t e r P N G I m a g e %
7137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7139 % RegisterPNGImage() adds properties for the PNG image format to
7140 % the list of supported formats. The properties include the image format
7141 % tag, a method to read and/or write the format, whether the format
7142 % supports the saving of more than one frame to the same file or blob,
7143 % whether the format supports native in-memory I/O, and a brief
7144 % description of the format.
7146 % The format of the RegisterPNGImage method is:
7148 % size_t RegisterPNGImage(void)
7151 ModuleExport size_t RegisterPNGImage(void)
7154 version[MaxTextExtent];
7162 "See http://www.libpng.org/ for details about the PNG format."
7167 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7173 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7179 #if defined(PNG_LIBPNG_VER_STRING)
7180 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7181 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7183 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7185 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7186 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7191 entry=SetMagickInfo("MNG");
7192 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7194 #if defined(MAGICKCORE_PNG_DELEGATE)
7195 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7196 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7199 entry->magick=(IsImageFormatHandler *) IsMNG;
7200 entry->description=ConstantString("Multiple-image Network Graphics");
7202 if (*version != '\0')
7203 entry->version=ConstantString(version);
7205 entry->module=ConstantString("PNG");
7206 entry->note=ConstantString(MNGNote);
7207 (void) RegisterMagickInfo(entry);
7209 entry=SetMagickInfo("PNG");
7211 #if defined(MAGICKCORE_PNG_DELEGATE)
7212 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7213 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7216 entry->magick=(IsImageFormatHandler *) IsPNG;
7217 entry->adjoin=MagickFalse;
7218 entry->description=ConstantString("Portable Network Graphics");
7219 entry->module=ConstantString("PNG");
7221 if (*version != '\0')
7222 entry->version=ConstantString(version);
7224 entry->note=ConstantString(PNGNote);
7225 (void) RegisterMagickInfo(entry);
7227 entry=SetMagickInfo("PNG8");
7229 #if defined(MAGICKCORE_PNG_DELEGATE)
7230 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7231 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7234 entry->magick=(IsImageFormatHandler *) IsPNG;
7235 entry->adjoin=MagickFalse;
7236 entry->description=ConstantString(
7237 "8-bit indexed with optional binary transparency");
7238 entry->module=ConstantString("PNG");
7239 (void) RegisterMagickInfo(entry);
7241 entry=SetMagickInfo("PNG24");
7244 #if defined(ZLIB_VERSION)
7245 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7246 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7248 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7250 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7251 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7255 if (*version != '\0')
7256 entry->version=ConstantString(version);
7258 #if defined(MAGICKCORE_PNG_DELEGATE)
7259 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7260 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7263 entry->magick=(IsImageFormatHandler *) IsPNG;
7264 entry->adjoin=MagickFalse;
7265 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7266 entry->module=ConstantString("PNG");
7267 (void) RegisterMagickInfo(entry);
7269 entry=SetMagickInfo("PNG32");
7271 #if defined(MAGICKCORE_PNG_DELEGATE)
7272 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7273 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7276 entry->magick=(IsImageFormatHandler *) IsPNG;
7277 entry->adjoin=MagickFalse;
7278 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7279 entry->module=ConstantString("PNG");
7280 (void) RegisterMagickInfo(entry);
7282 entry=SetMagickInfo("PNG48");
7284 #if defined(MAGICKCORE_PNG_DELEGATE)
7285 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7286 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7289 entry->magick=(IsImageFormatHandler *) IsPNG;
7290 entry->adjoin=MagickFalse;
7291 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7292 entry->module=ConstantString("PNG");
7293 (void) RegisterMagickInfo(entry);
7295 entry=SetMagickInfo("PNG64");
7297 #if defined(MAGICKCORE_PNG_DELEGATE)
7298 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7299 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7302 entry->magick=(IsImageFormatHandler *) IsPNG;
7303 entry->adjoin=MagickFalse;
7304 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7305 entry->module=ConstantString("PNG");
7306 (void) RegisterMagickInfo(entry);
7308 entry=SetMagickInfo("JNG");
7310 #if defined(JNG_SUPPORTED)
7311 #if defined(MAGICKCORE_PNG_DELEGATE)
7312 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7313 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7317 entry->magick=(IsImageFormatHandler *) IsJNG;
7318 entry->adjoin=MagickFalse;
7319 entry->description=ConstantString("JPEG Network Graphics");
7320 entry->module=ConstantString("PNG");
7321 entry->note=ConstantString(JNGNote);
7322 (void) RegisterMagickInfo(entry);
7324 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7325 ping_semaphore=AllocateSemaphoreInfo();
7328 return(MagickImageCoderSignature);
7332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7336 % U n r e g i s t e r P N G I m a g e %
7340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7342 % UnregisterPNGImage() removes format registrations made by the
7343 % PNG module from the list of supported formats.
7345 % The format of the UnregisterPNGImage method is:
7347 % UnregisterPNGImage(void)
7350 ModuleExport void UnregisterPNGImage(void)
7352 (void) UnregisterMagickInfo("MNG");
7353 (void) UnregisterMagickInfo("PNG");
7354 (void) UnregisterMagickInfo("PNG8");
7355 (void) UnregisterMagickInfo("PNG24");
7356 (void) UnregisterMagickInfo("PNG32");
7357 (void) UnregisterMagickInfo("PNG48");
7358 (void) UnregisterMagickInfo("PNG64");
7359 (void) UnregisterMagickInfo("JNG");
7361 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7362 if (ping_semaphore != (SemaphoreInfo *) NULL)
7363 DestroySemaphoreInfo(&ping_semaphore);
7367 #if defined(MAGICKCORE_PNG_DELEGATE)
7368 #if PNG_LIBPNG_VER > 10011
7370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7374 % W r i t e M N G I m a g e %
7378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7380 % WriteMNGImage() writes an image in the Portable Network Graphics
7381 % Group's "Multiple-image Network Graphics" encoded image format.
7383 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7385 % The format of the WriteMNGImage method is:
7387 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7388 % Image *image,ExceptionInfo *exception)
7390 % A description of each parameter follows.
7392 % o image_info: the image info.
7394 % o image: The image.
7396 % o exception: return any errors or warnings in this structure.
7398 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7399 % "To do" under ReadPNGImage):
7401 % Preserve all unknown and not-yet-handled known chunks found in input
7402 % PNG file and copy them into output PNG files according to the PNG
7405 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7407 % Improve selection of color type (use indexed-colour or indexed-colour
7408 % with tRNS when 256 or fewer unique RGBA values are present).
7410 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7411 % This will be complicated if we limit ourselves to generating MNG-LC
7412 % files. For now we ignore disposal method 3 and simply overlay the next
7415 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7416 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7417 % [mostly done 15 June 1999 but still need to take care of tRNS]
7419 % Check for identical sRGB and replace with a global sRGB (and remove
7420 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7421 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7422 % local gAMA/cHRM with local sRGB if appropriate).
7424 % Check for identical sBIT chunks and write global ones.
7426 % Provide option to skip writing the signature tEXt chunks.
7428 % Use signatures to detect identical objects and reuse the first
7429 % instance of such objects instead of writing duplicate objects.
7431 % Use a smaller-than-32k value of compression window size when
7434 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7435 % ancillary text chunks and save profiles.
7437 % Provide an option to force LC files (to ensure exact framing rate)
7440 % Provide an option to force VLC files instead of LC, even when offsets
7441 % are present. This will involve expanding the embedded images with a
7442 % transparent region at the top and/or left.
7446 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7447 png_info *ping_info, unsigned char *profile_type, unsigned char
7448 *profile_description, unsigned char *profile_data, png_uint_32 length)
7467 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7469 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7472 if (image_info->verbose)
7474 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7475 (char *) profile_type, (double) length);
7478 #if PNG_LIBPNG_VER >= 14000
7479 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7481 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7483 description_length=(png_uint_32) strlen((const char *) profile_description);
7484 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7485 + description_length);
7486 #if PNG_LIBPNG_VER >= 14000
7487 text[0].text=(png_charp) png_malloc(ping,
7488 (png_alloc_size_t) allocated_length);
7489 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7491 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7492 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7494 text[0].key[0]='\0';
7495 (void) ConcatenateMagickString(text[0].key,
7496 "Raw profile type ",MaxTextExtent);
7497 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7501 (void) CopyMagickString(dp,(const char *) profile_description,
7503 dp+=description_length;
7505 (void) FormatLocaleString(dp,allocated_length-
7506 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7509 for (i=0; i < (ssize_t) length; i++)
7513 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7514 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7519 text[0].text_length=(png_size_t) (dp-text[0].text);
7520 text[0].compression=image_info->compression == NoCompression ||
7521 (image_info->compression == UndefinedCompression &&
7522 text[0].text_length < 128) ? -1 : 0;
7524 if (text[0].text_length <= allocated_length)
7525 png_set_text(ping,ping_info,text,1);
7527 png_free(ping,text[0].text);
7528 png_free(ping,text[0].key);
7529 png_free(ping,text);
7532 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7533 const char *string, MagickBooleanType logging)
7546 ResetImageProfileIterator(image);
7548 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7550 profile=GetImageProfile(image,name);
7552 if (profile != (const StringInfo *) NULL)
7557 if (LocaleNCompare(name,string,11) == 0)
7559 if (logging != MagickFalse)
7560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7561 " Found %s profile",name);
7563 ping_profile=CloneStringInfo(profile);
7564 data=GetStringInfoDatum(ping_profile),
7565 length=(png_uint_32) GetStringInfoLength(ping_profile);
7570 (void) WriteBlobMSBULong(image,length-5); /* data length */
7571 (void) WriteBlob(image,length-1,data+1);
7572 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7573 ping_profile=DestroyStringInfo(ping_profile);
7577 name=GetNextImageProfile(image);
7584 /* Write one PNG image */
7585 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7586 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7610 ping_trans_alpha[256];
7638 ping_have_cheap_transparency,
7649 /* ping_exclude_EXIF, */
7652 /* ping_exclude_iTXt, */
7657 /* ping_exclude_tRNS, */
7659 ping_exclude_zCCP, /* hex-encoded iCCP */
7662 ping_preserve_colormap,
7663 ping_need_colortype_warning,
7681 *volatile ping_pixels;
7687 ping_interlace_method,
7688 ping_compression_method,
7705 number_semitransparent,
7707 ping_pHYs_unit_type;
7710 ping_pHYs_x_resolution,
7711 ping_pHYs_y_resolution;
7713 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7714 " Enter WriteOnePNGImage()");
7716 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7717 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7718 if (image_info == (ImageInfo *) NULL)
7719 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7721 /* Initialize some stuff */
7724 ping_interlace_method=0,
7725 ping_compression_method=0,
7726 ping_filter_method=0,
7729 ping_background.red = 0;
7730 ping_background.green = 0;
7731 ping_background.blue = 0;
7732 ping_background.gray = 0;
7733 ping_background.index = 0;
7735 ping_trans_color.red=0;
7736 ping_trans_color.green=0;
7737 ping_trans_color.blue=0;
7738 ping_trans_color.gray=0;
7740 ping_pHYs_unit_type = 0;
7741 ping_pHYs_x_resolution = 0;
7742 ping_pHYs_y_resolution = 0;
7744 ping_have_blob=MagickFalse;
7745 ping_have_color=MagickTrue;
7746 ping_have_non_bw=MagickTrue;
7747 ping_have_PLTE=MagickFalse;
7748 ping_have_bKGD=MagickFalse;
7749 ping_have_pHYs=MagickFalse;
7750 ping_have_tRNS=MagickFalse;
7752 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7753 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7754 ping_exclude_date=mng_info->ping_exclude_date;
7755 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7756 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7757 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7758 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7759 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7760 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7761 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7762 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7763 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7764 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7765 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7766 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7768 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7769 ping_need_colortype_warning = MagickFalse;
7771 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7772 * i.e., eliminate the ICC profile and set image->rendering_intent.
7773 * Note that this will not involve any changes to the actual pixels
7774 * but merely passes information to applications that read the resulting
7777 if (ping_exclude_sRGB == MagickFalse)
7785 ResetImageProfileIterator(image);
7786 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7788 profile=GetImageProfile(image,name);
7790 if (profile != (StringInfo *) NULL)
7792 if ((LocaleCompare(name,"ICC") == 0) ||
7793 (LocaleCompare(name,"ICM") == 0))
7798 /* 0: not a known sRGB profile
7799 * 1: HP-Microsoft sRGB v2
7800 * 2: ICC sRGB v4 perceptual
7801 * 3: ICC sRGB v2 perceptual no black-compensation
7804 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7805 check_len[4] = {0, 3144, 60960, 3052};
7814 length=(png_uint_32) GetStringInfoLength(profile);
7816 for (icheck=3; icheck > 0; icheck--)
7818 if (length == check_len[icheck])
7820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7821 " Got a %lu-byte ICC profile (potentially sRGB)",
7822 (unsigned long) length);
7824 data=GetStringInfoDatum(profile);
7825 profile_crc=crc32(0,data,length);
7827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7828 " with crc=%8x",(unsigned int) profile_crc);
7830 if (profile_crc == check_crc[icheck])
7832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7834 if (image->rendering_intent==UndefinedIntent)
7835 image->rendering_intent=PerceptualIntent;
7841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7842 " Got a %lu-byte ICC profile",
7843 (unsigned long) length);
7846 name=GetNextImageProfile(image);
7851 number_semitransparent = 0;
7852 number_transparent = 0;
7854 if (logging != MagickFalse)
7856 if (image->storage_class == UndefinedClass)
7857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7858 " storage_class=UndefinedClass");
7859 if (image->storage_class == DirectClass)
7860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7861 " storage_class=DirectClass");
7862 if (image->storage_class == PseudoClass)
7863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7864 " storage_class=PseudoClass");
7867 if (image->storage_class == PseudoClass &&
7868 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7869 mng_info->write_png48 || mng_info->write_png64 ||
7870 (mng_info->write_png_colortype != 1 &&
7871 mng_info->write_png_colortype != 5)))
7873 (void) SyncImage(image,exception);
7874 image->storage_class = DirectClass;
7877 if (ping_preserve_colormap == MagickFalse)
7879 if (image->storage_class != PseudoClass && image->colormap != NULL)
7881 /* Free the bogus colormap; it can cause trouble later */
7882 if (logging != MagickFalse)
7883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7884 " Freeing bogus colormap");
7885 (void) RelinquishMagickMemory(image->colormap);
7886 image->colormap=NULL;
7890 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
7891 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7894 Sometimes we get PseudoClass images whose RGB values don't match
7895 the colors in the colormap. This code syncs the RGB values.
7897 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7898 (void) SyncImage(image,exception);
7900 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7901 if (image->depth > 8)
7903 if (logging != MagickFalse)
7904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7905 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7911 /* Respect the -depth option */
7912 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7917 if (image->depth > 8)
7919 #if MAGICKCORE_QUANTUM_DEPTH > 16
7920 /* Scale to 16-bit */
7921 LBR16PacketRGBO(image->background_color);
7923 for (y=0; y < (ssize_t) image->rows; y++)
7925 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7927 if (r == (Quantum *) NULL)
7930 for (x=0; x < (ssize_t) image->columns; x++)
7933 r+=GetPixelChannels(image);
7936 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7940 if (image->storage_class == PseudoClass && image->colormap != NULL)
7942 for (i=0; i < (ssize_t) image->colors; i++)
7944 LBR16PacketRGBO(image->colormap[i]);
7947 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7950 else if (image->depth > 4)
7952 #if MAGICKCORE_QUANTUM_DEPTH > 8
7953 /* Scale to 8-bit */
7954 LBR08PacketRGBO(image->background_color);
7956 for (y=0; y < (ssize_t) image->rows; y++)
7958 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7960 if (r == (Quantum *) NULL)
7963 for (x=0; x < (ssize_t) image->columns; x++)
7966 r+=GetPixelChannels(image);
7969 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7973 if (image->storage_class == PseudoClass && image->colormap != NULL)
7975 for (i=0; i < (ssize_t) image->colors; i++)
7977 LBR08PacketRGBO(image->colormap[i]);
7980 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7983 if (image->depth > 2)
7985 /* Scale to 4-bit */
7986 LBR04PacketRGBO(image->background_color);
7988 for (y=0; y < (ssize_t) image->rows; y++)
7990 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7992 if (r == (Quantum *) NULL)
7995 for (x=0; x < (ssize_t) image->columns; x++)
7998 r+=GetPixelChannels(image);
8001 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8005 if (image->storage_class == PseudoClass && image->colormap != NULL)
8007 for (i=0; i < (ssize_t) image->colors; i++)
8009 LBR04PacketRGBO(image->colormap[i]);
8014 else if (image->depth > 1)
8016 /* Scale to 2-bit */
8017 LBR02PacketRGBO(image->background_color);
8019 for (y=0; y < (ssize_t) image->rows; y++)
8021 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8023 if (r == (Quantum *) NULL)
8026 for (x=0; x < (ssize_t) image->columns; x++)
8029 r+=GetPixelChannels(image);
8032 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8036 if (image->storage_class == PseudoClass && image->colormap != NULL)
8038 for (i=0; i < (ssize_t) image->colors; i++)
8040 LBR02PacketRGBO(image->colormap[i]);
8046 /* Scale to 1-bit */
8047 LBR01PacketRGBO(image->background_color);
8049 for (y=0; y < (ssize_t) image->rows; y++)
8051 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8053 if (r == (Quantum *) NULL)
8056 for (x=0; x < (ssize_t) image->columns; x++)
8059 r+=GetPixelChannels(image);
8062 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8066 if (image->storage_class == PseudoClass && image->colormap != NULL)
8068 for (i=0; i < (ssize_t) image->colors; i++)
8070 LBR01PacketRGBO(image->colormap[i]);
8076 /* To do: set to next higher multiple of 8 */
8077 if (image->depth < 8)
8080 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8081 /* PNG does not handle depths greater than 16 so reduce it even
8084 if (image->depth > 8)
8088 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8089 if (image->depth > 8)
8091 /* To do: fill low byte properly */
8095 if (image->depth == 16 && mng_info->write_png_depth != 16)
8096 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8100 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8101 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8102 mng_info->write_png_colortype < 4 &&
8103 image->alpha_trait != BlendPixelTrait)))
8105 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8106 * are not going to need the result.
8108 image_colors = (int) image->colors;
8109 number_opaque = (int) image->colors;
8110 if (mng_info->write_png_colortype == 1 ||
8111 mng_info->write_png_colortype == 5)
8112 ping_have_color=MagickFalse;
8114 ping_have_color=MagickTrue;
8115 ping_have_non_bw=MagickFalse;
8117 if (image->alpha_trait == BlendPixelTrait)
8119 number_transparent = 2;
8120 number_semitransparent = 1;
8125 number_transparent = 0;
8126 number_semitransparent = 0;
8134 * Normally we run this just once, but in the case of writing PNG8
8135 * we reduce the transparency to binary and run again, then if there
8136 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8137 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8138 * palette. Then (To do) we take care of a final reduction that is only
8139 * needed if there are still 256 colors present and one of them has both
8140 * transparent and opaque instances.
8143 tried_332 = MagickFalse;
8144 tried_333 = MagickFalse;
8145 tried_444 = MagickFalse;
8150 * Sometimes we get DirectClass images that have 256 colors or fewer.
8151 * This code will build a colormap.
8153 * Also, sometimes we get PseudoClass images with an out-of-date
8154 * colormap. This code will replace the colormap with a new one.
8155 * Sometimes we get PseudoClass images that have more than 256 colors.
8156 * This code will delete the colormap and change the image to
8159 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8160 * even though it sometimes contains left-over non-opaque values.
8162 * Also we gather some information (number of opaque, transparent,
8163 * and semitransparent pixels, and whether the image has any non-gray
8164 * pixels or only black-and-white pixels) that we might need later.
8166 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8167 * we need to check for bogus non-opaque values, at least.
8175 semitransparent[260],
8178 register const Quantum
8185 if (logging != MagickFalse)
8186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8187 " Enter BUILD_PALETTE:");
8189 if (logging != MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " image->columns=%.20g",(double) image->columns);
8193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8194 " image->rows=%.20g",(double) image->rows);
8195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8196 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " image->depth=%.20g",(double) image->depth);
8200 if (image->storage_class == PseudoClass && image->colormap != NULL)
8202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8203 " Original colormap:");
8204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8205 " i (red,green,blue,alpha)");
8207 for (i=0; i < 256; i++)
8209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8210 " %d (%d,%d,%d,%d)",
8212 (int) image->colormap[i].red,
8213 (int) image->colormap[i].green,
8214 (int) image->colormap[i].blue,
8215 (int) image->colormap[i].alpha);
8218 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8223 " %d (%d,%d,%d,%d)",
8225 (int) image->colormap[i].red,
8226 (int) image->colormap[i].green,
8227 (int) image->colormap[i].blue,
8228 (int) image->colormap[i].alpha);
8233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8234 " image->colors=%d",(int) image->colors);
8236 if (image->colors == 0)
8237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8238 " (zero means unknown)");
8240 if (ping_preserve_colormap == MagickFalse)
8241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8242 " Regenerate the colormap");
8247 number_semitransparent = 0;
8248 number_transparent = 0;
8250 for (y=0; y < (ssize_t) image->rows; y++)
8252 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8254 if (q == (Quantum *) NULL)
8257 for (x=0; x < (ssize_t) image->columns; x++)
8259 if (image->alpha_trait != BlendPixelTrait ||
8260 GetPixelAlpha(image,q) == OpaqueAlpha)
8262 if (number_opaque < 259)
8264 if (number_opaque == 0)
8266 GetPixelInfoPixel(image, q, opaque);
8267 opaque[0].alpha=OpaqueAlpha;
8271 for (i=0; i< (ssize_t) number_opaque; i++)
8273 if (IsPixelEquivalent(image,q, opaque+i))
8277 if (i == (ssize_t) number_opaque && number_opaque < 259)
8280 GetPixelInfoPixel(image, q, opaque+i);
8281 opaque[i].alpha=OpaqueAlpha;
8285 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8287 if (number_transparent < 259)
8289 if (number_transparent == 0)
8291 GetPixelInfoPixel(image, q, transparent);
8292 ping_trans_color.red=(unsigned short)
8293 GetPixelRed(image,q);
8294 ping_trans_color.green=(unsigned short)
8295 GetPixelGreen(image,q);
8296 ping_trans_color.blue=(unsigned short)
8297 GetPixelBlue(image,q);
8298 ping_trans_color.gray=(unsigned short)
8299 GetPixelGray(image,q);
8300 number_transparent = 1;
8303 for (i=0; i< (ssize_t) number_transparent; i++)
8305 if (IsPixelEquivalent(image,q, transparent+i))
8309 if (i == (ssize_t) number_transparent &&
8310 number_transparent < 259)
8312 number_transparent++;
8313 GetPixelInfoPixel(image,q,transparent+i);
8319 if (number_semitransparent < 259)
8321 if (number_semitransparent == 0)
8323 GetPixelInfoPixel(image,q,semitransparent);
8324 number_semitransparent = 1;
8327 for (i=0; i< (ssize_t) number_semitransparent; i++)
8329 if (IsPixelEquivalent(image,q, semitransparent+i)
8330 && GetPixelAlpha(image,q) ==
8331 semitransparent[i].alpha)
8335 if (i == (ssize_t) number_semitransparent &&
8336 number_semitransparent < 259)
8338 number_semitransparent++;
8339 GetPixelInfoPixel(image, q, semitransparent+i);
8343 q+=GetPixelChannels(image);
8347 if (mng_info->write_png8 == MagickFalse &&
8348 ping_exclude_bKGD == MagickFalse)
8350 /* Add the background color to the palette, if it
8351 * isn't already there.
8353 if (logging != MagickFalse)
8355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8356 " Check colormap for background (%d,%d,%d)",
8357 (int) image->background_color.red,
8358 (int) image->background_color.green,
8359 (int) image->background_color.blue);
8361 for (i=0; i<number_opaque; i++)
8363 if (opaque[i].red == image->background_color.red &&
8364 opaque[i].green == image->background_color.green &&
8365 opaque[i].blue == image->background_color.blue)
8368 if (number_opaque < 259 && i == number_opaque)
8370 opaque[i] = image->background_color;
8371 ping_background.index = i;
8373 if (logging != MagickFalse)
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 " background_color index is %d",(int) i);
8380 else if (logging != MagickFalse)
8381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8382 " No room in the colormap to add background color");
8385 image_colors=number_opaque+number_transparent+number_semitransparent;
8387 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8389 /* No room for the background color; remove it. */
8394 if (logging != MagickFalse)
8396 if (image_colors > 256)
8397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8398 " image has more than 256 colors");
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " image has %d colors",image_colors);
8405 if (ping_preserve_colormap != MagickFalse)
8408 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8410 ping_have_color=MagickFalse;
8411 ping_have_non_bw=MagickFalse;
8413 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8414 (IssRGBColorspace(image->colorspace) != MagickFalse))
8416 ping_have_color=MagickTrue;
8417 ping_have_non_bw=MagickTrue;
8420 if(image_colors > 256)
8422 for (y=0; y < (ssize_t) image->rows; y++)
8424 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8426 if (q == (Quantum *) NULL)
8430 for (x=0; x < (ssize_t) image->columns; x++)
8432 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8433 GetPixelRed(image,s) != GetPixelBlue(image,s))
8435 ping_have_color=MagickTrue;
8436 ping_have_non_bw=MagickTrue;
8439 s+=GetPixelChannels(image);
8442 if (ping_have_color != MagickFalse)
8445 /* Worst case is black-and-white; we are looking at every
8449 if (ping_have_non_bw == MagickFalse)
8452 for (x=0; x < (ssize_t) image->columns; x++)
8454 if (GetPixelRed(image,s) != 0 &&
8455 GetPixelRed(image,s) != QuantumRange)
8457 ping_have_non_bw=MagickTrue;
8460 s+=GetPixelChannels(image);
8467 if (image_colors < 257)
8473 * Initialize image colormap.
8476 if (logging != MagickFalse)
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8478 " Sort the new colormap");
8480 /* Sort palette, transparent first */;
8484 for (i=0; i<number_transparent; i++)
8485 colormap[n++] = transparent[i];
8487 for (i=0; i<number_semitransparent; i++)
8488 colormap[n++] = semitransparent[i];
8490 for (i=0; i<number_opaque; i++)
8491 colormap[n++] = opaque[i];
8493 ping_background.index +=
8494 (number_transparent + number_semitransparent);
8496 /* image_colors < 257; search the colormap instead of the pixels
8497 * to get ping_have_color and ping_have_non_bw
8501 if (ping_have_color == MagickFalse)
8503 if (colormap[i].red != colormap[i].green ||
8504 colormap[i].red != colormap[i].blue)
8506 ping_have_color=MagickTrue;
8507 ping_have_non_bw=MagickTrue;
8512 if (ping_have_non_bw == MagickFalse)
8514 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8515 ping_have_non_bw=MagickTrue;
8519 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8520 (number_transparent == 0 && number_semitransparent == 0)) &&
8521 (((mng_info->write_png_colortype-1) ==
8522 PNG_COLOR_TYPE_PALETTE) ||
8523 (mng_info->write_png_colortype == 0)))
8525 if (logging != MagickFalse)
8527 if (n != (ssize_t) image_colors)
8528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8529 " image_colors (%d) and n (%d) don't match",
8532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8533 " AcquireImageColormap");
8536 image->colors = image_colors;
8538 if (AcquireImageColormap(image,image_colors,exception) ==
8540 ThrowWriterException(ResourceLimitError,
8541 "MemoryAllocationFailed");
8543 for (i=0; i< (ssize_t) image_colors; i++)
8544 image->colormap[i] = colormap[i];
8546 if (logging != MagickFalse)
8548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8549 " image->colors=%d (%d)",
8550 (int) image->colors, image_colors);
8552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553 " Update the pixel indexes");
8556 /* Sync the pixel indices with the new colormap */
8558 for (y=0; y < (ssize_t) image->rows; y++)
8560 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8562 if (q == (Quantum *) NULL)
8565 for (x=0; x < (ssize_t) image->columns; x++)
8567 for (i=0; i< (ssize_t) image_colors; i++)
8569 if ((image->alpha_trait != BlendPixelTrait ||
8570 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8571 image->colormap[i].red == GetPixelRed(image,q) &&
8572 image->colormap[i].green == GetPixelGreen(image,q) &&
8573 image->colormap[i].blue == GetPixelBlue(image,q))
8575 SetPixelIndex(image,i,q);
8579 q+=GetPixelChannels(image);
8582 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8588 if (logging != MagickFalse)
8590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8591 " image->colors=%d", (int) image->colors);
8593 if (image->colormap != NULL)
8595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8596 " i (red,green,blue,alpha)");
8598 for (i=0; i < (ssize_t) image->colors; i++)
8600 if (i < 300 || i >= (ssize_t) image->colors - 10)
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " %d (%d,%d,%d,%d)",
8605 (int) image->colormap[i].red,
8606 (int) image->colormap[i].green,
8607 (int) image->colormap[i].blue,
8608 (int) image->colormap[i].alpha);
8613 if (number_transparent < 257)
8614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615 " number_transparent = %d",
8616 number_transparent);
8619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8620 " number_transparent > 256");
8622 if (number_opaque < 257)
8623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8624 " number_opaque = %d",
8628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629 " number_opaque > 256");
8631 if (number_semitransparent < 257)
8632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8633 " number_semitransparent = %d",
8634 number_semitransparent);
8637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8638 " number_semitransparent > 256");
8640 if (ping_have_non_bw == MagickFalse)
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642 " All pixels and the background are black or white");
8644 else if (ping_have_color == MagickFalse)
8645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8646 " All pixels and the background are gray");
8649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8650 " At least one pixel or the background is non-gray");
8652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8653 " Exit BUILD_PALETTE:");
8656 if (mng_info->write_png8 == MagickFalse)
8659 /* Make any reductions necessary for the PNG8 format */
8660 if (image_colors <= 256 &&
8661 image_colors != 0 && image->colormap != NULL &&
8662 number_semitransparent == 0 &&
8663 number_transparent <= 1)
8666 /* PNG8 can't have semitransparent colors so we threshold the
8667 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8668 * transparent color so if more than one is transparent we merge
8669 * them into image->background_color.
8671 if (number_semitransparent != 0 || number_transparent > 1)
8673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8674 " Thresholding the alpha channel to binary");
8676 for (y=0; y < (ssize_t) image->rows; y++)
8678 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8680 if (r == (Quantum *) NULL)
8683 for (x=0; x < (ssize_t) image->columns; x++)
8685 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8687 SetPixelInfoPixel(image,&image->background_color,r);
8688 SetPixelAlpha(image,TransparentAlpha,r);
8691 SetPixelAlpha(image,OpaqueAlpha,r);
8692 r+=GetPixelChannels(image);
8695 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8698 if (image_colors != 0 && image_colors <= 256 &&
8699 image->colormap != NULL)
8700 for (i=0; i<image_colors; i++)
8701 image->colormap[i].alpha =
8702 (image->colormap[i].alpha > TransparentAlpha/2 ?
8703 TransparentAlpha : OpaqueAlpha);
8708 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8709 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8710 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8713 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8715 if (logging != MagickFalse)
8716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8717 " Quantizing the background color to 4-4-4");
8719 tried_444 = MagickTrue;
8721 LBR04PacketRGB(image->background_color);
8723 if (logging != MagickFalse)
8724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8725 " Quantizing the pixel colors to 4-4-4");
8727 if (image->colormap == NULL)
8729 for (y=0; y < (ssize_t) image->rows; y++)
8731 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8733 if (r == (Quantum *) NULL)
8736 for (x=0; x < (ssize_t) image->columns; x++)
8738 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8740 r+=GetPixelChannels(image);
8743 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8748 else /* Should not reach this; colormap already exists and
8751 if (logging != MagickFalse)
8752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753 " Quantizing the colormap to 4-4-4");
8755 for (i=0; i<image_colors; i++)
8757 LBR04PacketRGB(image->colormap[i]);
8763 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8765 if (logging != MagickFalse)
8766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8767 " Quantizing the background color to 3-3-3");
8769 tried_333 = MagickTrue;
8771 LBR03PacketRGB(image->background_color);
8773 if (logging != MagickFalse)
8774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8775 " Quantizing the pixel colors to 3-3-3-1");
8777 if (image->colormap == NULL)
8779 for (y=0; y < (ssize_t) image->rows; y++)
8781 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8783 if (r == (Quantum *) NULL)
8786 for (x=0; x < (ssize_t) image->columns; x++)
8788 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8790 r+=GetPixelChannels(image);
8793 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8798 else /* Should not reach this; colormap already exists and
8801 if (logging != MagickFalse)
8802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8803 " Quantizing the colormap to 3-3-3-1");
8804 for (i=0; i<image_colors; i++)
8806 LBR03PacketRGB(image->colormap[i]);
8812 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8814 if (logging != MagickFalse)
8815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8816 " Quantizing the background color to 3-3-2");
8818 tried_332 = MagickTrue;
8820 /* Red and green were already done so we only quantize the blue
8824 LBR02PacketBlue(image->background_color);
8826 if (logging != MagickFalse)
8827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828 " Quantizing the pixel colors to 3-3-2-1");
8830 if (image->colormap == NULL)
8832 for (y=0; y < (ssize_t) image->rows; y++)
8834 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8836 if (r == (Quantum *) NULL)
8839 for (x=0; x < (ssize_t) image->columns; x++)
8841 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8843 r+=GetPixelChannels(image);
8846 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8851 else /* Should not reach this; colormap already exists and
8854 if (logging != MagickFalse)
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " Quantizing the colormap to 3-3-2-1");
8857 for (i=0; i<image_colors; i++)
8859 LBR02PacketBlue(image->colormap[i]);
8866 if (image_colors == 0 || image_colors > 256)
8868 /* Take care of special case with 256 colors + 1 transparent
8869 * color. We don't need to quantize to 2-3-2-1; we only need to
8870 * eliminate one color, so we'll merge the two darkest red
8871 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8873 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8874 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8875 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8877 image->background_color.red=ScaleCharToQuantum(0x24);
8880 if (image->colormap == NULL)
8882 for (y=0; y < (ssize_t) image->rows; y++)
8884 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8886 if (r == (Quantum *) NULL)
8889 for (x=0; x < (ssize_t) image->columns; x++)
8891 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8892 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8893 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8894 GetPixelAlpha(image,r) == OpaqueAlpha)
8896 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8898 r+=GetPixelChannels(image);
8901 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8909 for (i=0; i<image_colors; i++)
8911 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8912 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8913 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8915 image->colormap[i].red=ScaleCharToQuantum(0x24);
8922 /* END OF BUILD_PALETTE */
8924 /* If we are excluding the tRNS chunk and there is transparency,
8925 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8928 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8929 (number_transparent != 0 || number_semitransparent != 0))
8931 unsigned int colortype=mng_info->write_png_colortype;
8933 if (ping_have_color == MagickFalse)
8934 mng_info->write_png_colortype = 5;
8937 mng_info->write_png_colortype = 7;
8939 if (colortype != 0 &&
8940 mng_info->write_png_colortype != colortype)
8941 ping_need_colortype_warning=MagickTrue;
8945 /* See if cheap transparency is possible. It is only possible
8946 * when there is a single transparent color, no semitransparent
8947 * color, and no opaque color that has the same RGB components
8948 * as the transparent color. We only need this information if
8949 * we are writing a PNG with colortype 0 or 2, and we have not
8950 * excluded the tRNS chunk.
8952 if (number_transparent == 1 &&
8953 mng_info->write_png_colortype < 4)
8955 ping_have_cheap_transparency = MagickTrue;
8957 if (number_semitransparent != 0)
8958 ping_have_cheap_transparency = MagickFalse;
8960 else if (image_colors == 0 || image_colors > 256 ||
8961 image->colormap == NULL)
8963 register const Quantum
8966 for (y=0; y < (ssize_t) image->rows; y++)
8968 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8970 if (q == (Quantum *) NULL)
8973 for (x=0; x < (ssize_t) image->columns; x++)
8975 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8976 (unsigned short) GetPixelRed(image,q) ==
8977 ping_trans_color.red &&
8978 (unsigned short) GetPixelGreen(image,q) ==
8979 ping_trans_color.green &&
8980 (unsigned short) GetPixelBlue(image,q) ==
8981 ping_trans_color.blue)
8983 ping_have_cheap_transparency = MagickFalse;
8987 q+=GetPixelChannels(image);
8990 if (ping_have_cheap_transparency == MagickFalse)
8996 /* Assuming that image->colormap[0] is the one transparent color
8997 * and that all others are opaque.
8999 if (image_colors > 1)
9000 for (i=1; i<image_colors; i++)
9001 if (image->colormap[i].red == image->colormap[0].red &&
9002 image->colormap[i].green == image->colormap[0].green &&
9003 image->colormap[i].blue == image->colormap[0].blue)
9005 ping_have_cheap_transparency = MagickFalse;
9010 if (logging != MagickFalse)
9012 if (ping_have_cheap_transparency == MagickFalse)
9013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014 " Cheap transparency is not possible.");
9017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018 " Cheap transparency is possible.");
9022 ping_have_cheap_transparency = MagickFalse;
9024 image_depth=image->depth;
9026 quantum_info = (QuantumInfo *) NULL;
9028 image_colors=(int) image->colors;
9029 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9031 mng_info->IsPalette=image->storage_class == PseudoClass &&
9032 image_colors <= 256 && image->colormap != NULL;
9034 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9035 (image->colors == 0 || image->colormap == NULL))
9037 image_info=DestroyImageInfo(image_info);
9038 image=DestroyImage(image);
9039 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9040 "Cannot write PNG8 or color-type 3; colormap is NULL",
9041 "`%s'",IMimage->filename);
9042 return(MagickFalse);
9046 Allocate the PNG structures
9048 #ifdef PNG_USER_MEM_SUPPORTED
9049 error_info.image=image;
9050 error_info.exception=exception;
9051 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9052 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9053 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9056 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9057 MagickPNGErrorHandler,MagickPNGWarningHandler);
9060 if (ping == (png_struct *) NULL)
9061 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9063 ping_info=png_create_info_struct(ping);
9065 if (ping_info == (png_info *) NULL)
9067 png_destroy_write_struct(&ping,(png_info **) NULL);
9068 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9071 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9072 ping_pixels=(unsigned char *) NULL;
9074 if (setjmp(png_jmpbuf(ping)))
9080 if (image_info->verbose)
9081 (void) printf("PNG write has failed.\n");
9083 png_destroy_write_struct(&ping,&ping_info);
9084 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9085 UnlockSemaphoreInfo(ping_semaphore);
9088 if (ping_pixels != (unsigned char *) NULL)
9089 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9091 if (quantum_info != (QuantumInfo *) NULL)
9092 quantum_info=DestroyQuantumInfo(quantum_info);
9094 if (ping_have_blob != MagickFalse)
9095 (void) CloseBlob(image);
9096 image_info=DestroyImageInfo(image_info);
9097 image=DestroyImage(image);
9098 return(MagickFalse);
9101 /* { For navigation to end of SETJMP-protected block. Within this
9102 * block, use png_error() instead of Throwing an Exception, to ensure
9103 * that libpng is able to clean up, and that the semaphore is unlocked.
9106 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9107 LockSemaphoreInfo(ping_semaphore);
9111 Prepare PNG for writing.
9114 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9115 if (mng_info->write_mng)
9117 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9118 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9119 /* Disable new libpng-1.5.10 feature when writing a MNG because
9120 * zero-length PLTE is OK
9122 png_set_check_for_invalid_index (ping, 0);
9127 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9128 if (mng_info->write_mng)
9129 png_permit_empty_plte(ping,MagickTrue);
9136 ping_width=(png_uint_32) image->columns;
9137 ping_height=(png_uint_32) image->rows;
9139 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9142 if (mng_info->write_png48 || mng_info->write_png64)
9145 if (mng_info->write_png_depth != 0)
9146 image_depth=mng_info->write_png_depth;
9148 /* Adjust requested depth to next higher valid depth if necessary */
9149 if (image_depth > 8)
9152 if ((image_depth > 4) && (image_depth < 8))
9155 if (image_depth == 3)
9158 if (logging != MagickFalse)
9160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9161 " width=%.20g",(double) ping_width);
9162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9163 " height=%.20g",(double) ping_height);
9164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165 " image_matte=%.20g",(double) image->alpha_trait);
9166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9167 " image->depth=%.20g",(double) image->depth);
9168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9169 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9172 save_image_depth=image_depth;
9173 ping_bit_depth=(png_byte) save_image_depth;
9176 #if defined(PNG_pHYs_SUPPORTED)
9177 if (ping_exclude_pHYs == MagickFalse)
9179 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9180 (!mng_info->write_mng || !mng_info->equal_physs))
9182 if (logging != MagickFalse)
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184 " Setting up pHYs chunk");
9186 if (image->units == PixelsPerInchResolution)
9188 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9189 ping_pHYs_x_resolution=
9190 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9191 ping_pHYs_y_resolution=
9192 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9195 else if (image->units == PixelsPerCentimeterResolution)
9197 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9198 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9199 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9204 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9205 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9206 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9209 if (logging != MagickFalse)
9210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9211 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9212 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9213 (int) ping_pHYs_unit_type);
9214 ping_have_pHYs = MagickTrue;
9219 if (ping_exclude_bKGD == MagickFalse)
9221 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9227 if (ping_bit_depth == 8)
9230 if (ping_bit_depth == 4)
9233 if (ping_bit_depth == 2)
9236 if (ping_bit_depth == 1)
9239 ping_background.red=(png_uint_16)
9240 (ScaleQuantumToShort(image->background_color.red) & mask);
9242 ping_background.green=(png_uint_16)
9243 (ScaleQuantumToShort(image->background_color.green) & mask);
9245 ping_background.blue=(png_uint_16)
9246 (ScaleQuantumToShort(image->background_color.blue) & mask);
9248 ping_background.gray=(png_uint_16) ping_background.green;
9251 if (logging != MagickFalse)
9253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9254 " Setting up bKGD chunk (1)");
9255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256 " background_color index is %d",
9257 (int) ping_background.index);
9259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9260 " ping_bit_depth=%d",ping_bit_depth);
9263 ping_have_bKGD = MagickTrue;
9267 Select the color type.
9272 if (mng_info->IsPalette && mng_info->write_png8)
9275 /* To do: make this a function cause it's used twice, except
9276 for reducing the sample depth from 8. */
9278 number_colors=image_colors;
9280 ping_have_tRNS=MagickFalse;
9285 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9287 if (logging != MagickFalse)
9288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9289 " Setting up PLTE chunk with %d colors (%d)",
9290 number_colors, image_colors);
9292 for (i=0; i < (ssize_t) number_colors; i++)
9294 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9295 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9296 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9297 if (logging != MagickFalse)
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 #if MAGICKCORE_QUANTUM_DEPTH == 8
9300 " %3ld (%3d,%3d,%3d)",
9302 " %5ld (%5d,%5d,%5d)",
9304 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9308 ping_have_PLTE=MagickTrue;
9309 image_depth=ping_bit_depth;
9312 if (matte != MagickFalse)
9315 Identify which colormap entry is transparent.
9317 assert(number_colors <= 256);
9318 assert(image->colormap != NULL);
9320 for (i=0; i < (ssize_t) number_transparent; i++)
9321 ping_trans_alpha[i]=0;
9324 ping_num_trans=(unsigned short) (number_transparent +
9325 number_semitransparent);
9327 if (ping_num_trans == 0)
9328 ping_have_tRNS=MagickFalse;
9331 ping_have_tRNS=MagickTrue;
9334 if (ping_exclude_bKGD == MagickFalse)
9337 * Identify which colormap entry is the background color.
9340 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9341 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9344 ping_background.index=(png_byte) i;
9346 if (logging != MagickFalse)
9348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349 " background_color index is %d",
9350 (int) ping_background.index);
9353 } /* end of write_png8 */
9355 else if (mng_info->write_png_colortype == 1)
9357 image_matte=MagickFalse;
9358 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9361 else if (mng_info->write_png24 || mng_info->write_png48 ||
9362 mng_info->write_png_colortype == 3)
9364 image_matte=MagickFalse;
9365 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9368 else if (mng_info->write_png32 || mng_info->write_png64 ||
9369 mng_info->write_png_colortype == 7)
9371 image_matte=MagickTrue;
9372 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9375 else /* mng_info->write_pngNN not specified */
9377 image_depth=ping_bit_depth;
9379 if (mng_info->write_png_colortype != 0)
9381 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9383 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9384 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9385 image_matte=MagickTrue;
9388 image_matte=MagickFalse;
9390 if (logging != MagickFalse)
9391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9392 " PNG colortype %d was specified:",(int) ping_color_type);
9395 else /* write_png_colortype not specified */
9397 if (logging != MagickFalse)
9398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9399 " Selecting PNG colortype:");
9401 ping_color_type=(png_byte) ((matte != MagickFalse)?
9402 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9404 if (image_info->type == TrueColorType)
9406 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9407 image_matte=MagickFalse;
9410 if (image_info->type == TrueColorMatteType)
9412 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9413 image_matte=MagickTrue;
9416 if (image_info->type == PaletteType ||
9417 image_info->type == PaletteMatteType)
9418 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9420 if (mng_info->write_png_colortype == 0 &&
9421 (image_info->type == UndefinedType ||
9422 image_info->type == OptimizeType))
9424 if (ping_have_color == MagickFalse)
9426 if (image_matte == MagickFalse)
9428 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9429 image_matte=MagickFalse;
9434 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9435 image_matte=MagickTrue;
9440 if (image_matte == MagickFalse)
9442 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9443 image_matte=MagickFalse;
9448 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9449 image_matte=MagickTrue;
9456 if (logging != MagickFalse)
9457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9458 " Selected PNG colortype=%d",ping_color_type);
9460 if (ping_bit_depth < 8)
9462 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9463 ping_color_type == PNG_COLOR_TYPE_RGB ||
9464 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9468 old_bit_depth=ping_bit_depth;
9470 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9472 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9476 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9481 if (image->colors == 0)
9484 png_error(ping,"image has 0 colors");
9487 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9488 ping_bit_depth <<= 1;
9491 if (logging != MagickFalse)
9493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9494 " Number of colors: %.20g",(double) image_colors);
9496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9497 " Tentative PNG bit depth: %d",ping_bit_depth);
9500 if (ping_bit_depth < (int) mng_info->write_png_depth)
9501 ping_bit_depth = mng_info->write_png_depth;
9504 image_depth=ping_bit_depth;
9506 if (logging != MagickFalse)
9508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9509 " Tentative PNG color type: %s (%.20g)",
9510 PngColorTypeToString(ping_color_type),
9511 (double) ping_color_type);
9513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9514 " image_info->type: %.20g",(double) image_info->type);
9516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9517 " image_depth: %.20g",(double) image_depth);
9519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9521 " image->depth: %.20g",(double) image->depth);
9523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9524 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9527 if (matte != MagickFalse)
9529 if (mng_info->IsPalette)
9531 if (mng_info->write_png_colortype == 0)
9533 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9535 if (ping_have_color != MagickFalse)
9536 ping_color_type=PNG_COLOR_TYPE_RGBA;
9540 * Determine if there is any transparent color.
9542 if (number_transparent + number_semitransparent == 0)
9545 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9548 image_matte=MagickFalse;
9550 if (mng_info->write_png_colortype == 0)
9551 ping_color_type&=0x03;
9561 if (ping_bit_depth == 8)
9564 if (ping_bit_depth == 4)
9567 if (ping_bit_depth == 2)
9570 if (ping_bit_depth == 1)
9573 ping_trans_color.red=(png_uint_16)
9574 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9576 ping_trans_color.green=(png_uint_16)
9577 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9579 ping_trans_color.blue=(png_uint_16)
9580 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9582 ping_trans_color.gray=(png_uint_16)
9583 (ScaleQuantumToShort(GetPixelInfoIntensity(
9584 image->colormap)) & mask);
9586 ping_trans_color.index=(png_byte) 0;
9588 ping_have_tRNS=MagickTrue;
9591 if (ping_have_tRNS != MagickFalse)
9594 * Determine if there is one and only one transparent color
9595 * and if so if it is fully transparent.
9597 if (ping_have_cheap_transparency == MagickFalse)
9598 ping_have_tRNS=MagickFalse;
9601 if (ping_have_tRNS != MagickFalse)
9603 if (mng_info->write_png_colortype == 0)
9604 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9606 if (image_depth == 8)
9608 ping_trans_color.red&=0xff;
9609 ping_trans_color.green&=0xff;
9610 ping_trans_color.blue&=0xff;
9611 ping_trans_color.gray&=0xff;
9617 if (image_depth == 8)
9619 ping_trans_color.red&=0xff;
9620 ping_trans_color.green&=0xff;
9621 ping_trans_color.blue&=0xff;
9622 ping_trans_color.gray&=0xff;
9629 if (ping_have_tRNS != MagickFalse)
9630 image_matte=MagickFalse;
9632 if ((mng_info->IsPalette) &&
9633 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9634 ping_have_color == MagickFalse &&
9635 (image_matte == MagickFalse || image_depth >= 8))
9639 if (image_matte != MagickFalse)
9640 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9642 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9644 ping_color_type=PNG_COLOR_TYPE_GRAY;
9646 if (save_image_depth == 16 && image_depth == 8)
9648 if (logging != MagickFalse)
9650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9651 " Scaling ping_trans_color (0)");
9653 ping_trans_color.gray*=0x0101;
9657 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9658 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9660 if ((image_colors == 0) ||
9661 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9662 image_colors=(int) (one << image_depth);
9664 if (image_depth > 8)
9670 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9672 if(!mng_info->write_png_depth)
9676 while ((int) (one << ping_bit_depth)
9677 < (ssize_t) image_colors)
9678 ping_bit_depth <<= 1;
9682 else if (ping_color_type ==
9683 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9684 mng_info->IsPalette)
9686 /* Check if grayscale is reducible */
9689 depth_4_ok=MagickTrue,
9690 depth_2_ok=MagickTrue,
9691 depth_1_ok=MagickTrue;
9693 for (i=0; i < (ssize_t) image_colors; i++)
9698 intensity=ScaleQuantumToChar(image->colormap[i].red);
9700 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9701 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9702 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9703 depth_2_ok=depth_1_ok=MagickFalse;
9704 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9705 depth_1_ok=MagickFalse;
9708 if (depth_1_ok && mng_info->write_png_depth <= 1)
9711 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9714 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9719 image_depth=ping_bit_depth;
9724 if (mng_info->IsPalette)
9726 number_colors=image_colors;
9728 if (image_depth <= 8)
9733 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9735 if (mng_info->have_write_global_plte && matte == MagickFalse)
9737 png_set_PLTE(ping,ping_info,NULL,0);
9739 if (logging != MagickFalse)
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " Setting up empty PLTE chunk");
9746 for (i=0; i < (ssize_t) number_colors; i++)
9748 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9749 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9750 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9753 if (logging != MagickFalse)
9754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755 " Setting up PLTE chunk with %d colors",
9758 ping_have_PLTE=MagickTrue;
9761 /* color_type is PNG_COLOR_TYPE_PALETTE */
9762 if (mng_info->write_png_depth == 0)
9770 while ((one << ping_bit_depth) < (size_t) number_colors)
9771 ping_bit_depth <<= 1;
9776 if (matte != MagickFalse)
9779 * Set up trans_colors array.
9781 assert(number_colors <= 256);
9783 ping_num_trans=(unsigned short) (number_transparent +
9784 number_semitransparent);
9786 if (ping_num_trans == 0)
9787 ping_have_tRNS=MagickFalse;
9791 if (logging != MagickFalse)
9793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794 " Scaling ping_trans_color (1)");
9796 ping_have_tRNS=MagickTrue;
9798 for (i=0; i < ping_num_trans; i++)
9800 ping_trans_alpha[i]= (png_byte)
9801 ScaleQuantumToChar(image->colormap[i].alpha);
9811 if (image_depth < 8)
9814 if ((save_image_depth == 16) && (image_depth == 8))
9816 if (logging != MagickFalse)
9818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9819 " Scaling ping_trans_color from (%d,%d,%d)",
9820 (int) ping_trans_color.red,
9821 (int) ping_trans_color.green,
9822 (int) ping_trans_color.blue);
9825 ping_trans_color.red*=0x0101;
9826 ping_trans_color.green*=0x0101;
9827 ping_trans_color.blue*=0x0101;
9828 ping_trans_color.gray*=0x0101;
9830 if (logging != MagickFalse)
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 (int) ping_trans_color.red,
9835 (int) ping_trans_color.green,
9836 (int) ping_trans_color.blue);
9841 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9842 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9845 Adjust background and transparency samples in sub-8-bit grayscale files.
9847 if (ping_bit_depth < 8 && ping_color_type ==
9848 PNG_COLOR_TYPE_GRAY)
9856 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9858 if (ping_exclude_bKGD == MagickFalse)
9861 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9862 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9863 &image->background_color))) +.5)));
9865 if (logging != MagickFalse)
9866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9867 " Setting up bKGD chunk (2)");
9868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9869 " background_color index is %d",
9870 (int) ping_background.index);
9872 ping_have_bKGD = MagickTrue;
9875 if (logging != MagickFalse)
9876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9877 " Scaling ping_trans_color.gray from %d",
9878 (int)ping_trans_color.gray);
9880 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9881 ping_trans_color.gray)+.5);
9883 if (logging != MagickFalse)
9884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9885 " to %d", (int)ping_trans_color.gray);
9888 if (ping_exclude_bKGD == MagickFalse)
9890 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9893 Identify which colormap entry is the background color.
9896 number_colors=image_colors;
9898 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9899 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9902 ping_background.index=(png_byte) i;
9904 if (logging != MagickFalse)
9906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9907 " Setting up bKGD chunk with index=%d",(int) i);
9910 if (i < (ssize_t) number_colors)
9912 ping_have_bKGD = MagickTrue;
9914 if (logging != MagickFalse)
9916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9917 " background =(%d,%d,%d)",
9918 (int) ping_background.red,
9919 (int) ping_background.green,
9920 (int) ping_background.blue);
9924 else /* Can't happen */
9926 if (logging != MagickFalse)
9927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928 " No room in PLTE to add bKGD color");
9929 ping_have_bKGD = MagickFalse;
9934 if (logging != MagickFalse)
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9939 Initialize compression level and filtering.
9941 if (logging != MagickFalse)
9943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9944 " Setting up deflate compression");
9946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9947 " Compression buffer size: 32768");
9950 png_set_compression_buffer_size(ping,32768L);
9952 if (logging != MagickFalse)
9953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9954 " Compression mem level: 9");
9956 png_set_compression_mem_level(ping, 9);
9958 /* Untangle the "-quality" setting:
9960 Undefined is 0; the default is used.
9965 0: Use Z_HUFFMAN_ONLY strategy with the
9966 zlib default compression level
9968 1-9: the zlib compression level
9972 0-4: the PNG filter method
9974 5: libpng adaptive filtering if compression level > 5
9975 libpng filter type "none" if compression level <= 5
9976 or if image is grayscale or palette
9978 6: libpng adaptive filtering
9980 7: "LOCO" filtering (intrapixel differing) if writing
9981 a MNG, othewise "none". Did not work in IM-6.7.0-9
9982 and earlier because of a missing "else".
9984 8: Z_RLE strategy, all filters
9985 Unused prior to IM-6.7.0-10, was same as 6
9987 9: Z_RLE strategy, no PNG filters
9988 Unused prior to IM-6.7.0-10, was same as 6
9990 Note that using the -quality option, not all combinations of
9991 PNG filter type, zlib compression level, and zlib compression
9992 strategy are possible. This will be addressed soon in a
9993 release that accomodates "-define png:compression-strategy", etc.
9997 quality=image->quality == UndefinedCompressionQuality ? 75UL :
10002 if (mng_info->write_png_compression_strategy == 0)
10003 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10006 else if (mng_info->write_png_compression_level == 0)
10011 level=(int) MagickMin((ssize_t) quality/10,9);
10013 mng_info->write_png_compression_level = level+1;
10016 if (mng_info->write_png_compression_strategy == 0)
10018 if ((quality %10) == 8 || (quality %10) == 9)
10019 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10020 mng_info->write_png_compression_strategy=Z_RLE+1;
10022 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10026 if (mng_info->write_png_compression_filter == 0)
10027 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10029 if (logging != MagickFalse)
10031 if (mng_info->write_png_compression_level)
10032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10033 " Compression level: %d",
10034 (int) mng_info->write_png_compression_level-1);
10036 if (mng_info->write_png_compression_strategy)
10037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10038 " Compression strategy: %d",
10039 (int) mng_info->write_png_compression_strategy-1);
10041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10042 " Setting up filtering");
10044 if (mng_info->write_png_compression_filter == 6)
10045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10046 " Base filter method: ADAPTIVE");
10047 else if (mng_info->write_png_compression_filter == 0 ||
10048 mng_info->write_png_compression_filter == 1)
10049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050 " Base filter method: NONE");
10052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10053 " Base filter method: %d",
10054 (int) mng_info->write_png_compression_filter-1);
10057 if (mng_info->write_png_compression_level != 0)
10058 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10060 if (mng_info->write_png_compression_filter == 6)
10062 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10063 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10065 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10067 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10069 else if (mng_info->write_png_compression_filter == 7 ||
10070 mng_info->write_png_compression_filter == 10)
10071 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10073 else if (mng_info->write_png_compression_filter == 8)
10075 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10076 if (mng_info->write_mng)
10078 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10079 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10080 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10083 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10086 else if (mng_info->write_png_compression_filter == 9)
10087 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10089 else if (mng_info->write_png_compression_filter != 0)
10090 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10091 mng_info->write_png_compression_filter-1);
10093 if (mng_info->write_png_compression_strategy != 0)
10094 png_set_compression_strategy(ping,
10095 mng_info->write_png_compression_strategy-1);
10097 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10098 if (ping_exclude_sRGB != MagickFalse ||
10099 (image->rendering_intent == UndefinedIntent))
10101 if ((ping_exclude_tEXt == MagickFalse ||
10102 ping_exclude_zTXt == MagickFalse) &&
10103 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10105 ResetImageProfileIterator(image);
10106 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10108 profile=GetImageProfile(image,name);
10110 if (profile != (StringInfo *) NULL)
10112 #ifdef PNG_WRITE_iCCP_SUPPORTED
10113 if ((LocaleCompare(name,"ICC") == 0) ||
10114 (LocaleCompare(name,"ICM") == 0))
10117 if (ping_exclude_iCCP == MagickFalse)
10119 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10120 #if (PNG_LIBPNG_VER < 10500)
10121 (png_charp) GetStringInfoDatum(profile),
10123 (png_const_bytep) GetStringInfoDatum(profile),
10125 (png_uint_32) GetStringInfoLength(profile));
10131 if (ping_exclude_zCCP == MagickFalse)
10133 Magick_png_write_raw_profile(image_info,ping,ping_info,
10134 (unsigned char *) name,(unsigned char *) name,
10135 GetStringInfoDatum(profile),
10136 (png_uint_32) GetStringInfoLength(profile));
10140 if (logging != MagickFalse)
10141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10142 " Setting up text chunk with %s profile",name);
10144 name=GetNextImageProfile(image);
10149 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10150 if ((mng_info->have_write_global_srgb == 0) &&
10151 (image->rendering_intent != UndefinedIntent))
10153 if (ping_exclude_sRGB == MagickFalse)
10156 Note image rendering intent.
10158 if (logging != MagickFalse)
10159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10160 " Setting up sRGB chunk");
10162 (void) png_set_sRGB(ping,ping_info,(
10163 Magick_RenderingIntent_to_PNG_RenderingIntent(
10164 image->rendering_intent)));
10168 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10171 if (ping_exclude_gAMA == MagickFalse &&
10172 (ping_exclude_sRGB == MagickFalse ||
10173 (image->gamma < .45 || image->gamma > .46)))
10175 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10179 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10181 if (logging != MagickFalse)
10182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10183 " Setting up gAMA chunk");
10185 png_set_gAMA(ping,ping_info,image->gamma);
10189 if (ping_exclude_cHRM == MagickFalse)
10191 if ((mng_info->have_write_global_chrm == 0) &&
10192 (image->chromaticity.red_primary.x != 0.0))
10195 Note image chromaticity.
10196 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10204 wp=image->chromaticity.white_point;
10205 rp=image->chromaticity.red_primary;
10206 gp=image->chromaticity.green_primary;
10207 bp=image->chromaticity.blue_primary;
10209 if (logging != MagickFalse)
10210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10211 " Setting up cHRM chunk");
10213 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10219 ping_interlace_method=image_info->interlace != NoInterlace;
10221 if (mng_info->write_mng)
10222 png_set_sig_bytes(ping,8);
10224 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10226 if (mng_info->write_png_colortype != 0)
10228 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10229 if (ping_have_color != MagickFalse)
10231 ping_color_type = PNG_COLOR_TYPE_RGB;
10233 if (ping_bit_depth < 8)
10237 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10238 if (ping_have_color != MagickFalse)
10239 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10242 if (ping_need_colortype_warning != MagickFalse ||
10243 ((mng_info->write_png_depth &&
10244 (int) mng_info->write_png_depth != ping_bit_depth) ||
10245 (mng_info->write_png_colortype &&
10246 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10247 mng_info->write_png_colortype != 7 &&
10248 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10250 if (logging != MagickFalse)
10252 if (ping_need_colortype_warning != MagickFalse)
10254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10255 " Image has transparency but tRNS chunk was excluded");
10258 if (mng_info->write_png_depth)
10260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10261 " Defined png:bit-depth=%u, Computed depth=%u",
10262 mng_info->write_png_depth,
10266 if (mng_info->write_png_colortype)
10268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10269 " Defined png:color-type=%u, Computed color type=%u",
10270 mng_info->write_png_colortype-1,
10276 "Cannot write image with defined png:bit-depth or png:color-type.");
10279 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10281 /* Add an opaque matte channel */
10282 image->alpha_trait = BlendPixelTrait;
10283 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10285 if (logging != MagickFalse)
10286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287 " Added an opaque matte channel");
10290 if (number_transparent != 0 || number_semitransparent != 0)
10292 if (ping_color_type < 4)
10294 ping_have_tRNS=MagickTrue;
10295 if (logging != MagickFalse)
10296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10297 " Setting ping_have_tRNS=MagickTrue.");
10301 if (logging != MagickFalse)
10302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10303 " Writing PNG header chunks");
10305 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10306 ping_bit_depth,ping_color_type,
10307 ping_interlace_method,ping_compression_method,
10308 ping_filter_method);
10310 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10312 png_set_PLTE(ping,ping_info,palette,number_colors);
10314 if (logging != MagickFalse)
10316 for (i=0; i< (ssize_t) number_colors; i++)
10318 if (i < ping_num_trans)
10319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10320 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10322 (int) palette[i].red,
10323 (int) palette[i].green,
10324 (int) palette[i].blue,
10326 (int) ping_trans_alpha[i]);
10328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329 " PLTE[%d] = (%d,%d,%d)",
10331 (int) palette[i].red,
10332 (int) palette[i].green,
10333 (int) palette[i].blue);
10338 if (ping_exclude_bKGD == MagickFalse)
10340 if (ping_have_bKGD != MagickFalse)
10342 png_set_bKGD(ping,ping_info,&ping_background);
10345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346 " Setting up bKGD chunk");
10347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " background color = (%d,%d,%d)",
10349 (int) ping_background.red,
10350 (int) ping_background.green,
10351 (int) ping_background.blue);
10352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10353 " index = %d, gray=%d",
10354 (int) ping_background.index,
10355 (int) ping_background.gray);
10360 if (ping_exclude_pHYs == MagickFalse)
10362 if (ping_have_pHYs != MagickFalse)
10364 png_set_pHYs(ping,ping_info,
10365 ping_pHYs_x_resolution,
10366 ping_pHYs_y_resolution,
10367 ping_pHYs_unit_type);
10371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10372 " Setting up pHYs chunk");
10373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374 " x_resolution=%lu",
10375 (unsigned long) ping_pHYs_x_resolution);
10376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377 " y_resolution=%lu",
10378 (unsigned long) ping_pHYs_y_resolution);
10379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381 (unsigned long) ping_pHYs_unit_type);
10386 #if defined(PNG_oFFs_SUPPORTED)
10387 if (ping_exclude_oFFs == MagickFalse)
10389 if (image->page.x || image->page.y)
10391 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10392 (png_int_32) image->page.y, 0);
10394 if (logging != MagickFalse)
10395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10397 (int) image->page.x, (int) image->page.y);
10402 if (mng_info->need_blob != MagickFalse)
10404 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10406 png_error(ping,"WriteBlob Failed");
10408 ping_have_blob=MagickTrue;
10411 png_write_info_before_PLTE(ping, ping_info);
10413 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10415 if (logging != MagickFalse)
10417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10418 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10421 if (ping_color_type == 3)
10422 (void) png_set_tRNS(ping, ping_info,
10429 (void) png_set_tRNS(ping, ping_info,
10432 &ping_trans_color);
10434 if (logging != MagickFalse)
10436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10437 " tRNS color =(%d,%d,%d)",
10438 (int) ping_trans_color.red,
10439 (int) ping_trans_color.green,
10440 (int) ping_trans_color.blue);
10445 /* write any png-chunk-b profiles */
10446 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10448 png_write_info(ping,ping_info);
10450 /* write any PNG-chunk-m profiles */
10451 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10453 if (ping_exclude_vpAg == MagickFalse)
10455 if ((image->page.width != 0 && image->page.width != image->columns) ||
10456 (image->page.height != 0 && image->page.height != image->rows))
10461 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10462 PNGType(chunk,mng_vpAg);
10463 LogPNGChunk(logging,mng_vpAg,9L);
10464 PNGLong(chunk+4,(png_uint_32) image->page.width);
10465 PNGLong(chunk+8,(png_uint_32) image->page.height);
10466 chunk[12]=0; /* unit = pixels */
10467 (void) WriteBlob(image,13,chunk);
10468 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10472 #if (PNG_LIBPNG_VER == 10206)
10473 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10474 #define PNG_HAVE_IDAT 0x04
10475 ping->mode |= PNG_HAVE_IDAT;
10476 #undef PNG_HAVE_IDAT
10479 png_set_packing(ping);
10483 rowbytes=image->columns;
10484 if (image_depth > 8)
10486 switch (ping_color_type)
10488 case PNG_COLOR_TYPE_RGB:
10492 case PNG_COLOR_TYPE_GRAY_ALPHA:
10496 case PNG_COLOR_TYPE_RGBA:
10504 if (logging != MagickFalse)
10506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10507 " Writing PNG image data");
10509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10512 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10513 sizeof(*ping_pixels));
10515 if (ping_pixels == (unsigned char *) NULL)
10516 png_error(ping,"Allocation of memory for pixels failed");
10519 Initialize image scanlines.
10521 quantum_info=AcquireQuantumInfo(image_info,image);
10522 if (quantum_info == (QuantumInfo *) NULL)
10523 png_error(ping,"Memory allocation for quantum_info failed");
10524 quantum_info->format=UndefinedQuantumFormat;
10525 quantum_info->depth=image_depth;
10526 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10527 num_passes=png_set_interlace_handling(ping);
10529 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10530 !mng_info->write_png48 && !mng_info->write_png64 &&
10531 !mng_info->write_png32) &&
10532 (mng_info->IsPalette ||
10533 (image_info->type == BilevelType)) &&
10534 image_matte == MagickFalse &&
10535 ping_have_non_bw == MagickFalse)
10537 /* Palette, Bilevel, or Opaque Monochrome */
10538 register const Quantum
10541 quantum_info->depth=8;
10542 for (pass=0; pass < num_passes; pass++)
10545 Convert PseudoClass image to a PNG monochrome image.
10547 for (y=0; y < (ssize_t) image->rows; y++)
10549 if (logging != MagickFalse && y == 0)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " Writing row of pixels (0)");
10553 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10555 if (p == (const Quantum *) NULL)
10558 if (mng_info->IsPalette)
10560 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10561 quantum_info,GrayQuantum,ping_pixels,exception);
10562 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10563 mng_info->write_png_depth &&
10564 mng_info->write_png_depth != old_bit_depth)
10566 /* Undo pixel scaling */
10567 for (i=0; i < (ssize_t) image->columns; i++)
10568 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10569 >> (8-old_bit_depth));
10575 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10576 quantum_info,RedQuantum,ping_pixels,exception);
10579 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10580 for (i=0; i < (ssize_t) image->columns; i++)
10581 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10584 if (logging != MagickFalse && y == 0)
10585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586 " Writing row of pixels (1)");
10588 png_write_row(ping,ping_pixels);
10590 if (image->previous == (Image *) NULL)
10592 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10593 if (status == MagickFalse)
10599 else /* Not Palette, Bilevel, or Opaque Monochrome */
10601 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10602 !mng_info->write_png48 && !mng_info->write_png64 &&
10603 !mng_info->write_png32) && (image_matte != MagickFalse ||
10604 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10605 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10607 register const Quantum
10610 for (pass=0; pass < num_passes; pass++)
10613 for (y=0; y < (ssize_t) image->rows; y++)
10615 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10617 if (p == (const Quantum *) NULL)
10620 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10622 if (mng_info->IsPalette)
10623 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10624 quantum_info,GrayQuantum,ping_pixels,exception);
10627 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10628 quantum_info,RedQuantum,ping_pixels,exception);
10630 if (logging != MagickFalse && y == 0)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " Writing GRAY PNG pixels (2)");
10635 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10637 if (logging != MagickFalse && y == 0)
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " Writing GRAY_ALPHA PNG pixels (2)");
10641 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10642 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10645 if (logging != MagickFalse && y == 0)
10646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10647 " Writing row of pixels (2)");
10649 png_write_row(ping,ping_pixels);
10652 if (image->previous == (Image *) NULL)
10654 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10655 if (status == MagickFalse)
10663 register const Quantum
10666 for (pass=0; pass < num_passes; pass++)
10668 if ((image_depth > 8) ||
10669 mng_info->write_png24 ||
10670 mng_info->write_png32 ||
10671 mng_info->write_png48 ||
10672 mng_info->write_png64 ||
10673 (!mng_info->write_png8 && !mng_info->IsPalette))
10675 for (y=0; y < (ssize_t) image->rows; y++)
10677 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10679 if (p == (const Quantum *) NULL)
10682 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10684 if (image->storage_class == DirectClass)
10685 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10686 quantum_info,RedQuantum,ping_pixels,exception);
10689 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10690 quantum_info,GrayQuantum,ping_pixels,exception);
10693 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10695 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10696 quantum_info,GrayAlphaQuantum,ping_pixels,
10699 if (logging != MagickFalse && y == 0)
10700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10701 " Writing GRAY_ALPHA PNG pixels (3)");
10704 else if (image_matte != MagickFalse)
10705 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10706 quantum_info,RGBAQuantum,ping_pixels,exception);
10709 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10710 quantum_info,RGBQuantum,ping_pixels,exception);
10712 if (logging != MagickFalse && y == 0)
10713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10714 " Writing row of pixels (3)");
10716 png_write_row(ping,ping_pixels);
10721 /* not ((image_depth > 8) ||
10722 mng_info->write_png24 || mng_info->write_png32 ||
10723 mng_info->write_png48 || mng_info->write_png64 ||
10724 (!mng_info->write_png8 && !mng_info->IsPalette))
10727 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10728 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10730 if (logging != MagickFalse)
10731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10732 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10734 quantum_info->depth=8;
10738 for (y=0; y < (ssize_t) image->rows; y++)
10740 if (logging != MagickFalse && y == 0)
10741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10742 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10744 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10746 if (p == (const Quantum *) NULL)
10749 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10751 quantum_info->depth=image->depth;
10753 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10754 quantum_info,GrayQuantum,ping_pixels,exception);
10757 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10759 if (logging != MagickFalse && y == 0)
10760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10761 " Writing GRAY_ALPHA PNG pixels (4)");
10763 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10764 quantum_info,GrayAlphaQuantum,ping_pixels,
10770 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10771 quantum_info,IndexQuantum,ping_pixels,exception);
10773 if (logging != MagickFalse && y <= 2)
10775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10776 " Writing row of non-gray pixels (4)");
10778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10779 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10780 (int)ping_pixels[0],(int)ping_pixels[1]);
10783 png_write_row(ping,ping_pixels);
10787 if (image->previous == (Image *) NULL)
10789 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10790 if (status == MagickFalse)
10797 if (quantum_info != (QuantumInfo *) NULL)
10798 quantum_info=DestroyQuantumInfo(quantum_info);
10800 if (logging != MagickFalse)
10802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803 " Wrote PNG image data");
10805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10806 " Width: %.20g",(double) ping_width);
10808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10809 " Height: %.20g",(double) ping_height);
10811 if (mng_info->write_png_depth)
10813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10814 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10818 " PNG bit-depth written: %d",ping_bit_depth);
10820 if (mng_info->write_png_colortype)
10822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10823 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10827 " PNG color-type written: %d",ping_color_type);
10829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10830 " PNG Interlace method: %d",ping_interlace_method);
10833 Generate text chunks after IDAT.
10835 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10837 ResetImagePropertyIterator(image);
10838 property=GetNextImageProperty(image);
10839 while (property != (const char *) NULL)
10844 value=GetImageProperty(image,property,exception);
10846 /* Don't write any "png:" properties; those are just for "identify" */
10847 if (LocaleNCompare(property,"png:",4) != 0 &&
10849 /* Suppress density and units if we wrote a pHYs chunk */
10850 (ping_exclude_pHYs != MagickFalse ||
10851 LocaleCompare(property,"density") != 0 ||
10852 LocaleCompare(property,"units") != 0) &&
10854 /* Suppress the IM-generated Date:create and Date:modify */
10855 (ping_exclude_date == MagickFalse ||
10856 LocaleNCompare(property, "Date:",5) != 0))
10858 if (value != (const char *) NULL)
10861 #if PNG_LIBPNG_VER >= 14000
10862 text=(png_textp) png_malloc(ping,
10863 (png_alloc_size_t) sizeof(png_text));
10865 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10867 text[0].key=(char *) property;
10868 text[0].text=(char *) value;
10869 text[0].text_length=strlen(value);
10871 if (ping_exclude_tEXt != MagickFalse)
10872 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10874 else if (ping_exclude_zTXt != MagickFalse)
10875 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10879 text[0].compression=image_info->compression == NoCompression ||
10880 (image_info->compression == UndefinedCompression &&
10881 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10882 PNG_TEXT_COMPRESSION_zTXt ;
10885 if (logging != MagickFalse)
10887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10888 " Setting up text chunk");
10890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10891 " keyword: %s",text[0].key);
10894 png_set_text(ping,ping_info,text,1);
10895 png_free(ping,text);
10898 property=GetNextImageProperty(image);
10902 /* write any PNG-chunk-e profiles */
10903 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10905 if (logging != MagickFalse)
10906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10907 " Writing PNG end info");
10909 png_write_end(ping,ping_info);
10911 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10913 if (mng_info->page.x || mng_info->page.y ||
10914 (ping_width != mng_info->page.width) ||
10915 (ping_height != mng_info->page.height))
10921 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10923 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10924 PNGType(chunk,mng_FRAM);
10925 LogPNGChunk(logging,mng_FRAM,27L);
10927 chunk[5]=0; /* frame name separator (no name) */
10928 chunk[6]=1; /* flag for changing delay, for next frame only */
10929 chunk[7]=0; /* flag for changing frame timeout */
10930 chunk[8]=1; /* flag for changing frame clipping for next frame */
10931 chunk[9]=0; /* flag for changing frame sync_id */
10932 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10933 chunk[14]=0; /* clipping boundaries delta type */
10934 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10936 (png_uint_32) (mng_info->page.x + ping_width));
10937 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10939 (png_uint_32) (mng_info->page.y + ping_height));
10940 (void) WriteBlob(image,31,chunk);
10941 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10942 mng_info->old_framing_mode=4;
10943 mng_info->framing_mode=1;
10947 mng_info->framing_mode=3;
10949 if (mng_info->write_mng && !mng_info->need_fram &&
10950 ((int) image->dispose == 3))
10951 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10954 Free PNG resources.
10957 png_destroy_write_struct(&ping,&ping_info);
10959 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10961 if (ping_have_blob != MagickFalse)
10962 (void) CloseBlob(image);
10964 image_info=DestroyImageInfo(image_info);
10965 image=DestroyImage(image);
10967 /* Store bit depth actually written */
10968 s[0]=(char) ping_bit_depth;
10971 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10973 if (logging != MagickFalse)
10974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10975 " exit WriteOnePNGImage()");
10977 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10978 UnlockSemaphoreInfo(ping_semaphore);
10981 /* } for navigation to beginning of SETJMP-protected block. Revert to
10982 * Throwing an Exception when an error occurs.
10985 return(MagickTrue);
10986 /* End write one PNG image */
10991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10995 % W r i t e P N G I m a g e %
10999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11001 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11002 % Multiple-image Network Graphics (MNG) image file.
11004 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11006 % The format of the WritePNGImage method is:
11008 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11009 % Image *image,ExceptionInfo *exception)
11011 % A description of each parameter follows:
11013 % o image_info: the image info.
11015 % o image: The image.
11017 % o exception: return any errors or warnings in this structure.
11019 % Returns MagickTrue on success, MagickFalse on failure.
11021 % Communicating with the PNG encoder:
11023 % While the datastream written is always in PNG format and normally would
11024 % be given the "png" file extension, this method also writes the following
11025 % pseudo-formats which are subsets of png:
11027 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11028 % a depth greater than 8, the depth is reduced. If transparency
11029 % is present, the tRNS chunk must only have values 0 and 255
11030 % (i.e., transparency is binary: fully opaque or fully
11031 % transparent). If other values are present they will be
11032 % 50%-thresholded to binary transparency. If more than 256
11033 % colors are present, they will be quantized to the 4-4-4-1,
11034 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11035 % of any resulting fully-transparent pixels is changed to
11036 % the image's background color.
11038 % If you want better quantization or dithering of the colors
11039 % or alpha than that, you need to do it before calling the
11040 % PNG encoder. The pixels contain 8-bit indices even if
11041 % they could be represented with 1, 2, or 4 bits. Grayscale
11042 % images will be written as indexed PNG files even though the
11043 % PNG grayscale type might be slightly more efficient. Please
11044 % note that writing to the PNG8 format may result in loss
11045 % of color and alpha data.
11047 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11048 % chunk can be present to convey binary transparency by naming
11049 % one of the colors as transparent. The only loss incurred
11050 % is reduction of sample depth to 8. If the image has more
11051 % than one transparent color, has semitransparent pixels, or
11052 % has an opaque pixel with the same RGB components as the
11053 % transparent color, an image is not written.
11055 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11056 % transparency is permitted, i.e., the alpha sample for
11057 % each pixel can have any value from 0 to 255. The alpha
11058 % channel is present even if the image is fully opaque.
11059 % The only loss in data is the reduction of the sample depth
11062 % o PNG48: A 16-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. If the image has more
11065 % than one transparent color, has semitransparent pixels, or
11066 % has an opaque pixel with the same RGB components as the
11067 % transparent color, an image is not written.
11069 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11070 % transparency is permitted, i.e., the alpha sample for
11071 % each pixel can have any value from 0 to 65535. The alpha
11072 % channel is present even if the image is fully opaque.
11074 % o -define: For more precise control of the PNG output, you can use the
11075 % Image options "png:bit-depth" and "png:color-type". These
11076 % can be set from the commandline with "-define" and also
11077 % from the application programming interfaces. The options
11078 % are case-independent and are converted to lowercase before
11079 % being passed to this encoder.
11081 % png:color-type can be 0, 2, 3, 4, or 6.
11083 % When png:color-type is 0 (Grayscale), png:bit-depth can
11084 % be 1, 2, 4, 8, or 16.
11086 % When png:color-type is 2 (RGB), png:bit-depth can
11089 % When png:color-type is 3 (Indexed), png:bit-depth can
11090 % be 1, 2, 4, or 8. This refers to the number of bits
11091 % used to store the index. The color samples always have
11092 % bit-depth 8 in indexed PNG files.
11094 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11095 % png:bit-depth can be 8 or 16.
11097 % If the image cannot be written without loss with the
11098 % requested bit-depth and color-type, a PNG file will not
11099 % be written, a warning will be issued, and the encoder will
11100 % return MagickFalse.
11102 % Since image encoders should not be responsible for the "heavy lifting",
11103 % the user should make sure that ImageMagick has already reduced the
11104 % image depth and number of colors and limit transparency to binary
11105 % transparency prior to attempting to write the image with depth, color,
11106 % or transparency limitations.
11108 % Note that another definition, "png:bit-depth-written" exists, but it
11109 % is not intended for external use. It is only used internally by the
11110 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11112 % It is possible to request that the PNG encoder write previously-formatted
11113 % ancillary chunks in the output PNG file, using the "-profile" commandline
11114 % option as shown below or by setting the profile via a programming
11117 % -profile PNG-chunk-x:<file>
11119 % where x is a location flag and <file> is a file containing the chunk
11120 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11121 % This encoder will compute the chunk length and CRC, so those must not
11122 % be included in the file.
11124 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11125 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11126 % of the same type, then add a short unique string after the "x" to prevent
11127 % subsequent profiles from overwriting the preceding ones, e.g.,
11129 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11131 % As of version 6.6.6 the following optimizations are always done:
11133 % o 32-bit depth is reduced to 16.
11134 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11135 % high byte and low byte are identical.
11136 % o Palette is sorted to remove unused entries and to put a
11137 % transparent color first, if BUILD_PNG_PALETTE is defined.
11138 % o Opaque matte channel is removed when writing an indexed PNG.
11139 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11140 % this can be done without loss and a larger bit depth N was not
11141 % requested via the "-define png:bit-depth=N" option.
11142 % o If matte channel is present but only one transparent color is
11143 % present, RGB+tRNS is written instead of RGBA
11144 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11145 % was requested when converting an opaque image).
11147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11149 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11150 Image *image,ExceptionInfo *exception)
11155 have_mng_structure,
11171 assert(image_info != (const ImageInfo *) NULL);
11172 assert(image_info->signature == MagickSignature);
11173 assert(image != (Image *) NULL);
11174 assert(image->signature == MagickSignature);
11175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11176 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11178 Allocate a MngInfo structure.
11180 have_mng_structure=MagickFalse;
11181 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11183 if (mng_info == (MngInfo *) NULL)
11184 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11187 Initialize members of the MngInfo structure.
11189 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11190 mng_info->image=image;
11191 mng_info->equal_backgrounds=MagickTrue;
11192 have_mng_structure=MagickTrue;
11194 /* See if user has requested a specific PNG subformat */
11196 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11197 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11198 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11199 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11200 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11202 value=GetImageOption(image_info,"png:format");
11204 if (value != (char *) NULL)
11206 mng_info->write_png8 = MagickFalse;
11207 mng_info->write_png24 = MagickFalse;
11208 mng_info->write_png32 = MagickFalse;
11209 mng_info->write_png48 = MagickFalse;
11210 mng_info->write_png64 = MagickFalse;
11212 if (LocaleCompare(value,"png8") == 0)
11213 mng_info->write_png8 = MagickTrue;
11215 else if (LocaleCompare(value,"png24") == 0)
11216 mng_info->write_png24 = MagickTrue;
11218 else if (LocaleCompare(value,"png32") == 0)
11219 mng_info->write_png32 = MagickTrue;
11221 else if (LocaleCompare(value,"png48") == 0)
11222 mng_info->write_png48 = MagickTrue;
11224 else if (LocaleCompare(value,"png64") == 0)
11225 mng_info->write_png64 = MagickTrue;
11227 if (mng_info->write_png8)
11229 mng_info->write_png_colortype = /* 3 */ 4;
11230 mng_info->write_png_depth = 8;
11234 if (mng_info->write_png24)
11236 mng_info->write_png_colortype = /* 2 */ 3;
11237 mng_info->write_png_depth = 8;
11240 if (image->alpha_trait == BlendPixelTrait)
11241 (void) SetImageType(image,TrueColorMatteType,exception);
11244 (void) SetImageType(image,TrueColorType,exception);
11246 (void) SyncImage(image,exception);
11249 if (mng_info->write_png32)
11251 mng_info->write_png_colortype = /* 6 */ 7;
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_png48)
11266 mng_info->write_png_colortype = /* 2 */ 3;
11267 mng_info->write_png_depth = 16;
11270 if (image->matte == MagickTrue)
11271 (void) SetImageType(image,TrueColorMatteType);
11274 (void) SetImageType(image,TrueColorType);
11276 (void) SyncImage(image);
11279 if (mng_info->write_png64)
11281 mng_info->write_png_colortype = /* 6 */ 7;
11282 mng_info->write_png_depth = 16;
11285 if (image->matte == MagickTrue)
11286 (void) SetImageType(image,TrueColorMatteType);
11289 (void) SetImageType(image,TrueColorType);
11291 (void) SyncImage(image);
11294 value=GetImageOption(image_info,"png:bit-depth");
11296 if (value != (char *) NULL)
11298 if (LocaleCompare(value,"1") == 0)
11299 mng_info->write_png_depth = 1;
11301 else if (LocaleCompare(value,"2") == 0)
11302 mng_info->write_png_depth = 2;
11304 else if (LocaleCompare(value,"4") == 0)
11305 mng_info->write_png_depth = 4;
11307 else if (LocaleCompare(value,"8") == 0)
11308 mng_info->write_png_depth = 8;
11310 else if (LocaleCompare(value,"16") == 0)
11311 mng_info->write_png_depth = 16;
11314 (void) ThrowMagickException(exception,
11315 GetMagickModule(),CoderWarning,
11316 "ignoring invalid defined png:bit-depth",
11319 if (logging != MagickFalse)
11320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11321 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11324 value=GetImageOption(image_info,"png:color-type");
11326 if (value != (char *) NULL)
11328 /* We must store colortype+1 because 0 is a valid colortype */
11329 if (LocaleCompare(value,"0") == 0)
11330 mng_info->write_png_colortype = 1;
11332 else if (LocaleCompare(value,"1") == 0)
11333 mng_info->write_png_colortype = 2;
11335 else if (LocaleCompare(value,"2") == 0)
11336 mng_info->write_png_colortype = 3;
11338 else if (LocaleCompare(value,"3") == 0)
11339 mng_info->write_png_colortype = 4;
11341 else if (LocaleCompare(value,"4") == 0)
11342 mng_info->write_png_colortype = 5;
11344 else if (LocaleCompare(value,"6") == 0)
11345 mng_info->write_png_colortype = 7;
11348 (void) ThrowMagickException(exception,
11349 GetMagickModule(),CoderWarning,
11350 "ignoring invalid defined png:color-type",
11353 if (logging != MagickFalse)
11354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11355 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11358 /* Check for chunks to be excluded:
11360 * The default is to not exclude any known chunks except for any
11361 * listed in the "unused_chunks" array, above.
11363 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11364 * define (in the image properties or in the image artifacts)
11365 * or via a mng_info member. For convenience, in addition
11366 * to or instead of a comma-separated list of chunks, the
11367 * "exclude-chunk" string can be simply "all" or "none".
11369 * The exclude-chunk define takes priority over the mng_info.
11371 * A "png:include-chunk" define takes priority over both the
11372 * mng_info and the "png:exclude-chunk" define. Like the
11373 * "exclude-chunk" string, it can define "all" or "none" as
11374 * well as a comma-separated list. Chunks that are unknown to
11375 * ImageMagick are always excluded, regardless of their "copy-safe"
11376 * status according to the PNG specification, and even if they
11377 * appear in the "include-chunk" list. Such defines appearing among
11378 * the image options take priority over those found among the image
11381 * Finally, all chunks listed in the "unused_chunks" array are
11382 * automatically excluded, regardless of the other instructions
11385 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11386 * will not be written and the gAMA chunk will only be written if it
11387 * is not between .45 and .46, or approximately (1.0/2.2).
11389 * If you exclude tRNS and the image has transparency, the colortype
11390 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11392 * The -strip option causes StripImage() to set the png:include-chunk
11393 * artifact to "none,trns,gama".
11396 mng_info->ping_exclude_bKGD=MagickFalse;
11397 mng_info->ping_exclude_cHRM=MagickFalse;
11398 mng_info->ping_exclude_date=MagickFalse;
11399 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11400 mng_info->ping_exclude_gAMA=MagickFalse;
11401 mng_info->ping_exclude_iCCP=MagickFalse;
11402 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11403 mng_info->ping_exclude_oFFs=MagickFalse;
11404 mng_info->ping_exclude_pHYs=MagickFalse;
11405 mng_info->ping_exclude_sRGB=MagickFalse;
11406 mng_info->ping_exclude_tEXt=MagickFalse;
11407 mng_info->ping_exclude_tRNS=MagickFalse;
11408 mng_info->ping_exclude_vpAg=MagickFalse;
11409 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11410 mng_info->ping_exclude_zTXt=MagickFalse;
11412 mng_info->ping_preserve_colormap=MagickFalse;
11414 value=GetImageArtifact(image,"png:preserve-colormap");
11416 value=GetImageOption(image_info,"png:preserve-colormap");
11418 mng_info->ping_preserve_colormap=MagickTrue;
11420 /* Thes compression-level, compression-strategy, and compression-filter
11421 * defines take precedence over values from the -quality option.
11423 value=GetImageArtifact(image,"png:compression-level");
11425 value=GetImageOption(image_info,"png:compression-level");
11428 /* We have to add 1 to everything because 0 is a valid input,
11429 * and we want to use 0 (the default) to mean undefined.
11431 if (LocaleCompare(value,"0") == 0)
11432 mng_info->write_png_compression_level = 1;
11434 else if (LocaleCompare(value,"1") == 0)
11435 mng_info->write_png_compression_level = 2;
11437 else if (LocaleCompare(value,"2") == 0)
11438 mng_info->write_png_compression_level = 3;
11440 else if (LocaleCompare(value,"3") == 0)
11441 mng_info->write_png_compression_level = 4;
11443 else if (LocaleCompare(value,"4") == 0)
11444 mng_info->write_png_compression_level = 5;
11446 else if (LocaleCompare(value,"5") == 0)
11447 mng_info->write_png_compression_level = 6;
11449 else if (LocaleCompare(value,"6") == 0)
11450 mng_info->write_png_compression_level = 7;
11452 else if (LocaleCompare(value,"7") == 0)
11453 mng_info->write_png_compression_level = 8;
11455 else if (LocaleCompare(value,"8") == 0)
11456 mng_info->write_png_compression_level = 9;
11458 else if (LocaleCompare(value,"9") == 0)
11459 mng_info->write_png_compression_level = 10;
11462 (void) ThrowMagickException(exception,
11463 GetMagickModule(),CoderWarning,
11464 "ignoring invalid defined png:compression-level",
11468 value=GetImageArtifact(image,"png:compression-strategy");
11470 value=GetImageOption(image_info,"png:compression-strategy");
11474 if (LocaleCompare(value,"0") == 0)
11475 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11477 else if (LocaleCompare(value,"1") == 0)
11478 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11480 else if (LocaleCompare(value,"2") == 0)
11481 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11483 else if (LocaleCompare(value,"3") == 0)
11484 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11485 mng_info->write_png_compression_strategy = Z_RLE+1;
11487 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11490 else if (LocaleCompare(value,"4") == 0)
11491 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11492 mng_info->write_png_compression_strategy = Z_FIXED+1;
11494 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11498 (void) ThrowMagickException(exception,
11499 GetMagickModule(),CoderWarning,
11500 "ignoring invalid defined png:compression-strategy",
11504 value=GetImageArtifact(image,"png:compression-filter");
11506 value=GetImageOption(image_info,"png:compression-filter");
11510 /* To do: combinations of filters allowed by libpng
11511 * masks 0x08 through 0xf8
11513 * Implement this as a comma-separated list of 0,1,2,3,4,5
11514 * where 5 is a special case meaning PNG_ALL_FILTERS.
11517 if (LocaleCompare(value,"0") == 0)
11518 mng_info->write_png_compression_filter = 1;
11520 else if (LocaleCompare(value,"1") == 0)
11521 mng_info->write_png_compression_filter = 2;
11523 else if (LocaleCompare(value,"2") == 0)
11524 mng_info->write_png_compression_filter = 3;
11526 else if (LocaleCompare(value,"3") == 0)
11527 mng_info->write_png_compression_filter = 4;
11529 else if (LocaleCompare(value,"4") == 0)
11530 mng_info->write_png_compression_filter = 5;
11532 else if (LocaleCompare(value,"5") == 0)
11533 mng_info->write_png_compression_filter = 6;
11536 (void) ThrowMagickException(exception,
11537 GetMagickModule(),CoderWarning,
11538 "ignoring invalid defined png:compression-filter",
11542 excluding=MagickFalse;
11544 for (source=0; source<1; source++)
11548 value=GetImageArtifact(image,"png:exclude-chunk");
11551 value=GetImageArtifact(image,"png:exclude-chunks");
11555 value=GetImageOption(image_info,"png:exclude-chunk");
11558 value=GetImageOption(image_info,"png:exclude-chunks");
11567 excluding=MagickTrue;
11569 if (logging != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " png:exclude-chunk=%s found in image artifacts.\n", value);
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " png:exclude-chunk=%s found in image properties.\n", value);
11579 last=strlen(value);
11581 for (i=0; i<(int) last; i+=5)
11584 if (LocaleNCompare(value+i,"all",3) == 0)
11586 mng_info->ping_exclude_bKGD=MagickTrue;
11587 mng_info->ping_exclude_cHRM=MagickTrue;
11588 mng_info->ping_exclude_date=MagickTrue;
11589 mng_info->ping_exclude_EXIF=MagickTrue;
11590 mng_info->ping_exclude_gAMA=MagickTrue;
11591 mng_info->ping_exclude_iCCP=MagickTrue;
11592 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11593 mng_info->ping_exclude_oFFs=MagickTrue;
11594 mng_info->ping_exclude_pHYs=MagickTrue;
11595 mng_info->ping_exclude_sRGB=MagickTrue;
11596 mng_info->ping_exclude_tEXt=MagickTrue;
11597 mng_info->ping_exclude_tRNS=MagickTrue;
11598 mng_info->ping_exclude_vpAg=MagickTrue;
11599 mng_info->ping_exclude_zCCP=MagickTrue;
11600 mng_info->ping_exclude_zTXt=MagickTrue;
11604 if (LocaleNCompare(value+i,"none",4) == 0)
11606 mng_info->ping_exclude_bKGD=MagickFalse;
11607 mng_info->ping_exclude_cHRM=MagickFalse;
11608 mng_info->ping_exclude_date=MagickFalse;
11609 mng_info->ping_exclude_EXIF=MagickFalse;
11610 mng_info->ping_exclude_gAMA=MagickFalse;
11611 mng_info->ping_exclude_iCCP=MagickFalse;
11612 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11613 mng_info->ping_exclude_oFFs=MagickFalse;
11614 mng_info->ping_exclude_pHYs=MagickFalse;
11615 mng_info->ping_exclude_sRGB=MagickFalse;
11616 mng_info->ping_exclude_tEXt=MagickFalse;
11617 mng_info->ping_exclude_tRNS=MagickFalse;
11618 mng_info->ping_exclude_vpAg=MagickFalse;
11619 mng_info->ping_exclude_zCCP=MagickFalse;
11620 mng_info->ping_exclude_zTXt=MagickFalse;
11623 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11624 mng_info->ping_exclude_bKGD=MagickTrue;
11626 if (LocaleNCompare(value+i,"chrm",4) == 0)
11627 mng_info->ping_exclude_cHRM=MagickTrue;
11629 if (LocaleNCompare(value+i,"date",4) == 0)
11630 mng_info->ping_exclude_date=MagickTrue;
11632 if (LocaleNCompare(value+i,"exif",4) == 0)
11633 mng_info->ping_exclude_EXIF=MagickTrue;
11635 if (LocaleNCompare(value+i,"gama",4) == 0)
11636 mng_info->ping_exclude_gAMA=MagickTrue;
11638 if (LocaleNCompare(value+i,"iccp",4) == 0)
11639 mng_info->ping_exclude_iCCP=MagickTrue;
11642 if (LocaleNCompare(value+i,"itxt",4) == 0)
11643 mng_info->ping_exclude_iTXt=MagickTrue;
11646 if (LocaleNCompare(value+i,"gama",4) == 0)
11647 mng_info->ping_exclude_gAMA=MagickTrue;
11649 if (LocaleNCompare(value+i,"offs",4) == 0)
11650 mng_info->ping_exclude_oFFs=MagickTrue;
11652 if (LocaleNCompare(value+i,"phys",4) == 0)
11653 mng_info->ping_exclude_pHYs=MagickTrue;
11655 if (LocaleNCompare(value+i,"srgb",4) == 0)
11656 mng_info->ping_exclude_sRGB=MagickTrue;
11658 if (LocaleNCompare(value+i,"text",4) == 0)
11659 mng_info->ping_exclude_tEXt=MagickTrue;
11661 if (LocaleNCompare(value+i,"trns",4) == 0)
11662 mng_info->ping_exclude_tRNS=MagickTrue;
11664 if (LocaleNCompare(value+i,"vpag",4) == 0)
11665 mng_info->ping_exclude_vpAg=MagickTrue;
11667 if (LocaleNCompare(value+i,"zccp",4) == 0)
11668 mng_info->ping_exclude_zCCP=MagickTrue;
11670 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11671 mng_info->ping_exclude_zTXt=MagickTrue;
11677 for (source=0; source<1; source++)
11681 value=GetImageArtifact(image,"png:include-chunk");
11684 value=GetImageArtifact(image,"png:include-chunks");
11688 value=GetImageOption(image_info,"png:include-chunk");
11691 value=GetImageOption(image_info,"png:include-chunks");
11699 excluding=MagickTrue;
11701 if (logging != MagickFalse)
11704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11705 " png:include-chunk=%s found in image artifacts.\n", value);
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 " png:include-chunk=%s found in image properties.\n", value);
11711 last=strlen(value);
11713 for (i=0; i<(int) last; i+=5)
11715 if (LocaleNCompare(value+i,"all",3) == 0)
11717 mng_info->ping_exclude_bKGD=MagickFalse;
11718 mng_info->ping_exclude_cHRM=MagickFalse;
11719 mng_info->ping_exclude_date=MagickFalse;
11720 mng_info->ping_exclude_EXIF=MagickFalse;
11721 mng_info->ping_exclude_gAMA=MagickFalse;
11722 mng_info->ping_exclude_iCCP=MagickFalse;
11723 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11724 mng_info->ping_exclude_oFFs=MagickFalse;
11725 mng_info->ping_exclude_pHYs=MagickFalse;
11726 mng_info->ping_exclude_sRGB=MagickFalse;
11727 mng_info->ping_exclude_tEXt=MagickFalse;
11728 mng_info->ping_exclude_tRNS=MagickFalse;
11729 mng_info->ping_exclude_vpAg=MagickFalse;
11730 mng_info->ping_exclude_zCCP=MagickFalse;
11731 mng_info->ping_exclude_zTXt=MagickFalse;
11735 if (LocaleNCompare(value+i,"none",4) == 0)
11737 mng_info->ping_exclude_bKGD=MagickTrue;
11738 mng_info->ping_exclude_cHRM=MagickTrue;
11739 mng_info->ping_exclude_date=MagickTrue;
11740 mng_info->ping_exclude_EXIF=MagickTrue;
11741 mng_info->ping_exclude_gAMA=MagickTrue;
11742 mng_info->ping_exclude_iCCP=MagickTrue;
11743 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11744 mng_info->ping_exclude_oFFs=MagickTrue;
11745 mng_info->ping_exclude_pHYs=MagickTrue;
11746 mng_info->ping_exclude_sRGB=MagickTrue;
11747 mng_info->ping_exclude_tEXt=MagickTrue;
11748 mng_info->ping_exclude_tRNS=MagickTrue;
11749 mng_info->ping_exclude_vpAg=MagickTrue;
11750 mng_info->ping_exclude_zCCP=MagickTrue;
11751 mng_info->ping_exclude_zTXt=MagickTrue;
11754 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11755 mng_info->ping_exclude_bKGD=MagickFalse;
11757 if (LocaleNCompare(value+i,"chrm",4) == 0)
11758 mng_info->ping_exclude_cHRM=MagickFalse;
11760 if (LocaleNCompare(value+i,"date",4) == 0)
11761 mng_info->ping_exclude_date=MagickFalse;
11763 if (LocaleNCompare(value+i,"exif",4) == 0)
11764 mng_info->ping_exclude_EXIF=MagickFalse;
11766 if (LocaleNCompare(value+i,"gama",4) == 0)
11767 mng_info->ping_exclude_gAMA=MagickFalse;
11769 if (LocaleNCompare(value+i,"iccp",4) == 0)
11770 mng_info->ping_exclude_iCCP=MagickFalse;
11773 if (LocaleNCompare(value+i,"itxt",4) == 0)
11774 mng_info->ping_exclude_iTXt=MagickFalse;
11777 if (LocaleNCompare(value+i,"gama",4) == 0)
11778 mng_info->ping_exclude_gAMA=MagickFalse;
11780 if (LocaleNCompare(value+i,"offs",4) == 0)
11781 mng_info->ping_exclude_oFFs=MagickFalse;
11783 if (LocaleNCompare(value+i,"phys",4) == 0)
11784 mng_info->ping_exclude_pHYs=MagickFalse;
11786 if (LocaleNCompare(value+i,"srgb",4) == 0)
11787 mng_info->ping_exclude_sRGB=MagickFalse;
11789 if (LocaleNCompare(value+i,"text",4) == 0)
11790 mng_info->ping_exclude_tEXt=MagickFalse;
11792 if (LocaleNCompare(value+i,"trns",4) == 0)
11793 mng_info->ping_exclude_tRNS=MagickFalse;
11795 if (LocaleNCompare(value+i,"vpag",4) == 0)
11796 mng_info->ping_exclude_vpAg=MagickFalse;
11798 if (LocaleNCompare(value+i,"zccp",4) == 0)
11799 mng_info->ping_exclude_zCCP=MagickFalse;
11801 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11802 mng_info->ping_exclude_zTXt=MagickFalse;
11808 if (excluding != MagickFalse && logging != MagickFalse)
11810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11811 " Chunks to be excluded from the output png:");
11812 if (mng_info->ping_exclude_bKGD != MagickFalse)
11813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11815 if (mng_info->ping_exclude_cHRM != MagickFalse)
11816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11818 if (mng_info->ping_exclude_date != MagickFalse)
11819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11821 if (mng_info->ping_exclude_EXIF != MagickFalse)
11822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11824 if (mng_info->ping_exclude_gAMA != MagickFalse)
11825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11827 if (mng_info->ping_exclude_iCCP != MagickFalse)
11828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11831 if (mng_info->ping_exclude_iTXt != MagickFalse)
11832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11835 if (mng_info->ping_exclude_oFFs != MagickFalse)
11836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11838 if (mng_info->ping_exclude_pHYs != MagickFalse)
11839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11841 if (mng_info->ping_exclude_sRGB != MagickFalse)
11842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11844 if (mng_info->ping_exclude_tEXt != MagickFalse)
11845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11847 if (mng_info->ping_exclude_tRNS != MagickFalse)
11848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11850 if (mng_info->ping_exclude_vpAg != MagickFalse)
11851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11853 if (mng_info->ping_exclude_zCCP != MagickFalse)
11854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11856 if (mng_info->ping_exclude_zTXt != MagickFalse)
11857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11861 mng_info->need_blob = MagickTrue;
11863 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11865 MngInfoFreeStruct(mng_info,&have_mng_structure);
11867 if (logging != MagickFalse)
11868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11873 #if defined(JNG_SUPPORTED)
11875 /* Write one JNG image */
11876 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11877 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11898 jng_alpha_compression_method,
11899 jng_alpha_sample_depth,
11907 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11908 " Enter WriteOneJNGImage()");
11910 blob=(unsigned char *) NULL;
11911 jpeg_image=(Image *) NULL;
11912 jpeg_image_info=(ImageInfo *) NULL;
11915 transparent=image_info->type==GrayscaleMatteType ||
11916 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
11918 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11920 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11922 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11923 image_info->quality;
11925 if (jng_alpha_quality >= 1000)
11926 jng_alpha_quality /= 1000;
11932 /* Create JPEG blob, image, and image_info */
11933 if (logging != MagickFalse)
11934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11935 " Creating jpeg_image_info for alpha.");
11937 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11939 if (jpeg_image_info == (ImageInfo *) NULL)
11940 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11942 if (logging != MagickFalse)
11943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11944 " Creating jpeg_image.");
11946 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11947 if (jpeg_image == (Image *) NULL)
11948 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11949 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11950 jpeg_image->alpha_trait=UndefinedPixelTrait;
11951 jpeg_image->quality=jng_alpha_quality;
11952 jpeg_image_info->type=GrayscaleType;
11953 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11954 (void) AcquireUniqueFilename(jpeg_image->filename);
11955 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11956 "%s",jpeg_image->filename);
11960 jng_alpha_compression_method=0;
11962 jng_alpha_sample_depth=0;
11965 /* To do: check bit depth of PNG alpha channel */
11967 /* Check if image is grayscale. */
11968 if (image_info->type != TrueColorMatteType && image_info->type !=
11969 TrueColorType && IsImageGray(image,exception))
11972 if (logging != MagickFalse)
11974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11975 " JNG Quality = %d",(int) jng_quality);
11976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11977 " JNG Color Type = %d",jng_color_type);
11980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11981 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11983 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11985 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11991 if (jng_alpha_compression_method==0)
11996 /* Encode alpha as a grayscale PNG blob */
11997 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11999 if (logging != MagickFalse)
12000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12001 " Creating PNG blob.");
12004 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12005 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12006 jpeg_image_info->interlace=NoInterlace;
12008 /* Exclude all ancillary chunks */
12009 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12011 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12014 /* Retrieve sample depth used */
12015 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12016 if (value != (char *) NULL)
12017 jng_alpha_sample_depth= (unsigned int) value[0];
12021 /* Encode alpha as a grayscale JPEG blob */
12023 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12026 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12027 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12028 jpeg_image_info->interlace=NoInterlace;
12029 if (logging != MagickFalse)
12030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12031 " Creating blob.");
12032 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12034 jng_alpha_sample_depth=8;
12036 if (logging != MagickFalse)
12037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12038 " Successfully read jpeg_image into a blob, length=%.20g.",
12042 /* Destroy JPEG image and image_info */
12043 jpeg_image=DestroyImage(jpeg_image);
12044 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12045 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12048 /* Write JHDR chunk */
12049 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12050 PNGType(chunk,mng_JHDR);
12051 LogPNGChunk(logging,mng_JHDR,16L);
12052 PNGLong(chunk+4,(png_uint_32) image->columns);
12053 PNGLong(chunk+8,(png_uint_32) image->rows);
12054 chunk[12]=jng_color_type;
12055 chunk[13]=8; /* sample depth */
12056 chunk[14]=8; /*jng_image_compression_method */
12057 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12058 chunk[16]=jng_alpha_sample_depth;
12059 chunk[17]=jng_alpha_compression_method;
12060 chunk[18]=0; /*jng_alpha_filter_method */
12061 chunk[19]=0; /*jng_alpha_interlace_method */
12062 (void) WriteBlob(image,20,chunk);
12063 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12064 if (logging != MagickFalse)
12066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12067 " JNG width:%15lu",(unsigned long) image->columns);
12069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12070 " JNG height:%14lu",(unsigned long) image->rows);
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12073 " JNG color type:%10d",jng_color_type);
12075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12076 " JNG sample depth:%8d",8);
12078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12079 " JNG compression:%9d",8);
12081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12082 " JNG interlace:%11d",0);
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12088 " JNG alpha compression:%3d",jng_alpha_compression_method);
12090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12091 " JNG alpha filter:%8d",0);
12093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12094 " JNG alpha interlace:%5d",0);
12097 /* Write any JNG-chunk-b profiles */
12098 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12101 Write leading ancillary chunks
12107 Write JNG bKGD chunk
12118 if (jng_color_type == 8 || jng_color_type == 12)
12122 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12123 PNGType(chunk,mng_bKGD);
12124 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12125 red=ScaleQuantumToChar(image->background_color.red);
12126 green=ScaleQuantumToChar(image->background_color.green);
12127 blue=ScaleQuantumToChar(image->background_color.blue);
12134 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12135 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12138 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12141 Write JNG sRGB chunk
12143 (void) WriteBlobMSBULong(image,1L);
12144 PNGType(chunk,mng_sRGB);
12145 LogPNGChunk(logging,mng_sRGB,1L);
12147 if (image->rendering_intent != UndefinedIntent)
12148 chunk[4]=(unsigned char)
12149 Magick_RenderingIntent_to_PNG_RenderingIntent(
12150 (image->rendering_intent));
12153 chunk[4]=(unsigned char)
12154 Magick_RenderingIntent_to_PNG_RenderingIntent(
12155 (PerceptualIntent));
12157 (void) WriteBlob(image,5,chunk);
12158 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12162 if (image->gamma != 0.0)
12165 Write JNG gAMA chunk
12167 (void) WriteBlobMSBULong(image,4L);
12168 PNGType(chunk,mng_gAMA);
12169 LogPNGChunk(logging,mng_gAMA,4L);
12170 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12171 (void) WriteBlob(image,8,chunk);
12172 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12175 if ((mng_info->equal_chrms == MagickFalse) &&
12176 (image->chromaticity.red_primary.x != 0.0))
12182 Write JNG cHRM chunk
12184 (void) WriteBlobMSBULong(image,32L);
12185 PNGType(chunk,mng_cHRM);
12186 LogPNGChunk(logging,mng_cHRM,32L);
12187 primary=image->chromaticity.white_point;
12188 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12189 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12190 primary=image->chromaticity.red_primary;
12191 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12192 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12193 primary=image->chromaticity.green_primary;
12194 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12195 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12196 primary=image->chromaticity.blue_primary;
12197 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12198 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12199 (void) WriteBlob(image,36,chunk);
12200 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12204 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12207 Write JNG pHYs chunk
12209 (void) WriteBlobMSBULong(image,9L);
12210 PNGType(chunk,mng_pHYs);
12211 LogPNGChunk(logging,mng_pHYs,9L);
12212 if (image->units == PixelsPerInchResolution)
12214 PNGLong(chunk+4,(png_uint_32)
12215 (image->resolution.x*100.0/2.54+0.5));
12217 PNGLong(chunk+8,(png_uint_32)
12218 (image->resolution.y*100.0/2.54+0.5));
12225 if (image->units == PixelsPerCentimeterResolution)
12227 PNGLong(chunk+4,(png_uint_32)
12228 (image->resolution.x*100.0+0.5));
12230 PNGLong(chunk+8,(png_uint_32)
12231 (image->resolution.y*100.0+0.5));
12238 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12239 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12243 (void) WriteBlob(image,13,chunk);
12244 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12247 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12250 Write JNG oFFs chunk
12252 (void) WriteBlobMSBULong(image,9L);
12253 PNGType(chunk,mng_oFFs);
12254 LogPNGChunk(logging,mng_oFFs,9L);
12255 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12256 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12258 (void) WriteBlob(image,13,chunk);
12259 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12261 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12263 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12264 PNGType(chunk,mng_vpAg);
12265 LogPNGChunk(logging,mng_vpAg,9L);
12266 PNGLong(chunk+4,(png_uint_32) image->page.width);
12267 PNGLong(chunk+8,(png_uint_32) image->page.height);
12268 chunk[12]=0; /* unit = pixels */
12269 (void) WriteBlob(image,13,chunk);
12270 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12276 if (jng_alpha_compression_method==0)
12284 /* Write IDAT chunk header */
12285 if (logging != MagickFalse)
12286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12287 " Write IDAT chunks from blob, length=%.20g.",(double)
12290 /* Copy IDAT chunks */
12293 for (i=8; i<(ssize_t) length; i+=len+12)
12295 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12298 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12300 /* Found an IDAT chunk. */
12301 (void) WriteBlobMSBULong(image,(size_t) len);
12302 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12303 (void) WriteBlob(image,(size_t) len+4,p);
12304 (void) WriteBlobMSBULong(image,
12305 crc32(0,p,(uInt) len+4));
12310 if (logging != MagickFalse)
12311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12312 " Skipping %c%c%c%c chunk, length=%.20g.",
12313 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12320 /* Write JDAA chunk header */
12321 if (logging != MagickFalse)
12322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12323 " Write JDAA chunk, length=%.20g.",(double) length);
12324 (void) WriteBlobMSBULong(image,(size_t) length);
12325 PNGType(chunk,mng_JDAA);
12326 LogPNGChunk(logging,mng_JDAA,length);
12327 /* Write JDAT chunk(s) data */
12328 (void) WriteBlob(image,4,chunk);
12329 (void) WriteBlob(image,length,blob);
12330 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12333 blob=(unsigned char *) RelinquishMagickMemory(blob);
12336 /* Encode image as a JPEG blob */
12337 if (logging != MagickFalse)
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " Creating jpeg_image_info.");
12340 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12341 if (jpeg_image_info == (ImageInfo *) NULL)
12342 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12344 if (logging != MagickFalse)
12345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12346 " Creating jpeg_image.");
12348 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12349 if (jpeg_image == (Image *) NULL)
12350 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12351 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12353 (void) AcquireUniqueFilename(jpeg_image->filename);
12354 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12355 jpeg_image->filename);
12357 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12360 if (logging != MagickFalse)
12361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12362 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12363 (double) jpeg_image->rows);
12365 if (jng_color_type == 8 || jng_color_type == 12)
12366 jpeg_image_info->type=GrayscaleType;
12368 jpeg_image_info->quality=jng_quality;
12369 jpeg_image->quality=jng_quality;
12370 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12371 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12373 if (logging != MagickFalse)
12374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12375 " Creating blob.");
12377 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12379 if (logging != MagickFalse)
12381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12382 " Successfully read jpeg_image into a blob, length=%.20g.",
12385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12386 " Write JDAT chunk, length=%.20g.",(double) length);
12389 /* Write JDAT chunk(s) */
12390 (void) WriteBlobMSBULong(image,(size_t) length);
12391 PNGType(chunk,mng_JDAT);
12392 LogPNGChunk(logging,mng_JDAT,length);
12393 (void) WriteBlob(image,4,chunk);
12394 (void) WriteBlob(image,length,blob);
12395 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12397 jpeg_image=DestroyImage(jpeg_image);
12398 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12399 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12400 blob=(unsigned char *) RelinquishMagickMemory(blob);
12402 /* Write any JNG-chunk-e profiles */
12403 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12405 /* Write IEND chunk */
12406 (void) WriteBlobMSBULong(image,0L);
12407 PNGType(chunk,mng_IEND);
12408 LogPNGChunk(logging,mng_IEND,0);
12409 (void) WriteBlob(image,4,chunk);
12410 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12412 if (logging != MagickFalse)
12413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12414 " exit WriteOneJNGImage()");
12421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12425 % W r i t e J N G I m a g e %
12429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12431 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12433 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12435 % The format of the WriteJNGImage method is:
12437 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12438 % Image *image,ExceptionInfo *exception)
12440 % A description of each parameter follows:
12442 % o image_info: the image info.
12444 % o image: The image.
12446 % o exception: return any errors or warnings in this structure.
12448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12450 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12451 ExceptionInfo *exception)
12454 have_mng_structure,
12464 assert(image_info != (const ImageInfo *) NULL);
12465 assert(image_info->signature == MagickSignature);
12466 assert(image != (Image *) NULL);
12467 assert(image->signature == MagickSignature);
12468 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12469 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12470 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12471 if (status == MagickFalse)
12475 Allocate a MngInfo structure.
12477 have_mng_structure=MagickFalse;
12478 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12479 if (mng_info == (MngInfo *) NULL)
12480 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12482 Initialize members of the MngInfo structure.
12484 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12485 mng_info->image=image;
12486 have_mng_structure=MagickTrue;
12488 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12490 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12491 (void) CloseBlob(image);
12493 (void) CatchImageException(image);
12494 MngInfoFreeStruct(mng_info,&have_mng_structure);
12495 if (logging != MagickFalse)
12496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12501 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12502 ExceptionInfo *exception)
12511 have_mng_structure,
12514 volatile MagickBooleanType
12526 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12527 defined(PNG_MNG_FEATURES_SUPPORTED)
12530 all_images_are_gray,
12540 volatile unsigned int
12551 #if (PNG_LIBPNG_VER < 10200)
12552 if (image_info->verbose)
12553 printf("Your PNG library (libpng-%s) is rather old.\n",
12554 PNG_LIBPNG_VER_STRING);
12560 assert(image_info != (const ImageInfo *) NULL);
12561 assert(image_info->signature == MagickSignature);
12562 assert(image != (Image *) NULL);
12563 assert(image->signature == MagickSignature);
12564 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12565 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12566 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12567 if (status == MagickFalse)
12571 Allocate a MngInfo structure.
12573 have_mng_structure=MagickFalse;
12574 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12575 if (mng_info == (MngInfo *) NULL)
12576 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12578 Initialize members of the MngInfo structure.
12580 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12581 mng_info->image=image;
12582 have_mng_structure=MagickTrue;
12583 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12586 * See if user has requested a specific PNG subformat to be used
12587 * for all of the PNGs in the MNG being written, e.g.,
12589 * convert *.png png8:animation.mng
12591 * To do: check -define png:bit_depth and png:color_type as well,
12592 * or perhaps use mng:bit_depth and mng:color_type instead for
12596 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12597 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12598 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12600 write_jng=MagickFalse;
12601 if (image_info->compression == JPEGCompression)
12602 write_jng=MagickTrue;
12604 mng_info->adjoin=image_info->adjoin &&
12605 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12607 if (logging != MagickFalse)
12609 /* Log some info about the input */
12613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12614 " Checking input image(s)");
12616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12617 " Image_info depth: %.20g",(double) image_info->depth);
12619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12620 " Type: %d",image_info->type);
12623 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12626 " Scene: %.20g",(double) scene++);
12628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12629 " Image depth: %.20g",(double) p->depth);
12631 if (p->alpha_trait)
12632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12639 if (p->storage_class == PseudoClass)
12640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12641 " Storage class: PseudoClass");
12644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12645 " Storage class: DirectClass");
12648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12649 " Number of colors: %.20g",(double) p->colors);
12652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12653 " Number of colors: unspecified");
12655 if (mng_info->adjoin == MagickFalse)
12660 use_global_plte=MagickFalse;
12661 all_images_are_gray=MagickFalse;
12662 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12663 need_local_plte=MagickTrue;
12665 need_defi=MagickFalse;
12666 need_matte=MagickFalse;
12667 mng_info->framing_mode=1;
12668 mng_info->old_framing_mode=1;
12671 if (image_info->page != (char *) NULL)
12674 Determine image bounding box.
12676 SetGeometry(image,&mng_info->page);
12677 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12678 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12690 mng_info->page=image->page;
12691 need_geom=MagickTrue;
12692 if (mng_info->page.width || mng_info->page.height)
12693 need_geom=MagickFalse;
12695 Check all the scenes.
12697 initial_delay=image->delay;
12698 need_iterations=MagickFalse;
12699 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12700 mng_info->equal_physs=MagickTrue,
12701 mng_info->equal_gammas=MagickTrue;
12702 mng_info->equal_srgbs=MagickTrue;
12703 mng_info->equal_backgrounds=MagickTrue;
12705 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12706 defined(PNG_MNG_FEATURES_SUPPORTED)
12707 all_images_are_gray=MagickTrue;
12708 mng_info->equal_palettes=MagickFalse;
12709 need_local_plte=MagickFalse;
12711 for (next_image=image; next_image != (Image *) NULL; )
12715 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12716 mng_info->page.width=next_image->columns+next_image->page.x;
12718 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12719 mng_info->page.height=next_image->rows+next_image->page.y;
12722 if (next_image->page.x || next_image->page.y)
12723 need_defi=MagickTrue;
12725 if (next_image->alpha_trait)
12726 need_matte=MagickTrue;
12728 if ((int) next_image->dispose >= BackgroundDispose)
12729 if (next_image->alpha_trait || next_image->page.x || next_image->page.y ||
12730 ((next_image->columns < mng_info->page.width) &&
12731 (next_image->rows < mng_info->page.height)))
12732 mng_info->need_fram=MagickTrue;
12734 if (next_image->iterations)
12735 need_iterations=MagickTrue;
12737 final_delay=next_image->delay;
12739 if (final_delay != initial_delay || final_delay > 1UL*
12740 next_image->ticks_per_second)
12741 mng_info->need_fram=1;
12743 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12744 defined(PNG_MNG_FEATURES_SUPPORTED)
12746 check for global palette possibility.
12748 if (image->alpha_trait == BlendPixelTrait)
12749 need_local_plte=MagickTrue;
12751 if (need_local_plte == 0)
12753 if (IsImageGray(image,exception) == MagickFalse)
12754 all_images_are_gray=MagickFalse;
12755 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12756 if (use_global_plte == 0)
12757 use_global_plte=mng_info->equal_palettes;
12758 need_local_plte=!mng_info->equal_palettes;
12761 if (GetNextImageInList(next_image) != (Image *) NULL)
12763 if (next_image->background_color.red !=
12764 next_image->next->background_color.red ||
12765 next_image->background_color.green !=
12766 next_image->next->background_color.green ||
12767 next_image->background_color.blue !=
12768 next_image->next->background_color.blue)
12769 mng_info->equal_backgrounds=MagickFalse;
12771 if (next_image->gamma != next_image->next->gamma)
12772 mng_info->equal_gammas=MagickFalse;
12774 if (next_image->rendering_intent !=
12775 next_image->next->rendering_intent)
12776 mng_info->equal_srgbs=MagickFalse;
12778 if ((next_image->units != next_image->next->units) ||
12779 (next_image->resolution.x != next_image->next->resolution.x) ||
12780 (next_image->resolution.y != next_image->next->resolution.y))
12781 mng_info->equal_physs=MagickFalse;
12783 if (mng_info->equal_chrms)
12785 if (next_image->chromaticity.red_primary.x !=
12786 next_image->next->chromaticity.red_primary.x ||
12787 next_image->chromaticity.red_primary.y !=
12788 next_image->next->chromaticity.red_primary.y ||
12789 next_image->chromaticity.green_primary.x !=
12790 next_image->next->chromaticity.green_primary.x ||
12791 next_image->chromaticity.green_primary.y !=
12792 next_image->next->chromaticity.green_primary.y ||
12793 next_image->chromaticity.blue_primary.x !=
12794 next_image->next->chromaticity.blue_primary.x ||
12795 next_image->chromaticity.blue_primary.y !=
12796 next_image->next->chromaticity.blue_primary.y ||
12797 next_image->chromaticity.white_point.x !=
12798 next_image->next->chromaticity.white_point.x ||
12799 next_image->chromaticity.white_point.y !=
12800 next_image->next->chromaticity.white_point.y)
12801 mng_info->equal_chrms=MagickFalse;
12805 next_image=GetNextImageInList(next_image);
12807 if (image_count < 2)
12809 mng_info->equal_backgrounds=MagickFalse;
12810 mng_info->equal_chrms=MagickFalse;
12811 mng_info->equal_gammas=MagickFalse;
12812 mng_info->equal_srgbs=MagickFalse;
12813 mng_info->equal_physs=MagickFalse;
12814 use_global_plte=MagickFalse;
12815 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12816 need_local_plte=MagickTrue;
12818 need_iterations=MagickFalse;
12821 if (mng_info->need_fram == MagickFalse)
12824 Only certain framing rates 100/n are exactly representable without
12825 the FRAM chunk but we'll allow some slop in VLC files
12827 if (final_delay == 0)
12829 if (need_iterations != MagickFalse)
12832 It's probably a GIF with loop; don't run it *too* fast.
12834 if (mng_info->adjoin)
12837 (void) ThrowMagickException(exception,GetMagickModule(),
12839 "input has zero delay between all frames; assuming",
12844 mng_info->ticks_per_second=0;
12846 if (final_delay != 0)
12847 mng_info->ticks_per_second=(png_uint_32)
12848 (image->ticks_per_second/final_delay);
12849 if (final_delay > 50)
12850 mng_info->ticks_per_second=2;
12852 if (final_delay > 75)
12853 mng_info->ticks_per_second=1;
12855 if (final_delay > 125)
12856 mng_info->need_fram=MagickTrue;
12858 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12859 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12860 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12861 1UL*image->ticks_per_second))
12862 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12865 if (mng_info->need_fram != MagickFalse)
12866 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12868 If pseudocolor, we should also check to see if all the
12869 palettes are identical and write a global PLTE if they are.
12873 Write the MNG version 1.0 signature and MHDR chunk.
12875 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12876 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12877 PNGType(chunk,mng_MHDR);
12878 LogPNGChunk(logging,mng_MHDR,28L);
12879 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12880 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12881 PNGLong(chunk+12,mng_info->ticks_per_second);
12882 PNGLong(chunk+16,0L); /* layer count=unknown */
12883 PNGLong(chunk+20,0L); /* frame count=unknown */
12884 PNGLong(chunk+24,0L); /* play time=unknown */
12889 if (need_defi || mng_info->need_fram || use_global_plte)
12890 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12893 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12898 if (need_defi || mng_info->need_fram || use_global_plte)
12899 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12902 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12910 if (need_defi || mng_info->need_fram || use_global_plte)
12911 PNGLong(chunk+28,11L); /* simplicity=LC */
12914 PNGLong(chunk+28,9L); /* simplicity=VLC */
12919 if (need_defi || mng_info->need_fram || use_global_plte)
12920 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12923 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12926 (void) WriteBlob(image,32,chunk);
12927 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12928 option=GetImageOption(image_info,"mng:need-cacheoff");
12929 if (option != (const char *) NULL)
12935 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12937 PNGType(chunk,mng_nEED);
12938 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12939 (void) WriteBlobMSBULong(image,(size_t) length);
12940 LogPNGChunk(logging,mng_nEED,(size_t) length);
12942 (void) WriteBlob(image,length,chunk);
12943 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12945 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12946 (GetNextImageInList(image) != (Image *) NULL) &&
12947 (image->iterations != 1))
12950 Write MNG TERM chunk
12952 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12953 PNGType(chunk,mng_TERM);
12954 LogPNGChunk(logging,mng_TERM,10L);
12955 chunk[4]=3; /* repeat animation */
12956 chunk[5]=0; /* show last frame when done */
12957 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12958 final_delay/MagickMax(image->ticks_per_second,1)));
12960 if (image->iterations == 0)
12961 PNGLong(chunk+10,PNG_UINT_31_MAX);
12964 PNGLong(chunk+10,(png_uint_32) image->iterations);
12966 if (logging != MagickFalse)
12968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12970 final_delay/MagickMax(image->ticks_per_second,1)));
12972 if (image->iterations == 0)
12973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12974 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978 " Image iterations: %.20g",(double) image->iterations);
12980 (void) WriteBlob(image,14,chunk);
12981 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12984 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12986 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12987 mng_info->equal_srgbs)
12990 Write MNG sRGB chunk
12992 (void) WriteBlobMSBULong(image,1L);
12993 PNGType(chunk,mng_sRGB);
12994 LogPNGChunk(logging,mng_sRGB,1L);
12996 if (image->rendering_intent != UndefinedIntent)
12997 chunk[4]=(unsigned char)
12998 Magick_RenderingIntent_to_PNG_RenderingIntent(
12999 (image->rendering_intent));
13002 chunk[4]=(unsigned char)
13003 Magick_RenderingIntent_to_PNG_RenderingIntent(
13004 (PerceptualIntent));
13006 (void) WriteBlob(image,5,chunk);
13007 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13008 mng_info->have_write_global_srgb=MagickTrue;
13013 if (image->gamma && mng_info->equal_gammas)
13016 Write MNG gAMA chunk
13018 (void) WriteBlobMSBULong(image,4L);
13019 PNGType(chunk,mng_gAMA);
13020 LogPNGChunk(logging,mng_gAMA,4L);
13021 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13022 (void) WriteBlob(image,8,chunk);
13023 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13024 mng_info->have_write_global_gama=MagickTrue;
13026 if (mng_info->equal_chrms)
13032 Write MNG cHRM chunk
13034 (void) WriteBlobMSBULong(image,32L);
13035 PNGType(chunk,mng_cHRM);
13036 LogPNGChunk(logging,mng_cHRM,32L);
13037 primary=image->chromaticity.white_point;
13038 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13039 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13040 primary=image->chromaticity.red_primary;
13041 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13042 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13043 primary=image->chromaticity.green_primary;
13044 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13045 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13046 primary=image->chromaticity.blue_primary;
13047 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13048 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13049 (void) WriteBlob(image,36,chunk);
13050 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13051 mng_info->have_write_global_chrm=MagickTrue;
13054 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13057 Write MNG pHYs chunk
13059 (void) WriteBlobMSBULong(image,9L);
13060 PNGType(chunk,mng_pHYs);
13061 LogPNGChunk(logging,mng_pHYs,9L);
13063 if (image->units == PixelsPerInchResolution)
13065 PNGLong(chunk+4,(png_uint_32)
13066 (image->resolution.x*100.0/2.54+0.5));
13068 PNGLong(chunk+8,(png_uint_32)
13069 (image->resolution.y*100.0/2.54+0.5));
13076 if (image->units == PixelsPerCentimeterResolution)
13078 PNGLong(chunk+4,(png_uint_32)
13079 (image->resolution.x*100.0+0.5));
13081 PNGLong(chunk+8,(png_uint_32)
13082 (image->resolution.y*100.0+0.5));
13089 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13090 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13094 (void) WriteBlob(image,13,chunk);
13095 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13098 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13099 or does not cover the entire frame.
13101 if (write_mng && (image->alpha_trait || image->page.x > 0 ||
13102 image->page.y > 0 || (image->page.width &&
13103 (image->page.width+image->page.x < mng_info->page.width))
13104 || (image->page.height && (image->page.height+image->page.y
13105 < mng_info->page.height))))
13107 (void) WriteBlobMSBULong(image,6L);
13108 PNGType(chunk,mng_BACK);
13109 LogPNGChunk(logging,mng_BACK,6L);
13110 red=ScaleQuantumToShort(image->background_color.red);
13111 green=ScaleQuantumToShort(image->background_color.green);
13112 blue=ScaleQuantumToShort(image->background_color.blue);
13113 PNGShort(chunk+4,red);
13114 PNGShort(chunk+6,green);
13115 PNGShort(chunk+8,blue);
13116 (void) WriteBlob(image,10,chunk);
13117 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13118 if (mng_info->equal_backgrounds)
13120 (void) WriteBlobMSBULong(image,6L);
13121 PNGType(chunk,mng_bKGD);
13122 LogPNGChunk(logging,mng_bKGD,6L);
13123 (void) WriteBlob(image,10,chunk);
13124 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13128 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13129 if ((need_local_plte == MagickFalse) &&
13130 (image->storage_class == PseudoClass) &&
13131 (all_images_are_gray == MagickFalse))
13137 Write MNG PLTE chunk
13139 data_length=3*image->colors;
13140 (void) WriteBlobMSBULong(image,data_length);
13141 PNGType(chunk,mng_PLTE);
13142 LogPNGChunk(logging,mng_PLTE,data_length);
13144 for (i=0; i < (ssize_t) image->colors; i++)
13146 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13147 image->colormap[i].red) & 0xff);
13148 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13149 image->colormap[i].green) & 0xff);
13150 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13151 image->colormap[i].blue) & 0xff);
13154 (void) WriteBlob(image,data_length+4,chunk);
13155 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13156 mng_info->have_write_global_plte=MagickTrue;
13162 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13163 defined(PNG_MNG_FEATURES_SUPPORTED)
13164 mng_info->equal_palettes=MagickFalse;
13168 if (mng_info->adjoin)
13170 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13171 defined(PNG_MNG_FEATURES_SUPPORTED)
13173 If we aren't using a global palette for the entire MNG, check to
13174 see if we can use one for two or more consecutive images.
13176 if (need_local_plte && use_global_plte && !all_images_are_gray)
13178 if (mng_info->IsPalette)
13181 When equal_palettes is true, this image has the same palette
13182 as the previous PseudoClass image
13184 mng_info->have_write_global_plte=mng_info->equal_palettes;
13185 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13186 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13189 Write MNG PLTE chunk
13194 data_length=3*image->colors;
13195 (void) WriteBlobMSBULong(image,data_length);
13196 PNGType(chunk,mng_PLTE);
13197 LogPNGChunk(logging,mng_PLTE,data_length);
13199 for (i=0; i < (ssize_t) image->colors; i++)
13201 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13202 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13203 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13206 (void) WriteBlob(image,data_length+4,chunk);
13207 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13208 (uInt) (data_length+4)));
13209 mng_info->have_write_global_plte=MagickTrue;
13213 mng_info->have_write_global_plte=MagickFalse;
13224 previous_x=mng_info->page.x;
13225 previous_y=mng_info->page.y;
13232 mng_info->page=image->page;
13233 if ((mng_info->page.x != previous_x) ||
13234 (mng_info->page.y != previous_y))
13236 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13237 PNGType(chunk,mng_DEFI);
13238 LogPNGChunk(logging,mng_DEFI,12L);
13239 chunk[4]=0; /* object 0 MSB */
13240 chunk[5]=0; /* object 0 LSB */
13241 chunk[6]=0; /* visible */
13242 chunk[7]=0; /* abstract */
13243 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13244 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13245 (void) WriteBlob(image,16,chunk);
13246 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13251 mng_info->write_mng=write_mng;
13253 if ((int) image->dispose >= 3)
13254 mng_info->framing_mode=3;
13256 if (mng_info->need_fram && mng_info->adjoin &&
13257 ((image->delay != mng_info->delay) ||
13258 (mng_info->framing_mode != mng_info->old_framing_mode)))
13260 if (image->delay == mng_info->delay)
13263 Write a MNG FRAM chunk with the new framing mode.
13265 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13266 PNGType(chunk,mng_FRAM);
13267 LogPNGChunk(logging,mng_FRAM,1L);
13268 chunk[4]=(unsigned char) mng_info->framing_mode;
13269 (void) WriteBlob(image,5,chunk);
13270 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13275 Write a MNG FRAM chunk with the delay.
13277 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13278 PNGType(chunk,mng_FRAM);
13279 LogPNGChunk(logging,mng_FRAM,10L);
13280 chunk[4]=(unsigned char) mng_info->framing_mode;
13281 chunk[5]=0; /* frame name separator (no name) */
13282 chunk[6]=2; /* flag for changing default delay */
13283 chunk[7]=0; /* flag for changing frame timeout */
13284 chunk[8]=0; /* flag for changing frame clipping */
13285 chunk[9]=0; /* flag for changing frame sync_id */
13286 PNGLong(chunk+10,(png_uint_32)
13287 ((mng_info->ticks_per_second*
13288 image->delay)/MagickMax(image->ticks_per_second,1)));
13289 (void) WriteBlob(image,14,chunk);
13290 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13291 mng_info->delay=(png_uint_32) image->delay;
13293 mng_info->old_framing_mode=mng_info->framing_mode;
13296 #if defined(JNG_SUPPORTED)
13297 if (image_info->compression == JPEGCompression)
13302 if (logging != MagickFalse)
13303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13304 " Writing JNG object.");
13305 /* To do: specify the desired alpha compression method. */
13306 write_info=CloneImageInfo(image_info);
13307 write_info->compression=UndefinedCompression;
13308 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13309 write_info=DestroyImageInfo(write_info);
13314 if (logging != MagickFalse)
13315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13316 " Writing PNG object.");
13318 mng_info->need_blob = MagickFalse;
13319 mng_info->ping_preserve_colormap = MagickFalse;
13321 /* We don't want any ancillary chunks written */
13322 mng_info->ping_exclude_bKGD=MagickTrue;
13323 mng_info->ping_exclude_cHRM=MagickTrue;
13324 mng_info->ping_exclude_date=MagickTrue;
13325 mng_info->ping_exclude_EXIF=MagickTrue;
13326 mng_info->ping_exclude_gAMA=MagickTrue;
13327 mng_info->ping_exclude_iCCP=MagickTrue;
13328 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13329 mng_info->ping_exclude_oFFs=MagickTrue;
13330 mng_info->ping_exclude_pHYs=MagickTrue;
13331 mng_info->ping_exclude_sRGB=MagickTrue;
13332 mng_info->ping_exclude_tEXt=MagickTrue;
13333 mng_info->ping_exclude_tRNS=MagickTrue;
13334 mng_info->ping_exclude_vpAg=MagickTrue;
13335 mng_info->ping_exclude_zCCP=MagickTrue;
13336 mng_info->ping_exclude_zTXt=MagickTrue;
13338 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13341 if (status == MagickFalse)
13343 MngInfoFreeStruct(mng_info,&have_mng_structure);
13344 (void) CloseBlob(image);
13345 return(MagickFalse);
13347 (void) CatchImageException(image);
13348 if (GetNextImageInList(image) == (Image *) NULL)
13350 image=SyncNextImageInList(image);
13351 status=SetImageProgress(image,SaveImagesTag,scene++,
13352 GetImageListLength(image));
13354 if (status == MagickFalse)
13357 } while (mng_info->adjoin);
13361 while (GetPreviousImageInList(image) != (Image *) NULL)
13362 image=GetPreviousImageInList(image);
13364 Write the MEND chunk.
13366 (void) WriteBlobMSBULong(image,0x00000000L);
13367 PNGType(chunk,mng_MEND);
13368 LogPNGChunk(logging,mng_MEND,0L);
13369 (void) WriteBlob(image,4,chunk);
13370 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13373 Relinquish resources.
13375 (void) CloseBlob(image);
13376 MngInfoFreeStruct(mng_info,&have_mng_structure);
13378 if (logging != MagickFalse)
13379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13381 return(MagickTrue);
13383 #else /* PNG_LIBPNG_VER > 10011 */
13385 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13388 printf("Your PNG library is too old: You have libpng-%s\n",
13389 PNG_LIBPNG_VER_STRING);
13391 ThrowBinaryException(CoderError,"PNG library is too old",
13392 image_info->filename);
13395 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13397 return(WritePNGImage(image_info,image));
13399 #endif /* PNG_LIBPNG_VER > 10011 */