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 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
140 const struct sRGB_info_struct sRGB_info[] =
142 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143 { 3048, 0x3b8772b9UL, 0},
145 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146 { 3052, 0x427ebb21UL, 1},
148 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149 {60988, 0x306fd8aeUL, 0},
151 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152 {60960, 0xbbef7812UL, 0},
154 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155 { 3024, 0x5d5129ceUL, 1},
157 /* HP-Microsoft sRGB v2 perceptual */
158 { 3144, 0x182ea552UL, 0},
160 /* HP-Microsoft sRGB v2 media-relative */
161 { 3144, 0xf29e526dUL, 1},
163 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164 { 524, 0xd4938c39UL, 0},
166 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167 { 3212, 0x034af5a1UL, 0},
170 { 0, 0x00000000UL, 0},
173 /* Macros for left-bit-replication to ensure that pixels
174 * and PixelInfos all have the same image->depth, and for use
175 * in PNG8 quantization.
178 /* LBR01: Replicate top bit */
180 #define LBR01PacketRed(pixelpacket) \
181 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
184 #define LBR01PacketGreen(pixelpacket) \
185 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
188 #define LBR01PacketBlue(pixelpacket) \
189 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
192 #define LBR01PacketAlpha(pixelpacket) \
193 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
196 #define LBR01PacketRGB(pixelpacket) \
198 LBR01PacketRed((pixelpacket)); \
199 LBR01PacketGreen((pixelpacket)); \
200 LBR01PacketBlue((pixelpacket)); \
203 #define LBR01PacketRGBO(pixelpacket) \
205 LBR01PacketRGB((pixelpacket)); \
206 LBR01PacketAlpha((pixelpacket)); \
209 #define LBR01PixelRed(pixel) \
210 (SetPixelRed(image, \
211 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212 0 : QuantumRange,(pixel)));
214 #define LBR01PixelGreen(pixel) \
215 (SetPixelGreen(image, \
216 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217 0 : QuantumRange,(pixel)));
219 #define LBR01PixelBlue(pixel) \
220 (SetPixelBlue(image, \
221 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222 0 : QuantumRange,(pixel)));
224 #define LBR01PixelAlpha(pixel) \
225 (SetPixelAlpha(image, \
226 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227 0 : QuantumRange,(pixel)));
229 #define LBR01PixelRGB(pixel) \
231 LBR01PixelRed((pixel)); \
232 LBR01PixelGreen((pixel)); \
233 LBR01PixelBlue((pixel)); \
236 #define LBR01PixelRGBA(pixel) \
238 LBR01PixelRGB((pixel)); \
239 LBR01PixelAlpha((pixel)); \
242 /* LBR02: Replicate top 2 bits */
244 #define LBR02PacketRed(pixelpacket) \
246 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247 (pixelpacket).red=ScaleCharToQuantum( \
248 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
250 #define LBR02PacketGreen(pixelpacket) \
252 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253 (pixelpacket).green=ScaleCharToQuantum( \
254 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
256 #define LBR02PacketBlue(pixelpacket) \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259 (pixelpacket).blue=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
262 #define LBR02PacketAlpha(pixelpacket) \
264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265 (pixelpacket).alpha=ScaleCharToQuantum( \
266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
269 #define LBR02PacketRGB(pixelpacket) \
271 LBR02PacketRed((pixelpacket)); \
272 LBR02PacketGreen((pixelpacket)); \
273 LBR02PacketBlue((pixelpacket)); \
276 #define LBR02PacketRGBO(pixelpacket) \
278 LBR02PacketRGB((pixelpacket)); \
279 LBR02PacketAlpha((pixelpacket)); \
282 #define LBR02PixelRed(pixel) \
284 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
286 SetPixelRed(image, ScaleCharToQuantum( \
287 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
290 #define LBR02PixelGreen(pixel) \
292 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
294 SetPixelGreen(image, ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
298 #define LBR02PixelBlue(pixel) \
300 unsigned char lbr_bits= \
301 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302 SetPixelBlue(image, ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
306 #define LBR02PixelAlpha(pixel) \
308 unsigned char lbr_bits= \
309 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310 SetPixelAlpha(image, ScaleCharToQuantum( \
311 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
315 #define LBR02PixelRGB(pixel) \
317 LBR02PixelRed((pixel)); \
318 LBR02PixelGreen((pixel)); \
319 LBR02PixelBlue((pixel)); \
322 #define LBR02PixelRGBA(pixel) \
324 LBR02PixelRGB((pixel)); \
325 LBR02PixelAlpha((pixel)); \
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329 PNG8 quantization) */
331 #define LBR03PacketRed(pixelpacket) \
333 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334 (pixelpacket).red=ScaleCharToQuantum( \
335 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
337 #define LBR03PacketGreen(pixelpacket) \
339 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340 (pixelpacket).green=ScaleCharToQuantum( \
341 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
343 #define LBR03PacketBlue(pixelpacket) \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346 (pixelpacket).blue=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
350 #define LBR03PacketRGB(pixelpacket) \
352 LBR03PacketRed((pixelpacket)); \
353 LBR03PacketGreen((pixelpacket)); \
354 LBR03PacketBlue((pixelpacket)); \
357 #define LBR03PixelRed(pixel) \
359 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
361 SetPixelRed(image, ScaleCharToQuantum( \
362 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
364 #define LBR03Green(pixel) \
366 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
368 SetPixelGreen(image, ScaleCharToQuantum( \
369 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
371 #define LBR03Blue(pixel) \
373 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
375 SetPixelBlue(image, ScaleCharToQuantum( \
376 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
379 #define LBR03RGB(pixel) \
381 LBR03PixelRed((pixel)); \
382 LBR03Green((pixel)); \
383 LBR03Blue((pixel)); \
386 /* LBR04: Replicate top 4 bits */
388 #define LBR04PacketRed(pixelpacket) \
390 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
393 #define LBR04PacketGreen(pixelpacket) \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
398 #define LBR04PacketBlue(pixelpacket) \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
403 #define LBR04PacketAlpha(pixelpacket) \
405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
409 #define LBR04PacketRGB(pixelpacket) \
411 LBR04PacketRed((pixelpacket)); \
412 LBR04PacketGreen((pixelpacket)); \
413 LBR04PacketBlue((pixelpacket)); \
416 #define LBR04PacketRGBO(pixelpacket) \
418 LBR04PacketRGB((pixelpacket)); \
419 LBR04PacketAlpha((pixelpacket)); \
422 #define LBR04PixelRed(pixel) \
424 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
427 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
429 #define LBR04PixelGreen(pixel) \
431 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
433 SetPixelGreen(image,\
434 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
436 #define LBR04PixelBlue(pixel) \
438 unsigned char lbr_bits= \
439 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
441 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
443 #define LBR04PixelAlpha(pixel) \
445 unsigned char lbr_bits= \
446 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447 SetPixelAlpha(image,\
448 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
451 #define LBR04PixelRGB(pixel) \
453 LBR04PixelRed((pixel)); \
454 LBR04PixelGreen((pixel)); \
455 LBR04PixelBlue((pixel)); \
458 #define LBR04PixelRGBA(pixel) \
460 LBR04PixelRGB((pixel)); \
461 LBR04PixelAlpha((pixel)); \
465 #if MAGICKCORE_QUANTUM_DEPTH > 8
466 /* LBR08: Replicate top 8 bits */
468 #define LBR08PacketRed(pixelpacket) \
470 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
471 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
473 #define LBR08PacketGreen(pixelpacket) \
475 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
476 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
478 #define LBR08PacketBlue(pixelpacket) \
480 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
481 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
483 #define LBR08PacketAlpha(pixelpacket) \
485 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
486 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
489 #define LBR08PacketRGB(pixelpacket) \
491 LBR08PacketRed((pixelpacket)); \
492 LBR08PacketGreen((pixelpacket)); \
493 LBR08PacketBlue((pixelpacket)); \
496 #define LBR08PacketRGBO(pixelpacket) \
498 LBR08PacketRGB((pixelpacket)); \
499 LBR08PacketAlpha((pixelpacket)); \
502 #define LBR08PixelRed(pixel) \
504 unsigned char lbr_bits= \
505 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
507 ScaleCharToQuantum((lbr_bits)), (pixel)); \
509 #define LBR08PixelGreen(pixel) \
511 unsigned char lbr_bits= \
512 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
513 SetPixelGreen(image,\
514 ScaleCharToQuantum((lbr_bits)), (pixel)); \
516 #define LBR08PixelBlue(pixel) \
518 unsigned char lbr_bits= \
519 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
521 ScaleCharToQuantum((lbr_bits)), (pixel)); \
523 #define LBR08PixelAlpha(pixel) \
525 unsigned char lbr_bits= \
526 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
527 SetPixelAlpha(image,\
528 ScaleCharToQuantum((lbr_bits)), (pixel)); \
531 #define LBR08PixelRGB(pixel) \
533 LBR08PixelRed((pixel)); \
534 LBR08PixelGreen((pixel)); \
535 LBR08PixelBlue((pixel)); \
538 #define LBR08PixelRGBA(pixel) \
540 LBR08PixelRGB((pixel)); \
541 LBR08PixelAlpha((pixel)); \
543 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
546 #if MAGICKCORE_QUANTUM_DEPTH > 16
547 /* LBR16: Replicate top 16 bits */
549 #define LBR16PacketRed(pixelpacket) \
551 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
552 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
554 #define LBR16PacketGreen(pixelpacket) \
556 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
557 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
559 #define LBR16PacketBlue(pixelpacket) \
561 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
562 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
564 #define LBR16PacketAlpha(pixelpacket) \
566 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
567 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
570 #define LBR16PacketRGB(pixelpacket) \
572 LBR16PacketRed((pixelpacket)); \
573 LBR16PacketGreen((pixelpacket)); \
574 LBR16PacketBlue((pixelpacket)); \
577 #define LBR16PacketRGBO(pixelpacket) \
579 LBR16PacketRGB((pixelpacket)); \
580 LBR16PacketAlpha((pixelpacket)); \
583 #define LBR16PixelRed(pixel) \
585 unsigned short lbr_bits= \
586 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
588 ScaleShortToQuantum((lbr_bits)),(pixel)); \
590 #define LBR16PixelGreen(pixel) \
592 unsigned short lbr_bits= \
593 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
594 SetPixelGreen(image,\
595 ScaleShortToQuantum((lbr_bits)),(pixel)); \
597 #define LBR16PixelBlue(pixel) \
599 unsigned short lbr_bits= \
600 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
602 ScaleShortToQuantum((lbr_bits)),(pixel)); \
604 #define LBR16PixelAlpha(pixel) \
606 unsigned short lbr_bits= \
607 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
608 SetPixelAlpha(image,\
609 ScaleShortToQuantum((lbr_bits)),(pixel)); \
612 #define LBR16PixelRGB(pixel) \
614 LBR16PixelRed((pixel)); \
615 LBR16PixelGreen((pixel)); \
616 LBR16PixelBlue((pixel)); \
619 #define LBR16PixelRGBA(pixel) \
621 LBR16PixelRGB((pixel)); \
622 LBR16PixelAlpha((pixel)); \
624 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
627 Establish thread safety.
628 setjmp/longjmp is claimed to be safe on these platforms:
629 setjmp/longjmp is alleged to be unsafe on these platforms:
631 #ifndef SETJMP_IS_THREAD_SAFE
632 #define PNG_SETJMP_NOT_THREAD_SAFE
635 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
637 *ping_semaphore = (SemaphoreInfo *) NULL;
641 This temporary until I set up malloc'ed object attributes array.
642 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
645 #define MNG_MAX_OBJECTS 256
648 If this not defined, spec is interpreted strictly. If it is
649 defined, an attempt will be made to recover from some errors,
651 o global PLTE too short
656 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
657 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
658 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
659 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
660 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
661 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
662 will be enabled by default in libpng-1.2.0.
664 #ifdef PNG_MNG_FEATURES_SUPPORTED
665 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
666 # define PNG_READ_EMPTY_PLTE_SUPPORTED
668 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
669 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
674 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
675 This macro is only defined in libpng-1.0.3 and later.
676 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
678 #ifndef PNG_UINT_31_MAX
679 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
683 Constant strings for known chunk types. If you need to add a chunk,
684 add a string holding the name here. To make the code more
685 portable, we use ASCII numbers like this, not characters.
688 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
689 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
690 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
691 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
692 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
693 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
694 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
695 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
696 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
697 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
698 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
699 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
700 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
701 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
702 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
703 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
704 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
705 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
706 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
707 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
708 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
709 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
710 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
711 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
712 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
713 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
714 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
715 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
716 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
717 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
718 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
719 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
720 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
721 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
723 #if defined(JNG_SUPPORTED)
724 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
725 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
726 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
727 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
728 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
729 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
733 Other known chunks that are not yet supported by ImageMagick:
734 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
735 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
736 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
737 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
738 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
739 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
740 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
741 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
744 typedef struct _MngBox
753 typedef struct _MngPair
760 #ifdef MNG_OBJECT_BUFFERS
761 typedef struct _MngBuffer
793 typedef struct _MngInfo
796 #ifdef MNG_OBJECT_BUFFERS
798 *ob[MNG_MAX_OBJECTS];
809 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
810 bytes_in_read_buffer,
816 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
817 defined(PNG_MNG_FEATURES_SUPPORTED)
829 have_saved_bkgd_index,
830 have_write_global_chrm,
831 have_write_global_gama,
832 have_write_global_plte,
833 have_write_global_srgb,
847 x_off[MNG_MAX_OBJECTS],
848 y_off[MNG_MAX_OBJECTS];
854 object_clip[MNG_MAX_OBJECTS];
857 /* These flags could be combined into one byte */
858 exists[MNG_MAX_OBJECTS],
859 frozen[MNG_MAX_OBJECTS],
861 invisible[MNG_MAX_OBJECTS],
862 viewable[MNG_MAX_OBJECTS];
874 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
892 global_x_pixels_per_unit,
893 global_y_pixels_per_unit,
903 global_phys_unit_type,
918 write_png_compression_level,
919 write_png_compression_strategy,
920 write_png_compression_filter,
927 #ifdef MNG_BASI_SUPPORTED
935 basi_compression_method,
937 basi_interlace_method,
960 /* Added at version 6.6.6-7 */
968 /* ping_exclude_iTXt, */
975 ping_exclude_zCCP, /* hex-encoded iCCP */
977 ping_preserve_colormap,
978 /* Added at version 6.8.5-7 */
985 Forward declarations.
987 static MagickBooleanType
988 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
990 static MagickBooleanType
991 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
993 #if defined(JNG_SUPPORTED)
994 static MagickBooleanType
995 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
998 #if PNG_LIBPNG_VER > 10011
1001 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1002 static MagickBooleanType
1003 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
1005 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1007 * This is true if the high byte and the next highest byte of
1008 * each sample of the image, the colormap, and the background color
1009 * are equal to each other. We check this by seeing if the samples
1010 * are unchanged when we scale them down to 8 and back up to Quantum.
1012 * We don't use the method GetImageDepth() because it doesn't check
1013 * background and doesn't handle PseudoClass specially.
1016 #define QuantumToCharToQuantumEqQuantum(quantum) \
1017 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1020 ok_to_reduce=MagickFalse;
1022 if (image->depth >= 16)
1029 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1030 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1031 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1032 MagickTrue : MagickFalse;
1034 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1038 for (indx=0; indx < (ssize_t) image->colors; indx++)
1041 QuantumToCharToQuantumEqQuantum(
1042 image->colormap[indx].red) &&
1043 QuantumToCharToQuantumEqQuantum(
1044 image->colormap[indx].green) &&
1045 QuantumToCharToQuantumEqQuantum(
1046 image->colormap[indx].blue)) ?
1047 MagickTrue : MagickFalse;
1049 if (ok_to_reduce == MagickFalse)
1054 if ((ok_to_reduce != MagickFalse) &&
1055 (image->storage_class != PseudoClass))
1063 for (y=0; y < (ssize_t) image->rows; y++)
1065 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1067 if (p == (const Quantum *) NULL)
1069 ok_to_reduce = MagickFalse;
1073 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1076 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1077 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1078 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1079 MagickTrue : MagickFalse;
1081 if (ok_to_reduce == MagickFalse)
1084 p+=GetPixelChannels(image);
1091 if (ok_to_reduce != MagickFalse)
1093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1094 " OK to reduce PNG bit depth to 8 without loss of info");
1098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1099 " Not OK to reduce PNG bit depth to 8 without loss of info");
1103 return ok_to_reduce;
1105 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1107 static const char* PngColorTypeToString(const unsigned int color_type)
1110 *result = "Unknown";
1114 case PNG_COLOR_TYPE_GRAY:
1117 case PNG_COLOR_TYPE_GRAY_ALPHA:
1118 result = "Gray+Alpha";
1120 case PNG_COLOR_TYPE_PALETTE:
1123 case PNG_COLOR_TYPE_RGB:
1126 case PNG_COLOR_TYPE_RGB_ALPHA:
1127 result = "RGB+Alpha";
1135 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1139 case PerceptualIntent:
1142 case RelativeIntent:
1145 case SaturationIntent:
1148 case AbsoluteIntent:
1156 static RenderingIntent
1157 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1159 switch (ping_intent)
1162 return PerceptualIntent;
1165 return RelativeIntent;
1168 return SaturationIntent;
1171 return AbsoluteIntent;
1174 return UndefinedIntent;
1179 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1181 switch (ping_intent)
1184 return "Perceptual Intent";
1187 return "Relative Intent";
1190 return "Saturation Intent";
1193 return "Absolute Intent";
1196 return "Undefined Intent";
1200 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1209 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1211 switch (ping_colortype)
1229 return "UndefinedColorType";
1234 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1241 #endif /* PNG_LIBPNG_VER > 10011 */
1242 #endif /* MAGICKCORE_PNG_DELEGATE */
1245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1255 % IsMNG() returns MagickTrue if the image format type, identified by the
1256 % magick string, is MNG.
1258 % The format of the IsMNG method is:
1260 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1262 % A description of each parameter follows:
1264 % o magick: compare image format pattern against these bytes.
1266 % o length: Specifies the length of the magick string.
1270 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1273 return(MagickFalse);
1275 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1278 return(MagickFalse);
1282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1292 % IsJNG() returns MagickTrue if the image format type, identified by the
1293 % magick string, is JNG.
1295 % The format of the IsJNG method is:
1297 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1299 % A description of each parameter follows:
1301 % o magick: compare image format pattern against these bytes.
1303 % o length: Specifies the length of the magick string.
1307 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1310 return(MagickFalse);
1312 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1315 return(MagickFalse);
1319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1329 % IsPNG() returns MagickTrue if the image format type, identified by the
1330 % magick string, is PNG.
1332 % The format of the IsPNG method is:
1334 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1336 % A description of each parameter follows:
1338 % o magick: compare image format pattern against these bytes.
1340 % o length: Specifies the length of the magick string.
1343 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1346 return(MagickFalse);
1348 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1351 return(MagickFalse);
1354 #if defined(MAGICKCORE_PNG_DELEGATE)
1355 #if defined(__cplusplus) || defined(c_plusplus)
1359 #if (PNG_LIBPNG_VER > 10011)
1360 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1365 assert(image != (Image *) NULL);
1366 assert(image->signature == MagickSignature);
1367 buffer[0]=(unsigned char) (value >> 24);
1368 buffer[1]=(unsigned char) (value >> 16);
1369 buffer[2]=(unsigned char) (value >> 8);
1370 buffer[3]=(unsigned char) value;
1371 return((size_t) WriteBlob(image,4,buffer));
1374 static void PNGLong(png_bytep p,png_uint_32 value)
1376 *p++=(png_byte) ((value >> 24) & 0xff);
1377 *p++=(png_byte) ((value >> 16) & 0xff);
1378 *p++=(png_byte) ((value >> 8) & 0xff);
1379 *p++=(png_byte) (value & 0xff);
1382 #if defined(JNG_SUPPORTED)
1383 static void PNGsLong(png_bytep p,png_int_32 value)
1385 *p++=(png_byte) ((value >> 24) & 0xff);
1386 *p++=(png_byte) ((value >> 16) & 0xff);
1387 *p++=(png_byte) ((value >> 8) & 0xff);
1388 *p++=(png_byte) (value & 0xff);
1392 static void PNGShort(png_bytep p,png_uint_16 value)
1394 *p++=(png_byte) ((value >> 8) & 0xff);
1395 *p++=(png_byte) (value & 0xff);
1398 static void PNGType(png_bytep p,png_bytep type)
1400 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1403 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1406 if (logging != MagickFalse)
1407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1408 " Writing %c%c%c%c chunk, length: %.20g",
1409 type[0],type[1],type[2],type[3],(double) length);
1411 #endif /* PNG_LIBPNG_VER > 10011 */
1413 #if defined(__cplusplus) || defined(c_plusplus)
1417 #if PNG_LIBPNG_VER > 10011
1419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1423 % R e a d P N G I m a g e %
1427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1430 % Multiple-image Network Graphics (MNG) image file and returns it. It
1431 % allocates the memory necessary for the new Image structure and returns a
1432 % pointer to the new image or set of images.
1434 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1436 % The format of the ReadPNGImage method is:
1438 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1440 % A description of each parameter follows:
1442 % o image_info: the image info.
1444 % o exception: return any errors or warnings in this structure.
1446 % To do, more or less in chronological order (as of version 5.5.2,
1447 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1449 % Get 16-bit cheap transparency working.
1451 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1453 % Preserve all unknown and not-yet-handled known chunks found in input
1454 % PNG file and copy them into output PNG files according to the PNG
1457 % (At this point, PNG encoding should be in full MNG compliance)
1459 % Provide options for choice of background to use when the MNG BACK
1460 % chunk is not present or is not mandatory (i.e., leave transparent,
1461 % user specified, MNG BACK, PNG bKGD)
1463 % Implement LOOP/ENDL [done, but could do discretionary loops more
1464 % efficiently by linking in the duplicate frames.].
1466 % Decode and act on the MHDR simplicity profile (offer option to reject
1467 % files or attempt to process them anyway when the profile isn't LC or VLC).
1469 % Upgrade to full MNG without Delta-PNG.
1471 % o BACK [done a while ago except for background image ID]
1472 % o MOVE [done 15 May 1999]
1473 % o CLIP [done 15 May 1999]
1474 % o DISC [done 19 May 1999]
1475 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1476 % o SEEK [partially done 19 May 1999 (discard function only)]
1480 % o MNG-level tEXt/iTXt/zTXt
1485 % o iTXt (wait for libpng implementation).
1487 % Use the scene signature to discover when an identical scene is
1488 % being reused, and just point to the original image->exception instead
1489 % of storing another set of pixels. This not specific to MNG
1490 % but could be applied generally.
1492 % Upgrade to full MNG with Delta-PNG.
1494 % JNG tEXt/iTXt/zTXt
1496 % We will not attempt to read files containing the CgBI chunk.
1497 % They are really Xcode files meant for display on the iPhone.
1498 % These are not valid PNG files and it is impossible to recover
1499 % the original PNG from files that have been converted to Xcode-PNG,
1500 % since irretrievable loss of color data has occurred due to the
1501 % use of premultiplied alpha.
1504 #if defined(__cplusplus) || defined(c_plusplus)
1509 This the function that does the actual reading of data. It is
1510 the same as the one supplied in libpng, except that it receives the
1511 datastream from the ReadBlob() function instead of standard input.
1513 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1518 image=(Image *) png_get_io_ptr(png_ptr);
1524 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1525 if (check != length)
1530 (void) FormatLocaleString(msg,MaxTextExtent,
1531 "Expected %.20g bytes; found %.20g bytes",(double) length,
1533 png_warning(png_ptr,msg);
1534 png_error(png_ptr,"Read Exception");
1539 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1540 !defined(PNG_MNG_FEATURES_SUPPORTED)
1541 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1542 * older than libpng-1.0.3a, which was the first to allow the empty
1543 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1544 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1545 * encountered after an empty PLTE, so we have to look ahead for bKGD
1546 * chunks and remove them from the datastream that is passed to libpng,
1547 * and store their contents for later use.
1549 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1564 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1565 image=(Image *) mng_info->image;
1566 while (mng_info->bytes_in_read_buffer && length)
1568 data[i]=mng_info->read_buffer[i];
1569 mng_info->bytes_in_read_buffer--;
1575 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1577 if (check != length)
1578 png_error(png_ptr,"Read Exception");
1582 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1585 check=(png_size_t) ReadBlob(image,(size_t) length,
1586 (char *) mng_info->read_buffer);
1587 mng_info->read_buffer[4]=0;
1588 mng_info->bytes_in_read_buffer=4;
1589 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1590 mng_info->found_empty_plte=MagickTrue;
1591 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1593 mng_info->found_empty_plte=MagickFalse;
1594 mng_info->have_saved_bkgd_index=MagickFalse;
1598 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1601 check=(png_size_t) ReadBlob(image,(size_t) length,
1602 (char *) mng_info->read_buffer);
1603 mng_info->read_buffer[4]=0;
1604 mng_info->bytes_in_read_buffer=4;
1605 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1606 if (mng_info->found_empty_plte)
1609 Skip the bKGD data byte and CRC.
1612 ReadBlob(image,5,(char *) mng_info->read_buffer);
1613 check=(png_size_t) ReadBlob(image,(size_t) length,
1614 (char *) mng_info->read_buffer);
1615 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1616 mng_info->have_saved_bkgd_index=MagickTrue;
1617 mng_info->bytes_in_read_buffer=0;
1625 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1630 image=(Image *) png_get_io_ptr(png_ptr);
1636 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1638 if (check != length)
1639 png_error(png_ptr,"WriteBlob Failed");
1643 static void png_flush_data(png_structp png_ptr)
1648 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1649 static int PalettesAreEqual(Image *a,Image *b)
1654 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1655 return((int) MagickFalse);
1657 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1658 return((int) MagickFalse);
1660 if (a->colors != b->colors)
1661 return((int) MagickFalse);
1663 for (i=0; i < (ssize_t) a->colors; i++)
1665 if ((a->colormap[i].red != b->colormap[i].red) ||
1666 (a->colormap[i].green != b->colormap[i].green) ||
1667 (a->colormap[i].blue != b->colormap[i].blue))
1668 return((int) MagickFalse);
1671 return((int) MagickTrue);
1675 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1677 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1678 mng_info->exists[i] && !mng_info->frozen[i])
1680 #ifdef MNG_OBJECT_BUFFERS
1681 if (mng_info->ob[i] != (MngBuffer *) NULL)
1683 if (mng_info->ob[i]->reference_count > 0)
1684 mng_info->ob[i]->reference_count--;
1686 if (mng_info->ob[i]->reference_count == 0)
1688 if (mng_info->ob[i]->image != (Image *) NULL)
1689 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1691 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1694 mng_info->ob[i]=(MngBuffer *) NULL;
1696 mng_info->exists[i]=MagickFalse;
1697 mng_info->invisible[i]=MagickFalse;
1698 mng_info->viewable[i]=MagickFalse;
1699 mng_info->frozen[i]=MagickFalse;
1700 mng_info->x_off[i]=0;
1701 mng_info->y_off[i]=0;
1702 mng_info->object_clip[i].left=0;
1703 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1704 mng_info->object_clip[i].top=0;
1705 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1709 static void MngInfoFreeStruct(MngInfo *mng_info,
1710 MagickBooleanType *have_mng_structure)
1712 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1717 for (i=1; i < MNG_MAX_OBJECTS; i++)
1718 MngInfoDiscardObject(mng_info,i);
1720 if (mng_info->global_plte != (png_colorp) NULL)
1721 mng_info->global_plte=(png_colorp)
1722 RelinquishMagickMemory(mng_info->global_plte);
1724 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1725 *have_mng_structure=MagickFalse;
1729 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1735 if (box.left < box2.left)
1738 if (box.top < box2.top)
1741 if (box.right > box2.right)
1742 box.right=box2.right;
1744 if (box.bottom > box2.bottom)
1745 box.bottom=box2.bottom;
1750 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1756 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1758 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1759 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1760 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1761 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1762 if (delta_type != 0)
1764 box.left+=previous_box.left;
1765 box.right+=previous_box.right;
1766 box.top+=previous_box.top;
1767 box.bottom+=previous_box.bottom;
1773 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1779 Read two ssize_ts from CLON, MOVE or PAST chunk
1781 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1782 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1784 if (delta_type != 0)
1786 pair.a+=previous_pair.a;
1787 pair.b+=previous_pair.b;
1793 static long mng_get_long(unsigned char *p)
1795 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1798 typedef struct _PNGErrorInfo
1807 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1818 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1819 image=error_info->image;
1820 exception=error_info->exception;
1822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1823 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1825 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1826 "`%s'",image->filename);
1828 #if (PNG_LIBPNG_VER < 10500)
1829 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1830 * are building with libpng-1.4.x and can be ignored.
1832 longjmp(ping->jmpbuf,1);
1834 png_longjmp(ping,1);
1838 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1849 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1850 png_error(ping, message);
1852 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1853 image=error_info->image;
1854 exception=error_info->exception;
1855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1856 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1858 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1859 message,"`%s'",image->filename);
1862 #ifdef PNG_USER_MEM_SUPPORTED
1863 #if PNG_LIBPNG_VER >= 10400
1864 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1866 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1870 return((png_voidp) AcquireMagickMemory((size_t) size));
1874 Free a pointer. It is removed from the list at the same time.
1876 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1879 ptr=RelinquishMagickMemory(ptr);
1880 return((png_free_ptr) NULL);
1884 #if defined(__cplusplus) || defined(c_plusplus)
1889 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1890 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1895 register unsigned char
1909 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1910 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1911 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1912 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1913 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1917 /* look for newline */
1921 /* look for length */
1922 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1925 length=(png_uint_32) StringToLong(sp);
1927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1928 " length: %lu",(unsigned long) length);
1930 while (*sp != ' ' && *sp != '\n')
1933 /* allocate space */
1936 png_warning(ping,"invalid profile length");
1937 return(MagickFalse);
1940 profile=BlobToStringInfo((const void *) NULL,length);
1942 if (profile == (StringInfo *) NULL)
1944 png_warning(ping, "unable to copy profile");
1945 return(MagickFalse);
1948 /* copy profile, skipping white space and column 1 "=" signs */
1949 dp=GetStringInfoDatum(profile);
1952 for (i=0; i < (ssize_t) nibbles; i++)
1954 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1958 png_warning(ping, "ran out of profile data");
1959 profile=DestroyStringInfo(profile);
1960 return(MagickFalse);
1966 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1969 (*dp++)+=unhex[(int) *sp++];
1972 We have already read "Raw profile type.
1974 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1975 profile=DestroyStringInfo(profile);
1977 if (image_info->verbose)
1978 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1983 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1984 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1990 /* The unknown chunk structure contains the chunk data:
1995 Note that libpng has already taken care of the CRC handling.
1998 LogMagickEvent(CoderEvent,GetMagickModule(),
1999 " read_vpag_chunk: found %c%c%c%c chunk",
2000 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2002 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2003 chunk->name[2] != 65 ||chunk-> name[3] != 103)
2004 return(0); /* Did not recognize */
2006 /* recognized vpAg */
2008 if (chunk->size != 9)
2009 return(-1); /* Error return */
2011 if (chunk->data[8] != 0)
2012 return(0); /* ImageMagick requires pixel units */
2014 image=(Image *) png_get_user_chunk_ptr(ping);
2016 image->page.width=(size_t) ((chunk->data[0] << 24) |
2017 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2019 image->page.height=(size_t) ((chunk->data[4] << 24) |
2020 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2022 /* Return one of the following: */
2023 /* return(-n); chunk had an error */
2024 /* return(0); did not recognize */
2025 /* return(n); success */
2033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2037 % R e a d O n e P N G I m a g e %
2041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2043 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2044 % (minus the 8-byte signature) and returns it. It allocates the memory
2045 % necessary for the new Image structure and returns a pointer to the new
2048 % The format of the ReadOnePNGImage method is:
2050 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2051 % ExceptionInfo *exception)
2053 % A description of each parameter follows:
2055 % o mng_info: Specifies a pointer to a MngInfo structure.
2057 % o image_info: the image info.
2059 % o exception: return any errors or warnings in this structure.
2062 static Image *ReadOnePNGImage(MngInfo *mng_info,
2063 const ImageInfo *image_info, ExceptionInfo *exception)
2065 /* Read one PNG image */
2067 /* To do: Read the tIME chunk into the date:modify property */
2068 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2081 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2091 ping_interlace_method,
2092 ping_compression_method,
2106 ping_found_sRGB_cHRM,
2111 *volatile pixel_info;
2149 register unsigned char
2169 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2170 png_byte unused_chunks[]=
2172 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2173 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2174 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2175 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2176 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2177 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2178 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2179 /* ignore the APNG chunks */
2180 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2181 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2182 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2187 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2188 " Enter ReadOnePNGImage()");
2190 /* Define these outside of the following "if logging()" block so they will
2191 * show in debuggers.
2194 (void) ConcatenateMagickString(im_vers,
2195 MagickLibVersionText,32);
2196 (void) ConcatenateMagickString(im_vers,
2197 MagickLibAddendum,32);
2200 (void) ConcatenateMagickString(libpng_vers,
2201 PNG_LIBPNG_VER_STRING,32);
2203 (void) ConcatenateMagickString(libpng_runv,
2204 png_get_libpng_ver(NULL),32);
2207 (void) ConcatenateMagickString(zlib_vers,
2210 (void) ConcatenateMagickString(zlib_runv,
2215 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
2217 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
2219 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2221 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2224 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2226 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2228 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2233 #if (PNG_LIBPNG_VER < 10200)
2234 if (image_info->verbose)
2235 printf("Your PNG library (libpng-%s) is rather old.\n",
2236 PNG_LIBPNG_VER_STRING);
2239 #if (PNG_LIBPNG_VER >= 10400)
2240 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2241 if (image_info->verbose)
2243 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2244 PNG_LIBPNG_VER_STRING);
2245 printf("Please update it.\n");
2251 quantum_info = (QuantumInfo *) NULL;
2252 image=mng_info->image;
2254 if (logging != MagickFalse)
2256 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2257 " Before reading:");
2259 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " image->alpha_trait=%d",(int) image->alpha_trait);
2262 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2263 " image->rendering_intent=%d",(int) image->rendering_intent);
2265 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2266 " image->colorspace=%d",(int) image->colorspace);
2268 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2269 " image->gamma=%f", image->gamma);
2271 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2273 /* Set to an out-of-range color unless tRNS chunk is present */
2274 transparent_color.red=65537;
2275 transparent_color.green=65537;
2276 transparent_color.blue=65537;
2277 transparent_color.alpha=65537;
2282 num_raw_profiles = 0;
2284 ping_found_cHRM = MagickFalse;
2285 ping_found_gAMA = MagickFalse;
2286 ping_found_iCCP = MagickFalse;
2287 ping_found_sRGB = MagickFalse;
2288 ping_found_sRGB_cHRM = MagickFalse;
2289 ping_preserve_iCCP = MagickFalse;
2295 value=GetImageOption(image_info,"png:preserve-iCCP");
2298 value=GetImageArtifact(image,"png:preserve-iCCP");
2301 ping_preserve_iCCP=MagickTrue;
2305 Allocate the PNG structures
2307 #ifdef PNG_USER_MEM_SUPPORTED
2308 error_info.image=image;
2309 error_info.exception=exception;
2310 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2311 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2312 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2314 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2315 MagickPNGErrorHandler,MagickPNGWarningHandler);
2317 if (ping == (png_struct *) NULL)
2318 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2320 ping_info=png_create_info_struct(ping);
2322 if (ping_info == (png_info *) NULL)
2324 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2325 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2328 end_info=png_create_info_struct(ping);
2330 if (end_info == (png_info *) NULL)
2332 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2333 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2336 pixel_info=(MemoryInfo *) NULL;
2338 if (setjmp(png_jmpbuf(ping)))
2341 PNG image is corrupt.
2343 png_destroy_read_struct(&ping,&ping_info,&end_info);
2345 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2346 UnlockSemaphoreInfo(ping_semaphore);
2349 if (pixel_info != (MemoryInfo *) NULL)
2350 pixel_info=RelinquishVirtualMemory(pixel_info);
2352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2354 " exit ReadOnePNGImage() with error.");
2356 if (image != (Image *) NULL)
2358 InheritException(exception,exception);
2362 return(GetFirstImageInList(image));
2365 /* { For navigation to end of SETJMP-protected block. Within this
2366 * block, use png_error() instead of Throwing an Exception, to ensure
2367 * that libpng is able to clean up, and that the semaphore is unlocked.
2370 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2371 LockSemaphoreInfo(ping_semaphore);
2374 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2375 /* Allow benign errors */
2376 png_set_benign_errors(ping, 1);
2380 Prepare PNG for reading.
2383 mng_info->image_found++;
2384 png_set_sig_bytes(ping,8);
2386 if (LocaleCompare(image_info->magick,"MNG") == 0)
2388 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2389 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2390 png_set_read_fn(ping,image,png_get_data);
2392 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2393 png_permit_empty_plte(ping,MagickTrue);
2394 png_set_read_fn(ping,image,png_get_data);
2396 mng_info->image=image;
2397 mng_info->bytes_in_read_buffer=0;
2398 mng_info->found_empty_plte=MagickFalse;
2399 mng_info->have_saved_bkgd_index=MagickFalse;
2400 png_set_read_fn(ping,mng_info,mng_get_data);
2406 png_set_read_fn(ping,image,png_get_data);
2408 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2409 /* Ignore unused chunks and all unknown chunks except for vpAg */
2410 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2411 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2413 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2415 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2416 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2417 (int)sizeof(unused_chunks)/5);
2418 /* Callback for other unknown chunks */
2419 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2422 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2423 # if (PNG_LIBPNG_VER >= 10400)
2424 /* Limit the size of the chunk storage cache used for sPLT, text,
2425 * and unknown chunks.
2427 png_set_chunk_cache_max(ping, 32767);
2431 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2432 /* Disable new libpng-1.5.10 feature */
2433 png_set_check_for_invalid_index (ping, 0);
2436 #if (PNG_LIBPNG_VER < 10400)
2437 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2438 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2439 /* Disable thread-unsafe features of pnggccrd */
2440 if (png_access_version_number() >= 10200)
2442 png_uint_32 mmx_disable_mask=0;
2443 png_uint_32 asm_flags;
2445 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2446 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2447 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2448 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2449 asm_flags=png_get_asm_flags(ping);
2450 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2455 png_read_info(ping,ping_info);
2457 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2458 &ping_bit_depth,&ping_color_type,
2459 &ping_interlace_method,&ping_compression_method,
2460 &ping_filter_method);
2462 ping_file_depth = ping_bit_depth;
2464 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2469 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2470 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2472 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2473 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2476 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2479 (void) png_get_bKGD(ping, ping_info, &ping_background);
2481 if (ping_bit_depth < 8)
2483 png_set_packing(ping);
2487 image->depth=ping_bit_depth;
2488 image->depth=GetImageQuantumDepth(image,MagickFalse);
2489 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2491 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2492 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2494 image->rendering_intent=UndefinedIntent;
2495 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2497 (void) ResetMagickMemory(&image->chromaticity,0,
2498 sizeof(image->chromaticity));
2501 if (logging != MagickFalse)
2503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2504 " PNG width: %.20g, height: %.20g",
2505 (double) ping_width, (double) ping_height);
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " PNG color_type: %d, bit_depth: %d",
2509 ping_color_type, ping_bit_depth);
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 " PNG compression_method: %d",
2513 ping_compression_method);
2515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2516 " PNG interlace_method: %d, filter_method: %d",
2517 ping_interlace_method,ping_filter_method);
2520 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2522 ping_found_iCCP=MagickTrue;
2523 if (logging != MagickFalse)
2524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525 " Found PNG iCCP chunk.");
2528 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2530 ping_found_gAMA=MagickTrue;
2531 if (logging != MagickFalse)
2532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2533 " Found PNG gAMA chunk.");
2536 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2538 ping_found_cHRM=MagickTrue;
2539 if (logging != MagickFalse)
2540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2541 " Found PNG cHRM chunk.");
2544 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2547 ping_found_sRGB=MagickTrue;
2548 if (logging != MagickFalse)
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550 " Found PNG sRGB chunk.");
2553 #ifdef PNG_READ_iCCP_SUPPORTED
2554 if (ping_found_iCCP !=MagickTrue &&
2555 ping_found_sRGB != MagickTrue &&
2556 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2558 ping_found_iCCP=MagickTrue;
2559 if (logging != MagickFalse)
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2561 " Found PNG iCCP chunk.");
2564 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2569 #if (PNG_LIBPNG_VER < 10500)
2583 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2586 if (profile_length != 0)
2591 if (logging != MagickFalse)
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 " Reading PNG iCCP chunk.");
2595 profile=BlobToStringInfo(info,profile_length);
2597 if (profile == (StringInfo *) NULL)
2599 png_warning(ping, "ICC profile is NULL");
2600 profile=DestroyStringInfo(profile);
2604 if (ping_preserve_iCCP == MagickFalse)
2618 length=(png_uint_32) GetStringInfoLength(profile);
2620 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2622 if (length == sRGB_info[icheck].len)
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Got a %lu-byte ICC profile (potentially sRGB)",
2628 (unsigned long) length);
2630 data=GetStringInfoDatum(profile);
2631 profile_crc=crc32(0,data,length);
2633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2634 " with crc=%8x",(unsigned int) profile_crc);
2638 if (profile_crc == sRGB_info[icheck].crc)
2640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2641 " It is sRGB with rendering intent = %s",
2642 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2643 sRGB_info[icheck].intent));
2644 if (image->rendering_intent==UndefinedIntent)
2646 image->rendering_intent=
2647 Magick_RenderingIntent_from_PNG_RenderingIntent(
2648 sRGB_info[icheck].intent);
2654 if (sRGB_info[icheck].len == 0)
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " Got a %lu-byte ICC profile not recognized as sRGB",
2658 (unsigned long) length);
2659 (void) SetImageProfile(image,"icc",profile,exception);
2662 else /* Preserve-iCCP */
2664 (void) SetImageProfile(image,"icc",profile,exception);
2667 profile=DestroyStringInfo(profile);
2673 #if defined(PNG_READ_sRGB_SUPPORTED)
2675 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2678 if (png_get_sRGB(ping,ping_info,&intent))
2680 if (image->rendering_intent == UndefinedIntent)
2681 image->rendering_intent=
2682 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2684 if (logging != MagickFalse)
2685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2686 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2690 else if (mng_info->have_global_srgb)
2692 if (image->rendering_intent == UndefinedIntent)
2693 image->rendering_intent=
2694 Magick_RenderingIntent_from_PNG_RenderingIntent
2695 (mng_info->global_srgb_intent);
2702 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2703 if (mng_info->have_global_gama)
2704 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2706 if (png_get_gAMA(ping,ping_info,&file_gamma))
2708 image->gamma=(float) file_gamma;
2709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2715 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2717 if (mng_info->have_global_chrm != MagickFalse)
2719 (void) png_set_cHRM(ping,ping_info,
2720 mng_info->global_chrm.white_point.x,
2721 mng_info->global_chrm.white_point.y,
2722 mng_info->global_chrm.red_primary.x,
2723 mng_info->global_chrm.red_primary.y,
2724 mng_info->global_chrm.green_primary.x,
2725 mng_info->global_chrm.green_primary.y,
2726 mng_info->global_chrm.blue_primary.x,
2727 mng_info->global_chrm.blue_primary.y);
2731 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2733 (void) png_get_cHRM(ping,ping_info,
2734 &image->chromaticity.white_point.x,
2735 &image->chromaticity.white_point.y,
2736 &image->chromaticity.red_primary.x,
2737 &image->chromaticity.red_primary.y,
2738 &image->chromaticity.green_primary.x,
2739 &image->chromaticity.green_primary.y,
2740 &image->chromaticity.blue_primary.x,
2741 &image->chromaticity.blue_primary.y);
2743 ping_found_cHRM=MagickTrue;
2745 if (image->chromaticity.red_primary.x>0.6399f &&
2746 image->chromaticity.red_primary.x<0.6401f &&
2747 image->chromaticity.red_primary.y>0.3299f &&
2748 image->chromaticity.red_primary.y<0.3301f &&
2749 image->chromaticity.green_primary.x>0.2999f &&
2750 image->chromaticity.green_primary.x<0.3001f &&
2751 image->chromaticity.green_primary.y>0.5999f &&
2752 image->chromaticity.green_primary.y<0.6001f &&
2753 image->chromaticity.blue_primary.x>0.1499f &&
2754 image->chromaticity.blue_primary.x<0.1501f &&
2755 image->chromaticity.blue_primary.y>0.0599f &&
2756 image->chromaticity.blue_primary.y<0.0601f &&
2757 image->chromaticity.white_point.x>0.3126f &&
2758 image->chromaticity.white_point.x<0.3128f &&
2759 image->chromaticity.white_point.y>0.3289f &&
2760 image->chromaticity.white_point.y<0.3291f)
2761 ping_found_sRGB_cHRM=MagickTrue;
2764 if (image->rendering_intent != UndefinedIntent)
2766 if (ping_found_sRGB != MagickTrue &&
2767 (ping_found_gAMA != MagickTrue ||
2768 (image->gamma > .45 && image->gamma < .46)) &&
2769 (ping_found_cHRM != MagickTrue ||
2770 ping_found_sRGB_cHRM == MagickTrue) &&
2771 ping_found_iCCP != MagickTrue)
2773 png_set_sRGB(ping,ping_info,
2774 Magick_RenderingIntent_to_PNG_RenderingIntent
2775 (image->rendering_intent));
2776 file_gamma=1.000f/2.200f;
2777 ping_found_sRGB=MagickTrue;
2778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2779 " Setting sRGB as if in input");
2783 #if defined(PNG_oFFs_SUPPORTED)
2784 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2786 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2787 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2789 if (logging != MagickFalse)
2790 if (image->page.x || image->page.y)
2791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2792 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2793 image->page.x,(double) image->page.y);
2796 #if defined(PNG_pHYs_SUPPORTED)
2797 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2799 if (mng_info->have_global_phys)
2801 png_set_pHYs(ping,ping_info,
2802 mng_info->global_x_pixels_per_unit,
2803 mng_info->global_y_pixels_per_unit,
2804 mng_info->global_phys_unit_type);
2808 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2811 Set image resolution.
2813 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2815 image->resolution.x=(double) x_resolution;
2816 image->resolution.y=(double) y_resolution;
2818 if (unit_type == PNG_RESOLUTION_METER)
2820 image->units=PixelsPerCentimeterResolution;
2821 image->resolution.x=(double) x_resolution/100.0;
2822 image->resolution.y=(double) y_resolution/100.0;
2825 if (logging != MagickFalse)
2826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2827 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2828 (double) x_resolution,(double) y_resolution,unit_type);
2832 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2837 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2839 if ((number_colors == 0) &&
2840 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2842 if (mng_info->global_plte_length)
2844 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2845 (int) mng_info->global_plte_length);
2847 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2849 if (mng_info->global_trns_length)
2852 "global tRNS has more entries than global PLTE");
2856 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2857 (int) mng_info->global_trns_length,NULL);
2860 #ifdef PNG_READ_bKGD_SUPPORTED
2862 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2863 mng_info->have_saved_bkgd_index ||
2865 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2870 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2871 if (mng_info->have_saved_bkgd_index)
2872 background.index=mng_info->saved_bkgd_index;
2874 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2875 background.index=ping_background->index;
2877 background.red=(png_uint_16)
2878 mng_info->global_plte[background.index].red;
2880 background.green=(png_uint_16)
2881 mng_info->global_plte[background.index].green;
2883 background.blue=(png_uint_16)
2884 mng_info->global_plte[background.index].blue;
2886 background.gray=(png_uint_16)
2887 mng_info->global_plte[background.index].green;
2889 png_set_bKGD(ping,ping_info,&background);
2894 png_error(ping,"No global PLTE in file");
2898 #ifdef PNG_READ_bKGD_SUPPORTED
2899 if (mng_info->have_global_bkgd &&
2900 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2901 image->background_color=mng_info->mng_global_bkgd;
2903 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2909 Set image background color.
2911 if (logging != MagickFalse)
2912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2913 " Reading PNG bKGD chunk.");
2915 /* Scale background components to 16-bit, then scale
2918 if (logging != MagickFalse)
2919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2920 " raw ping_background=(%d,%d,%d).",ping_background->red,
2921 ping_background->green,ping_background->blue);
2925 if (ping_file_depth == 1)
2928 else if (ping_file_depth == 2)
2931 else if (ping_file_depth == 4)
2934 if (ping_file_depth <= 8)
2937 ping_background->red *= bkgd_scale;
2938 ping_background->green *= bkgd_scale;
2939 ping_background->blue *= bkgd_scale;
2941 if (logging != MagickFalse)
2943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2944 " bkgd_scale=%d.",bkgd_scale);
2946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2947 " ping_background=(%d,%d,%d).",ping_background->red,
2948 ping_background->green,ping_background->blue);
2951 image->background_color.red=
2952 ScaleShortToQuantum(ping_background->red);
2954 image->background_color.green=
2955 ScaleShortToQuantum(ping_background->green);
2957 image->background_color.blue=
2958 ScaleShortToQuantum(ping_background->blue);
2960 image->background_color.alpha=OpaqueAlpha;
2962 if (logging != MagickFalse)
2963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2964 " image->background_color=(%.20g,%.20g,%.20g).",
2965 (double) image->background_color.red,
2966 (double) image->background_color.green,
2967 (double) image->background_color.blue);
2969 #endif /* PNG_READ_bKGD_SUPPORTED */
2971 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2974 Image has a tRNS chunk.
2982 if (logging != MagickFalse)
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984 " Reading PNG tRNS chunk.");
2986 max_sample = (int) ((one << ping_file_depth) - 1);
2988 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2989 (int)ping_trans_color->gray > max_sample) ||
2990 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2991 ((int)ping_trans_color->red > max_sample ||
2992 (int)ping_trans_color->green > max_sample ||
2993 (int)ping_trans_color->blue > max_sample)))
2995 if (logging != MagickFalse)
2996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2997 " Ignoring PNG tRNS chunk with out-of-range sample.");
2998 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2999 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3000 image->alpha_trait=UndefinedPixelTrait;
3007 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3009 /* Scale transparent_color to short */
3010 transparent_color.red= scale_to_short*ping_trans_color->red;
3011 transparent_color.green= scale_to_short*ping_trans_color->green;
3012 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3013 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3015 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3017 if (logging != MagickFalse)
3019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3020 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " scaled graylevel is %.20g.",transparent_color.alpha);
3025 transparent_color.red=transparent_color.alpha;
3026 transparent_color.green=transparent_color.alpha;
3027 transparent_color.blue=transparent_color.alpha;
3031 #if defined(PNG_READ_sBIT_SUPPORTED)
3032 if (mng_info->have_global_sbit)
3034 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3035 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3038 num_passes=png_set_interlace_handling(ping);
3040 png_read_update_info(ping,ping_info);
3042 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3045 Initialize image structure.
3047 mng_info->image_box.left=0;
3048 mng_info->image_box.right=(ssize_t) ping_width;
3049 mng_info->image_box.top=0;
3050 mng_info->image_box.bottom=(ssize_t) ping_height;
3051 if (mng_info->mng_type == 0)
3053 mng_info->mng_width=ping_width;
3054 mng_info->mng_height=ping_height;
3055 mng_info->frame=mng_info->image_box;
3056 mng_info->clip=mng_info->image_box;
3061 image->page.y=mng_info->y_off[mng_info->object_id];
3064 image->compression=ZipCompression;
3065 image->columns=ping_width;
3066 image->rows=ping_height;
3068 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3069 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3071 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
3072 image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
3074 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
3075 * image->colorspace to GRAY, and reset image->chromaticity.
3077 SetImageColorspace(image,GRAYColorspace,exception);
3081 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3082 " image->colorspace=%d",(int) image->colorspace);
3084 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3085 ((int) ping_bit_depth < 16 &&
3086 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3091 image->storage_class=PseudoClass;
3093 image->colors=one << ping_file_depth;
3094 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3095 if (image->colors > 256)
3098 if (image->colors > 65536L)
3099 image->colors=65536L;
3101 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3106 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3107 image->colors=(size_t) number_colors;
3109 if (logging != MagickFalse)
3110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3111 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3115 if (image->storage_class == PseudoClass)
3118 Initialize image colormap.
3120 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3121 png_error(ping,"Memory allocation failed");
3123 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3128 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3130 for (i=0; i < (ssize_t) number_colors; i++)
3132 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3133 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3134 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3137 for ( ; i < (ssize_t) image->colors; i++)
3139 image->colormap[i].red=0;
3140 image->colormap[i].green=0;
3141 image->colormap[i].blue=0;
3150 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3155 for (i=0; i < (ssize_t) image->colors; i++)
3157 image->colormap[i].red=(Quantum) (i*scale);
3158 image->colormap[i].green=(Quantum) (i*scale);
3159 image->colormap[i].blue=(Quantum) (i*scale);
3164 /* Set some properties for reporting by "identify" */
3169 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3170 ping_interlace_method in value */
3172 (void) FormatLocaleString(msg,MaxTextExtent,
3173 "%d, %d",(int) ping_width, (int) ping_height);
3174 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3176 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3177 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3179 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3180 (int) ping_color_type,
3181 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3182 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3184 if (ping_interlace_method == 0)
3186 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3187 (int) ping_interlace_method);
3189 else if (ping_interlace_method == 1)
3191 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3192 (int) ping_interlace_method);
3196 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3197 (int) ping_interlace_method);
3199 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3201 if (number_colors != 0)
3203 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3204 (int) number_colors);
3205 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3211 Read image scanlines.
3213 if (image->delay != 0)
3214 mng_info->scenes_found++;
3216 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3217 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3218 (image_info->first_scene+image_info->number_scenes))))
3220 /* This happens later in non-ping decodes */
3221 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3222 image->storage_class=DirectClass;
3224 if (logging != MagickFalse)
3225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3226 " Skipping PNG image data for scene %.20g",(double)
3227 mng_info->scenes_found-1);
3228 png_destroy_read_struct(&ping,&ping_info,&end_info);
3230 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3231 UnlockSemaphoreInfo(ping_semaphore);
3234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage().");
3241 if (logging != MagickFalse)
3242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " Reading PNG IDAT chunk(s)");
3246 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3247 sizeof(*ping_pixels));
3249 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3251 if (pixel_info == (MemoryInfo *) NULL)
3252 png_error(ping,"Memory allocation failed");
3253 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3255 if (logging != MagickFalse)
3256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3257 " Converting PNG pixels to pixel packets");
3259 Convert PNG pixels to pixel packets.
3261 quantum_info=AcquireQuantumInfo(image_info,image);
3263 if (quantum_info == (QuantumInfo *) NULL)
3264 png_error(ping,"Failed to allocate quantum_info");
3266 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3271 found_transparent_pixel;
3273 found_transparent_pixel=MagickFalse;
3275 if (image->storage_class == DirectClass)
3277 for (pass=0; pass < num_passes; pass++)
3280 Convert image to DirectClass pixel packets.
3282 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3283 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3284 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3285 BlendPixelTrait : UndefinedPixelTrait;
3287 for (y=0; y < (ssize_t) image->rows; y++)
3290 row_offset=ping_rowbytes*y;
3295 png_read_row(ping,ping_pixels+row_offset,NULL);
3297 if (pass < num_passes-1)
3300 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3302 if (q == (Quantum *) NULL)
3305 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3306 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3307 GrayQuantum,ping_pixels+row_offset,exception);
3309 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3310 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3311 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3313 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3314 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3315 RGBAQuantum,ping_pixels+row_offset,exception);
3317 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3318 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3319 IndexQuantum,ping_pixels+row_offset,exception);
3321 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3322 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3323 RGBQuantum,ping_pixels+row_offset,exception);
3325 if (found_transparent_pixel == MagickFalse)
3327 /* Is there a transparent pixel in the row? */
3328 if (y== 0 && logging != MagickFalse)
3329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3330 " Looking for cheap transparent pixel");
3332 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3334 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3335 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3336 (GetPixelAlpha(image,q) != OpaqueAlpha))
3338 if (logging != MagickFalse)
3339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3342 found_transparent_pixel = MagickTrue;
3345 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3346 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3347 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3348 transparent_color.red &&
3349 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3350 transparent_color.green &&
3351 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3352 transparent_color.blue))
3354 if (logging != MagickFalse)
3355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3357 found_transparent_pixel = MagickTrue;
3360 q+=GetPixelChannels(image);
3364 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3366 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3369 if (status == MagickFalse)
3372 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3376 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3378 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3379 if (status == MagickFalse)
3385 else /* image->storage_class != DirectClass */
3387 for (pass=0; pass < num_passes; pass++)
3396 Convert grayscale image to PseudoClass pixel packets.
3398 if (logging != MagickFalse)
3399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3400 " Converting grayscale pixels to pixel packets");
3402 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3403 BlendPixelTrait : UndefinedPixelTrait;
3405 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3406 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3407 sizeof(*quantum_scanline));
3409 if (quantum_scanline == (Quantum *) NULL)
3410 png_error(ping,"Memory allocation failed");
3412 for (y=0; y < (ssize_t) image->rows; y++)
3418 row_offset=ping_rowbytes*y;
3423 png_read_row(ping,ping_pixels+row_offset,NULL);
3425 if (pass < num_passes-1)
3428 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3430 if (q == (Quantum *) NULL)
3433 p=ping_pixels+row_offset;
3436 switch (ping_bit_depth)
3441 if (ping_color_type == 4)
3442 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3446 alpha=ScaleCharToQuantum((unsigned char)*p++);
3448 SetPixelAlpha(image,alpha,q);
3450 if (alpha != OpaqueAlpha)
3451 found_transparent_pixel = MagickTrue;
3453 q+=GetPixelChannels(image);
3457 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3465 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3467 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3471 if (image->colors > 256)
3472 quantum=((*p++) << 8);
3478 *r=ScaleShortToQuantum(quantum);
3481 if (ping_color_type == 4)
3483 if (image->colors > 256)
3484 quantum=((*p++) << 8);
3490 alpha=ScaleShortToQuantum(quantum);
3491 SetPixelAlpha(image,alpha,q);
3493 if (alpha != OpaqueAlpha)
3494 found_transparent_pixel = MagickTrue;
3496 q+=GetPixelChannels(image);
3499 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3501 p++; /* strip low byte */
3503 if (ping_color_type == 4)
3505 SetPixelAlpha(image,*p++,q);
3507 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3508 found_transparent_pixel = MagickTrue;
3511 q+=GetPixelChannels(image);
3524 Transfer image scanline.
3528 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3530 if (q == (Quantum *) NULL)
3532 for (x=0; x < (ssize_t) image->columns; x++)
3534 SetPixelIndex(image,*r++,q);
3535 q+=GetPixelChannels(image);
3538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3541 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3543 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3546 if (status == MagickFalse)
3551 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3553 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3555 if (status == MagickFalse)
3559 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3562 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3563 UndefinedPixelTrait;
3565 if (logging != MagickFalse)
3567 if (found_transparent_pixel != MagickFalse)
3568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3569 " Found transparent pixel");
3572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3573 " No transparent pixel was found");
3575 ping_color_type&=0x03;
3580 if (quantum_info != (QuantumInfo *) NULL)
3581 quantum_info=DestroyQuantumInfo(quantum_info);
3583 if (image->storage_class == PseudoClass)
3588 alpha_trait=image->alpha_trait;
3589 image->alpha_trait=UndefinedPixelTrait;
3590 (void) SyncImage(image,exception);
3591 image->alpha_trait=alpha_trait;
3594 png_read_end(ping,end_info);
3596 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3597 (ssize_t) image_info->first_scene && image->delay != 0)
3599 png_destroy_read_struct(&ping,&ping_info,&end_info);
3600 pixel_info=RelinquishVirtualMemory(pixel_info);
3602 (void) SetImageBackgroundColor(image,exception);
3603 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3604 UnlockSemaphoreInfo(ping_semaphore);
3606 if (logging != MagickFalse)
3607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3608 " exit ReadOnePNGImage() early.");
3612 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3618 Image has a transparent background.
3620 storage_class=image->storage_class;
3621 image->alpha_trait=BlendPixelTrait;
3623 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3625 if (storage_class == PseudoClass)
3627 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3629 for (x=0; x < ping_num_trans; x++)
3631 image->colormap[x].alpha_trait=BlendPixelTrait;
3632 image->colormap[x].alpha =
3633 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3637 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3639 for (x=0; x < (int) image->colors; x++)
3641 if (ScaleQuantumToShort(image->colormap[x].red) ==
3642 transparent_color.alpha)
3644 image->colormap[x].alpha_trait=BlendPixelTrait;
3645 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3649 (void) SyncImage(image,exception);
3652 #if 1 /* Should have already been done above, but glennrp problem P10
3657 for (y=0; y < (ssize_t) image->rows; y++)
3659 image->storage_class=storage_class;
3660 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3662 if (q == (Quantum *) NULL)
3666 /* Caution: on a Q8 build, this does not distinguish between
3667 * 16-bit colors that differ only in the low byte
3669 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3671 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3672 transparent_color.red &&
3673 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3674 transparent_color.green &&
3675 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3676 transparent_color.blue)
3678 SetPixelAlpha(image,TransparentAlpha,q);
3681 #if 0 /* I have not found a case where this is needed. */
3684 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3688 q+=GetPixelChannels(image);
3691 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3697 image->storage_class=DirectClass;
3700 for (j = 0; j < 2; j++)
3703 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3704 MagickTrue : MagickFalse;
3706 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3707 MagickTrue : MagickFalse;
3709 if (status != MagickFalse)
3710 for (i=0; i < (ssize_t) num_text; i++)
3712 /* Check for a profile */
3714 if (logging != MagickFalse)
3715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3716 " Reading PNG text chunk");
3718 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3720 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3730 length=text[i].text_length;
3731 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3733 if (value == (char *) NULL)
3735 png_error(ping,"Memory allocation failed");
3739 (void) ConcatenateMagickString(value,text[i].text,length+2);
3741 /* Don't save "density" or "units" property if we have a pHYs
3744 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3745 (LocaleCompare(text[i].key,"density") != 0 &&
3746 LocaleCompare(text[i].key,"units") != 0))
3747 (void) SetImageProperty(image,text[i].key,value,exception);
3749 if (logging != MagickFalse)
3751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3752 " length: %lu",(unsigned long) length);
3753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3754 " Keyword: %s",text[i].key);
3757 value=DestroyString(value);
3760 num_text_total += num_text;
3763 #ifdef MNG_OBJECT_BUFFERS
3765 Store the object if necessary.
3767 if (object_id && !mng_info->frozen[object_id])
3769 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3772 create a new object buffer.
3774 mng_info->ob[object_id]=(MngBuffer *)
3775 AcquireMagickMemory(sizeof(MngBuffer));
3777 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3779 mng_info->ob[object_id]->image=(Image *) NULL;
3780 mng_info->ob[object_id]->reference_count=1;
3784 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3785 mng_info->ob[object_id]->frozen)
3787 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3788 png_error(ping,"Memory allocation failed");
3790 if (mng_info->ob[object_id]->frozen)
3791 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3797 if (mng_info->ob[object_id]->image != (Image *) NULL)
3798 mng_info->ob[object_id]->image=DestroyImage
3799 (mng_info->ob[object_id]->image);
3801 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3804 if (mng_info->ob[object_id]->image != (Image *) NULL)
3805 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3808 png_error(ping, "Cloning image for object buffer failed");
3810 if (ping_width > 250000L || ping_height > 250000L)
3811 png_error(ping,"PNG Image dimensions are too large.");
3813 mng_info->ob[object_id]->width=ping_width;
3814 mng_info->ob[object_id]->height=ping_height;
3815 mng_info->ob[object_id]->color_type=ping_color_type;
3816 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3817 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3818 mng_info->ob[object_id]->compression_method=
3819 ping_compression_method;
3820 mng_info->ob[object_id]->filter_method=ping_filter_method;
3822 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3828 Copy the PLTE to the object buffer.
3830 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3831 mng_info->ob[object_id]->plte_length=number_colors;
3833 for (i=0; i < number_colors; i++)
3835 mng_info->ob[object_id]->plte[i]=plte[i];
3840 mng_info->ob[object_id]->plte_length=0;
3845 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3846 * alpha or if a valid tRNS chunk is present, no matter whether there
3847 * is actual transparency present.
3849 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3850 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3851 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3852 BlendPixelTrait : UndefinedPixelTrait;
3854 #if 0 /* I'm not sure what's wrong here but it does not work. */
3855 if (image->alpha_trait == BlendPixelTrait)
3857 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3858 (void) SetImageType(image,GrayscaleMatteType,exception);
3860 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3861 (void) SetImageType(image,PaletteMatteType,exception);
3864 (void) SetImageType(image,TrueColorMatteType,exception);
3869 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3870 (void) SetImageType(image,GrayscaleType,exception);
3872 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3873 (void) SetImageType(image,PaletteType,exception);
3876 (void) SetImageType(image,TrueColorType,exception);
3880 /* Set more properties for identify to retrieve */
3885 if (num_text_total != 0)
3887 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3888 (void) FormatLocaleString(msg,MaxTextExtent,
3889 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3890 (void) SetImageProperty(image,"png:text",msg,
3894 if (num_raw_profiles != 0)
3896 (void) FormatLocaleString(msg,MaxTextExtent,
3897 "%d were found", num_raw_profiles);
3898 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3902 if (ping_found_cHRM != MagickFalse)
3904 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3905 "chunk was found (see Chromaticity, above)");
3906 (void) SetImageProperty(image,"png:cHRM",msg,
3910 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3912 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3913 "chunk was found (see Background color, above)");
3914 (void) SetImageProperty(image,"png:bKGD",msg,
3918 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3921 #if defined(PNG_iCCP_SUPPORTED)
3922 if (ping_found_iCCP != MagickFalse)
3923 (void) SetImageProperty(image,"png:iCCP",msg,
3927 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3928 (void) SetImageProperty(image,"png:tRNS",msg,
3931 #if defined(PNG_sRGB_SUPPORTED)
3932 if (ping_found_sRGB != MagickFalse)
3934 (void) FormatLocaleString(msg,MaxTextExtent,
3937 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3938 (void) SetImageProperty(image,"png:sRGB",msg,
3943 if (ping_found_gAMA != MagickFalse)
3945 (void) FormatLocaleString(msg,MaxTextExtent,
3946 "gamma=%.8g (See Gamma, above)",
3948 (void) SetImageProperty(image,"png:gAMA",msg,
3952 #if defined(PNG_pHYs_SUPPORTED)
3953 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3955 (void) FormatLocaleString(msg,MaxTextExtent,
3956 "x_res=%.10g, y_res=%.10g, units=%d",
3957 (double) x_resolution,(double) y_resolution, unit_type);
3958 (void) SetImageProperty(image,"png:pHYs",msg,
3963 #if defined(PNG_oFFs_SUPPORTED)
3964 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3966 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3967 (double) image->page.x,(double) image->page.y);
3968 (void) SetImageProperty(image,"png:oFFs",msg,
3973 if ((image->page.width != 0 && image->page.width != image->columns) ||
3974 (image->page.height != 0 && image->page.height != image->rows))
3976 (void) FormatLocaleString(msg,MaxTextExtent,
3977 "width=%.20g, height=%.20g",
3978 (double) image->page.width,(double) image->page.height);
3979 (void) SetImageProperty(image,"png:vpAg",msg,
3985 Relinquish resources.
3987 png_destroy_read_struct(&ping,&ping_info,&end_info);
3989 pixel_info=RelinquishVirtualMemory(pixel_info);
3991 if (logging != MagickFalse)
3992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993 " exit ReadOnePNGImage()");
3995 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3996 UnlockSemaphoreInfo(ping_semaphore);
3999 /* } for navigation to beginning of SETJMP-protected block, revert to
4000 * Throwing an Exception when an error occurs.
4005 /* end of reading one PNG image */
4008 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4023 magic_number[MaxTextExtent];
4031 assert(image_info != (const ImageInfo *) NULL);
4032 assert(image_info->signature == MagickSignature);
4034 if (image_info->debug != MagickFalse)
4035 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4036 image_info->filename);
4038 assert(exception != (ExceptionInfo *) NULL);
4039 assert(exception->signature == MagickSignature);
4040 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4041 image=AcquireImage(image_info,exception);
4042 mng_info=(MngInfo *) NULL;
4043 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4045 if (status == MagickFalse)
4046 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4049 Verify PNG signature.
4051 count=ReadBlob(image,8,(unsigned char *) magic_number);
4053 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4054 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4057 Allocate a MngInfo structure.
4059 have_mng_structure=MagickFalse;
4060 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4062 if (mng_info == (MngInfo *) NULL)
4063 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4066 Initialize members of the MngInfo structure.
4068 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4069 mng_info->image=image;
4070 have_mng_structure=MagickTrue;
4073 image=ReadOnePNGImage(mng_info,image_info,exception);
4074 MngInfoFreeStruct(mng_info,&have_mng_structure);
4076 if (image == (Image *) NULL)
4078 if (previous != (Image *) NULL)
4080 if (previous->signature != MagickSignature)
4081 ThrowReaderException(CorruptImageError,"CorruptImage");
4083 (void) CloseBlob(previous);
4084 (void) DestroyImageList(previous);
4087 if (logging != MagickFalse)
4088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4089 "exit ReadPNGImage() with error");
4091 return((Image *) NULL);
4094 (void) CloseBlob(image);
4096 if ((image->columns == 0) || (image->rows == 0))
4098 if (logging != MagickFalse)
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4100 "exit ReadPNGImage() with error.");
4102 ThrowReaderException(CorruptImageError,"CorruptImage");
4105 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4106 ((image->gamma < .45) || (image->gamma > .46)) &&
4107 !(image->chromaticity.red_primary.x>0.6399f &&
4108 image->chromaticity.red_primary.x<0.6401f &&
4109 image->chromaticity.red_primary.y>0.3299f &&
4110 image->chromaticity.red_primary.y<0.3301f &&
4111 image->chromaticity.green_primary.x>0.2999f &&
4112 image->chromaticity.green_primary.x<0.3001f &&
4113 image->chromaticity.green_primary.y>0.5999f &&
4114 image->chromaticity.green_primary.y<0.6001f &&
4115 image->chromaticity.blue_primary.x>0.1499f &&
4116 image->chromaticity.blue_primary.x<0.1501f &&
4117 image->chromaticity.blue_primary.y>0.0599f &&
4118 image->chromaticity.blue_primary.y<0.0601f &&
4119 image->chromaticity.white_point.x>0.3126f &&
4120 image->chromaticity.white_point.x<0.3128f &&
4121 image->chromaticity.white_point.y>0.3289f &&
4122 image->chromaticity.white_point.y<0.3291f))
4123 SetImageColorspace(image,RGBColorspace,exception);
4125 if (logging != MagickFalse)
4126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4127 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4128 (double) image->page.width,(double) image->page.height,
4129 (double) image->page.x,(double) image->page.y);
4131 if (logging != MagickFalse)
4132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4139 #if defined(JNG_SUPPORTED)
4141 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4145 % R e a d O n e J N G I m a g e %
4149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4151 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4152 % (minus the 8-byte signature) and returns it. It allocates the memory
4153 % necessary for the new Image structure and returns a pointer to the new
4156 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4158 % The format of the ReadOneJNGImage method is:
4160 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4161 % ExceptionInfo *exception)
4163 % A description of each parameter follows:
4165 % o mng_info: Specifies a pointer to a MngInfo structure.
4167 % o image_info: the image info.
4169 % o exception: return any errors or warnings in this structure.
4172 static Image *ReadOneJNGImage(MngInfo *mng_info,
4173 const ImageInfo *image_info, ExceptionInfo *exception)
4200 jng_image_sample_depth,
4201 jng_image_compression_method,
4202 jng_image_interlace_method,
4203 jng_alpha_sample_depth,
4204 jng_alpha_compression_method,
4205 jng_alpha_filter_method,
4206 jng_alpha_interlace_method;
4208 register const Quantum
4218 register unsigned char
4229 jng_alpha_compression_method=0;
4230 jng_alpha_sample_depth=8;
4234 alpha_image=(Image *) NULL;
4235 color_image=(Image *) NULL;
4236 alpha_image_info=(ImageInfo *) NULL;
4237 color_image_info=(ImageInfo *) NULL;
4239 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4240 " Enter ReadOneJNGImage()");
4242 image=mng_info->image;
4244 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4247 Allocate next image structure.
4249 if (logging != MagickFalse)
4250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4251 " AcquireNextImage()");
4253 AcquireNextImage(image_info,image,exception);
4255 if (GetNextImageInList(image) == (Image *) NULL)
4256 return((Image *) NULL);
4258 image=SyncNextImageInList(image);
4260 mng_info->image=image;
4263 Signature bytes have already been read.
4266 read_JSEP=MagickFalse;
4267 reading_idat=MagickFalse;
4268 skip_to_iend=MagickFalse;
4272 type[MaxTextExtent];
4281 Read a new JNG chunk.
4283 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4284 2*GetBlobSize(image));
4286 if (status == MagickFalse)
4290 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4291 length=ReadBlobMSBLong(image);
4292 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4294 if (logging != MagickFalse)
4295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4296 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4297 type[0],type[1],type[2],type[3],(double) length);
4299 if (length > PNG_UINT_31_MAX || count == 0)
4300 ThrowReaderException(CorruptImageError,"CorruptImage");
4303 chunk=(unsigned char *) NULL;
4307 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4309 if (chunk == (unsigned char *) NULL)
4310 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4312 for (i=0; i < (ssize_t) length; i++)
4313 chunk[i]=(unsigned char) ReadBlobByte(image);
4318 (void) ReadBlobMSBLong(image); /* read crc word */
4323 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4328 if (memcmp(type,mng_JHDR,4) == 0)
4332 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4333 (p[2] << 8) | p[3]);
4334 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4335 (p[6] << 8) | p[7]);
4336 jng_color_type=p[8];
4337 jng_image_sample_depth=p[9];
4338 jng_image_compression_method=p[10];
4339 jng_image_interlace_method=p[11];
4341 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4344 jng_alpha_sample_depth=p[12];
4345 jng_alpha_compression_method=p[13];
4346 jng_alpha_filter_method=p[14];
4347 jng_alpha_interlace_method=p[15];
4349 if (logging != MagickFalse)
4351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4352 " jng_width: %16lu",(unsigned long) jng_width);
4354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4355 " jng_width: %16lu",(unsigned long) jng_height);
4357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4358 " jng_color_type: %16d",jng_color_type);
4360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4361 " jng_image_sample_depth: %3d",
4362 jng_image_sample_depth);
4364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4365 " jng_image_compression_method:%3d",
4366 jng_image_compression_method);
4368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4369 " jng_image_interlace_method: %3d",
4370 jng_image_interlace_method);
4372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4373 " jng_alpha_sample_depth: %3d",
4374 jng_alpha_sample_depth);
4376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4377 " jng_alpha_compression_method:%3d",
4378 jng_alpha_compression_method);
4380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4381 " jng_alpha_filter_method: %3d",
4382 jng_alpha_filter_method);
4384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4385 " jng_alpha_interlace_method: %3d",
4386 jng_alpha_interlace_method);
4391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4397 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4398 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4399 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4402 o create color_image
4403 o open color_blob, attached to color_image
4404 o if (color type has alpha)
4405 open alpha_blob, attached to alpha_image
4408 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4410 if (color_image_info == (ImageInfo *) NULL)
4411 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4413 GetImageInfo(color_image_info);
4414 color_image=AcquireImage(color_image_info,exception);
4416 if (color_image == (Image *) NULL)
4417 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4419 if (logging != MagickFalse)
4420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4421 " Creating color_blob.");
4423 (void) AcquireUniqueFilename(color_image->filename);
4424 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4427 if (status == MagickFalse)
4428 return((Image *) NULL);
4430 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4432 alpha_image_info=(ImageInfo *)
4433 AcquireMagickMemory(sizeof(ImageInfo));
4435 if (alpha_image_info == (ImageInfo *) NULL)
4436 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4438 GetImageInfo(alpha_image_info);
4439 alpha_image=AcquireImage(alpha_image_info,exception);
4441 if (alpha_image == (Image *) NULL)
4443 alpha_image=DestroyImage(alpha_image);
4444 ThrowReaderException(ResourceLimitError,
4445 "MemoryAllocationFailed");
4448 if (logging != MagickFalse)
4449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450 " Creating alpha_blob.");
4452 (void) AcquireUniqueFilename(alpha_image->filename);
4453 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4456 if (status == MagickFalse)
4457 return((Image *) NULL);
4459 if (jng_alpha_compression_method == 0)
4464 if (logging != MagickFalse)
4465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4466 " Writing IHDR chunk to alpha_blob.");
4468 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4469 "\211PNG\r\n\032\n");
4471 (void) WriteBlobMSBULong(alpha_image,13L);
4472 PNGType(data,mng_IHDR);
4473 LogPNGChunk(logging,mng_IHDR,13L);
4474 PNGLong(data+4,jng_width);
4475 PNGLong(data+8,jng_height);
4476 data[12]=jng_alpha_sample_depth;
4477 data[13]=0; /* color_type gray */
4478 data[14]=0; /* compression method 0 */
4479 data[15]=0; /* filter_method 0 */
4480 data[16]=0; /* interlace_method 0 */
4481 (void) WriteBlob(alpha_image,17,data);
4482 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4485 reading_idat=MagickTrue;
4488 if (memcmp(type,mng_JDAT,4) == 0)
4490 /* Copy chunk to color_image->blob */
4492 if (logging != MagickFalse)
4493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4494 " Copying JDAT chunk data to color_blob.");
4496 (void) WriteBlob(color_image,length,chunk);
4499 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4504 if (memcmp(type,mng_IDAT,4) == 0)
4509 /* Copy IDAT header and chunk data to alpha_image->blob */
4511 if (image_info->ping == MagickFalse)
4513 if (logging != MagickFalse)
4514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4515 " Copying IDAT chunk data to alpha_blob.");
4517 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4518 PNGType(data,mng_IDAT);
4519 LogPNGChunk(logging,mng_IDAT,length);
4520 (void) WriteBlob(alpha_image,4,data);
4521 (void) WriteBlob(alpha_image,length,chunk);
4522 (void) WriteBlobMSBULong(alpha_image,
4523 crc32(crc32(0,data,4),chunk,(uInt) length));
4527 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4532 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4534 /* Copy chunk data to alpha_image->blob */
4536 if (image_info->ping == MagickFalse)
4538 if (logging != MagickFalse)
4539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4540 " Copying JDAA chunk data to alpha_blob.");
4542 (void) WriteBlob(alpha_image,length,chunk);
4546 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4551 if (memcmp(type,mng_JSEP,4) == 0)
4553 read_JSEP=MagickTrue;
4556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4561 if (memcmp(type,mng_bKGD,4) == 0)
4565 image->background_color.red=ScaleCharToQuantum(p[1]);
4566 image->background_color.green=image->background_color.red;
4567 image->background_color.blue=image->background_color.red;
4572 image->background_color.red=ScaleCharToQuantum(p[1]);
4573 image->background_color.green=ScaleCharToQuantum(p[3]);
4574 image->background_color.blue=ScaleCharToQuantum(p[5]);
4577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4581 if (memcmp(type,mng_gAMA,4) == 0)
4584 image->gamma=((float) mng_get_long(p))*0.00001;
4586 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4590 if (memcmp(type,mng_cHRM,4) == 0)
4594 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4595 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4596 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4597 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4598 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4599 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4600 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4601 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4604 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4608 if (memcmp(type,mng_sRGB,4) == 0)
4612 image->rendering_intent=
4613 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4614 image->gamma=1.000f/2.200f;
4615 image->chromaticity.red_primary.x=0.6400f;
4616 image->chromaticity.red_primary.y=0.3300f;
4617 image->chromaticity.green_primary.x=0.3000f;
4618 image->chromaticity.green_primary.y=0.6000f;
4619 image->chromaticity.blue_primary.x=0.1500f;
4620 image->chromaticity.blue_primary.y=0.0600f;
4621 image->chromaticity.white_point.x=0.3127f;
4622 image->chromaticity.white_point.y=0.3290f;
4625 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4629 if (memcmp(type,mng_oFFs,4) == 0)
4633 image->page.x=(ssize_t) mng_get_long(p);
4634 image->page.y=(ssize_t) mng_get_long(&p[4]);
4636 if ((int) p[8] != 0)
4638 image->page.x/=10000;
4639 image->page.y/=10000;
4644 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4649 if (memcmp(type,mng_pHYs,4) == 0)
4653 image->resolution.x=(double) mng_get_long(p);
4654 image->resolution.y=(double) mng_get_long(&p[4]);
4655 if ((int) p[8] == PNG_RESOLUTION_METER)
4657 image->units=PixelsPerCentimeterResolution;
4658 image->resolution.x=image->resolution.x/100.0f;
4659 image->resolution.y=image->resolution.y/100.0f;
4663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4668 if (memcmp(type,mng_iCCP,4) == 0)
4672 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4679 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4681 if (memcmp(type,mng_IEND,4))
4691 Finish up reading image data:
4693 o read main image from color_blob.
4697 o if (color_type has alpha)
4698 if alpha_encoding is PNG
4699 read secondary image from alpha_blob via ReadPNG
4700 if alpha_encoding is JPEG
4701 read secondary image from alpha_blob via ReadJPEG
4705 o copy intensity of secondary image into
4706 alpha samples of main image.
4708 o destroy the secondary image.
4711 (void) CloseBlob(color_image);
4713 if (logging != MagickFalse)
4714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4715 " Reading jng_image from color_blob.");
4717 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4718 color_image->filename);
4720 color_image_info->ping=MagickFalse; /* To do: avoid this */
4721 jng_image=ReadImage(color_image_info,exception);
4723 if (jng_image == (Image *) NULL)
4724 return((Image *) NULL);
4726 (void) RelinquishUniqueFileResource(color_image->filename);
4727 color_image=DestroyImage(color_image);
4728 color_image_info=DestroyImageInfo(color_image_info);
4730 if (jng_image == (Image *) NULL)
4731 return((Image *) NULL);
4733 if (logging != MagickFalse)
4734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4735 " Copying jng_image pixels to main image.");
4737 image->rows=jng_height;
4738 image->columns=jng_width;
4740 for (y=0; y < (ssize_t) image->rows; y++)
4742 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4743 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4744 for (x=(ssize_t) image->columns; x != 0; x--)
4746 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4747 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4748 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4749 q+=GetPixelChannels(image);
4750 s+=GetPixelChannels(jng_image);
4753 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4757 jng_image=DestroyImage(jng_image);
4759 if (image_info->ping == MagickFalse)
4761 if (jng_color_type >= 12)
4763 if (jng_alpha_compression_method == 0)
4767 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4768 PNGType(data,mng_IEND);
4769 LogPNGChunk(logging,mng_IEND,0L);
4770 (void) WriteBlob(alpha_image,4,data);
4771 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4774 (void) CloseBlob(alpha_image);
4776 if (logging != MagickFalse)
4777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4778 " Reading alpha from alpha_blob.");
4780 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4781 "%s",alpha_image->filename);
4783 jng_image=ReadImage(alpha_image_info,exception);
4785 if (jng_image != (Image *) NULL)
4786 for (y=0; y < (ssize_t) image->rows; y++)
4788 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4790 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4792 if (image->alpha_trait == BlendPixelTrait)
4793 for (x=(ssize_t) image->columns; x != 0; x--)
4795 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4796 q+=GetPixelChannels(image);
4797 s+=GetPixelChannels(jng_image);
4801 for (x=(ssize_t) image->columns; x != 0; x--)
4803 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4804 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4805 image->alpha_trait=BlendPixelTrait;
4806 q+=GetPixelChannels(image);
4807 s+=GetPixelChannels(jng_image);
4810 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4813 (void) RelinquishUniqueFileResource(alpha_image->filename);
4814 alpha_image=DestroyImage(alpha_image);
4815 alpha_image_info=DestroyImageInfo(alpha_image_info);
4816 if (jng_image != (Image *) NULL)
4817 jng_image=DestroyImage(jng_image);
4821 /* Read the JNG image. */
4823 if (mng_info->mng_type == 0)
4825 mng_info->mng_width=jng_width;
4826 mng_info->mng_height=jng_height;
4829 if (image->page.width == 0 && image->page.height == 0)
4831 image->page.width=jng_width;
4832 image->page.height=jng_height;
4835 if (image->page.x == 0 && image->page.y == 0)
4837 image->page.x=mng_info->x_off[mng_info->object_id];
4838 image->page.y=mng_info->y_off[mng_info->object_id];
4843 image->page.y=mng_info->y_off[mng_info->object_id];
4846 mng_info->image_found++;
4847 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4848 2*GetBlobSize(image));
4850 if (logging != MagickFalse)
4851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4852 " exit ReadOneJNGImage()");
4858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4862 % R e a d J N G I m a g e %
4866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4868 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4869 % (including the 8-byte signature) and returns it. It allocates the memory
4870 % necessary for the new Image structure and returns a pointer to the new
4873 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4875 % The format of the ReadJNGImage method is:
4877 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4880 % A description of each parameter follows:
4882 % o image_info: the image info.
4884 % o exception: return any errors or warnings in this structure.
4888 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4903 magic_number[MaxTextExtent];
4911 assert(image_info != (const ImageInfo *) NULL);
4912 assert(image_info->signature == MagickSignature);
4913 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4914 assert(exception != (ExceptionInfo *) NULL);
4915 assert(exception->signature == MagickSignature);
4916 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4917 image=AcquireImage(image_info,exception);
4918 mng_info=(MngInfo *) NULL;
4919 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4921 if (status == MagickFalse)
4922 return((Image *) NULL);
4924 if (LocaleCompare(image_info->magick,"JNG") != 0)
4925 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4927 /* Verify JNG signature. */
4929 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4931 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4932 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4934 /* Allocate a MngInfo structure. */
4936 have_mng_structure=MagickFalse;
4937 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4939 if (mng_info == (MngInfo *) NULL)
4940 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4942 /* Initialize members of the MngInfo structure. */
4944 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4945 have_mng_structure=MagickTrue;
4947 mng_info->image=image;
4949 image=ReadOneJNGImage(mng_info,image_info,exception);
4950 MngInfoFreeStruct(mng_info,&have_mng_structure);
4952 if (image == (Image *) NULL)
4954 if (IsImageObject(previous) != MagickFalse)
4956 (void) CloseBlob(previous);
4957 (void) DestroyImageList(previous);
4960 if (logging != MagickFalse)
4961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4962 "exit ReadJNGImage() with error");
4964 return((Image *) NULL);
4966 (void) CloseBlob(image);
4968 if (image->columns == 0 || image->rows == 0)
4970 if (logging != MagickFalse)
4971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4972 "exit ReadJNGImage() with error");
4974 ThrowReaderException(CorruptImageError,"CorruptImage");
4977 if (logging != MagickFalse)
4978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4984 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4987 page_geometry[MaxTextExtent];
5020 #if defined(MNG_INSERT_LAYERS)
5022 mng_background_color;
5025 register unsigned char
5040 #if defined(MNG_INSERT_LAYERS)
5045 volatile unsigned int
5046 #ifdef MNG_OBJECT_BUFFERS
5047 mng_background_object=0,
5049 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5052 default_frame_timeout,
5054 #if defined(MNG_INSERT_LAYERS)
5060 /* These delays are all measured in image ticks_per_second,
5061 * not in MNG ticks_per_second
5064 default_frame_delay,
5068 #if defined(MNG_INSERT_LAYERS)
5077 previous_fb.bottom=0;
5079 previous_fb.right=0;
5081 default_fb.bottom=0;
5085 /* Open image file. */
5087 assert(image_info != (const ImageInfo *) NULL);
5088 assert(image_info->signature == MagickSignature);
5089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5090 assert(exception != (ExceptionInfo *) NULL);
5091 assert(exception->signature == MagickSignature);
5092 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5093 image=AcquireImage(image_info,exception);
5094 mng_info=(MngInfo *) NULL;
5095 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5097 if (status == MagickFalse)
5098 return((Image *) NULL);
5100 first_mng_object=MagickFalse;
5102 have_mng_structure=MagickFalse;
5104 /* Allocate a MngInfo structure. */
5106 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5108 if (mng_info == (MngInfo *) NULL)
5109 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5111 /* Initialize members of the MngInfo structure. */
5113 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5114 mng_info->image=image;
5115 have_mng_structure=MagickTrue;
5117 if (LocaleCompare(image_info->magick,"MNG") == 0)
5120 magic_number[MaxTextExtent];
5122 /* Verify MNG signature. */
5123 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5124 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5125 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5127 /* Initialize some nonzero members of the MngInfo structure. */
5128 for (i=0; i < MNG_MAX_OBJECTS; i++)
5130 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5131 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5133 mng_info->exists[0]=MagickTrue;
5136 first_mng_object=MagickTrue;
5138 #if defined(MNG_INSERT_LAYERS)
5139 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5141 default_frame_delay=0;
5142 default_frame_timeout=0;
5145 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5147 skip_to_iend=MagickFalse;
5148 term_chunk_found=MagickFalse;
5149 mng_info->framing_mode=1;
5150 #if defined(MNG_INSERT_LAYERS)
5151 mandatory_back=MagickFalse;
5153 #if defined(MNG_INSERT_LAYERS)
5154 mng_background_color=image->background_color;
5156 default_fb=mng_info->frame;
5157 previous_fb=mng_info->frame;
5161 type[MaxTextExtent];
5163 if (LocaleCompare(image_info->magick,"MNG") == 0)
5172 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5173 length=ReadBlobMSBLong(image);
5174 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5176 if (logging != MagickFalse)
5177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5178 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5179 type[0],type[1],type[2],type[3],(double) length);
5181 if (length > PNG_UINT_31_MAX)
5185 ThrowReaderException(CorruptImageError,"CorruptImage");
5188 chunk=(unsigned char *) NULL;
5192 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5194 if (chunk == (unsigned char *) NULL)
5195 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5197 for (i=0; i < (ssize_t) length; i++)
5198 chunk[i]=(unsigned char) ReadBlobByte(image);
5203 (void) ReadBlobMSBLong(image); /* read crc word */
5205 #if !defined(JNG_SUPPORTED)
5206 if (memcmp(type,mng_JHDR,4) == 0)
5208 skip_to_iend=MagickTrue;
5210 if (mng_info->jhdr_warning == 0)
5211 (void) ThrowMagickException(exception,GetMagickModule(),
5212 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5214 mng_info->jhdr_warning++;
5217 if (memcmp(type,mng_DHDR,4) == 0)
5219 skip_to_iend=MagickTrue;
5221 if (mng_info->dhdr_warning == 0)
5222 (void) ThrowMagickException(exception,GetMagickModule(),
5223 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5225 mng_info->dhdr_warning++;
5227 if (memcmp(type,mng_MEND,4) == 0)
5232 if (memcmp(type,mng_IEND,4) == 0)
5233 skip_to_iend=MagickFalse;
5236 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5238 if (logging != MagickFalse)
5239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5245 if (memcmp(type,mng_MHDR,4) == 0)
5247 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5248 (p[2] << 8) | p[3]);
5250 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5251 (p[6] << 8) | p[7]);
5253 if (logging != MagickFalse)
5255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5256 " MNG width: %.20g",(double) mng_info->mng_width);
5257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5258 " MNG height: %.20g",(double) mng_info->mng_height);
5262 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5264 if (mng_info->ticks_per_second == 0)
5265 default_frame_delay=0;
5268 default_frame_delay=1UL*image->ticks_per_second/
5269 mng_info->ticks_per_second;
5271 frame_delay=default_frame_delay;
5277 simplicity=(size_t) mng_get_long(p);
5280 mng_type=1; /* Full MNG */
5282 if ((simplicity != 0) && ((simplicity | 11) == 11))
5283 mng_type=2; /* LC */
5285 if ((simplicity != 0) && ((simplicity | 9) == 9))
5286 mng_type=3; /* VLC */
5288 #if defined(MNG_INSERT_LAYERS)
5290 insert_layers=MagickTrue;
5292 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5294 /* Allocate next image structure. */
5295 AcquireNextImage(image_info,image,exception);
5297 if (GetNextImageInList(image) == (Image *) NULL)
5298 return((Image *) NULL);
5300 image=SyncNextImageInList(image);
5301 mng_info->image=image;
5304 if ((mng_info->mng_width > 65535L) ||
5305 (mng_info->mng_height > 65535L))
5306 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5308 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5309 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5310 mng_info->mng_height);
5312 mng_info->frame.left=0;
5313 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5314 mng_info->frame.top=0;
5315 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5316 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5318 for (i=0; i < MNG_MAX_OBJECTS; i++)
5319 mng_info->object_clip[i]=mng_info->frame;
5321 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5325 if (memcmp(type,mng_TERM,4) == 0)
5336 final_delay=(png_uint_32) mng_get_long(&p[2]);
5337 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5339 if (mng_iterations == PNG_UINT_31_MAX)
5342 image->iterations=mng_iterations;
5343 term_chunk_found=MagickTrue;
5346 if (logging != MagickFalse)
5348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5349 " repeat=%d",repeat);
5351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5352 " final_delay=%.20g",(double) final_delay);
5354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5355 " image->iterations=%.20g",(double) image->iterations);
5358 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5361 if (memcmp(type,mng_DEFI,4) == 0)
5364 (void) ThrowMagickException(exception,GetMagickModule(),
5365 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5368 object_id=(p[0] << 8) | p[1];
5370 if (mng_type == 2 && object_id != 0)
5371 (void) ThrowMagickException(exception,GetMagickModule(),
5372 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5375 if (object_id > MNG_MAX_OBJECTS)
5378 Instead of using a warning we should allocate a larger
5379 MngInfo structure and continue.
5381 (void) ThrowMagickException(exception,GetMagickModule(),
5382 CoderError,"object id too large","`%s'",image->filename);
5383 object_id=MNG_MAX_OBJECTS;
5386 if (mng_info->exists[object_id])
5387 if (mng_info->frozen[object_id])
5389 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5390 (void) ThrowMagickException(exception,
5391 GetMagickModule(),CoderError,
5392 "DEFI cannot redefine a frozen MNG object","`%s'",
5397 mng_info->exists[object_id]=MagickTrue;
5400 mng_info->invisible[object_id]=p[2];
5403 Extract object offset info.
5407 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5408 (p[5] << 16) | (p[6] << 8) | p[7]);
5410 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5411 (p[9] << 16) | (p[10] << 8) | p[11]);
5413 if (logging != MagickFalse)
5415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5416 " x_off[%d]: %.20g",object_id,(double)
5417 mng_info->x_off[object_id]);
5419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5420 " y_off[%d]: %.20g",object_id,(double)
5421 mng_info->y_off[object_id]);
5426 Extract object clipping info.
5429 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5435 if (memcmp(type,mng_bKGD,4) == 0)
5437 mng_info->have_global_bkgd=MagickFalse;
5441 mng_info->mng_global_bkgd.red=
5442 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5444 mng_info->mng_global_bkgd.green=
5445 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5447 mng_info->mng_global_bkgd.blue=
5448 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5450 mng_info->have_global_bkgd=MagickTrue;
5453 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5456 if (memcmp(type,mng_BACK,4) == 0)
5458 #if defined(MNG_INSERT_LAYERS)
5460 mandatory_back=p[6];
5465 if (mandatory_back && length > 5)
5467 mng_background_color.red=
5468 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5470 mng_background_color.green=
5471 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5473 mng_background_color.blue=
5474 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5476 mng_background_color.alpha=OpaqueAlpha;
5479 #ifdef MNG_OBJECT_BUFFERS
5481 mng_background_object=(p[7] << 8) | p[8];
5484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5488 if (memcmp(type,mng_PLTE,4) == 0)
5490 /* Read global PLTE. */
5492 if (length && (length < 769))
5494 if (mng_info->global_plte == (png_colorp) NULL)
5495 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5496 sizeof(*mng_info->global_plte));
5498 for (i=0; i < (ssize_t) (length/3); i++)
5500 mng_info->global_plte[i].red=p[3*i];
5501 mng_info->global_plte[i].green=p[3*i+1];
5502 mng_info->global_plte[i].blue=p[3*i+2];
5505 mng_info->global_plte_length=(unsigned int) (length/3);
5508 for ( ; i < 256; i++)
5510 mng_info->global_plte[i].red=i;
5511 mng_info->global_plte[i].green=i;
5512 mng_info->global_plte[i].blue=i;
5516 mng_info->global_plte_length=256;
5519 mng_info->global_plte_length=0;
5521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5525 if (memcmp(type,mng_tRNS,4) == 0)
5527 /* read global tRNS */
5530 for (i=0; i < (ssize_t) length; i++)
5531 mng_info->global_trns[i]=p[i];
5534 for ( ; i < 256; i++)
5535 mng_info->global_trns[i]=255;
5537 mng_info->global_trns_length=(unsigned int) length;
5538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5541 if (memcmp(type,mng_gAMA,4) == 0)
5548 igamma=mng_get_long(p);
5549 mng_info->global_gamma=((float) igamma)*0.00001;
5550 mng_info->have_global_gama=MagickTrue;
5554 mng_info->have_global_gama=MagickFalse;
5556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5560 if (memcmp(type,mng_cHRM,4) == 0)
5562 /* Read global cHRM */
5566 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5567 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5568 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5569 mng_info->global_chrm.red_primary.y=0.00001*
5570 mng_get_long(&p[12]);
5571 mng_info->global_chrm.green_primary.x=0.00001*
5572 mng_get_long(&p[16]);
5573 mng_info->global_chrm.green_primary.y=0.00001*
5574 mng_get_long(&p[20]);
5575 mng_info->global_chrm.blue_primary.x=0.00001*
5576 mng_get_long(&p[24]);
5577 mng_info->global_chrm.blue_primary.y=0.00001*
5578 mng_get_long(&p[28]);
5579 mng_info->have_global_chrm=MagickTrue;
5582 mng_info->have_global_chrm=MagickFalse;
5584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5588 if (memcmp(type,mng_sRGB,4) == 0)
5595 mng_info->global_srgb_intent=
5596 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5597 mng_info->have_global_srgb=MagickTrue;
5600 mng_info->have_global_srgb=MagickFalse;
5602 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5606 if (memcmp(type,mng_iCCP,4) == 0)
5614 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5619 if (memcmp(type,mng_FRAM,4) == 0)
5622 (void) ThrowMagickException(exception,GetMagickModule(),
5623 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5626 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5627 image->delay=frame_delay;
5629 frame_delay=default_frame_delay;
5630 frame_timeout=default_frame_timeout;
5635 mng_info->framing_mode=p[0];
5637 if (logging != MagickFalse)
5638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5639 " Framing_mode=%d",mng_info->framing_mode);
5643 /* Note the delay and frame clipping boundaries. */
5645 p++; /* framing mode */
5647 while (*p && ((p-chunk) < (ssize_t) length))
5648 p++; /* frame name */
5650 p++; /* frame name terminator */
5652 if ((p-chunk) < (ssize_t) (length-4))
5659 change_delay=(*p++);
5660 change_timeout=(*p++);
5661 change_clipping=(*p++);
5662 p++; /* change_sync */
5666 frame_delay=1UL*image->ticks_per_second*
5669 if (mng_info->ticks_per_second != 0)
5670 frame_delay/=mng_info->ticks_per_second;
5673 frame_delay=PNG_UINT_31_MAX;
5675 if (change_delay == 2)
5676 default_frame_delay=frame_delay;
5680 if (logging != MagickFalse)
5681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5682 " Framing_delay=%.20g",(double) frame_delay);
5687 frame_timeout=1UL*image->ticks_per_second*
5690 if (mng_info->ticks_per_second != 0)
5691 frame_timeout/=mng_info->ticks_per_second;
5694 frame_timeout=PNG_UINT_31_MAX;
5696 if (change_delay == 2)
5697 default_frame_timeout=frame_timeout;
5701 if (logging != MagickFalse)
5702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5703 " Framing_timeout=%.20g",(double) frame_timeout);
5706 if (change_clipping)
5708 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5712 if (logging != MagickFalse)
5713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5714 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5715 (double) fb.left,(double) fb.right,(double) fb.top,
5716 (double) fb.bottom);
5718 if (change_clipping == 2)
5724 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5726 subframe_width=(size_t) (mng_info->clip.right
5727 -mng_info->clip.left);
5729 subframe_height=(size_t) (mng_info->clip.bottom
5730 -mng_info->clip.top);
5732 Insert a background layer behind the frame if framing_mode is 4.
5734 #if defined(MNG_INSERT_LAYERS)
5735 if (logging != MagickFalse)
5736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5737 " subframe_width=%.20g, subframe_height=%.20g",(double)
5738 subframe_width,(double) subframe_height);
5740 if (insert_layers && (mng_info->framing_mode == 4) &&
5741 (subframe_width) && (subframe_height))
5743 /* Allocate next image structure. */
5744 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5746 AcquireNextImage(image_info,image,exception);
5748 if (GetNextImageInList(image) == (Image *) NULL)
5750 image=DestroyImageList(image);
5751 MngInfoFreeStruct(mng_info,&have_mng_structure);
5752 return((Image *) NULL);
5755 image=SyncNextImageInList(image);
5758 mng_info->image=image;
5760 if (term_chunk_found)
5762 image->start_loop=MagickTrue;
5763 image->iterations=mng_iterations;
5764 term_chunk_found=MagickFalse;
5768 image->start_loop=MagickFalse;
5770 image->columns=subframe_width;
5771 image->rows=subframe_height;
5772 image->page.width=subframe_width;
5773 image->page.height=subframe_height;
5774 image->page.x=mng_info->clip.left;
5775 image->page.y=mng_info->clip.top;
5776 image->background_color=mng_background_color;
5777 image->alpha_trait=UndefinedPixelTrait;
5779 (void) SetImageBackgroundColor(image,exception);
5781 if (logging != MagickFalse)
5782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5783 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5784 (double) mng_info->clip.left,(double) mng_info->clip.right,
5785 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5788 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5791 if (memcmp(type,mng_CLIP,4) == 0)
5800 first_object=(p[0] << 8) | p[1];
5801 last_object=(p[2] << 8) | p[3];
5803 for (i=(int) first_object; i <= (int) last_object; i++)
5805 if (mng_info->exists[i] && !mng_info->frozen[i])
5810 box=mng_info->object_clip[i];
5811 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5815 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5818 if (memcmp(type,mng_SAVE,4) == 0)
5820 for (i=1; i < MNG_MAX_OBJECTS; i++)
5821 if (mng_info->exists[i])
5823 mng_info->frozen[i]=MagickTrue;
5824 #ifdef MNG_OBJECT_BUFFERS
5825 if (mng_info->ob[i] != (MngBuffer *) NULL)
5826 mng_info->ob[i]->frozen=MagickTrue;
5831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5836 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5838 /* Read DISC or SEEK. */
5840 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5842 for (i=1; i < MNG_MAX_OBJECTS; i++)
5843 MngInfoDiscardObject(mng_info,i);
5851 for (j=0; j < (ssize_t) length; j+=2)
5853 i=p[j] << 8 | p[j+1];
5854 MngInfoDiscardObject(mng_info,i);
5859 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5864 if (memcmp(type,mng_MOVE,4) == 0)
5872 first_object=(p[0] << 8) | p[1];
5873 last_object=(p[2] << 8) | p[3];
5874 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5876 if (mng_info->exists[i] && !mng_info->frozen[i])
5884 old_pair.a=mng_info->x_off[i];
5885 old_pair.b=mng_info->y_off[i];
5886 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5887 mng_info->x_off[i]=new_pair.a;
5888 mng_info->y_off[i]=new_pair.b;
5892 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5896 if (memcmp(type,mng_LOOP,4) == 0)
5898 ssize_t loop_iters=1;
5899 loop_level=chunk[0];
5900 mng_info->loop_active[loop_level]=1; /* mark loop active */
5902 /* Record starting point. */
5903 loop_iters=mng_get_long(&chunk[1]);
5905 if (logging != MagickFalse)
5906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5907 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5908 (double) loop_iters);
5910 if (loop_iters == 0)
5911 skipping_loop=loop_level;
5915 mng_info->loop_jump[loop_level]=TellBlob(image);
5916 mng_info->loop_count[loop_level]=loop_iters;
5919 mng_info->loop_iteration[loop_level]=0;
5920 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5924 if (memcmp(type,mng_ENDL,4) == 0)
5926 loop_level=chunk[0];
5928 if (skipping_loop > 0)
5930 if (skipping_loop == loop_level)
5933 Found end of zero-iteration loop.
5936 mng_info->loop_active[loop_level]=0;
5942 if (mng_info->loop_active[loop_level] == 1)
5944 mng_info->loop_count[loop_level]--;
5945 mng_info->loop_iteration[loop_level]++;
5947 if (logging != MagickFalse)
5948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5949 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5950 (double) loop_level,(double)
5951 mng_info->loop_count[loop_level]);
5953 if (mng_info->loop_count[loop_level] != 0)
5955 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5959 ThrowReaderException(CorruptImageError,
5960 "ImproperImageHeader");
5971 mng_info->loop_active[loop_level]=0;
5973 for (i=0; i < loop_level; i++)
5974 if (mng_info->loop_active[i] == 1)
5975 last_level=(short) i;
5976 loop_level=last_level;
5981 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5985 if (memcmp(type,mng_CLON,4) == 0)
5987 if (mng_info->clon_warning == 0)
5988 (void) ThrowMagickException(exception,GetMagickModule(),
5989 CoderError,"CLON is not implemented yet","`%s'",
5992 mng_info->clon_warning++;
5995 if (memcmp(type,mng_MAGN,4) == 0)
6010 magn_first=(p[0] << 8) | p[1];
6016 magn_last=(p[2] << 8) | p[3];
6019 magn_last=magn_first;
6020 #ifndef MNG_OBJECT_BUFFERS
6021 if (magn_first || magn_last)
6022 if (mng_info->magn_warning == 0)
6024 (void) ThrowMagickException(exception,
6025 GetMagickModule(),CoderError,
6026 "MAGN is not implemented yet for nonzero objects",
6027 "`%s'",image->filename);
6029 mng_info->magn_warning++;
6039 magn_mx=(p[5] << 8) | p[6];
6048 magn_my=(p[7] << 8) | p[8];
6057 magn_ml=(p[9] << 8) | p[10];
6066 magn_mr=(p[11] << 8) | p[12];
6075 magn_mt=(p[13] << 8) | p[14];
6084 magn_mb=(p[15] << 8) | p[16];
6096 magn_methy=magn_methx;
6099 if (magn_methx > 5 || magn_methy > 5)
6100 if (mng_info->magn_warning == 0)
6102 (void) ThrowMagickException(exception,
6103 GetMagickModule(),CoderError,
6104 "Unknown MAGN method in MNG datastream","`%s'",
6107 mng_info->magn_warning++;
6109 #ifdef MNG_OBJECT_BUFFERS
6110 /* Magnify existing objects in the range magn_first to magn_last */
6112 if (magn_first == 0 || magn_last == 0)
6114 /* Save the magnification factors for object 0 */
6115 mng_info->magn_mb=magn_mb;
6116 mng_info->magn_ml=magn_ml;
6117 mng_info->magn_mr=magn_mr;
6118 mng_info->magn_mt=magn_mt;
6119 mng_info->magn_mx=magn_mx;
6120 mng_info->magn_my=magn_my;
6121 mng_info->magn_methx=magn_methx;
6122 mng_info->magn_methy=magn_methy;
6126 if (memcmp(type,mng_PAST,4) == 0)
6128 if (mng_info->past_warning == 0)
6129 (void) ThrowMagickException(exception,GetMagickModule(),
6130 CoderError,"PAST is not implemented yet","`%s'",
6133 mng_info->past_warning++;
6136 if (memcmp(type,mng_SHOW,4) == 0)
6138 if (mng_info->show_warning == 0)
6139 (void) ThrowMagickException(exception,GetMagickModule(),
6140 CoderError,"SHOW is not implemented yet","`%s'",
6143 mng_info->show_warning++;
6146 if (memcmp(type,mng_sBIT,4) == 0)
6149 mng_info->have_global_sbit=MagickFalse;
6153 mng_info->global_sbit.gray=p[0];
6154 mng_info->global_sbit.red=p[0];
6155 mng_info->global_sbit.green=p[1];
6156 mng_info->global_sbit.blue=p[2];
6157 mng_info->global_sbit.alpha=p[3];
6158 mng_info->have_global_sbit=MagickTrue;
6161 if (memcmp(type,mng_pHYs,4) == 0)
6165 mng_info->global_x_pixels_per_unit=
6166 (size_t) mng_get_long(p);
6167 mng_info->global_y_pixels_per_unit=
6168 (size_t) mng_get_long(&p[4]);
6169 mng_info->global_phys_unit_type=p[8];
6170 mng_info->have_global_phys=MagickTrue;
6174 mng_info->have_global_phys=MagickFalse;
6176 if (memcmp(type,mng_pHYg,4) == 0)
6178 if (mng_info->phyg_warning == 0)
6179 (void) ThrowMagickException(exception,GetMagickModule(),
6180 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6182 mng_info->phyg_warning++;
6184 if (memcmp(type,mng_BASI,4) == 0)
6186 skip_to_iend=MagickTrue;
6188 if (mng_info->basi_warning == 0)
6189 (void) ThrowMagickException(exception,GetMagickModule(),
6190 CoderError,"BASI is not implemented yet","`%s'",
6193 mng_info->basi_warning++;
6194 #ifdef MNG_BASI_SUPPORTED
6195 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6196 (p[2] << 8) | p[3]);
6197 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6198 (p[6] << 8) | p[7]);
6199 basi_color_type=p[8];
6200 basi_compression_method=p[9];
6201 basi_filter_type=p[10];
6202 basi_interlace_method=p[11];
6204 basi_red=(p[12] << 8) & p[13];
6210 basi_green=(p[14] << 8) & p[15];
6216 basi_blue=(p[16] << 8) & p[17];
6222 basi_alpha=(p[18] << 8) & p[19];
6226 if (basi_sample_depth == 16)
6233 basi_viewable=p[20];
6239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6243 if (memcmp(type,mng_IHDR,4)
6244 #if defined(JNG_SUPPORTED)
6245 && memcmp(type,mng_JHDR,4)
6249 /* Not an IHDR or JHDR chunk */
6251 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6256 if (logging != MagickFalse)
6257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6258 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6260 mng_info->exists[object_id]=MagickTrue;
6261 mng_info->viewable[object_id]=MagickTrue;
6263 if (mng_info->invisible[object_id])
6265 if (logging != MagickFalse)
6266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6267 " Skipping invisible object");
6269 skip_to_iend=MagickTrue;
6270 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6273 #if defined(MNG_INSERT_LAYERS)
6275 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6277 image_width=(size_t) mng_get_long(p);
6278 image_height=(size_t) mng_get_long(&p[4]);
6280 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6283 Insert a transparent background layer behind the entire animation
6284 if it is not full screen.
6286 #if defined(MNG_INSERT_LAYERS)
6287 if (insert_layers && mng_type && first_mng_object)
6289 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6290 (image_width < mng_info->mng_width) ||
6291 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6292 (image_height < mng_info->mng_height) ||
6293 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6295 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6298 Allocate next image structure.
6300 AcquireNextImage(image_info,image,exception);
6302 if (GetNextImageInList(image) == (Image *) NULL)
6304 image=DestroyImageList(image);
6305 MngInfoFreeStruct(mng_info,&have_mng_structure);
6306 return((Image *) NULL);
6309 image=SyncNextImageInList(image);
6311 mng_info->image=image;
6313 if (term_chunk_found)
6315 image->start_loop=MagickTrue;
6316 image->iterations=mng_iterations;
6317 term_chunk_found=MagickFalse;
6321 image->start_loop=MagickFalse;
6323 /* Make a background rectangle. */
6326 image->columns=mng_info->mng_width;
6327 image->rows=mng_info->mng_height;
6328 image->page.width=mng_info->mng_width;
6329 image->page.height=mng_info->mng_height;
6332 image->background_color=mng_background_color;
6333 (void) SetImageBackgroundColor(image,exception);
6334 if (logging != MagickFalse)
6335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6336 " Inserted transparent background layer, W=%.20g, H=%.20g",
6337 (double) mng_info->mng_width,(double) mng_info->mng_height);
6341 Insert a background layer behind the upcoming image if
6342 framing_mode is 3, and we haven't already inserted one.
6344 if (insert_layers && (mng_info->framing_mode == 3) &&
6345 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6346 (simplicity & 0x08)))
6348 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6351 Allocate next image structure.
6353 AcquireNextImage(image_info,image,exception);
6355 if (GetNextImageInList(image) == (Image *) NULL)
6357 image=DestroyImageList(image);
6358 MngInfoFreeStruct(mng_info,&have_mng_structure);
6359 return((Image *) NULL);
6362 image=SyncNextImageInList(image);
6365 mng_info->image=image;
6367 if (term_chunk_found)
6369 image->start_loop=MagickTrue;
6370 image->iterations=mng_iterations;
6371 term_chunk_found=MagickFalse;
6375 image->start_loop=MagickFalse;
6378 image->columns=subframe_width;
6379 image->rows=subframe_height;
6380 image->page.width=subframe_width;
6381 image->page.height=subframe_height;
6382 image->page.x=mng_info->clip.left;
6383 image->page.y=mng_info->clip.top;
6384 image->background_color=mng_background_color;
6385 image->alpha_trait=UndefinedPixelTrait;
6386 (void) SetImageBackgroundColor(image,exception);
6388 if (logging != MagickFalse)
6389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6390 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6391 (double) mng_info->clip.left,(double) mng_info->clip.right,
6392 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6394 #endif /* MNG_INSERT_LAYERS */
6395 first_mng_object=MagickFalse;
6397 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6400 Allocate next image structure.
6402 AcquireNextImage(image_info,image,exception);
6404 if (GetNextImageInList(image) == (Image *) NULL)
6406 image=DestroyImageList(image);
6407 MngInfoFreeStruct(mng_info,&have_mng_structure);
6408 return((Image *) NULL);
6411 image=SyncNextImageInList(image);
6413 mng_info->image=image;
6414 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6415 GetBlobSize(image));
6417 if (status == MagickFalse)
6420 if (term_chunk_found)
6422 image->start_loop=MagickTrue;
6423 term_chunk_found=MagickFalse;
6427 image->start_loop=MagickFalse;
6429 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6431 image->delay=frame_delay;
6432 frame_delay=default_frame_delay;
6438 image->page.width=mng_info->mng_width;
6439 image->page.height=mng_info->mng_height;
6440 image->page.x=mng_info->x_off[object_id];
6441 image->page.y=mng_info->y_off[object_id];
6442 image->iterations=mng_iterations;
6445 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6448 if (logging != MagickFalse)
6449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6450 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6453 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6456 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6460 mng_info->image=image;
6461 mng_info->mng_type=mng_type;
6462 mng_info->object_id=object_id;
6464 if (memcmp(type,mng_IHDR,4) == 0)
6465 image=ReadOnePNGImage(mng_info,image_info,exception);
6467 #if defined(JNG_SUPPORTED)
6469 image=ReadOneJNGImage(mng_info,image_info,exception);
6472 if (image == (Image *) NULL)
6474 if (IsImageObject(previous) != MagickFalse)
6476 (void) DestroyImageList(previous);
6477 (void) CloseBlob(previous);
6480 MngInfoFreeStruct(mng_info,&have_mng_structure);
6481 return((Image *) NULL);
6484 if (image->columns == 0 || image->rows == 0)
6486 (void) CloseBlob(image);
6487 image=DestroyImageList(image);
6488 MngInfoFreeStruct(mng_info,&have_mng_structure);
6489 return((Image *) NULL);
6492 mng_info->image=image;
6499 if (mng_info->magn_methx || mng_info->magn_methy)
6505 if (logging != MagickFalse)
6506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6507 " Processing MNG MAGN chunk");
6509 if (mng_info->magn_methx == 1)
6511 magnified_width=mng_info->magn_ml;
6513 if (image->columns > 1)
6514 magnified_width += mng_info->magn_mr;
6516 if (image->columns > 2)
6517 magnified_width += (png_uint_32)
6518 ((image->columns-2)*(mng_info->magn_mx));
6523 magnified_width=(png_uint_32) image->columns;
6525 if (image->columns > 1)
6526 magnified_width += mng_info->magn_ml-1;
6528 if (image->columns > 2)
6529 magnified_width += mng_info->magn_mr-1;
6531 if (image->columns > 3)
6532 magnified_width += (png_uint_32)
6533 ((image->columns-3)*(mng_info->magn_mx-1));
6536 if (mng_info->magn_methy == 1)
6538 magnified_height=mng_info->magn_mt;
6540 if (image->rows > 1)
6541 magnified_height += mng_info->magn_mb;
6543 if (image->rows > 2)
6544 magnified_height += (png_uint_32)
6545 ((image->rows-2)*(mng_info->magn_my));
6550 magnified_height=(png_uint_32) image->rows;
6552 if (image->rows > 1)
6553 magnified_height += mng_info->magn_mt-1;
6555 if (image->rows > 2)
6556 magnified_height += mng_info->magn_mb-1;
6558 if (image->rows > 3)
6559 magnified_height += (png_uint_32)
6560 ((image->rows-3)*(mng_info->magn_my-1));
6563 if (magnified_height > image->rows ||
6564 magnified_width > image->columns)
6591 /* Allocate next image structure. */
6593 if (logging != MagickFalse)
6594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6595 " Allocate magnified image");
6597 AcquireNextImage(image_info,image,exception);
6599 if (GetNextImageInList(image) == (Image *) NULL)
6601 image=DestroyImageList(image);
6602 MngInfoFreeStruct(mng_info,&have_mng_structure);
6603 return((Image *) NULL);
6606 large_image=SyncNextImageInList(image);
6608 large_image->columns=magnified_width;
6609 large_image->rows=magnified_height;
6611 magn_methx=mng_info->magn_methx;
6612 magn_methy=mng_info->magn_methy;
6614 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6615 #define QM unsigned short
6616 if (magn_methx != 1 || magn_methy != 1)
6619 Scale pixels to unsigned shorts to prevent
6620 overflow of intermediate values of interpolations
6622 for (y=0; y < (ssize_t) image->rows; y++)
6624 q=GetAuthenticPixels(image,0,y,image->columns,1,
6627 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6629 SetPixelRed(image,ScaleQuantumToShort(
6630 GetPixelRed(image,q)),q);
6631 SetPixelGreen(image,ScaleQuantumToShort(
6632 GetPixelGreen(image,q)),q);
6633 SetPixelBlue(image,ScaleQuantumToShort(
6634 GetPixelBlue(image,q)),q);
6635 SetPixelAlpha(image,ScaleQuantumToShort(
6636 GetPixelAlpha(image,q)),q);
6637 q+=GetPixelChannels(image);
6640 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6648 if (image->alpha_trait == BlendPixelTrait)
6649 (void) SetImageBackgroundColor(large_image,exception);
6653 large_image->background_color.alpha=OpaqueAlpha;
6654 (void) SetImageBackgroundColor(large_image,exception);
6656 if (magn_methx == 4)
6659 if (magn_methx == 5)
6662 if (magn_methy == 4)
6665 if (magn_methy == 5)
6669 /* magnify the rows into the right side of the large image */
6671 if (logging != MagickFalse)
6672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6673 " Magnify the rows to %.20g",(double) large_image->rows);
6674 m=(ssize_t) mng_info->magn_mt;
6676 length=(size_t) image->columns*GetPixelChannels(image);
6677 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6678 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6680 if ((prev == (Quantum *) NULL) ||
6681 (next == (Quantum *) NULL))
6683 image=DestroyImageList(image);
6684 MngInfoFreeStruct(mng_info,&have_mng_structure);
6685 ThrowReaderException(ResourceLimitError,
6686 "MemoryAllocationFailed");
6689 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6690 (void) CopyMagickMemory(next,n,length);
6692 for (y=0; y < (ssize_t) image->rows; y++)
6695 m=(ssize_t) mng_info->magn_mt;
6697 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6698 m=(ssize_t) mng_info->magn_mb;
6700 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6701 m=(ssize_t) mng_info->magn_mb;
6703 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6707 m=(ssize_t) mng_info->magn_my;
6713 if (y < (ssize_t) image->rows-1)
6715 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6717 (void) CopyMagickMemory(next,n,length);
6720 for (i=0; i < m; i++, yy++)
6725 assert(yy < (ssize_t) large_image->rows);
6728 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6730 q+=(large_image->columns-image->columns)*
6731 GetPixelChannels(large_image);
6733 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6735 /* To do: get color as function of indexes[x] */
6737 if (image->storage_class == PseudoClass)
6742 if (magn_methy <= 1)
6744 /* replicate previous */
6745 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6746 SetPixelGreen(large_image,GetPixelGreen(image,
6748 SetPixelBlue(large_image,GetPixelBlue(image,
6750 SetPixelAlpha(large_image,GetPixelAlpha(image,
6754 else if (magn_methy == 2 || magn_methy == 4)
6758 SetPixelRed(large_image,GetPixelRed(image,
6760 SetPixelGreen(large_image,GetPixelGreen(image,
6762 SetPixelBlue(large_image,GetPixelBlue(image,
6764 SetPixelAlpha(large_image,GetPixelAlpha(image,
6771 SetPixelRed(large_image,((QM) (((ssize_t)
6772 (2*i*(GetPixelRed(image,n)
6773 -GetPixelRed(image,pixels)+m))/
6775 +GetPixelRed(image,pixels)))),q);
6776 SetPixelGreen(large_image,((QM) (((ssize_t)
6777 (2*i*(GetPixelGreen(image,n)
6778 -GetPixelGreen(image,pixels)+m))/
6780 +GetPixelGreen(image,pixels)))),q);
6781 SetPixelBlue(large_image,((QM) (((ssize_t)
6782 (2*i*(GetPixelBlue(image,n)
6783 -GetPixelBlue(image,pixels)+m))/
6785 +GetPixelBlue(image,pixels)))),q);
6787 if (image->alpha_trait == BlendPixelTrait)
6788 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6789 (2*i*(GetPixelAlpha(image,n)
6790 -GetPixelAlpha(image,pixels)+m))
6792 GetPixelAlpha(image,pixels)))),q);
6795 if (magn_methy == 4)
6797 /* Replicate nearest */
6798 if (i <= ((m+1) << 1))
6799 SetPixelAlpha(large_image,GetPixelAlpha(image,
6802 SetPixelAlpha(large_image,GetPixelAlpha(image,
6807 else /* if (magn_methy == 3 || magn_methy == 5) */
6809 /* Replicate nearest */
6810 if (i <= ((m+1) << 1))
6812 SetPixelRed(large_image,GetPixelRed(image,
6814 SetPixelGreen(large_image,GetPixelGreen(image,
6816 SetPixelBlue(large_image,GetPixelBlue(image,
6818 SetPixelAlpha(large_image,GetPixelAlpha(image,
6824 SetPixelRed(large_image,GetPixelRed(image,n),q);
6825 SetPixelGreen(large_image,GetPixelGreen(image,n),
6827 SetPixelBlue(large_image,GetPixelBlue(image,n),
6829 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6833 if (magn_methy == 5)
6835 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6836 (GetPixelAlpha(image,n)
6837 -GetPixelAlpha(image,pixels))
6838 +m))/((ssize_t) (m*2))
6839 +GetPixelAlpha(image,pixels)),q);
6842 n+=GetPixelChannels(image);
6843 q+=GetPixelChannels(large_image);
6844 pixels+=GetPixelChannels(image);
6847 if (SyncAuthenticPixels(large_image,exception) == 0)
6853 prev=(Quantum *) RelinquishMagickMemory(prev);
6854 next=(Quantum *) RelinquishMagickMemory(next);
6856 length=image->columns;
6858 if (logging != MagickFalse)
6859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6860 " Delete original image");
6862 DeleteImageFromList(&image);
6866 mng_info->image=image;
6868 /* magnify the columns */
6869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " Magnify the columns to %.20g",(double) image->columns);
6873 for (y=0; y < (ssize_t) image->rows; y++)
6878 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6879 pixels=q+(image->columns-length)*GetPixelChannels(image);
6880 n=pixels+GetPixelChannels(image);
6882 for (x=(ssize_t) (image->columns-length);
6883 x < (ssize_t) image->columns; x++)
6885 /* To do: Rewrite using Get/Set***PixelChannel() */
6887 if (x == (ssize_t) (image->columns-length))
6888 m=(ssize_t) mng_info->magn_ml;
6890 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6891 m=(ssize_t) mng_info->magn_mr;
6893 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6894 m=(ssize_t) mng_info->magn_mr;
6896 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6900 m=(ssize_t) mng_info->magn_mx;
6902 for (i=0; i < m; i++)
6904 if (magn_methx <= 1)
6906 /* replicate previous */
6907 SetPixelRed(image,GetPixelRed(image,pixels),q);
6908 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6909 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6910 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6913 else if (magn_methx == 2 || magn_methx == 4)
6917 SetPixelRed(image,GetPixelRed(image,pixels),q);
6918 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6919 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6920 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6923 /* To do: Rewrite using Get/Set***PixelChannel() */
6927 SetPixelRed(image,(QM) ((2*i*(
6928 GetPixelRed(image,n)
6929 -GetPixelRed(image,pixels))+m)
6931 GetPixelRed(image,pixels)),q);
6933 SetPixelGreen(image,(QM) ((2*i*(
6934 GetPixelGreen(image,n)
6935 -GetPixelGreen(image,pixels))+m)
6937 GetPixelGreen(image,pixels)),q);
6939 SetPixelBlue(image,(QM) ((2*i*(
6940 GetPixelBlue(image,n)
6941 -GetPixelBlue(image,pixels))+m)
6943 GetPixelBlue(image,pixels)),q);
6944 if (image->alpha_trait == BlendPixelTrait)
6945 SetPixelAlpha(image,(QM) ((2*i*(
6946 GetPixelAlpha(image,n)
6947 -GetPixelAlpha(image,pixels))+m)
6949 GetPixelAlpha(image,pixels)),q);
6952 if (magn_methx == 4)
6954 /* Replicate nearest */
6955 if (i <= ((m+1) << 1))
6957 SetPixelAlpha(image,
6958 GetPixelAlpha(image,pixels)+0,q);
6962 SetPixelAlpha(image,
6963 GetPixelAlpha(image,n)+0,q);
6968 else /* if (magn_methx == 3 || magn_methx == 5) */
6970 /* Replicate nearest */
6971 if (i <= ((m+1) << 1))
6973 SetPixelRed(image,GetPixelRed(image,pixels),q);
6974 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6975 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6976 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6981 SetPixelRed(image,GetPixelRed(image,n),q);
6982 SetPixelGreen(image,GetPixelGreen(image,n),q);
6983 SetPixelBlue(image,GetPixelBlue(image,n),q);
6984 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6987 if (magn_methx == 5)
6990 SetPixelAlpha(image,
6991 (QM) ((2*i*( GetPixelAlpha(image,n)
6992 -GetPixelAlpha(image,pixels))+m)/
6994 +GetPixelAlpha(image,pixels)),q);
6997 q+=GetPixelChannels(image);
6999 n+=GetPixelChannels(image);
7000 p+=GetPixelChannels(image);
7003 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7006 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7007 if (magn_methx != 1 || magn_methy != 1)
7010 Rescale pixels to Quantum
7012 for (y=0; y < (ssize_t) image->rows; y++)
7014 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7016 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7018 SetPixelRed(image,ScaleShortToQuantum(
7019 GetPixelRed(image,q)),q);
7020 SetPixelGreen(image,ScaleShortToQuantum(
7021 GetPixelGreen(image,q)),q);
7022 SetPixelBlue(image,ScaleShortToQuantum(
7023 GetPixelBlue(image,q)),q);
7024 SetPixelAlpha(image,ScaleShortToQuantum(
7025 GetPixelAlpha(image,q)),q);
7026 q+=GetPixelChannels(image);
7029 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7034 if (logging != MagickFalse)
7035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7036 " Finished MAGN processing");
7041 Crop_box is with respect to the upper left corner of the MNG.
7043 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7044 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7045 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7046 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7047 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7048 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7049 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7050 if ((crop_box.left != (mng_info->image_box.left
7051 +mng_info->x_off[object_id])) ||
7052 (crop_box.right != (mng_info->image_box.right
7053 +mng_info->x_off[object_id])) ||
7054 (crop_box.top != (mng_info->image_box.top
7055 +mng_info->y_off[object_id])) ||
7056 (crop_box.bottom != (mng_info->image_box.bottom
7057 +mng_info->y_off[object_id])))
7059 if (logging != MagickFalse)
7060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7061 " Crop the PNG image");
7063 if ((crop_box.left < crop_box.right) &&
7064 (crop_box.top < crop_box.bottom))
7073 Crop_info is with respect to the upper left corner of
7076 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7077 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7078 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7079 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7080 image->page.width=image->columns;
7081 image->page.height=image->rows;
7084 im=CropImage(image,&crop_info,exception);
7086 if (im != (Image *) NULL)
7088 image->columns=im->columns;
7089 image->rows=im->rows;
7090 im=DestroyImage(im);
7091 image->page.width=image->columns;
7092 image->page.height=image->rows;
7093 image->page.x=crop_box.left;
7094 image->page.y=crop_box.top;
7101 No pixels in crop area. The MNG spec still requires
7102 a layer, though, so make a single transparent pixel in
7103 the top left corner.
7108 (void) SetImageBackgroundColor(image,exception);
7109 image->page.width=1;
7110 image->page.height=1;
7115 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7116 image=mng_info->image;
7120 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7121 /* PNG does not handle depths greater than 16 so reduce it even
7124 if (image->depth > 16)
7128 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7129 if (image->depth > 8)
7131 /* To do: fill low byte properly */
7135 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7139 if (image_info->number_scenes != 0)
7141 if (mng_info->scenes_found >
7142 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7146 if (logging != MagickFalse)
7147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7148 " Finished reading image datastream.");
7150 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7152 (void) CloseBlob(image);
7154 if (logging != MagickFalse)
7155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7156 " Finished reading all image datastreams.");
7158 #if defined(MNG_INSERT_LAYERS)
7159 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7160 (mng_info->mng_height))
7163 Insert a background layer if nothing else was found.
7165 if (logging != MagickFalse)
7166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7167 " No images found. Inserting a background layer.");
7169 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7172 Allocate next image structure.
7174 AcquireNextImage(image_info,image,exception);
7175 if (GetNextImageInList(image) == (Image *) NULL)
7177 image=DestroyImageList(image);
7178 MngInfoFreeStruct(mng_info,&have_mng_structure);
7180 if (logging != MagickFalse)
7181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7182 " Allocation failed, returning NULL.");
7184 return((Image *) NULL);
7186 image=SyncNextImageInList(image);
7188 image->columns=mng_info->mng_width;
7189 image->rows=mng_info->mng_height;
7190 image->page.width=mng_info->mng_width;
7191 image->page.height=mng_info->mng_height;
7194 image->background_color=mng_background_color;
7195 image->alpha_trait=UndefinedPixelTrait;
7197 if (image_info->ping == MagickFalse)
7198 (void) SetImageBackgroundColor(image,exception);
7200 mng_info->image_found++;
7203 image->iterations=mng_iterations;
7205 if (mng_iterations == 1)
7206 image->start_loop=MagickTrue;
7208 while (GetPreviousImageInList(image) != (Image *) NULL)
7211 if (image_count > 10*mng_info->image_found)
7213 if (logging != MagickFalse)
7214 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7216 (void) ThrowMagickException(exception,GetMagickModule(),
7217 CoderError,"Linked list is corrupted, beginning of list not found",
7218 "`%s'",image_info->filename);
7220 return((Image *) NULL);
7223 image=GetPreviousImageInList(image);
7225 if (GetNextImageInList(image) == (Image *) NULL)
7227 if (logging != MagickFalse)
7228 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7230 (void) ThrowMagickException(exception,GetMagickModule(),
7231 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7232 image_info->filename);
7236 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7237 GetNextImageInList(image) ==
7240 if (logging != MagickFalse)
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7242 " First image null");
7244 (void) ThrowMagickException(exception,GetMagickModule(),
7245 CoderError,"image->next for first image is NULL but shouldn't be.",
7246 "`%s'",image_info->filename);
7249 if (mng_info->image_found == 0)
7251 if (logging != MagickFalse)
7252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7253 " No visible images found.");
7255 (void) ThrowMagickException(exception,GetMagickModule(),
7256 CoderError,"No visible images in file","`%s'",image_info->filename);
7258 if (image != (Image *) NULL)
7259 image=DestroyImageList(image);
7261 MngInfoFreeStruct(mng_info,&have_mng_structure);
7262 return((Image *) NULL);
7265 if (mng_info->ticks_per_second)
7266 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7267 final_delay/mng_info->ticks_per_second;
7270 image->start_loop=MagickTrue;
7272 /* Find final nonzero image delay */
7273 final_image_delay=0;
7275 while (GetNextImageInList(image) != (Image *) NULL)
7278 final_image_delay=image->delay;
7280 image=GetNextImageInList(image);
7283 if (final_delay < final_image_delay)
7284 final_delay=final_image_delay;
7286 image->delay=final_delay;
7288 if (logging != MagickFalse)
7289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7290 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7291 (double) final_delay);
7293 if (logging != MagickFalse)
7299 image=GetFirstImageInList(image);
7301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7302 " Before coalesce:");
7304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7305 " scene 0 delay=%.20g",(double) image->delay);
7307 while (GetNextImageInList(image) != (Image *) NULL)
7309 image=GetNextImageInList(image);
7310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7311 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7315 image=GetFirstImageInList(image);
7316 #ifdef MNG_COALESCE_LAYERS
7326 if (logging != MagickFalse)
7327 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7330 next_image=CoalesceImages(image,exception);
7332 if (next_image == (Image *) NULL)
7333 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7335 image=DestroyImageList(image);
7338 for (next=image; next != (Image *) NULL; next=next_image)
7340 next->page.width=mng_info->mng_width;
7341 next->page.height=mng_info->mng_height;
7344 next->scene=scene++;
7345 next_image=GetNextImageInList(next);
7347 if (next_image == (Image *) NULL)
7350 if (next->delay == 0)
7353 next_image->previous=GetPreviousImageInList(next);
7354 if (GetPreviousImageInList(next) == (Image *) NULL)
7357 next->previous->next=next_image;
7358 next=DestroyImage(next);
7364 while (GetNextImageInList(image) != (Image *) NULL)
7365 image=GetNextImageInList(image);
7367 image->dispose=BackgroundDispose;
7369 if (logging != MagickFalse)
7375 image=GetFirstImageInList(image);
7377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7378 " After coalesce:");
7380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7381 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7382 (double) image->dispose);
7384 while (GetNextImageInList(image) != (Image *) NULL)
7386 image=GetNextImageInList(image);
7388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7389 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7390 (double) image->delay,(double) image->dispose);
7394 image=GetFirstImageInList(image);
7395 MngInfoFreeStruct(mng_info,&have_mng_structure);
7396 have_mng_structure=MagickFalse;
7398 if (logging != MagickFalse)
7399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7401 return(GetFirstImageInList(image));
7403 #else /* PNG_LIBPNG_VER > 10011 */
7404 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7406 printf("Your PNG library is too old: You have libpng-%s\n",
7407 PNG_LIBPNG_VER_STRING);
7409 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7410 "PNG library is too old","`%s'",image_info->filename);
7412 return(Image *) NULL;
7415 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7417 return(ReadPNGImage(image_info,exception));
7419 #endif /* PNG_LIBPNG_VER > 10011 */
7423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7427 % R e g i s t e r P N G I m a g e %
7431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7433 % RegisterPNGImage() adds properties for the PNG image format to
7434 % the list of supported formats. The properties include the image format
7435 % tag, a method to read and/or write the format, whether the format
7436 % supports the saving of more than one frame to the same file or blob,
7437 % whether the format supports native in-memory I/O, and a brief
7438 % description of the format.
7440 % The format of the RegisterPNGImage method is:
7442 % size_t RegisterPNGImage(void)
7445 ModuleExport size_t RegisterPNGImage(void)
7448 version[MaxTextExtent];
7456 "See http://www.libpng.org/ for details about the PNG format."
7461 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7467 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7473 #if defined(PNG_LIBPNG_VER_STRING)
7474 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7475 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7477 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7479 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7480 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7485 entry=SetMagickInfo("MNG");
7486 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7488 #if defined(MAGICKCORE_PNG_DELEGATE)
7489 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7490 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7493 entry->magick=(IsImageFormatHandler *) IsMNG;
7494 entry->description=ConstantString("Multiple-image Network Graphics");
7496 if (*version != '\0')
7497 entry->version=ConstantString(version);
7499 entry->module=ConstantString("PNG");
7500 entry->note=ConstantString(MNGNote);
7501 (void) RegisterMagickInfo(entry);
7503 entry=SetMagickInfo("PNG");
7505 #if defined(MAGICKCORE_PNG_DELEGATE)
7506 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7507 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7510 entry->magick=(IsImageFormatHandler *) IsPNG;
7511 entry->adjoin=MagickFalse;
7512 entry->description=ConstantString("Portable Network Graphics");
7513 entry->module=ConstantString("PNG");
7515 if (*version != '\0')
7516 entry->version=ConstantString(version);
7518 entry->note=ConstantString(PNGNote);
7519 (void) RegisterMagickInfo(entry);
7521 entry=SetMagickInfo("PNG8");
7523 #if defined(MAGICKCORE_PNG_DELEGATE)
7524 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7525 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7528 entry->magick=(IsImageFormatHandler *) IsPNG;
7529 entry->adjoin=MagickFalse;
7530 entry->description=ConstantString(
7531 "8-bit indexed with optional binary transparency");
7532 entry->module=ConstantString("PNG");
7533 (void) RegisterMagickInfo(entry);
7535 entry=SetMagickInfo("PNG24");
7538 #if defined(ZLIB_VERSION)
7539 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7540 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7542 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7544 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7545 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7549 if (*version != '\0')
7550 entry->version=ConstantString(version);
7552 #if defined(MAGICKCORE_PNG_DELEGATE)
7553 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7554 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7557 entry->magick=(IsImageFormatHandler *) IsPNG;
7558 entry->adjoin=MagickFalse;
7559 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7560 entry->module=ConstantString("PNG");
7561 (void) RegisterMagickInfo(entry);
7563 entry=SetMagickInfo("PNG32");
7565 #if defined(MAGICKCORE_PNG_DELEGATE)
7566 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7567 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7570 entry->magick=(IsImageFormatHandler *) IsPNG;
7571 entry->adjoin=MagickFalse;
7572 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7573 entry->module=ConstantString("PNG");
7574 (void) RegisterMagickInfo(entry);
7576 entry=SetMagickInfo("PNG48");
7578 #if defined(MAGICKCORE_PNG_DELEGATE)
7579 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7580 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7583 entry->magick=(IsImageFormatHandler *) IsPNG;
7584 entry->adjoin=MagickFalse;
7585 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7586 entry->module=ConstantString("PNG");
7587 (void) RegisterMagickInfo(entry);
7589 entry=SetMagickInfo("PNG64");
7591 #if defined(MAGICKCORE_PNG_DELEGATE)
7592 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7593 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7596 entry->magick=(IsImageFormatHandler *) IsPNG;
7597 entry->adjoin=MagickFalse;
7598 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7599 entry->module=ConstantString("PNG");
7600 (void) RegisterMagickInfo(entry);
7602 entry=SetMagickInfo("PNG00");
7604 #if defined(MAGICKCORE_PNG_DELEGATE)
7605 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7606 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7609 entry->magick=(IsImageFormatHandler *) IsPNG;
7610 entry->adjoin=MagickFalse;
7611 entry->description=ConstantString(
7612 "PNG inheriting bit-depth and color-type from original");
7613 entry->module=ConstantString("PNG");
7614 (void) RegisterMagickInfo(entry);
7616 entry=SetMagickInfo("JNG");
7618 #if defined(JNG_SUPPORTED)
7619 #if defined(MAGICKCORE_PNG_DELEGATE)
7620 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7621 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7625 entry->magick=(IsImageFormatHandler *) IsJNG;
7626 entry->adjoin=MagickFalse;
7627 entry->description=ConstantString("JPEG Network Graphics");
7628 entry->module=ConstantString("PNG");
7629 entry->note=ConstantString(JNGNote);
7630 (void) RegisterMagickInfo(entry);
7632 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7633 ping_semaphore=AllocateSemaphoreInfo();
7636 return(MagickImageCoderSignature);
7640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7644 % U n r e g i s t e r P N G I m a g e %
7648 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7650 % UnregisterPNGImage() removes format registrations made by the
7651 % PNG module from the list of supported formats.
7653 % The format of the UnregisterPNGImage method is:
7655 % UnregisterPNGImage(void)
7658 ModuleExport void UnregisterPNGImage(void)
7660 (void) UnregisterMagickInfo("MNG");
7661 (void) UnregisterMagickInfo("PNG");
7662 (void) UnregisterMagickInfo("PNG8");
7663 (void) UnregisterMagickInfo("PNG24");
7664 (void) UnregisterMagickInfo("PNG32");
7665 (void) UnregisterMagickInfo("PNG48");
7666 (void) UnregisterMagickInfo("PNG64");
7667 (void) UnregisterMagickInfo("PNG00");
7668 (void) UnregisterMagickInfo("JNG");
7670 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7671 if (ping_semaphore != (SemaphoreInfo *) NULL)
7672 DestroySemaphoreInfo(&ping_semaphore);
7676 #if defined(MAGICKCORE_PNG_DELEGATE)
7677 #if PNG_LIBPNG_VER > 10011
7679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7683 % W r i t e M N G I m a g e %
7687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7689 % WriteMNGImage() writes an image in the Portable Network Graphics
7690 % Group's "Multiple-image Network Graphics" encoded image format.
7692 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7694 % The format of the WriteMNGImage method is:
7696 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7697 % Image *image,ExceptionInfo *exception)
7699 % A description of each parameter follows.
7701 % o image_info: the image info.
7703 % o image: The image.
7705 % o exception: return any errors or warnings in this structure.
7707 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7708 % "To do" under ReadPNGImage):
7710 % Preserve all unknown and not-yet-handled known chunks found in input
7711 % PNG file and copy them into output PNG files according to the PNG
7714 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7716 % Improve selection of color type (use indexed-colour or indexed-colour
7717 % with tRNS when 256 or fewer unique RGBA values are present).
7719 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7720 % This will be complicated if we limit ourselves to generating MNG-LC
7721 % files. For now we ignore disposal method 3 and simply overlay the next
7724 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7725 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7726 % [mostly done 15 June 1999 but still need to take care of tRNS]
7728 % Check for identical sRGB and replace with a global sRGB (and remove
7729 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7730 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7731 % local gAMA/cHRM with local sRGB if appropriate).
7733 % Check for identical sBIT chunks and write global ones.
7735 % Provide option to skip writing the signature tEXt chunks.
7737 % Use signatures to detect identical objects and reuse the first
7738 % instance of such objects instead of writing duplicate objects.
7740 % Use a smaller-than-32k value of compression window size when
7743 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7744 % ancillary text chunks and save profiles.
7746 % Provide an option to force LC files (to ensure exact framing rate)
7749 % Provide an option to force VLC files instead of LC, even when offsets
7750 % are present. This will involve expanding the embedded images with a
7751 % transparent region at the top and/or left.
7755 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7756 png_info *ping_info, unsigned char *profile_type, unsigned char
7757 *profile_description, unsigned char *profile_data, png_uint_32 length)
7776 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7778 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7781 if (image_info->verbose)
7783 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7784 (char *) profile_type, (double) length);
7787 #if PNG_LIBPNG_VER >= 10400
7788 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7790 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7792 description_length=(png_uint_32) strlen((const char *) profile_description);
7793 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7794 + description_length);
7795 #if PNG_LIBPNG_VER >= 10400
7796 text[0].text=(png_charp) png_malloc(ping,
7797 (png_alloc_size_t) allocated_length);
7798 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7800 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7801 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7803 text[0].key[0]='\0';
7804 (void) ConcatenateMagickString(text[0].key,
7805 "Raw profile type ",MaxTextExtent);
7806 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7810 (void) CopyMagickString(dp,(const char *) profile_description,
7812 dp+=description_length;
7814 (void) FormatLocaleString(dp,allocated_length-
7815 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7818 for (i=0; i < (ssize_t) length; i++)
7822 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7823 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7828 text[0].text_length=(png_size_t) (dp-text[0].text);
7829 text[0].compression=image_info->compression == NoCompression ||
7830 (image_info->compression == UndefinedCompression &&
7831 text[0].text_length < 128) ? -1 : 0;
7833 if (text[0].text_length <= allocated_length)
7834 png_set_text(ping,ping_info,text,1);
7836 png_free(ping,text[0].text);
7837 png_free(ping,text[0].key);
7838 png_free(ping,text);
7841 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7842 const char *string, MagickBooleanType logging)
7855 ResetImageProfileIterator(image);
7857 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7859 profile=GetImageProfile(image,name);
7861 if (profile != (const StringInfo *) NULL)
7866 if (LocaleNCompare(name,string,11) == 0)
7868 if (logging != MagickFalse)
7869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7870 " Found %s profile",name);
7872 ping_profile=CloneStringInfo(profile);
7873 data=GetStringInfoDatum(ping_profile),
7874 length=(png_uint_32) GetStringInfoLength(ping_profile);
7879 (void) WriteBlobMSBULong(image,length-5); /* data length */
7880 (void) WriteBlob(image,length-1,data+1);
7881 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7882 ping_profile=DestroyStringInfo(ping_profile);
7886 name=GetNextImageProfile(image);
7893 /* Write one PNG image */
7894 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7895 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7926 ping_trans_alpha[256];
7954 ping_have_cheap_transparency,
7967 /* ping_exclude_EXIF, */
7970 /* ping_exclude_iTXt, */
7975 /* ping_exclude_tRNS, */
7977 ping_exclude_zCCP, /* hex-encoded iCCP */
7980 ping_preserve_colormap,
7982 ping_need_colortype_warning,
7990 *volatile pixel_info;
8009 ping_interlace_method,
8010 ping_compression_method,
8027 number_semitransparent,
8029 ping_pHYs_unit_type;
8032 ping_pHYs_x_resolution,
8033 ping_pHYs_y_resolution;
8035 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8036 " Enter WriteOnePNGImage()");
8038 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8039 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8040 if (image_info == (ImageInfo *) NULL)
8041 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8043 /* Define these outside of the following "if logging()" block so they will
8044 * show in debuggers.
8047 (void) ConcatenateMagickString(im_vers,
8048 MagickLibVersionText,MaxTextExtent);
8049 (void) ConcatenateMagickString(im_vers,
8050 MagickLibAddendum,MaxTextExtent);
8053 (void) ConcatenateMagickString(libpng_vers,
8054 PNG_LIBPNG_VER_STRING,32);
8056 (void) ConcatenateMagickString(libpng_runv,
8057 png_get_libpng_ver(NULL),32);
8060 (void) ConcatenateMagickString(zlib_vers,
8063 (void) ConcatenateMagickString(zlib_runv,
8068 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8070 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8072 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8074 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8077 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8079 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8081 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8086 /* Initialize some stuff */
8089 ping_interlace_method=0,
8090 ping_compression_method=0,
8091 ping_filter_method=0,
8094 ping_background.red = 0;
8095 ping_background.green = 0;
8096 ping_background.blue = 0;
8097 ping_background.gray = 0;
8098 ping_background.index = 0;
8100 ping_trans_color.red=0;
8101 ping_trans_color.green=0;
8102 ping_trans_color.blue=0;
8103 ping_trans_color.gray=0;
8105 ping_pHYs_unit_type = 0;
8106 ping_pHYs_x_resolution = 0;
8107 ping_pHYs_y_resolution = 0;
8109 ping_have_blob=MagickFalse;
8110 ping_have_cheap_transparency=MagickFalse;
8111 ping_have_color=MagickTrue;
8112 ping_have_non_bw=MagickTrue;
8113 ping_have_PLTE=MagickFalse;
8114 ping_have_bKGD=MagickFalse;
8115 ping_have_iCCP=MagickFalse;
8116 ping_have_pHYs=MagickFalse;
8117 ping_have_sRGB=MagickFalse;
8118 ping_have_tRNS=MagickFalse;
8120 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8121 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8122 ping_exclude_date=mng_info->ping_exclude_date;
8123 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8124 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8125 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8126 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8127 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8128 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8129 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8130 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8131 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8132 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8133 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8134 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8136 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8137 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8138 ping_need_colortype_warning = MagickFalse;
8140 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8141 * i.e., eliminate the ICC profile and set image->rendering_intent.
8142 * Note that this will not involve any changes to the actual pixels
8143 * but merely passes information to applications that read the resulting
8146 * To do: recognize other variants of the sRGB profile, using the CRC to
8147 * verify all recognized variants including the 7 already known.
8149 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8151 * Use something other than image->rendering_intent to record the fact
8152 * that the sRGB profile was found.
8154 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8155 * profile. Record the Blackpoint Compensation, if any.
8157 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8165 ResetImageProfileIterator(image);
8166 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8168 profile=GetImageProfile(image,name);
8170 if (profile != (StringInfo *) NULL)
8172 if ((LocaleCompare(name,"ICC") == 0) ||
8173 (LocaleCompare(name,"ICM") == 0))
8188 length=(png_uint_32) GetStringInfoLength(profile);
8190 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8192 if (length == sRGB_info[icheck].len)
8196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8197 " Got a %lu-byte ICC profile (potentially sRGB)",
8198 (unsigned long) length);
8200 data=GetStringInfoDatum(profile);
8201 profile_crc=crc32(0,data,length);
8203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8204 " with crc=%8x",(unsigned int) profile_crc);
8208 if (profile_crc == sRGB_info[icheck].crc)
8210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8211 " It is sRGB with rendering intent = %s",
8212 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8213 sRGB_info[icheck].intent));
8214 if (image->rendering_intent==UndefinedIntent)
8216 image->rendering_intent=
8217 Magick_RenderingIntent_from_PNG_RenderingIntent(
8218 sRGB_info[icheck].intent);
8220 ping_exclude_iCCP = MagickTrue;
8221 ping_exclude_zCCP = MagickTrue;
8222 ping_have_sRGB = MagickTrue;
8227 if (sRGB_info[icheck].len == 0)
8228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8229 " Got a %lu-byte ICC profile not recognized as sRGB",
8230 (unsigned long) length);
8233 name=GetNextImageProfile(image);
8238 number_semitransparent = 0;
8239 number_transparent = 0;
8241 if (logging != MagickFalse)
8243 if (image->storage_class == UndefinedClass)
8244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8245 " storage_class=UndefinedClass");
8246 if (image->storage_class == DirectClass)
8247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 " storage_class=DirectClass");
8249 if (image->storage_class == PseudoClass)
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251 " storage_class=PseudoClass");
8254 if (image->storage_class == PseudoClass &&
8255 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8256 mng_info->write_png48 || mng_info->write_png64 ||
8257 (mng_info->write_png_colortype != 1 &&
8258 mng_info->write_png_colortype != 5)))
8260 (void) SyncImage(image,exception);
8261 image->storage_class = DirectClass;
8264 if (ping_preserve_colormap == MagickFalse)
8266 if (image->storage_class != PseudoClass && image->colormap != NULL)
8268 /* Free the bogus colormap; it can cause trouble later */
8269 if (logging != MagickFalse)
8270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8271 " Freeing bogus colormap");
8272 (void) RelinquishMagickMemory(image->colormap);
8273 image->colormap=NULL;
8277 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8278 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8281 Sometimes we get PseudoClass images whose RGB values don't match
8282 the colors in the colormap. This code syncs the RGB values.
8284 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8285 (void) SyncImage(image,exception);
8287 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8288 if (image->depth > 8)
8290 if (logging != MagickFalse)
8291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8292 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8298 /* Respect the -depth option */
8299 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8304 if (image->depth > 8)
8306 #if MAGICKCORE_QUANTUM_DEPTH > 16
8307 /* Scale to 16-bit */
8308 LBR16PacketRGBO(image->background_color);
8310 for (y=0; y < (ssize_t) image->rows; y++)
8312 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8314 if (r == (Quantum *) NULL)
8317 for (x=0; x < (ssize_t) image->columns; x++)
8320 r+=GetPixelChannels(image);
8323 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8327 if (image->storage_class == PseudoClass && image->colormap != NULL)
8329 for (i=0; i < (ssize_t) image->colors; i++)
8331 LBR16PacketRGBO(image->colormap[i]);
8334 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8337 else if (image->depth > 4)
8339 #if MAGICKCORE_QUANTUM_DEPTH > 8
8340 /* Scale to 8-bit */
8341 LBR08PacketRGBO(image->background_color);
8343 for (y=0; y < (ssize_t) image->rows; y++)
8345 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8347 if (r == (Quantum *) NULL)
8350 for (x=0; x < (ssize_t) image->columns; x++)
8353 r+=GetPixelChannels(image);
8356 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8360 if (image->storage_class == PseudoClass && image->colormap != NULL)
8362 for (i=0; i < (ssize_t) image->colors; i++)
8364 LBR08PacketRGBO(image->colormap[i]);
8367 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8370 if (image->depth > 2)
8372 /* Scale to 4-bit */
8373 LBR04PacketRGBO(image->background_color);
8375 for (y=0; y < (ssize_t) image->rows; y++)
8377 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8379 if (r == (Quantum *) NULL)
8382 for (x=0; x < (ssize_t) image->columns; x++)
8385 r+=GetPixelChannels(image);
8388 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8392 if (image->storage_class == PseudoClass && image->colormap != NULL)
8394 for (i=0; i < (ssize_t) image->colors; i++)
8396 LBR04PacketRGBO(image->colormap[i]);
8401 else if (image->depth > 1)
8403 /* Scale to 2-bit */
8404 LBR02PacketRGBO(image->background_color);
8406 for (y=0; y < (ssize_t) image->rows; y++)
8408 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8410 if (r == (Quantum *) NULL)
8413 for (x=0; x < (ssize_t) image->columns; x++)
8416 r+=GetPixelChannels(image);
8419 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8423 if (image->storage_class == PseudoClass && image->colormap != NULL)
8425 for (i=0; i < (ssize_t) image->colors; i++)
8427 LBR02PacketRGBO(image->colormap[i]);
8433 /* Scale to 1-bit */
8434 LBR01PacketRGBO(image->background_color);
8436 for (y=0; y < (ssize_t) image->rows; y++)
8438 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8440 if (r == (Quantum *) NULL)
8443 for (x=0; x < (ssize_t) image->columns; x++)
8446 r+=GetPixelChannels(image);
8449 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8453 if (image->storage_class == PseudoClass && image->colormap != NULL)
8455 for (i=0; i < (ssize_t) image->colors; i++)
8457 LBR01PacketRGBO(image->colormap[i]);
8463 /* To do: set to next higher multiple of 8 */
8464 if (image->depth < 8)
8467 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8468 /* PNG does not handle depths greater than 16 so reduce it even
8471 if (image->depth > 8)
8475 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8476 if (image->depth > 8)
8478 /* To do: fill low byte properly */
8482 if (image->depth == 16 && mng_info->write_png_depth != 16)
8483 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8487 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8488 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8489 mng_info->write_png_colortype < 4 &&
8490 image->alpha_trait != BlendPixelTrait)))
8492 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8493 * are not going to need the result.
8495 image_colors = (int) image->colors;
8496 number_opaque = (int) image->colors;
8497 if (mng_info->write_png_colortype == 1 ||
8498 mng_info->write_png_colortype == 5)
8499 ping_have_color=MagickFalse;
8501 ping_have_color=MagickTrue;
8502 ping_have_non_bw=MagickFalse;
8504 if (image->alpha_trait == BlendPixelTrait)
8506 number_transparent = 2;
8507 number_semitransparent = 1;
8512 number_transparent = 0;
8513 number_semitransparent = 0;
8521 * Normally we run this just once, but in the case of writing PNG8
8522 * we reduce the transparency to binary and run again, then if there
8523 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8524 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8525 * palette. Then (To do) we take care of a final reduction that is only
8526 * needed if there are still 256 colors present and one of them has both
8527 * transparent and opaque instances.
8530 tried_332 = MagickFalse;
8531 tried_333 = MagickFalse;
8532 tried_444 = MagickFalse;
8537 * Sometimes we get DirectClass images that have 256 colors or fewer.
8538 * This code will build a colormap.
8540 * Also, sometimes we get PseudoClass images with an out-of-date
8541 * colormap. This code will replace the colormap with a new one.
8542 * Sometimes we get PseudoClass images that have more than 256 colors.
8543 * This code will delete the colormap and change the image to
8546 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8547 * even though it sometimes contains left-over non-opaque values.
8549 * Also we gather some information (number of opaque, transparent,
8550 * and semitransparent pixels, and whether the image has any non-gray
8551 * pixels or only black-and-white pixels) that we might need later.
8553 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8554 * we need to check for bogus non-opaque values, at least.
8562 semitransparent[260],
8565 register const Quantum
8572 if (logging != MagickFalse)
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " Enter BUILD_PALETTE:");
8576 if (logging != MagickFalse)
8578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8579 " image->columns=%.20g",(double) image->columns);
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " image->rows=%.20g",(double) image->rows);
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8585 " image->depth=%.20g",(double) image->depth);
8587 if (image->storage_class == PseudoClass && image->colormap != NULL)
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590 " Original colormap:");
8591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8592 " i (red,green,blue,alpha)");
8594 for (i=0; i < 256; i++)
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " %d (%d,%d,%d,%d)",
8599 (int) image->colormap[i].red,
8600 (int) image->colormap[i].green,
8601 (int) image->colormap[i].blue,
8602 (int) image->colormap[i].alpha);
8605 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " %d (%d,%d,%d,%d)",
8612 (int) image->colormap[i].red,
8613 (int) image->colormap[i].green,
8614 (int) image->colormap[i].blue,
8615 (int) image->colormap[i].alpha);
8620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621 " image->colors=%d",(int) image->colors);
8623 if (image->colors == 0)
8624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625 " (zero means unknown)");
8627 if (ping_preserve_colormap == MagickFalse)
8628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629 " Regenerate the colormap");
8634 number_semitransparent = 0;
8635 number_transparent = 0;
8637 for (y=0; y < (ssize_t) image->rows; y++)
8639 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8641 if (q == (Quantum *) NULL)
8644 for (x=0; x < (ssize_t) image->columns; x++)
8646 if (image->alpha_trait != BlendPixelTrait ||
8647 GetPixelAlpha(image,q) == OpaqueAlpha)
8649 if (number_opaque < 259)
8651 if (number_opaque == 0)
8653 GetPixelInfoPixel(image, q, opaque);
8654 opaque[0].alpha=OpaqueAlpha;
8658 for (i=0; i< (ssize_t) number_opaque; i++)
8660 if (IsPixelEquivalent(image,q, opaque+i))
8664 if (i == (ssize_t) number_opaque && number_opaque < 259)
8667 GetPixelInfoPixel(image, q, opaque+i);
8668 opaque[i].alpha=OpaqueAlpha;
8672 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8674 if (number_transparent < 259)
8676 if (number_transparent == 0)
8678 GetPixelInfoPixel(image, q, transparent);
8679 ping_trans_color.red=(unsigned short)
8680 GetPixelRed(image,q);
8681 ping_trans_color.green=(unsigned short)
8682 GetPixelGreen(image,q);
8683 ping_trans_color.blue=(unsigned short)
8684 GetPixelBlue(image,q);
8685 ping_trans_color.gray=(unsigned short)
8686 GetPixelGray(image,q);
8687 number_transparent = 1;
8690 for (i=0; i< (ssize_t) number_transparent; i++)
8692 if (IsPixelEquivalent(image,q, transparent+i))
8696 if (i == (ssize_t) number_transparent &&
8697 number_transparent < 259)
8699 number_transparent++;
8700 GetPixelInfoPixel(image,q,transparent+i);
8706 if (number_semitransparent < 259)
8708 if (number_semitransparent == 0)
8710 GetPixelInfoPixel(image,q,semitransparent);
8711 number_semitransparent = 1;
8714 for (i=0; i< (ssize_t) number_semitransparent; i++)
8716 if (IsPixelEquivalent(image,q, semitransparent+i)
8717 && GetPixelAlpha(image,q) ==
8718 semitransparent[i].alpha)
8722 if (i == (ssize_t) number_semitransparent &&
8723 number_semitransparent < 259)
8725 number_semitransparent++;
8726 GetPixelInfoPixel(image, q, semitransparent+i);
8730 q+=GetPixelChannels(image);
8734 if (mng_info->write_png8 == MagickFalse &&
8735 ping_exclude_bKGD == MagickFalse)
8737 /* Add the background color to the palette, if it
8738 * isn't already there.
8740 if (logging != MagickFalse)
8742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8743 " Check colormap for background (%d,%d,%d)",
8744 (int) image->background_color.red,
8745 (int) image->background_color.green,
8746 (int) image->background_color.blue);
8748 for (i=0; i<number_opaque; i++)
8750 if (opaque[i].red == image->background_color.red &&
8751 opaque[i].green == image->background_color.green &&
8752 opaque[i].blue == image->background_color.blue)
8755 if (number_opaque < 259 && i == number_opaque)
8757 opaque[i] = image->background_color;
8758 ping_background.index = i;
8760 if (logging != MagickFalse)
8762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8763 " background_color index is %d",(int) i);
8767 else if (logging != MagickFalse)
8768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8769 " No room in the colormap to add background color");
8772 image_colors=number_opaque+number_transparent+number_semitransparent;
8774 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8776 /* No room for the background color; remove it. */
8781 if (logging != MagickFalse)
8783 if (image_colors > 256)
8784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785 " image has more than 256 colors");
8788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8789 " image has %d colors",image_colors);
8792 if (ping_preserve_colormap != MagickFalse)
8795 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8797 ping_have_color=MagickFalse;
8798 ping_have_non_bw=MagickFalse;
8800 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8801 (IssRGBColorspace(image->colorspace) != MagickFalse))
8803 ping_have_color=MagickTrue;
8804 ping_have_non_bw=MagickTrue;
8807 if(image_colors > 256)
8809 for (y=0; y < (ssize_t) image->rows; y++)
8811 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8813 if (q == (Quantum *) NULL)
8817 for (x=0; x < (ssize_t) image->columns; x++)
8819 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8820 GetPixelRed(image,s) != GetPixelBlue(image,s))
8822 ping_have_color=MagickTrue;
8823 ping_have_non_bw=MagickTrue;
8826 s+=GetPixelChannels(image);
8829 if (ping_have_color != MagickFalse)
8832 /* Worst case is black-and-white; we are looking at every
8836 if (ping_have_non_bw == MagickFalse)
8839 for (x=0; x < (ssize_t) image->columns; x++)
8841 if (GetPixelRed(image,s) != 0 &&
8842 GetPixelRed(image,s) != QuantumRange)
8844 ping_have_non_bw=MagickTrue;
8847 s+=GetPixelChannels(image);
8854 if (image_colors < 257)
8860 * Initialize image colormap.
8863 if (logging != MagickFalse)
8864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865 " Sort the new colormap");
8867 /* Sort palette, transparent first */;
8871 for (i=0; i<number_transparent; i++)
8872 colormap[n++] = transparent[i];
8874 for (i=0; i<number_semitransparent; i++)
8875 colormap[n++] = semitransparent[i];
8877 for (i=0; i<number_opaque; i++)
8878 colormap[n++] = opaque[i];
8880 ping_background.index +=
8881 (number_transparent + number_semitransparent);
8883 /* image_colors < 257; search the colormap instead of the pixels
8884 * to get ping_have_color and ping_have_non_bw
8888 if (ping_have_color == MagickFalse)
8890 if (colormap[i].red != colormap[i].green ||
8891 colormap[i].red != colormap[i].blue)
8893 ping_have_color=MagickTrue;
8894 ping_have_non_bw=MagickTrue;
8899 if (ping_have_non_bw == MagickFalse)
8901 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8902 ping_have_non_bw=MagickTrue;
8906 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8907 (number_transparent == 0 && number_semitransparent == 0)) &&
8908 (((mng_info->write_png_colortype-1) ==
8909 PNG_COLOR_TYPE_PALETTE) ||
8910 (mng_info->write_png_colortype == 0)))
8912 if (logging != MagickFalse)
8914 if (n != (ssize_t) image_colors)
8915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8916 " image_colors (%d) and n (%d) don't match",
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " AcquireImageColormap");
8923 image->colors = image_colors;
8925 if (AcquireImageColormap(image,image_colors,exception) ==
8927 ThrowWriterException(ResourceLimitError,
8928 "MemoryAllocationFailed");
8930 for (i=0; i< (ssize_t) image_colors; i++)
8931 image->colormap[i] = colormap[i];
8933 if (logging != MagickFalse)
8935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8936 " image->colors=%d (%d)",
8937 (int) image->colors, image_colors);
8939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8940 " Update the pixel indexes");
8943 /* Sync the pixel indices with the new colormap */
8945 for (y=0; y < (ssize_t) image->rows; y++)
8947 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8949 if (q == (Quantum *) NULL)
8952 for (x=0; x < (ssize_t) image->columns; x++)
8954 for (i=0; i< (ssize_t) image_colors; i++)
8956 if ((image->alpha_trait != BlendPixelTrait ||
8957 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8958 image->colormap[i].red == GetPixelRed(image,q) &&
8959 image->colormap[i].green == GetPixelGreen(image,q) &&
8960 image->colormap[i].blue == GetPixelBlue(image,q))
8962 SetPixelIndex(image,i,q);
8966 q+=GetPixelChannels(image);
8969 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8975 if (logging != MagickFalse)
8977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8978 " image->colors=%d", (int) image->colors);
8980 if (image->colormap != NULL)
8982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8983 " i (red,green,blue,alpha)");
8985 for (i=0; i < (ssize_t) image->colors; i++)
8987 if (i < 300 || i >= (ssize_t) image->colors - 10)
8989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8990 " %d (%d,%d,%d,%d)",
8992 (int) image->colormap[i].red,
8993 (int) image->colormap[i].green,
8994 (int) image->colormap[i].blue,
8995 (int) image->colormap[i].alpha);
9000 if (number_transparent < 257)
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " number_transparent = %d",
9003 number_transparent);
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " number_transparent > 256");
9009 if (number_opaque < 257)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " number_opaque = %d",
9015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 " number_opaque > 256");
9018 if (number_semitransparent < 257)
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " number_semitransparent = %d",
9021 number_semitransparent);
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " number_semitransparent > 256");
9027 if (ping_have_non_bw == MagickFalse)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " All pixels and the background are black or white");
9031 else if (ping_have_color == MagickFalse)
9032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9033 " All pixels and the background are gray");
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " At least one pixel or the background is non-gray");
9039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9040 " Exit BUILD_PALETTE:");
9043 if (mng_info->write_png8 == MagickFalse)
9046 /* Make any reductions necessary for the PNG8 format */
9047 if (image_colors <= 256 &&
9048 image_colors != 0 && image->colormap != NULL &&
9049 number_semitransparent == 0 &&
9050 number_transparent <= 1)
9053 /* PNG8 can't have semitransparent colors so we threshold the
9054 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9055 * transparent color so if more than one is transparent we merge
9056 * them into image->background_color.
9058 if (number_semitransparent != 0 || number_transparent > 1)
9060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9061 " Thresholding the alpha channel to binary");
9063 for (y=0; y < (ssize_t) image->rows; y++)
9065 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9067 if (r == (Quantum *) NULL)
9070 for (x=0; x < (ssize_t) image->columns; x++)
9072 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9074 SetPixelInfoPixel(image,&image->background_color,r);
9075 SetPixelAlpha(image,TransparentAlpha,r);
9078 SetPixelAlpha(image,OpaqueAlpha,r);
9079 r+=GetPixelChannels(image);
9082 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9085 if (image_colors != 0 && image_colors <= 256 &&
9086 image->colormap != NULL)
9087 for (i=0; i<image_colors; i++)
9088 image->colormap[i].alpha =
9089 (image->colormap[i].alpha > TransparentAlpha/2 ?
9090 TransparentAlpha : OpaqueAlpha);
9095 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9096 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9097 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9100 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9102 if (logging != MagickFalse)
9103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9104 " Quantizing the background color to 4-4-4");
9106 tried_444 = MagickTrue;
9108 LBR04PacketRGB(image->background_color);
9110 if (logging != MagickFalse)
9111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112 " Quantizing the pixel colors to 4-4-4");
9114 if (image->colormap == NULL)
9116 for (y=0; y < (ssize_t) image->rows; y++)
9118 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9120 if (r == (Quantum *) NULL)
9123 for (x=0; x < (ssize_t) image->columns; x++)
9125 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9127 r+=GetPixelChannels(image);
9130 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9135 else /* Should not reach this; colormap already exists and
9138 if (logging != MagickFalse)
9139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9140 " Quantizing the colormap to 4-4-4");
9142 for (i=0; i<image_colors; i++)
9144 LBR04PacketRGB(image->colormap[i]);
9150 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9152 if (logging != MagickFalse)
9153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9154 " Quantizing the background color to 3-3-3");
9156 tried_333 = MagickTrue;
9158 LBR03PacketRGB(image->background_color);
9160 if (logging != MagickFalse)
9161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9162 " Quantizing the pixel colors to 3-3-3-1");
9164 if (image->colormap == NULL)
9166 for (y=0; y < (ssize_t) image->rows; y++)
9168 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9170 if (r == (Quantum *) NULL)
9173 for (x=0; x < (ssize_t) image->columns; x++)
9175 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9177 r+=GetPixelChannels(image);
9180 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9185 else /* Should not reach this; colormap already exists and
9188 if (logging != MagickFalse)
9189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9190 " Quantizing the colormap to 3-3-3-1");
9191 for (i=0; i<image_colors; i++)
9193 LBR03PacketRGB(image->colormap[i]);
9199 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9201 if (logging != MagickFalse)
9202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9203 " Quantizing the background color to 3-3-2");
9205 tried_332 = MagickTrue;
9207 /* Red and green were already done so we only quantize the blue
9211 LBR02PacketBlue(image->background_color);
9213 if (logging != MagickFalse)
9214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9215 " Quantizing the pixel colors to 3-3-2-1");
9217 if (image->colormap == NULL)
9219 for (y=0; y < (ssize_t) image->rows; y++)
9221 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9223 if (r == (Quantum *) NULL)
9226 for (x=0; x < (ssize_t) image->columns; x++)
9228 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9230 r+=GetPixelChannels(image);
9233 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9238 else /* Should not reach this; colormap already exists and
9241 if (logging != MagickFalse)
9242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9243 " Quantizing the colormap to 3-3-2-1");
9244 for (i=0; i<image_colors; i++)
9246 LBR02PacketBlue(image->colormap[i]);
9253 if (image_colors == 0 || image_colors > 256)
9255 /* Take care of special case with 256 colors + 1 transparent
9256 * color. We don't need to quantize to 2-3-2-1; we only need to
9257 * eliminate one color, so we'll merge the two darkest red
9258 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9260 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9261 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9262 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9264 image->background_color.red=ScaleCharToQuantum(0x24);
9267 if (image->colormap == NULL)
9269 for (y=0; y < (ssize_t) image->rows; y++)
9271 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9273 if (r == (Quantum *) NULL)
9276 for (x=0; x < (ssize_t) image->columns; x++)
9278 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9279 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9280 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9281 GetPixelAlpha(image,r) == OpaqueAlpha)
9283 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9285 r+=GetPixelChannels(image);
9288 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9296 for (i=0; i<image_colors; i++)
9298 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9299 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9300 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9302 image->colormap[i].red=ScaleCharToQuantum(0x24);
9309 /* END OF BUILD_PALETTE */
9311 /* If we are excluding the tRNS chunk and there is transparency,
9312 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9315 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9316 (number_transparent != 0 || number_semitransparent != 0))
9318 unsigned int colortype=mng_info->write_png_colortype;
9320 if (ping_have_color == MagickFalse)
9321 mng_info->write_png_colortype = 5;
9324 mng_info->write_png_colortype = 7;
9326 if (colortype != 0 &&
9327 mng_info->write_png_colortype != colortype)
9328 ping_need_colortype_warning=MagickTrue;
9332 /* See if cheap transparency is possible. It is only possible
9333 * when there is a single transparent color, no semitransparent
9334 * color, and no opaque color that has the same RGB components
9335 * as the transparent color. We only need this information if
9336 * we are writing a PNG with colortype 0 or 2, and we have not
9337 * excluded the tRNS chunk.
9339 if (number_transparent == 1 &&
9340 mng_info->write_png_colortype < 4)
9342 ping_have_cheap_transparency = MagickTrue;
9344 if (number_semitransparent != 0)
9345 ping_have_cheap_transparency = MagickFalse;
9347 else if (image_colors == 0 || image_colors > 256 ||
9348 image->colormap == NULL)
9350 register const Quantum
9353 for (y=0; y < (ssize_t) image->rows; y++)
9355 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9357 if (q == (Quantum *) NULL)
9360 for (x=0; x < (ssize_t) image->columns; x++)
9362 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9363 (unsigned short) GetPixelRed(image,q) ==
9364 ping_trans_color.red &&
9365 (unsigned short) GetPixelGreen(image,q) ==
9366 ping_trans_color.green &&
9367 (unsigned short) GetPixelBlue(image,q) ==
9368 ping_trans_color.blue)
9370 ping_have_cheap_transparency = MagickFalse;
9374 q+=GetPixelChannels(image);
9377 if (ping_have_cheap_transparency == MagickFalse)
9383 /* Assuming that image->colormap[0] is the one transparent color
9384 * and that all others are opaque.
9386 if (image_colors > 1)
9387 for (i=1; i<image_colors; i++)
9388 if (image->colormap[i].red == image->colormap[0].red &&
9389 image->colormap[i].green == image->colormap[0].green &&
9390 image->colormap[i].blue == image->colormap[0].blue)
9392 ping_have_cheap_transparency = MagickFalse;
9397 if (logging != MagickFalse)
9399 if (ping_have_cheap_transparency == MagickFalse)
9400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9401 " Cheap transparency is not possible.");
9404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9405 " Cheap transparency is possible.");
9409 ping_have_cheap_transparency = MagickFalse;
9411 image_depth=image->depth;
9413 quantum_info = (QuantumInfo *) NULL;
9415 image_colors=(int) image->colors;
9416 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9418 mng_info->IsPalette=image->storage_class == PseudoClass &&
9419 image_colors <= 256 && image->colormap != NULL;
9421 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9422 (image->colors == 0 || image->colormap == NULL))
9424 image_info=DestroyImageInfo(image_info);
9425 image=DestroyImage(image);
9426 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9427 "Cannot write PNG8 or color-type 3; colormap is NULL",
9428 "`%s'",IMimage->filename);
9429 return(MagickFalse);
9433 Allocate the PNG structures
9435 #ifdef PNG_USER_MEM_SUPPORTED
9436 error_info.image=image;
9437 error_info.exception=exception;
9438 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9439 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9440 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9443 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9444 MagickPNGErrorHandler,MagickPNGWarningHandler);
9447 if (ping == (png_struct *) NULL)
9448 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9450 ping_info=png_create_info_struct(ping);
9452 if (ping_info == (png_info *) NULL)
9454 png_destroy_write_struct(&ping,(png_info **) NULL);
9455 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9458 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9459 pixel_info=(MemoryInfo *) NULL;
9461 if (setjmp(png_jmpbuf(ping)))
9467 if (image_info->verbose)
9468 (void) printf("PNG write has failed.\n");
9470 png_destroy_write_struct(&ping,&ping_info);
9471 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9472 UnlockSemaphoreInfo(ping_semaphore);
9475 if (pixel_info != (MemoryInfo *) NULL)
9476 pixel_info=RelinquishVirtualMemory(pixel_info);
9478 if (quantum_info != (QuantumInfo *) NULL)
9479 quantum_info=DestroyQuantumInfo(quantum_info);
9481 if (ping_have_blob != MagickFalse)
9482 (void) CloseBlob(image);
9483 image_info=DestroyImageInfo(image_info);
9484 image=DestroyImage(image);
9485 return(MagickFalse);
9488 /* { For navigation to end of SETJMP-protected block. Within this
9489 * block, use png_error() instead of Throwing an Exception, to ensure
9490 * that libpng is able to clean up, and that the semaphore is unlocked.
9493 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9494 LockSemaphoreInfo(ping_semaphore);
9497 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9498 /* Allow benign errors */
9499 png_set_benign_errors(ping, 1);
9503 Prepare PNG for writing.
9506 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9507 if (mng_info->write_mng)
9509 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9510 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9511 /* Disable new libpng-1.5.10 feature when writing a MNG because
9512 * zero-length PLTE is OK
9514 png_set_check_for_invalid_index (ping, 0);
9519 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9520 if (mng_info->write_mng)
9521 png_permit_empty_plte(ping,MagickTrue);
9528 ping_width=(png_uint_32) image->columns;
9529 ping_height=(png_uint_32) image->rows;
9531 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9534 if (mng_info->write_png48 || mng_info->write_png64)
9537 if (mng_info->write_png_depth != 0)
9538 image_depth=mng_info->write_png_depth;
9540 /* Adjust requested depth to next higher valid depth if necessary */
9541 if (image_depth > 8)
9544 if ((image_depth > 4) && (image_depth < 8))
9547 if (image_depth == 3)
9550 if (logging != MagickFalse)
9552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9553 " width=%.20g",(double) ping_width);
9554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9555 " height=%.20g",(double) ping_height);
9556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9557 " image_matte=%.20g",(double) image->alpha_trait);
9558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9559 " image->depth=%.20g",(double) image->depth);
9560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9561 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9564 save_image_depth=image_depth;
9565 ping_bit_depth=(png_byte) save_image_depth;
9568 #if defined(PNG_pHYs_SUPPORTED)
9569 if (ping_exclude_pHYs == MagickFalse)
9571 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9572 (!mng_info->write_mng || !mng_info->equal_physs))
9574 if (logging != MagickFalse)
9575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9576 " Setting up pHYs chunk");
9578 if (image->units == PixelsPerInchResolution)
9580 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9581 ping_pHYs_x_resolution=
9582 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9583 ping_pHYs_y_resolution=
9584 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9587 else if (image->units == PixelsPerCentimeterResolution)
9589 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9590 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9591 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9596 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9597 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9598 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9601 if (logging != MagickFalse)
9602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9604 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9605 (int) ping_pHYs_unit_type);
9606 ping_have_pHYs = MagickTrue;
9611 if (ping_exclude_bKGD == MagickFalse)
9613 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9619 if (ping_bit_depth == 8)
9622 if (ping_bit_depth == 4)
9625 if (ping_bit_depth == 2)
9628 if (ping_bit_depth == 1)
9631 ping_background.red=(png_uint_16)
9632 (ScaleQuantumToShort(image->background_color.red) & mask);
9634 ping_background.green=(png_uint_16)
9635 (ScaleQuantumToShort(image->background_color.green) & mask);
9637 ping_background.blue=(png_uint_16)
9638 (ScaleQuantumToShort(image->background_color.blue) & mask);
9640 ping_background.gray=(png_uint_16) ping_background.green;
9643 if (logging != MagickFalse)
9645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9646 " Setting up bKGD chunk (1)");
9647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9648 " background_color index is %d",
9649 (int) ping_background.index);
9651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9652 " ping_bit_depth=%d",ping_bit_depth);
9655 ping_have_bKGD = MagickTrue;
9659 Select the color type.
9664 if (mng_info->IsPalette && mng_info->write_png8)
9667 /* To do: make this a function cause it's used twice, except
9668 for reducing the sample depth from 8. */
9670 number_colors=image_colors;
9672 ping_have_tRNS=MagickFalse;
9677 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9679 if (logging != MagickFalse)
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " Setting up PLTE chunk with %d colors (%d)",
9682 number_colors, image_colors);
9684 for (i=0; i < (ssize_t) number_colors; i++)
9686 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9687 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9688 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9689 if (logging != MagickFalse)
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 #if MAGICKCORE_QUANTUM_DEPTH == 8
9692 " %3ld (%3d,%3d,%3d)",
9694 " %5ld (%5d,%5d,%5d)",
9696 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9700 ping_have_PLTE=MagickTrue;
9701 image_depth=ping_bit_depth;
9704 if (matte != MagickFalse)
9707 Identify which colormap entry is transparent.
9709 assert(number_colors <= 256);
9710 assert(image->colormap != NULL);
9712 for (i=0; i < (ssize_t) number_transparent; i++)
9713 ping_trans_alpha[i]=0;
9716 ping_num_trans=(unsigned short) (number_transparent +
9717 number_semitransparent);
9719 if (ping_num_trans == 0)
9720 ping_have_tRNS=MagickFalse;
9723 ping_have_tRNS=MagickTrue;
9726 if (ping_exclude_bKGD == MagickFalse)
9729 * Identify which colormap entry is the background color.
9732 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9733 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9736 ping_background.index=(png_byte) i;
9738 if (logging != MagickFalse)
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " background_color index is %d",
9742 (int) ping_background.index);
9745 } /* end of write_png8 */
9747 else if (mng_info->write_png_colortype == 1)
9749 image_matte=MagickFalse;
9750 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9753 else if (mng_info->write_png24 || mng_info->write_png48 ||
9754 mng_info->write_png_colortype == 3)
9756 image_matte=MagickFalse;
9757 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9760 else if (mng_info->write_png32 || mng_info->write_png64 ||
9761 mng_info->write_png_colortype == 7)
9763 image_matte=MagickTrue;
9764 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9767 else /* mng_info->write_pngNN not specified */
9769 image_depth=ping_bit_depth;
9771 if (mng_info->write_png_colortype != 0)
9773 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9775 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9776 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9777 image_matte=MagickTrue;
9780 image_matte=MagickFalse;
9782 if (logging != MagickFalse)
9783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9784 " PNG colortype %d was specified:",(int) ping_color_type);
9787 else /* write_png_colortype not specified */
9789 if (logging != MagickFalse)
9790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9791 " Selecting PNG colortype:");
9793 ping_color_type=(png_byte) ((matte != MagickFalse)?
9794 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9796 if (image_info->type == TrueColorType)
9798 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9799 image_matte=MagickFalse;
9802 if (image_info->type == TrueColorMatteType)
9804 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9805 image_matte=MagickTrue;
9808 if (image_info->type == PaletteType ||
9809 image_info->type == PaletteMatteType)
9810 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9812 if (mng_info->write_png_colortype == 0 &&
9813 (image_info->type == UndefinedType ||
9814 image_info->type == OptimizeType))
9816 if (ping_have_color == MagickFalse)
9818 if (image_matte == MagickFalse)
9820 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9821 image_matte=MagickFalse;
9826 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9827 image_matte=MagickTrue;
9832 if (image_matte == MagickFalse)
9834 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9835 image_matte=MagickFalse;
9840 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9841 image_matte=MagickTrue;
9848 if (logging != MagickFalse)
9849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9850 " Selected PNG colortype=%d",ping_color_type);
9852 if (ping_bit_depth < 8)
9854 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9855 ping_color_type == PNG_COLOR_TYPE_RGB ||
9856 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9860 old_bit_depth=ping_bit_depth;
9862 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9864 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9868 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9873 if (image->colors == 0)
9876 png_error(ping,"image has 0 colors");
9879 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9880 ping_bit_depth <<= 1;
9883 if (logging != MagickFalse)
9885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9886 " Number of colors: %.20g",(double) image_colors);
9888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9889 " Tentative PNG bit depth: %d",ping_bit_depth);
9892 if (ping_bit_depth < (int) mng_info->write_png_depth)
9893 ping_bit_depth = mng_info->write_png_depth;
9896 image_depth=ping_bit_depth;
9898 if (logging != MagickFalse)
9900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9901 " Tentative PNG color type: %s (%.20g)",
9902 PngColorTypeToString(ping_color_type),
9903 (double) ping_color_type);
9905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9906 " image_info->type: %.20g",(double) image_info->type);
9908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9909 " image_depth: %.20g",(double) image_depth);
9911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9913 " image->depth: %.20g",(double) image->depth);
9915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9919 if (matte != MagickFalse)
9921 if (mng_info->IsPalette)
9923 if (mng_info->write_png_colortype == 0)
9925 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9927 if (ping_have_color != MagickFalse)
9928 ping_color_type=PNG_COLOR_TYPE_RGBA;
9932 * Determine if there is any transparent color.
9934 if (number_transparent + number_semitransparent == 0)
9937 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9940 image_matte=MagickFalse;
9942 if (mng_info->write_png_colortype == 0)
9943 ping_color_type&=0x03;
9953 if (ping_bit_depth == 8)
9956 if (ping_bit_depth == 4)
9959 if (ping_bit_depth == 2)
9962 if (ping_bit_depth == 1)
9965 ping_trans_color.red=(png_uint_16)
9966 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9968 ping_trans_color.green=(png_uint_16)
9969 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9971 ping_trans_color.blue=(png_uint_16)
9972 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9974 ping_trans_color.gray=(png_uint_16)
9975 (ScaleQuantumToShort(GetPixelInfoIntensity(
9976 image->colormap)) & mask);
9978 ping_trans_color.index=(png_byte) 0;
9980 ping_have_tRNS=MagickTrue;
9983 if (ping_have_tRNS != MagickFalse)
9986 * Determine if there is one and only one transparent color
9987 * and if so if it is fully transparent.
9989 if (ping_have_cheap_transparency == MagickFalse)
9990 ping_have_tRNS=MagickFalse;
9993 if (ping_have_tRNS != MagickFalse)
9995 if (mng_info->write_png_colortype == 0)
9996 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9998 if (image_depth == 8)
10000 ping_trans_color.red&=0xff;
10001 ping_trans_color.green&=0xff;
10002 ping_trans_color.blue&=0xff;
10003 ping_trans_color.gray&=0xff;
10009 if (image_depth == 8)
10011 ping_trans_color.red&=0xff;
10012 ping_trans_color.green&=0xff;
10013 ping_trans_color.blue&=0xff;
10014 ping_trans_color.gray&=0xff;
10021 if (ping_have_tRNS != MagickFalse)
10022 image_matte=MagickFalse;
10024 if ((mng_info->IsPalette) &&
10025 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10026 ping_have_color == MagickFalse &&
10027 (image_matte == MagickFalse || image_depth >= 8))
10031 if (image_matte != MagickFalse)
10032 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10034 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10036 ping_color_type=PNG_COLOR_TYPE_GRAY;
10038 if (save_image_depth == 16 && image_depth == 8)
10040 if (logging != MagickFalse)
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " Scaling ping_trans_color (0)");
10045 ping_trans_color.gray*=0x0101;
10049 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10050 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10052 if ((image_colors == 0) ||
10053 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10054 image_colors=(int) (one << image_depth);
10056 if (image_depth > 8)
10062 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10064 if(!mng_info->write_png_depth)
10068 while ((int) (one << ping_bit_depth)
10069 < (ssize_t) image_colors)
10070 ping_bit_depth <<= 1;
10074 else if (ping_color_type ==
10075 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10076 mng_info->IsPalette)
10078 /* Check if grayscale is reducible */
10081 depth_4_ok=MagickTrue,
10082 depth_2_ok=MagickTrue,
10083 depth_1_ok=MagickTrue;
10085 for (i=0; i < (ssize_t) image_colors; i++)
10090 intensity=ScaleQuantumToChar(image->colormap[i].red);
10092 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10093 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10094 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10095 depth_2_ok=depth_1_ok=MagickFalse;
10096 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10097 depth_1_ok=MagickFalse;
10100 if (depth_1_ok && mng_info->write_png_depth <= 1)
10103 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10106 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10111 image_depth=ping_bit_depth;
10116 if (mng_info->IsPalette)
10118 number_colors=image_colors;
10120 if (image_depth <= 8)
10125 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10127 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10129 for (i=0; i < (ssize_t) number_colors; i++)
10131 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10132 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10133 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10136 if (logging != MagickFalse)
10137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10138 " Setting up PLTE chunk with %d colors",
10141 ping_have_PLTE=MagickTrue;
10144 /* color_type is PNG_COLOR_TYPE_PALETTE */
10145 if (mng_info->write_png_depth == 0)
10153 while ((one << ping_bit_depth) < (size_t) number_colors)
10154 ping_bit_depth <<= 1;
10159 if (matte != MagickFalse)
10162 * Set up trans_colors array.
10164 assert(number_colors <= 256);
10166 ping_num_trans=(unsigned short) (number_transparent +
10167 number_semitransparent);
10169 if (ping_num_trans == 0)
10170 ping_have_tRNS=MagickFalse;
10174 if (logging != MagickFalse)
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10177 " Scaling ping_trans_color (1)");
10179 ping_have_tRNS=MagickTrue;
10181 for (i=0; i < ping_num_trans; i++)
10183 ping_trans_alpha[i]= (png_byte)
10184 ScaleQuantumToChar(image->colormap[i].alpha);
10194 if (image_depth < 8)
10197 if ((save_image_depth == 16) && (image_depth == 8))
10199 if (logging != MagickFalse)
10201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10202 " Scaling ping_trans_color from (%d,%d,%d)",
10203 (int) ping_trans_color.red,
10204 (int) ping_trans_color.green,
10205 (int) ping_trans_color.blue);
10208 ping_trans_color.red*=0x0101;
10209 ping_trans_color.green*=0x0101;
10210 ping_trans_color.blue*=0x0101;
10211 ping_trans_color.gray*=0x0101;
10213 if (logging != MagickFalse)
10215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10217 (int) ping_trans_color.red,
10218 (int) ping_trans_color.green,
10219 (int) ping_trans_color.blue);
10224 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10225 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10228 Adjust background and transparency samples in sub-8-bit grayscale files.
10230 if (ping_bit_depth < 8 && ping_color_type ==
10231 PNG_COLOR_TYPE_GRAY)
10239 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10241 if (ping_exclude_bKGD == MagickFalse)
10244 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10245 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10246 &image->background_color))) +.5)));
10248 if (logging != MagickFalse)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Setting up bKGD chunk (2)");
10251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10252 " background_color index is %d",
10253 (int) ping_background.index);
10255 ping_have_bKGD = MagickTrue;
10258 if (logging != MagickFalse)
10259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10260 " Scaling ping_trans_color.gray from %d",
10261 (int)ping_trans_color.gray);
10263 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10264 ping_trans_color.gray)+.5);
10266 if (logging != MagickFalse)
10267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10268 " to %d", (int)ping_trans_color.gray);
10271 if (ping_exclude_bKGD == MagickFalse)
10273 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10276 Identify which colormap entry is the background color.
10279 number_colors=image_colors;
10281 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10282 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10285 ping_background.index=(png_byte) i;
10287 if (logging != MagickFalse)
10289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10290 " Setting up bKGD chunk with index=%d",(int) i);
10293 if (i < (ssize_t) number_colors)
10295 ping_have_bKGD = MagickTrue;
10297 if (logging != MagickFalse)
10299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10300 " background =(%d,%d,%d)",
10301 (int) ping_background.red,
10302 (int) ping_background.green,
10303 (int) ping_background.blue);
10307 else /* Can't happen */
10309 if (logging != MagickFalse)
10310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311 " No room in PLTE to add bKGD color");
10312 ping_have_bKGD = MagickFalse;
10317 if (logging != MagickFalse)
10318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10322 Initialize compression level and filtering.
10324 if (logging != MagickFalse)
10326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10327 " Setting up deflate compression");
10329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330 " Compression buffer size: 32768");
10333 png_set_compression_buffer_size(ping,32768L);
10335 if (logging != MagickFalse)
10336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10337 " Compression mem level: 9");
10339 png_set_compression_mem_level(ping, 9);
10341 /* Untangle the "-quality" setting:
10343 Undefined is 0; the default is used.
10348 0: Use Z_HUFFMAN_ONLY strategy with the
10349 zlib default compression level
10351 1-9: the zlib compression level
10355 0-4: the PNG filter method
10357 5: libpng adaptive filtering if compression level > 5
10358 libpng filter type "none" if compression level <= 5
10359 or if image is grayscale or palette
10361 6: libpng adaptive filtering
10363 7: "LOCO" filtering (intrapixel differing) if writing
10364 a MNG, othewise "none". Did not work in IM-6.7.0-9
10365 and earlier because of a missing "else".
10367 8: Z_RLE strategy, all filters
10368 Unused prior to IM-6.7.0-10, was same as 6
10370 9: Z_RLE strategy, no PNG filters
10371 Unused prior to IM-6.7.0-10, was same as 6
10373 Note that using the -quality option, not all combinations of
10374 PNG filter type, zlib compression level, and zlib compression
10375 strategy are possible. This will be addressed soon in a
10376 release that accomodates "-define png:compression-strategy", etc.
10380 quality=image->quality == UndefinedCompressionQuality ? 75UL :
10385 if (mng_info->write_png_compression_strategy == 0)
10386 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10389 else if (mng_info->write_png_compression_level == 0)
10394 level=(int) MagickMin((ssize_t) quality/10,9);
10396 mng_info->write_png_compression_level = level+1;
10399 if (mng_info->write_png_compression_strategy == 0)
10401 if ((quality %10) == 8 || (quality %10) == 9)
10402 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10403 mng_info->write_png_compression_strategy=Z_RLE+1;
10405 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10409 if (mng_info->write_png_compression_filter == 0)
10410 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10412 if (logging != MagickFalse)
10414 if (mng_info->write_png_compression_level)
10415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416 " Compression level: %d",
10417 (int) mng_info->write_png_compression_level-1);
10419 if (mng_info->write_png_compression_strategy)
10420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10421 " Compression strategy: %d",
10422 (int) mng_info->write_png_compression_strategy-1);
10424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10425 " Setting up filtering");
10427 if (mng_info->write_png_compression_filter == 6)
10428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10429 " Base filter method: ADAPTIVE");
10430 else if (mng_info->write_png_compression_filter == 0 ||
10431 mng_info->write_png_compression_filter == 1)
10432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10433 " Base filter method: NONE");
10435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 " Base filter method: %d",
10437 (int) mng_info->write_png_compression_filter-1);
10440 if (mng_info->write_png_compression_level != 0)
10441 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10443 if (mng_info->write_png_compression_filter == 6)
10445 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10448 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10450 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10452 else if (mng_info->write_png_compression_filter == 7 ||
10453 mng_info->write_png_compression_filter == 10)
10454 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10456 else if (mng_info->write_png_compression_filter == 8)
10458 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10459 if (mng_info->write_mng)
10461 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10462 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10463 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10466 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10469 else if (mng_info->write_png_compression_filter == 9)
10470 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10472 else if (mng_info->write_png_compression_filter != 0)
10473 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10474 mng_info->write_png_compression_filter-1);
10476 if (mng_info->write_png_compression_strategy != 0)
10477 png_set_compression_strategy(ping,
10478 mng_info->write_png_compression_strategy-1);
10480 ping_interlace_method=image_info->interlace != NoInterlace;
10482 if (mng_info->write_mng)
10483 png_set_sig_bytes(ping,8);
10485 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10487 if (mng_info->write_png_colortype != 0)
10489 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10490 if (ping_have_color != MagickFalse)
10492 ping_color_type = PNG_COLOR_TYPE_RGB;
10494 if (ping_bit_depth < 8)
10498 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10499 if (ping_have_color != MagickFalse)
10500 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10503 if (ping_need_colortype_warning != MagickFalse ||
10504 ((mng_info->write_png_depth &&
10505 (int) mng_info->write_png_depth != ping_bit_depth) ||
10506 (mng_info->write_png_colortype &&
10507 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10508 mng_info->write_png_colortype != 7 &&
10509 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10511 if (logging != MagickFalse)
10513 if (ping_need_colortype_warning != MagickFalse)
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " Image has transparency but tRNS chunk was excluded");
10519 if (mng_info->write_png_depth)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Defined png:bit-depth=%u, Computed depth=%u",
10523 mng_info->write_png_depth,
10527 if (mng_info->write_png_colortype)
10529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10530 " Defined png:color-type=%u, Computed color type=%u",
10531 mng_info->write_png_colortype-1,
10537 "Cannot write image with defined png:bit-depth or png:color-type.");
10540 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10542 /* Add an opaque matte channel */
10543 image->alpha_trait = BlendPixelTrait;
10544 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10546 if (logging != MagickFalse)
10547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10548 " Added an opaque matte channel");
10551 if (number_transparent != 0 || number_semitransparent != 0)
10553 if (ping_color_type < 4)
10555 ping_have_tRNS=MagickTrue;
10556 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " Setting ping_have_tRNS=MagickTrue.");
10562 if (logging != MagickFalse)
10563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564 " Writing PNG header chunks");
10566 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10567 ping_bit_depth,ping_color_type,
10568 ping_interlace_method,ping_compression_method,
10569 ping_filter_method);
10571 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10573 png_set_PLTE(ping,ping_info,palette,number_colors);
10575 if (logging != MagickFalse)
10577 for (i=0; i< (ssize_t) number_colors; i++)
10579 if (i < ping_num_trans)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10583 (int) palette[i].red,
10584 (int) palette[i].green,
10585 (int) palette[i].blue,
10587 (int) ping_trans_alpha[i]);
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " PLTE[%d] = (%d,%d,%d)",
10592 (int) palette[i].red,
10593 (int) palette[i].green,
10594 (int) palette[i].blue);
10599 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10600 if (ping_exclude_sRGB != MagickFalse ||
10601 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10603 if ((ping_exclude_tEXt == MagickFalse ||
10604 ping_exclude_zTXt == MagickFalse) &&
10605 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10607 ResetImageProfileIterator(image);
10608 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10610 profile=GetImageProfile(image,name);
10612 if (profile != (StringInfo *) NULL)
10614 #ifdef PNG_WRITE_iCCP_SUPPORTED
10615 if ((LocaleCompare(name,"ICC") == 0) ||
10616 (LocaleCompare(name,"ICM") == 0))
10619 if (ping_exclude_iCCP == MagickFalse)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Setting up iCCP chunk");
10624 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10625 #if (PNG_LIBPNG_VER < 10500)
10626 (png_charp) GetStringInfoDatum(profile),
10628 (png_const_bytep) GetStringInfoDatum(profile),
10630 (png_uint_32) GetStringInfoLength(profile));
10631 ping_have_iCCP = MagickTrue;
10637 if (ping_exclude_zCCP == MagickFalse)
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " Setting up zTXT chunk with uuencoded ICC");
10641 Magick_png_write_raw_profile(image_info,ping,ping_info,
10642 (unsigned char *) name,(unsigned char *) name,
10643 GetStringInfoDatum(profile),
10644 (png_uint_32) GetStringInfoLength(profile));
10645 ping_have_iCCP = MagickTrue;
10649 if (logging != MagickFalse)
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " Setting up text chunk with %s profile",name);
10653 name=GetNextImageProfile(image);
10658 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10659 if ((mng_info->have_write_global_srgb == 0) &&
10660 ping_have_iCCP != MagickTrue &&
10661 (ping_have_sRGB != MagickFalse ||
10662 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10664 if (ping_exclude_sRGB == MagickFalse)
10667 Note image rendering intent.
10669 if (logging != MagickFalse)
10670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10671 " Setting up sRGB chunk");
10673 (void) png_set_sRGB(ping,ping_info,(
10674 Magick_RenderingIntent_to_PNG_RenderingIntent(
10675 image->rendering_intent)));
10677 ping_have_sRGB = MagickTrue;
10681 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10684 if (ping_exclude_gAMA == MagickFalse &&
10685 ping_have_iCCP == MagickFalse &&
10686 ping_have_sRGB == MagickFalse &&
10687 (ping_exclude_sRGB == MagickFalse ||
10688 (image->gamma < .45 || image->gamma > .46)))
10690 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10694 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10696 if (logging != MagickFalse)
10697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698 " Setting up gAMA chunk");
10700 png_set_gAMA(ping,ping_info,image->gamma);
10704 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10706 if ((mng_info->have_write_global_chrm == 0) &&
10707 (image->chromaticity.red_primary.x != 0.0))
10710 Note image chromaticity.
10711 Note: if cHRM+gAMA == sRGB write sRGB instead.
10719 wp=image->chromaticity.white_point;
10720 rp=image->chromaticity.red_primary;
10721 gp=image->chromaticity.green_primary;
10722 bp=image->chromaticity.blue_primary;
10724 if (logging != MagickFalse)
10725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10726 " Setting up cHRM chunk");
10728 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10734 if (ping_exclude_bKGD == MagickFalse)
10736 if (ping_have_bKGD != MagickFalse)
10738 png_set_bKGD(ping,ping_info,&ping_background);
10741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10742 " Setting up bKGD chunk");
10743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744 " background color = (%d,%d,%d)",
10745 (int) ping_background.red,
10746 (int) ping_background.green,
10747 (int) ping_background.blue);
10748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10749 " index = %d, gray=%d",
10750 (int) ping_background.index,
10751 (int) ping_background.gray);
10756 if (ping_exclude_pHYs == MagickFalse)
10758 if (ping_have_pHYs != MagickFalse)
10760 png_set_pHYs(ping,ping_info,
10761 ping_pHYs_x_resolution,
10762 ping_pHYs_y_resolution,
10763 ping_pHYs_unit_type);
10767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10768 " Setting up pHYs chunk");
10769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770 " x_resolution=%lu",
10771 (unsigned long) ping_pHYs_x_resolution);
10772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10773 " y_resolution=%lu",
10774 (unsigned long) ping_pHYs_y_resolution);
10775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10777 (unsigned long) ping_pHYs_unit_type);
10782 #if defined(PNG_oFFs_SUPPORTED)
10783 if (ping_exclude_oFFs == MagickFalse)
10785 if (image->page.x || image->page.y)
10787 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10788 (png_int_32) image->page.y, 0);
10790 if (logging != MagickFalse)
10791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10793 (int) image->page.x, (int) image->page.y);
10798 if (mng_info->need_blob != MagickFalse)
10800 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10802 png_error(ping,"WriteBlob Failed");
10804 ping_have_blob=MagickTrue;
10807 png_write_info_before_PLTE(ping, ping_info);
10809 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10811 if (logging != MagickFalse)
10813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10814 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10817 if (ping_color_type == 3)
10818 (void) png_set_tRNS(ping, ping_info,
10825 (void) png_set_tRNS(ping, ping_info,
10828 &ping_trans_color);
10830 if (logging != MagickFalse)
10832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10833 " tRNS color =(%d,%d,%d)",
10834 (int) ping_trans_color.red,
10835 (int) ping_trans_color.green,
10836 (int) ping_trans_color.blue);
10841 /* write any png-chunk-b profiles */
10842 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10844 png_write_info(ping,ping_info);
10846 /* write any PNG-chunk-m profiles */
10847 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10849 if (ping_exclude_vpAg == MagickFalse)
10851 if ((image->page.width != 0 && image->page.width != image->columns) ||
10852 (image->page.height != 0 && image->page.height != image->rows))
10857 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10858 PNGType(chunk,mng_vpAg);
10859 LogPNGChunk(logging,mng_vpAg,9L);
10860 PNGLong(chunk+4,(png_uint_32) image->page.width);
10861 PNGLong(chunk+8,(png_uint_32) image->page.height);
10862 chunk[12]=0; /* unit = pixels */
10863 (void) WriteBlob(image,13,chunk);
10864 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10868 #if (PNG_LIBPNG_VER == 10206)
10869 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10870 #define PNG_HAVE_IDAT 0x04
10871 ping->mode |= PNG_HAVE_IDAT;
10872 #undef PNG_HAVE_IDAT
10875 png_set_packing(ping);
10879 rowbytes=image->columns;
10880 if (image_depth > 8)
10882 switch (ping_color_type)
10884 case PNG_COLOR_TYPE_RGB:
10888 case PNG_COLOR_TYPE_GRAY_ALPHA:
10892 case PNG_COLOR_TYPE_RGBA:
10900 if (logging != MagickFalse)
10902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10903 " Writing PNG image data");
10905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10906 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10908 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10909 if (pixel_info == (MemoryInfo *) NULL)
10910 png_error(ping,"Allocation of memory for pixels failed");
10911 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10914 Initialize image scanlines.
10916 quantum_info=AcquireQuantumInfo(image_info,image);
10917 if (quantum_info == (QuantumInfo *) NULL)
10918 png_error(ping,"Memory allocation for quantum_info failed");
10919 quantum_info->format=UndefinedQuantumFormat;
10920 quantum_info->depth=image_depth;
10921 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10922 num_passes=png_set_interlace_handling(ping);
10924 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10925 !mng_info->write_png48 && !mng_info->write_png64 &&
10926 !mng_info->write_png32) &&
10927 (mng_info->IsPalette ||
10928 (image_info->type == BilevelType)) &&
10929 image_matte == MagickFalse &&
10930 ping_have_non_bw == MagickFalse)
10932 /* Palette, Bilevel, or Opaque Monochrome */
10933 register const Quantum
10936 quantum_info->depth=8;
10937 for (pass=0; pass < num_passes; pass++)
10940 Convert PseudoClass image to a PNG monochrome image.
10942 for (y=0; y < (ssize_t) image->rows; y++)
10944 if (logging != MagickFalse && y == 0)
10945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10946 " Writing row of pixels (0)");
10948 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10950 if (p == (const Quantum *) NULL)
10953 if (mng_info->IsPalette)
10955 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10956 quantum_info,GrayQuantum,ping_pixels,exception);
10957 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10958 mng_info->write_png_depth &&
10959 mng_info->write_png_depth != old_bit_depth)
10961 /* Undo pixel scaling */
10962 for (i=0; i < (ssize_t) image->columns; i++)
10963 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10964 >> (8-old_bit_depth));
10970 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10971 quantum_info,RedQuantum,ping_pixels,exception);
10974 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10975 for (i=0; i < (ssize_t) image->columns; i++)
10976 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10979 if (logging != MagickFalse && y == 0)
10980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10981 " Writing row of pixels (1)");
10983 png_write_row(ping,ping_pixels);
10985 if (image->previous == (Image *) NULL)
10987 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10988 if (status == MagickFalse)
10994 else /* Not Palette, Bilevel, or Opaque Monochrome */
10996 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10997 !mng_info->write_png48 && !mng_info->write_png64 &&
10998 !mng_info->write_png32) && (image_matte != MagickFalse ||
10999 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11000 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11002 register const Quantum
11005 for (pass=0; pass < num_passes; pass++)
11008 for (y=0; y < (ssize_t) image->rows; y++)
11010 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11012 if (p == (const Quantum *) NULL)
11015 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11017 if (mng_info->IsPalette)
11018 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11019 quantum_info,GrayQuantum,ping_pixels,exception);
11022 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11023 quantum_info,RedQuantum,ping_pixels,exception);
11025 if (logging != MagickFalse && y == 0)
11026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11027 " Writing GRAY PNG pixels (2)");
11030 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11032 if (logging != MagickFalse && y == 0)
11033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034 " Writing GRAY_ALPHA PNG pixels (2)");
11036 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11037 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11040 if (logging != MagickFalse && y == 0)
11041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11042 " Writing row of pixels (2)");
11044 png_write_row(ping,ping_pixels);
11047 if (image->previous == (Image *) NULL)
11049 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11050 if (status == MagickFalse)
11058 register const Quantum
11061 for (pass=0; pass < num_passes; pass++)
11063 if ((image_depth > 8) ||
11064 mng_info->write_png24 ||
11065 mng_info->write_png32 ||
11066 mng_info->write_png48 ||
11067 mng_info->write_png64 ||
11068 (!mng_info->write_png8 && !mng_info->IsPalette))
11070 for (y=0; y < (ssize_t) image->rows; y++)
11072 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11074 if (p == (const Quantum *) NULL)
11077 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11079 if (image->storage_class == DirectClass)
11080 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11081 quantum_info,RedQuantum,ping_pixels,exception);
11084 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11085 quantum_info,GrayQuantum,ping_pixels,exception);
11088 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11090 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11091 quantum_info,GrayAlphaQuantum,ping_pixels,
11094 if (logging != MagickFalse && y == 0)
11095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11096 " Writing GRAY_ALPHA PNG pixels (3)");
11099 else if (image_matte != MagickFalse)
11100 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11101 quantum_info,RGBAQuantum,ping_pixels,exception);
11104 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11105 quantum_info,RGBQuantum,ping_pixels,exception);
11107 if (logging != MagickFalse && y == 0)
11108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11109 " Writing row of pixels (3)");
11111 png_write_row(ping,ping_pixels);
11116 /* not ((image_depth > 8) ||
11117 mng_info->write_png24 || mng_info->write_png32 ||
11118 mng_info->write_png48 || mng_info->write_png64 ||
11119 (!mng_info->write_png8 && !mng_info->IsPalette))
11122 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11123 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11125 if (logging != MagickFalse)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11127 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11129 quantum_info->depth=8;
11133 for (y=0; y < (ssize_t) image->rows; y++)
11135 if (logging != MagickFalse && y == 0)
11136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11137 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11139 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11141 if (p == (const Quantum *) NULL)
11144 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11146 quantum_info->depth=image->depth;
11148 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11149 quantum_info,GrayQuantum,ping_pixels,exception);
11152 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11154 if (logging != MagickFalse && y == 0)
11155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156 " Writing GRAY_ALPHA PNG pixels (4)");
11158 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11159 quantum_info,GrayAlphaQuantum,ping_pixels,
11165 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11166 quantum_info,IndexQuantum,ping_pixels,exception);
11168 if (logging != MagickFalse && y <= 2)
11170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11171 " Writing row of non-gray pixels (4)");
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11175 (int)ping_pixels[0],(int)ping_pixels[1]);
11178 png_write_row(ping,ping_pixels);
11182 if (image->previous == (Image *) NULL)
11184 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11185 if (status == MagickFalse)
11192 if (quantum_info != (QuantumInfo *) NULL)
11193 quantum_info=DestroyQuantumInfo(quantum_info);
11195 if (logging != MagickFalse)
11197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11198 " Wrote PNG image data");
11200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11201 " Width: %.20g",(double) ping_width);
11203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11204 " Height: %.20g",(double) ping_height);
11206 if (mng_info->write_png_depth)
11208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11213 " PNG bit-depth written: %d",ping_bit_depth);
11215 if (mng_info->write_png_colortype)
11217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11218 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11222 " PNG color-type written: %d",ping_color_type);
11224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11225 " PNG Interlace method: %d",ping_interlace_method);
11228 Generate text chunks after IDAT.
11230 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11232 ResetImagePropertyIterator(image);
11233 property=GetNextImageProperty(image);
11234 while (property != (const char *) NULL)
11239 value=GetImageProperty(image,property,exception);
11241 /* Don't write any "png:" properties; those are just for "identify" */
11242 if (LocaleNCompare(property,"png:",4) != 0 &&
11244 /* Suppress density and units if we wrote a pHYs chunk */
11245 (ping_exclude_pHYs != MagickFalse ||
11246 LocaleCompare(property,"density") != 0 ||
11247 LocaleCompare(property,"units") != 0) &&
11249 /* Suppress the IM-generated Date:create and Date:modify */
11250 (ping_exclude_date == MagickFalse ||
11251 LocaleNCompare(property, "Date:",5) != 0))
11253 if (value != (const char *) NULL)
11256 #if PNG_LIBPNG_VER >= 10400
11257 text=(png_textp) png_malloc(ping,
11258 (png_alloc_size_t) sizeof(png_text));
11260 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11262 text[0].key=(char *) property;
11263 text[0].text=(char *) value;
11264 text[0].text_length=strlen(value);
11266 if (ping_exclude_tEXt != MagickFalse)
11267 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11269 else if (ping_exclude_zTXt != MagickFalse)
11270 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11274 text[0].compression=image_info->compression == NoCompression ||
11275 (image_info->compression == UndefinedCompression &&
11276 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11277 PNG_TEXT_COMPRESSION_zTXt ;
11280 if (logging != MagickFalse)
11282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11283 " Setting up text chunk");
11285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11286 " keyword: '%s'",text[0].key);
11289 png_set_text(ping,ping_info,text,1);
11290 png_free(ping,text);
11293 property=GetNextImageProperty(image);
11297 /* write any PNG-chunk-e profiles */
11298 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11300 if (logging != MagickFalse)
11301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11302 " Writing PNG end info");
11304 png_write_end(ping,ping_info);
11306 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11308 if (mng_info->page.x || mng_info->page.y ||
11309 (ping_width != mng_info->page.width) ||
11310 (ping_height != mng_info->page.height))
11316 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11318 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11319 PNGType(chunk,mng_FRAM);
11320 LogPNGChunk(logging,mng_FRAM,27L);
11322 chunk[5]=0; /* frame name separator (no name) */
11323 chunk[6]=1; /* flag for changing delay, for next frame only */
11324 chunk[7]=0; /* flag for changing frame timeout */
11325 chunk[8]=1; /* flag for changing frame clipping for next frame */
11326 chunk[9]=0; /* flag for changing frame sync_id */
11327 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11328 chunk[14]=0; /* clipping boundaries delta type */
11329 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11331 (png_uint_32) (mng_info->page.x + ping_width));
11332 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11334 (png_uint_32) (mng_info->page.y + ping_height));
11335 (void) WriteBlob(image,31,chunk);
11336 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11337 mng_info->old_framing_mode=4;
11338 mng_info->framing_mode=1;
11342 mng_info->framing_mode=3;
11344 if (mng_info->write_mng && !mng_info->need_fram &&
11345 ((int) image->dispose == 3))
11346 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11349 Free PNG resources.
11352 png_destroy_write_struct(&ping,&ping_info);
11354 pixel_info=RelinquishVirtualMemory(pixel_info);
11356 if (ping_have_blob != MagickFalse)
11357 (void) CloseBlob(image);
11359 image_info=DestroyImageInfo(image_info);
11360 image=DestroyImage(image);
11362 /* Store bit depth actually written */
11363 s[0]=(char) ping_bit_depth;
11366 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11368 if (logging != MagickFalse)
11369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370 " exit WriteOnePNGImage()");
11372 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11373 UnlockSemaphoreInfo(ping_semaphore);
11376 /* } for navigation to beginning of SETJMP-protected block. Revert to
11377 * Throwing an Exception when an error occurs.
11380 return(MagickTrue);
11381 /* End write one PNG image */
11386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11390 % W r i t e P N G I m a g e %
11394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11396 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11397 % Multiple-image Network Graphics (MNG) image file.
11399 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11401 % The format of the WritePNGImage method is:
11403 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11404 % Image *image,ExceptionInfo *exception)
11406 % A description of each parameter follows:
11408 % o image_info: the image info.
11410 % o image: The image.
11412 % o exception: return any errors or warnings in this structure.
11414 % Returns MagickTrue on success, MagickFalse on failure.
11416 % Communicating with the PNG encoder:
11418 % While the datastream written is always in PNG format and normally would
11419 % be given the "png" file extension, this method also writes the following
11420 % pseudo-formats which are subsets of png:
11422 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11423 % a depth greater than 8, the depth is reduced. If transparency
11424 % is present, the tRNS chunk must only have values 0 and 255
11425 % (i.e., transparency is binary: fully opaque or fully
11426 % transparent). If other values are present they will be
11427 % 50%-thresholded to binary transparency. If more than 256
11428 % colors are present, they will be quantized to the 4-4-4-1,
11429 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11430 % of any resulting fully-transparent pixels is changed to
11431 % the image's background color.
11433 % If you want better quantization or dithering of the colors
11434 % or alpha than that, you need to do it before calling the
11435 % PNG encoder. The pixels contain 8-bit indices even if
11436 % they could be represented with 1, 2, or 4 bits. Grayscale
11437 % images will be written as indexed PNG files even though the
11438 % PNG grayscale type might be slightly more efficient. Please
11439 % note that writing to the PNG8 format may result in loss
11440 % of color and alpha data.
11442 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11443 % chunk can be present to convey binary transparency by naming
11444 % one of the colors as transparent. The only loss incurred
11445 % is reduction of sample depth to 8. If the image has more
11446 % than one transparent color, has semitransparent pixels, or
11447 % has an opaque pixel with the same RGB components as the
11448 % transparent color, an image is not written.
11450 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11451 % transparency is permitted, i.e., the alpha sample for
11452 % each pixel can have any value from 0 to 255. The alpha
11453 % channel is present even if the image is fully opaque.
11454 % The only loss in data is the reduction of the sample depth
11457 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11458 % chunk can be present to convey binary transparency by naming
11459 % one of the colors as transparent. If the image has more
11460 % than one transparent color, has semitransparent pixels, or
11461 % has an opaque pixel with the same RGB components as the
11462 % transparent color, an image is not written.
11464 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11465 % transparency is permitted, i.e., the alpha sample for
11466 % each pixel can have any value from 0 to 65535. The alpha
11467 % channel is present even if the image is fully opaque.
11469 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11470 % image, if the input was a PNG, is written. If these values
11471 % cannot be found, then "PNG00" falls back to the regular "PNG"
11474 % o -define: For more precise control of the PNG output, you can use the
11475 % Image options "png:bit-depth" and "png:color-type". These
11476 % can be set from the commandline with "-define" and also
11477 % from the application programming interfaces. The options
11478 % are case-independent and are converted to lowercase before
11479 % being passed to this encoder.
11481 % png:color-type can be 0, 2, 3, 4, or 6.
11483 % When png:color-type is 0 (Grayscale), png:bit-depth can
11484 % be 1, 2, 4, 8, or 16.
11486 % When png:color-type is 2 (RGB), png:bit-depth can
11489 % When png:color-type is 3 (Indexed), png:bit-depth can
11490 % be 1, 2, 4, or 8. This refers to the number of bits
11491 % used to store the index. The color samples always have
11492 % bit-depth 8 in indexed PNG files.
11494 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11495 % png:bit-depth can be 8 or 16.
11497 % If the image cannot be written without loss with the
11498 % requested bit-depth and color-type, a PNG file will not
11499 % be written, a warning will be issued, and the encoder will
11500 % return MagickFalse.
11502 % Since image encoders should not be responsible for the "heavy lifting",
11503 % the user should make sure that ImageMagick has already reduced the
11504 % image depth and number of colors and limit transparency to binary
11505 % transparency prior to attempting to write the image with depth, color,
11506 % or transparency limitations.
11508 % Note that another definition, "png:bit-depth-written" exists, but it
11509 % is not intended for external use. It is only used internally by the
11510 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11512 % It is possible to request that the PNG encoder write previously-formatted
11513 % ancillary chunks in the output PNG file, using the "-profile" commandline
11514 % option as shown below or by setting the profile via a programming
11517 % -profile PNG-chunk-x:<file>
11519 % where x is a location flag and <file> is a file containing the chunk
11520 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11521 % This encoder will compute the chunk length and CRC, so those must not
11522 % be included in the file.
11524 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11525 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11526 % of the same type, then add a short unique string after the "x" to prevent
11527 % subsequent profiles from overwriting the preceding ones, e.g.,
11529 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11531 % As of version 6.6.6 the following optimizations are always done:
11533 % o 32-bit depth is reduced to 16.
11534 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11535 % high byte and low byte are identical.
11536 % o Palette is sorted to remove unused entries and to put a
11537 % transparent color first, if BUILD_PNG_PALETTE is defined.
11538 % o Opaque matte channel is removed when writing an indexed PNG.
11539 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11540 % this can be done without loss and a larger bit depth N was not
11541 % requested via the "-define png:bit-depth=N" option.
11542 % o If matte channel is present but only one transparent color is
11543 % present, RGB+tRNS is written instead of RGBA
11544 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11545 % was requested when converting an opaque image).
11547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11549 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11550 Image *image,ExceptionInfo *exception)
11555 have_mng_structure,
11571 assert(image_info != (const ImageInfo *) NULL);
11572 assert(image_info->signature == MagickSignature);
11573 assert(image != (Image *) NULL);
11574 assert(image->signature == MagickSignature);
11575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11576 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11578 Allocate a MngInfo structure.
11580 have_mng_structure=MagickFalse;
11581 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11583 if (mng_info == (MngInfo *) NULL)
11584 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11587 Initialize members of the MngInfo structure.
11589 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11590 mng_info->image=image;
11591 mng_info->equal_backgrounds=MagickTrue;
11592 have_mng_structure=MagickTrue;
11594 /* See if user has requested a specific PNG subformat */
11596 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11597 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11598 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11599 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11600 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11602 value=GetImageOption(image_info,"png:format");
11604 if (value != (char *) NULL)
11606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11607 " Format=%s",value);
11609 mng_info->write_png8 = MagickFalse;
11610 mng_info->write_png24 = MagickFalse;
11611 mng_info->write_png32 = MagickFalse;
11612 mng_info->write_png48 = MagickFalse;
11613 mng_info->write_png64 = MagickFalse;
11615 if (LocaleCompare(value,"png8") == 0)
11616 mng_info->write_png8 = MagickTrue;
11618 else if (LocaleCompare(value,"png24") == 0)
11619 mng_info->write_png24 = MagickTrue;
11621 else if (LocaleCompare(value,"png32") == 0)
11622 mng_info->write_png32 = MagickTrue;
11624 else if (LocaleCompare(value,"png48") == 0)
11625 mng_info->write_png48 = MagickTrue;
11627 else if (LocaleCompare(value,"png64") == 0)
11628 mng_info->write_png64 = MagickTrue;
11630 else if (LocaleCompare(value,"png00") == 0)
11632 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11633 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11635 if (value != (char *) NULL)
11637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11638 " png00 inherited bit depth=%s",value);
11640 if (LocaleCompare(value,"1") == 0)
11641 mng_info->write_png_depth = 1;
11643 else if (LocaleCompare(value,"1") == 0)
11644 mng_info->write_png_depth = 2;
11646 else if (LocaleCompare(value,"2") == 0)
11647 mng_info->write_png_depth = 4;
11649 else if (LocaleCompare(value,"8") == 0)
11650 mng_info->write_png_depth = 8;
11652 else if (LocaleCompare(value,"16") == 0)
11653 mng_info->write_png_depth = 16;
11656 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11658 if (value != (char *) NULL)
11660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11661 " png00 inherited color type=%s",value);
11663 if (LocaleCompare(value,"0") == 0)
11664 mng_info->write_png_colortype = 1;
11666 else if (LocaleCompare(value,"2") == 0)
11667 mng_info->write_png_colortype = 3;
11669 else if (LocaleCompare(value,"3") == 0)
11670 mng_info->write_png_colortype = 4;
11672 else if (LocaleCompare(value,"4") == 0)
11673 mng_info->write_png_colortype = 5;
11675 else if (LocaleCompare(value,"6") == 0)
11676 mng_info->write_png_colortype = 7;
11681 if (mng_info->write_png8)
11683 mng_info->write_png_colortype = /* 3 */ 4;
11684 mng_info->write_png_depth = 8;
11688 if (mng_info->write_png24)
11690 mng_info->write_png_colortype = /* 2 */ 3;
11691 mng_info->write_png_depth = 8;
11694 if (image->alpha_trait == BlendPixelTrait)
11695 (void) SetImageType(image,TrueColorMatteType,exception);
11698 (void) SetImageType(image,TrueColorType,exception);
11700 (void) SyncImage(image,exception);
11703 if (mng_info->write_png32)
11705 mng_info->write_png_colortype = /* 6 */ 7;
11706 mng_info->write_png_depth = 8;
11709 if (image->alpha_trait == BlendPixelTrait)
11710 (void) SetImageType(image,TrueColorMatteType,exception);
11713 (void) SetImageType(image,TrueColorType,exception);
11715 (void) SyncImage(image,exception);
11718 if (mng_info->write_png48)
11720 mng_info->write_png_colortype = /* 2 */ 3;
11721 mng_info->write_png_depth = 16;
11724 if (image->alpha_trait == BlendPixelTrait)
11725 (void) SetImageType(image,TrueColorMatteType,exception);
11728 (void) SetImageType(image,TrueColorType,exception);
11730 (void) SyncImage(image,exception);
11733 if (mng_info->write_png64)
11735 mng_info->write_png_colortype = /* 6 */ 7;
11736 mng_info->write_png_depth = 16;
11739 if (image->alpha_trait == BlendPixelTrait)
11740 (void) SetImageType(image,TrueColorMatteType,exception);
11743 (void) SetImageType(image,TrueColorType,exception);
11745 (void) SyncImage(image,exception);
11748 value=GetImageOption(image_info,"png:bit-depth");
11750 if (value != (char *) NULL)
11752 if (LocaleCompare(value,"1") == 0)
11753 mng_info->write_png_depth = 1;
11755 else if (LocaleCompare(value,"2") == 0)
11756 mng_info->write_png_depth = 2;
11758 else if (LocaleCompare(value,"4") == 0)
11759 mng_info->write_png_depth = 4;
11761 else if (LocaleCompare(value,"8") == 0)
11762 mng_info->write_png_depth = 8;
11764 else if (LocaleCompare(value,"16") == 0)
11765 mng_info->write_png_depth = 16;
11768 (void) ThrowMagickException(exception,
11769 GetMagickModule(),CoderWarning,
11770 "ignoring invalid defined png:bit-depth",
11773 if (logging != MagickFalse)
11774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11775 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11778 value=GetImageOption(image_info,"png:color-type");
11780 if (value != (char *) NULL)
11782 /* We must store colortype+1 because 0 is a valid colortype */
11783 if (LocaleCompare(value,"0") == 0)
11784 mng_info->write_png_colortype = 1;
11786 else if (LocaleCompare(value,"1") == 0)
11787 mng_info->write_png_colortype = 2;
11789 else if (LocaleCompare(value,"2") == 0)
11790 mng_info->write_png_colortype = 3;
11792 else if (LocaleCompare(value,"3") == 0)
11793 mng_info->write_png_colortype = 4;
11795 else if (LocaleCompare(value,"4") == 0)
11796 mng_info->write_png_colortype = 5;
11798 else if (LocaleCompare(value,"6") == 0)
11799 mng_info->write_png_colortype = 7;
11802 (void) ThrowMagickException(exception,
11803 GetMagickModule(),CoderWarning,
11804 "ignoring invalid defined png:color-type",
11807 if (logging != MagickFalse)
11808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11809 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11812 /* Check for chunks to be excluded:
11814 * The default is to not exclude any known chunks except for any
11815 * listed in the "unused_chunks" array, above.
11817 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11818 * define (in the image properties or in the image artifacts)
11819 * or via a mng_info member. For convenience, in addition
11820 * to or instead of a comma-separated list of chunks, the
11821 * "exclude-chunk" string can be simply "all" or "none".
11823 * The exclude-chunk define takes priority over the mng_info.
11825 * A "png:include-chunk" define takes priority over both the
11826 * mng_info and the "png:exclude-chunk" define. Like the
11827 * "exclude-chunk" string, it can define "all" or "none" as
11828 * well as a comma-separated list. Chunks that are unknown to
11829 * ImageMagick are always excluded, regardless of their "copy-safe"
11830 * status according to the PNG specification, and even if they
11831 * appear in the "include-chunk" list. Such defines appearing among
11832 * the image options take priority over those found among the image
11835 * Finally, all chunks listed in the "unused_chunks" array are
11836 * automatically excluded, regardless of the other instructions
11839 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11840 * will not be written and the gAMA chunk will only be written if it
11841 * is not between .45 and .46, or approximately (1.0/2.2).
11843 * If you exclude tRNS and the image has transparency, the colortype
11844 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11846 * The -strip option causes StripImage() to set the png:include-chunk
11847 * artifact to "none,trns,gama".
11850 mng_info->ping_exclude_bKGD=MagickFalse;
11851 mng_info->ping_exclude_cHRM=MagickFalse;
11852 mng_info->ping_exclude_date=MagickFalse;
11853 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11854 mng_info->ping_exclude_gAMA=MagickFalse;
11855 mng_info->ping_exclude_iCCP=MagickFalse;
11856 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11857 mng_info->ping_exclude_oFFs=MagickFalse;
11858 mng_info->ping_exclude_pHYs=MagickFalse;
11859 mng_info->ping_exclude_sRGB=MagickFalse;
11860 mng_info->ping_exclude_tEXt=MagickFalse;
11861 mng_info->ping_exclude_tRNS=MagickFalse;
11862 mng_info->ping_exclude_vpAg=MagickFalse;
11863 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11864 mng_info->ping_exclude_zTXt=MagickFalse;
11866 mng_info->ping_preserve_colormap=MagickFalse;
11868 value=GetImageOption(image_info,"png:preserve-colormap");
11870 value=GetImageArtifact(image,"png:preserve-colormap");
11872 mng_info->ping_preserve_colormap=MagickTrue;
11874 mng_info->ping_preserve_iCCP=MagickFalse;
11876 value=GetImageOption(image_info,"png:preserve-iCCP");
11878 value=GetImageArtifact(image,"png:preserve-iCCP");
11880 mng_info->ping_preserve_iCCP=MagickTrue;
11882 /* These compression-level, compression-strategy, and compression-filter
11883 * defines take precedence over values from the -quality option.
11885 value=GetImageOption(image_info,"png:compression-level");
11887 value=GetImageArtifact(image,"png:compression-level");
11890 /* We have to add 1 to everything because 0 is a valid input,
11891 * and we want to use 0 (the default) to mean undefined.
11893 if (LocaleCompare(value,"0") == 0)
11894 mng_info->write_png_compression_level = 1;
11896 else if (LocaleCompare(value,"1") == 0)
11897 mng_info->write_png_compression_level = 2;
11899 else if (LocaleCompare(value,"2") == 0)
11900 mng_info->write_png_compression_level = 3;
11902 else if (LocaleCompare(value,"3") == 0)
11903 mng_info->write_png_compression_level = 4;
11905 else if (LocaleCompare(value,"4") == 0)
11906 mng_info->write_png_compression_level = 5;
11908 else if (LocaleCompare(value,"5") == 0)
11909 mng_info->write_png_compression_level = 6;
11911 else if (LocaleCompare(value,"6") == 0)
11912 mng_info->write_png_compression_level = 7;
11914 else if (LocaleCompare(value,"7") == 0)
11915 mng_info->write_png_compression_level = 8;
11917 else if (LocaleCompare(value,"8") == 0)
11918 mng_info->write_png_compression_level = 9;
11920 else if (LocaleCompare(value,"9") == 0)
11921 mng_info->write_png_compression_level = 10;
11924 (void) ThrowMagickException(exception,
11925 GetMagickModule(),CoderWarning,
11926 "ignoring invalid defined png:compression-level",
11930 value=GetImageOption(image_info,"png:compression-strategy");
11932 value=GetImageArtifact(image,"png:compression-strategy");
11936 if (LocaleCompare(value,"0") == 0)
11937 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11939 else if (LocaleCompare(value,"1") == 0)
11940 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11942 else if (LocaleCompare(value,"2") == 0)
11943 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11945 else if (LocaleCompare(value,"3") == 0)
11946 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11947 mng_info->write_png_compression_strategy = Z_RLE+1;
11949 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11952 else if (LocaleCompare(value,"4") == 0)
11953 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11954 mng_info->write_png_compression_strategy = Z_FIXED+1;
11956 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11960 (void) ThrowMagickException(exception,
11961 GetMagickModule(),CoderWarning,
11962 "ignoring invalid defined png:compression-strategy",
11966 value=GetImageOption(image_info,"png:compression-filter");
11968 value=GetImageArtifact(image,"png:compression-filter");
11972 /* To do: combinations of filters allowed by libpng
11973 * masks 0x08 through 0xf8
11975 * Implement this as a comma-separated list of 0,1,2,3,4,5
11976 * where 5 is a special case meaning PNG_ALL_FILTERS.
11979 if (LocaleCompare(value,"0") == 0)
11980 mng_info->write_png_compression_filter = 1;
11982 else if (LocaleCompare(value,"1") == 0)
11983 mng_info->write_png_compression_filter = 2;
11985 else if (LocaleCompare(value,"2") == 0)
11986 mng_info->write_png_compression_filter = 3;
11988 else if (LocaleCompare(value,"3") == 0)
11989 mng_info->write_png_compression_filter = 4;
11991 else if (LocaleCompare(value,"4") == 0)
11992 mng_info->write_png_compression_filter = 5;
11994 else if (LocaleCompare(value,"5") == 0)
11995 mng_info->write_png_compression_filter = 6;
11998 (void) ThrowMagickException(exception,
11999 GetMagickModule(),CoderWarning,
12000 "ignoring invalid defined png:compression-filter",
12004 excluding=MagickFalse;
12006 for (source=0; source<1; source++)
12010 value=GetImageOption(image_info,"png:exclude-chunk");
12013 value=GetImageArtifact(image,"png:exclude-chunks");
12017 value=GetImageOption(image_info,"png:exclude-chunk");
12020 value=GetImageArtifact(image,"png:exclude-chunks");
12029 excluding=MagickTrue;
12031 if (logging != MagickFalse)
12034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12035 " png:exclude-chunk=%s found in image artifacts.\n", value);
12037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12038 " png:exclude-chunk=%s found in image properties.\n", value);
12041 last=strlen(value);
12043 for (i=0; i<(int) last; i+=5)
12046 if (LocaleNCompare(value+i,"none",4) == 0)
12048 mng_info->ping_exclude_bKGD=MagickFalse;
12049 mng_info->ping_exclude_cHRM=MagickFalse;
12050 mng_info->ping_exclude_date=MagickFalse;
12051 mng_info->ping_exclude_EXIF=MagickFalse;
12052 mng_info->ping_exclude_gAMA=MagickFalse;
12053 mng_info->ping_exclude_iCCP=MagickFalse;
12054 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12055 mng_info->ping_exclude_oFFs=MagickFalse;
12056 mng_info->ping_exclude_pHYs=MagickFalse;
12057 mng_info->ping_exclude_sRGB=MagickFalse;
12058 mng_info->ping_exclude_tEXt=MagickFalse;
12059 mng_info->ping_exclude_tRNS=MagickFalse;
12060 mng_info->ping_exclude_vpAg=MagickFalse;
12061 mng_info->ping_exclude_zCCP=MagickFalse;
12062 mng_info->ping_exclude_zTXt=MagickFalse;
12065 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12066 mng_info->ping_exclude_bKGD=MagickTrue;
12068 if (LocaleNCompare(value+i,"chrm",4) == 0)
12069 mng_info->ping_exclude_cHRM=MagickTrue;
12071 if (LocaleNCompare(value+i,"date",4) == 0)
12072 mng_info->ping_exclude_date=MagickTrue;
12074 if (LocaleNCompare(value+i,"exif",4) == 0)
12075 mng_info->ping_exclude_EXIF=MagickTrue;
12077 if (LocaleNCompare(value+i,"gama",4) == 0)
12078 mng_info->ping_exclude_gAMA=MagickTrue;
12080 if (LocaleNCompare(value+i,"iccp",4) == 0)
12081 mng_info->ping_exclude_iCCP=MagickTrue;
12084 if (LocaleNCompare(value+i,"itxt",4) == 0)
12085 mng_info->ping_exclude_iTXt=MagickTrue;
12088 if (LocaleNCompare(value+i,"gama",4) == 0)
12089 mng_info->ping_exclude_gAMA=MagickTrue;
12091 if (LocaleNCompare(value+i,"offs",4) == 0)
12092 mng_info->ping_exclude_oFFs=MagickTrue;
12094 if (LocaleNCompare(value+i,"phys",4) == 0)
12095 mng_info->ping_exclude_pHYs=MagickTrue;
12097 if (LocaleNCompare(value+i,"srgb",4) == 0)
12098 mng_info->ping_exclude_sRGB=MagickTrue;
12100 if (LocaleNCompare(value+i,"text",4) == 0)
12101 mng_info->ping_exclude_tEXt=MagickTrue;
12103 if (LocaleNCompare(value+i,"trns",4) == 0)
12104 mng_info->ping_exclude_tRNS=MagickTrue;
12106 if (LocaleNCompare(value+i,"vpag",4) == 0)
12107 mng_info->ping_exclude_vpAg=MagickTrue;
12109 if (LocaleNCompare(value+i,"zccp",4) == 0)
12110 mng_info->ping_exclude_zCCP=MagickTrue;
12112 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12113 mng_info->ping_exclude_zTXt=MagickTrue;
12115 if (LocaleNCompare(value+i,"all",3) == 0)
12117 mng_info->ping_exclude_bKGD=MagickTrue;
12118 mng_info->ping_exclude_cHRM=MagickTrue;
12119 mng_info->ping_exclude_date=MagickTrue;
12120 mng_info->ping_exclude_EXIF=MagickTrue;
12121 mng_info->ping_exclude_gAMA=MagickTrue;
12122 mng_info->ping_exclude_iCCP=MagickTrue;
12123 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12124 mng_info->ping_exclude_oFFs=MagickTrue;
12125 mng_info->ping_exclude_pHYs=MagickTrue;
12126 mng_info->ping_exclude_sRGB=MagickTrue;
12127 mng_info->ping_exclude_tEXt=MagickTrue;
12128 mng_info->ping_exclude_tRNS=MagickTrue;
12129 mng_info->ping_exclude_vpAg=MagickTrue;
12130 mng_info->ping_exclude_zCCP=MagickTrue;
12131 mng_info->ping_exclude_zTXt=MagickTrue;
12138 for (source=0; source<1; source++)
12142 value=GetImageOption(image_info,"png:include-chunk");
12145 value=GetImageArtifact(image,"png:include-chunks");
12149 value=GetImageOption(image_info,"png:include-chunk");
12152 value=GetImageArtifact(image,"png:include-chunks");
12160 excluding=MagickTrue;
12162 if (logging != MagickFalse)
12165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12166 " png:include-chunk=%s found in image artifacts.\n", value);
12168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12169 " png:include-chunk=%s found in image properties.\n", value);
12172 last=strlen(value);
12174 for (i=0; i<(int) last; i+=5)
12176 if (LocaleNCompare(value+i,"none",4) == 0)
12178 mng_info->ping_exclude_bKGD=MagickTrue;
12179 mng_info->ping_exclude_cHRM=MagickTrue;
12180 mng_info->ping_exclude_date=MagickTrue;
12181 mng_info->ping_exclude_EXIF=MagickTrue;
12182 mng_info->ping_exclude_gAMA=MagickTrue;
12183 mng_info->ping_exclude_iCCP=MagickTrue;
12184 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12185 mng_info->ping_exclude_oFFs=MagickTrue;
12186 mng_info->ping_exclude_pHYs=MagickTrue;
12187 mng_info->ping_exclude_sRGB=MagickTrue;
12188 mng_info->ping_exclude_tEXt=MagickTrue;
12189 mng_info->ping_exclude_tRNS=MagickTrue;
12190 mng_info->ping_exclude_vpAg=MagickTrue;
12191 mng_info->ping_exclude_zCCP=MagickTrue;
12192 mng_info->ping_exclude_zTXt=MagickTrue;
12195 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12196 mng_info->ping_exclude_bKGD=MagickFalse;
12198 if (LocaleNCompare(value+i,"chrm",4) == 0)
12199 mng_info->ping_exclude_cHRM=MagickFalse;
12201 if (LocaleNCompare(value+i,"date",4) == 0)
12202 mng_info->ping_exclude_date=MagickFalse;
12204 if (LocaleNCompare(value+i,"exif",4) == 0)
12205 mng_info->ping_exclude_EXIF=MagickFalse;
12207 if (LocaleNCompare(value+i,"gama",4) == 0)
12208 mng_info->ping_exclude_gAMA=MagickFalse;
12210 if (LocaleNCompare(value+i,"iccp",4) == 0)
12211 mng_info->ping_exclude_iCCP=MagickFalse;
12214 if (LocaleNCompare(value+i,"itxt",4) == 0)
12215 mng_info->ping_exclude_iTXt=MagickFalse;
12218 if (LocaleNCompare(value+i,"gama",4) == 0)
12219 mng_info->ping_exclude_gAMA=MagickFalse;
12221 if (LocaleNCompare(value+i,"offs",4) == 0)
12222 mng_info->ping_exclude_oFFs=MagickFalse;
12224 if (LocaleNCompare(value+i,"phys",4) == 0)
12225 mng_info->ping_exclude_pHYs=MagickFalse;
12227 if (LocaleNCompare(value+i,"srgb",4) == 0)
12228 mng_info->ping_exclude_sRGB=MagickFalse;
12230 if (LocaleNCompare(value+i,"text",4) == 0)
12231 mng_info->ping_exclude_tEXt=MagickFalse;
12233 if (LocaleNCompare(value+i,"trns",4) == 0)
12234 mng_info->ping_exclude_tRNS=MagickFalse;
12236 if (LocaleNCompare(value+i,"vpag",4) == 0)
12237 mng_info->ping_exclude_vpAg=MagickFalse;
12239 if (LocaleNCompare(value+i,"zccp",4) == 0)
12240 mng_info->ping_exclude_zCCP=MagickFalse;
12242 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12243 mng_info->ping_exclude_zTXt=MagickFalse;
12245 if (LocaleNCompare(value+i,"all",3) == 0)
12247 mng_info->ping_exclude_bKGD=MagickFalse;
12248 mng_info->ping_exclude_cHRM=MagickFalse;
12249 mng_info->ping_exclude_date=MagickFalse;
12250 mng_info->ping_exclude_EXIF=MagickFalse;
12251 mng_info->ping_exclude_gAMA=MagickFalse;
12252 mng_info->ping_exclude_iCCP=MagickFalse;
12253 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12254 mng_info->ping_exclude_oFFs=MagickFalse;
12255 mng_info->ping_exclude_pHYs=MagickFalse;
12256 mng_info->ping_exclude_sRGB=MagickFalse;
12257 mng_info->ping_exclude_tEXt=MagickFalse;
12258 mng_info->ping_exclude_tRNS=MagickFalse;
12259 mng_info->ping_exclude_vpAg=MagickFalse;
12260 mng_info->ping_exclude_zCCP=MagickFalse;
12261 mng_info->ping_exclude_zTXt=MagickFalse;
12268 if (excluding != MagickFalse && logging != MagickFalse)
12270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12271 " Chunks to be excluded from the output png:");
12272 if (mng_info->ping_exclude_bKGD != MagickFalse)
12273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12275 if (mng_info->ping_exclude_cHRM != MagickFalse)
12276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12278 if (mng_info->ping_exclude_date != MagickFalse)
12279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12281 if (mng_info->ping_exclude_EXIF != MagickFalse)
12282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12284 if (mng_info->ping_exclude_gAMA != MagickFalse)
12285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12287 if (mng_info->ping_exclude_iCCP != MagickFalse)
12288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12291 if (mng_info->ping_exclude_iTXt != MagickFalse)
12292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12295 if (mng_info->ping_exclude_oFFs != MagickFalse)
12296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12298 if (mng_info->ping_exclude_pHYs != MagickFalse)
12299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301 if (mng_info->ping_exclude_sRGB != MagickFalse)
12302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12304 if (mng_info->ping_exclude_tEXt != MagickFalse)
12305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307 if (mng_info->ping_exclude_tRNS != MagickFalse)
12308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12310 if (mng_info->ping_exclude_vpAg != MagickFalse)
12311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12313 if (mng_info->ping_exclude_zCCP != MagickFalse)
12314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12316 if (mng_info->ping_exclude_zTXt != MagickFalse)
12317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12321 mng_info->need_blob = MagickTrue;
12323 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12325 MngInfoFreeStruct(mng_info,&have_mng_structure);
12327 if (logging != MagickFalse)
12328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12333 #if defined(JNG_SUPPORTED)
12335 /* Write one JNG image */
12336 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12337 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12358 jng_alpha_compression_method,
12359 jng_alpha_sample_depth,
12367 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12368 " Enter WriteOneJNGImage()");
12370 blob=(unsigned char *) NULL;
12371 jpeg_image=(Image *) NULL;
12372 jpeg_image_info=(ImageInfo *) NULL;
12375 transparent=image_info->type==GrayscaleMatteType ||
12376 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12378 jng_alpha_sample_depth = 0;
12380 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12382 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12384 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12385 image_info->quality;
12387 if (jng_alpha_quality >= 1000)
12388 jng_alpha_quality /= 1000;
12394 /* Create JPEG blob, image, and image_info */
12395 if (logging != MagickFalse)
12396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12397 " Creating jpeg_image_info for alpha.");
12399 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12401 if (jpeg_image_info == (ImageInfo *) NULL)
12402 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12404 if (logging != MagickFalse)
12405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12406 " Creating jpeg_image.");
12408 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12409 if (jpeg_image == (Image *) NULL)
12410 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12411 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12412 jpeg_image->alpha_trait=UndefinedPixelTrait;
12413 jpeg_image->quality=jng_alpha_quality;
12414 jpeg_image_info->type=GrayscaleType;
12415 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12416 (void) AcquireUniqueFilename(jpeg_image->filename);
12417 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12418 "%s",jpeg_image->filename);
12422 jng_alpha_compression_method=0;
12424 jng_alpha_sample_depth=0;
12427 /* To do: check bit depth of PNG alpha channel */
12429 /* Check if image is grayscale. */
12430 if (image_info->type != TrueColorMatteType && image_info->type !=
12431 TrueColorType && IsImageGray(image,exception))
12434 if (logging != MagickFalse)
12436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12437 " JNG Quality = %d",(int) jng_quality);
12438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12439 " JNG Color Type = %d",jng_color_type);
12442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12443 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12445 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12447 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12453 if (jng_alpha_compression_method==0)
12458 /* Encode alpha as a grayscale PNG blob */
12459 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12461 if (logging != MagickFalse)
12462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12463 " Creating PNG blob.");
12466 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12467 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12468 jpeg_image_info->interlace=NoInterlace;
12470 /* Exclude all ancillary chunks */
12471 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12473 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12476 /* Retrieve sample depth used */
12477 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12478 if (value != (char *) NULL)
12479 jng_alpha_sample_depth= (unsigned int) value[0];
12483 /* Encode alpha as a grayscale JPEG blob */
12485 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12488 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12489 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12490 jpeg_image_info->interlace=NoInterlace;
12491 if (logging != MagickFalse)
12492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12493 " Creating blob.");
12494 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12496 jng_alpha_sample_depth=8;
12498 if (logging != MagickFalse)
12499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12500 " Successfully read jpeg_image into a blob, length=%.20g.",
12504 /* Destroy JPEG image and image_info */
12505 jpeg_image=DestroyImage(jpeg_image);
12506 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12507 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12510 /* Write JHDR chunk */
12511 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12512 PNGType(chunk,mng_JHDR);
12513 LogPNGChunk(logging,mng_JHDR,16L);
12514 PNGLong(chunk+4,(png_uint_32) image->columns);
12515 PNGLong(chunk+8,(png_uint_32) image->rows);
12516 chunk[12]=jng_color_type;
12517 chunk[13]=8; /* sample depth */
12518 chunk[14]=8; /*jng_image_compression_method */
12519 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12520 chunk[16]=jng_alpha_sample_depth;
12521 chunk[17]=jng_alpha_compression_method;
12522 chunk[18]=0; /*jng_alpha_filter_method */
12523 chunk[19]=0; /*jng_alpha_interlace_method */
12524 (void) WriteBlob(image,20,chunk);
12525 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12526 if (logging != MagickFalse)
12528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12529 " JNG width:%15lu",(unsigned long) image->columns);
12531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12532 " JNG height:%14lu",(unsigned long) image->rows);
12534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12535 " JNG color type:%10d",jng_color_type);
12537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12538 " JNG sample depth:%8d",8);
12540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12541 " JNG compression:%9d",8);
12543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12544 " JNG interlace:%11d",0);
12546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12547 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12550 " JNG alpha compression:%3d",jng_alpha_compression_method);
12552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12553 " JNG alpha filter:%8d",0);
12555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12556 " JNG alpha interlace:%5d",0);
12559 /* Write any JNG-chunk-b profiles */
12560 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12563 Write leading ancillary chunks
12569 Write JNG bKGD chunk
12580 if (jng_color_type == 8 || jng_color_type == 12)
12584 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12585 PNGType(chunk,mng_bKGD);
12586 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12587 red=ScaleQuantumToChar(image->background_color.red);
12588 green=ScaleQuantumToChar(image->background_color.green);
12589 blue=ScaleQuantumToChar(image->background_color.blue);
12596 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12597 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12600 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12603 Write JNG sRGB chunk
12605 (void) WriteBlobMSBULong(image,1L);
12606 PNGType(chunk,mng_sRGB);
12607 LogPNGChunk(logging,mng_sRGB,1L);
12609 if (image->rendering_intent != UndefinedIntent)
12610 chunk[4]=(unsigned char)
12611 Magick_RenderingIntent_to_PNG_RenderingIntent(
12612 (image->rendering_intent));
12615 chunk[4]=(unsigned char)
12616 Magick_RenderingIntent_to_PNG_RenderingIntent(
12617 (PerceptualIntent));
12619 (void) WriteBlob(image,5,chunk);
12620 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12624 if (image->gamma != 0.0)
12627 Write JNG gAMA chunk
12629 (void) WriteBlobMSBULong(image,4L);
12630 PNGType(chunk,mng_gAMA);
12631 LogPNGChunk(logging,mng_gAMA,4L);
12632 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12633 (void) WriteBlob(image,8,chunk);
12634 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12637 if ((mng_info->equal_chrms == MagickFalse) &&
12638 (image->chromaticity.red_primary.x != 0.0))
12644 Write JNG cHRM chunk
12646 (void) WriteBlobMSBULong(image,32L);
12647 PNGType(chunk,mng_cHRM);
12648 LogPNGChunk(logging,mng_cHRM,32L);
12649 primary=image->chromaticity.white_point;
12650 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12651 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12652 primary=image->chromaticity.red_primary;
12653 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12654 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12655 primary=image->chromaticity.green_primary;
12656 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12657 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12658 primary=image->chromaticity.blue_primary;
12659 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12660 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12661 (void) WriteBlob(image,36,chunk);
12662 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12666 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12669 Write JNG pHYs chunk
12671 (void) WriteBlobMSBULong(image,9L);
12672 PNGType(chunk,mng_pHYs);
12673 LogPNGChunk(logging,mng_pHYs,9L);
12674 if (image->units == PixelsPerInchResolution)
12676 PNGLong(chunk+4,(png_uint_32)
12677 (image->resolution.x*100.0/2.54+0.5));
12679 PNGLong(chunk+8,(png_uint_32)
12680 (image->resolution.y*100.0/2.54+0.5));
12687 if (image->units == PixelsPerCentimeterResolution)
12689 PNGLong(chunk+4,(png_uint_32)
12690 (image->resolution.x*100.0+0.5));
12692 PNGLong(chunk+8,(png_uint_32)
12693 (image->resolution.y*100.0+0.5));
12700 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12701 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12705 (void) WriteBlob(image,13,chunk);
12706 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12709 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12712 Write JNG oFFs chunk
12714 (void) WriteBlobMSBULong(image,9L);
12715 PNGType(chunk,mng_oFFs);
12716 LogPNGChunk(logging,mng_oFFs,9L);
12717 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12718 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12720 (void) WriteBlob(image,13,chunk);
12721 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12723 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12725 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12726 PNGType(chunk,mng_vpAg);
12727 LogPNGChunk(logging,mng_vpAg,9L);
12728 PNGLong(chunk+4,(png_uint_32) image->page.width);
12729 PNGLong(chunk+8,(png_uint_32) image->page.height);
12730 chunk[12]=0; /* unit = pixels */
12731 (void) WriteBlob(image,13,chunk);
12732 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12738 if (jng_alpha_compression_method==0)
12746 /* Write IDAT chunk header */
12747 if (logging != MagickFalse)
12748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12749 " Write IDAT chunks from blob, length=%.20g.",(double)
12752 /* Copy IDAT chunks */
12755 for (i=8; i<(ssize_t) length; i+=len+12)
12757 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12760 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12762 /* Found an IDAT chunk. */
12763 (void) WriteBlobMSBULong(image,(size_t) len);
12764 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12765 (void) WriteBlob(image,(size_t) len+4,p);
12766 (void) WriteBlobMSBULong(image,
12767 crc32(0,p,(uInt) len+4));
12772 if (logging != MagickFalse)
12773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12774 " Skipping %c%c%c%c chunk, length=%.20g.",
12775 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12782 /* Write JDAA chunk header */
12783 if (logging != MagickFalse)
12784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12785 " Write JDAA chunk, length=%.20g.",(double) length);
12786 (void) WriteBlobMSBULong(image,(size_t) length);
12787 PNGType(chunk,mng_JDAA);
12788 LogPNGChunk(logging,mng_JDAA,length);
12789 /* Write JDAT chunk(s) data */
12790 (void) WriteBlob(image,4,chunk);
12791 (void) WriteBlob(image,length,blob);
12792 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12795 blob=(unsigned char *) RelinquishMagickMemory(blob);
12798 /* Encode image as a JPEG blob */
12799 if (logging != MagickFalse)
12800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12801 " Creating jpeg_image_info.");
12802 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12803 if (jpeg_image_info == (ImageInfo *) NULL)
12804 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12806 if (logging != MagickFalse)
12807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12808 " Creating jpeg_image.");
12810 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12811 if (jpeg_image == (Image *) NULL)
12812 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12813 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12815 (void) AcquireUniqueFilename(jpeg_image->filename);
12816 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12817 jpeg_image->filename);
12819 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12822 if (logging != MagickFalse)
12823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12824 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12825 (double) jpeg_image->rows);
12827 if (jng_color_type == 8 || jng_color_type == 12)
12828 jpeg_image_info->type=GrayscaleType;
12830 jpeg_image_info->quality=jng_quality;
12831 jpeg_image->quality=jng_quality;
12832 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12833 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12835 if (logging != MagickFalse)
12836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12837 " Creating blob.");
12839 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12841 if (logging != MagickFalse)
12843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12844 " Successfully read jpeg_image into a blob, length=%.20g.",
12847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12848 " Write JDAT chunk, length=%.20g.",(double) length);
12851 /* Write JDAT chunk(s) */
12852 (void) WriteBlobMSBULong(image,(size_t) length);
12853 PNGType(chunk,mng_JDAT);
12854 LogPNGChunk(logging,mng_JDAT,length);
12855 (void) WriteBlob(image,4,chunk);
12856 (void) WriteBlob(image,length,blob);
12857 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12859 jpeg_image=DestroyImage(jpeg_image);
12860 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12861 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12862 blob=(unsigned char *) RelinquishMagickMemory(blob);
12864 /* Write any JNG-chunk-e profiles */
12865 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12867 /* Write IEND chunk */
12868 (void) WriteBlobMSBULong(image,0L);
12869 PNGType(chunk,mng_IEND);
12870 LogPNGChunk(logging,mng_IEND,0);
12871 (void) WriteBlob(image,4,chunk);
12872 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12874 if (logging != MagickFalse)
12875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12876 " exit WriteOneJNGImage()");
12883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12887 % W r i t e J N G I m a g e %
12891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12893 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12895 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12897 % The format of the WriteJNGImage method is:
12899 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12900 % Image *image,ExceptionInfo *exception)
12902 % A description of each parameter follows:
12904 % o image_info: the image info.
12906 % o image: The image.
12908 % o exception: return any errors or warnings in this structure.
12910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12912 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12913 ExceptionInfo *exception)
12916 have_mng_structure,
12926 assert(image_info != (const ImageInfo *) NULL);
12927 assert(image_info->signature == MagickSignature);
12928 assert(image != (Image *) NULL);
12929 assert(image->signature == MagickSignature);
12930 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12931 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12932 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12933 if (status == MagickFalse)
12937 Allocate a MngInfo structure.
12939 have_mng_structure=MagickFalse;
12940 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12941 if (mng_info == (MngInfo *) NULL)
12942 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12944 Initialize members of the MngInfo structure.
12946 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12947 mng_info->image=image;
12948 have_mng_structure=MagickTrue;
12950 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12952 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12953 (void) CloseBlob(image);
12955 (void) CatchImageException(image);
12956 MngInfoFreeStruct(mng_info,&have_mng_structure);
12957 if (logging != MagickFalse)
12958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12963 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12964 ExceptionInfo *exception)
12973 have_mng_structure,
12976 volatile MagickBooleanType
12988 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12989 defined(PNG_MNG_FEATURES_SUPPORTED)
12992 all_images_are_gray,
13002 volatile unsigned int
13013 #if (PNG_LIBPNG_VER < 10200)
13014 if (image_info->verbose)
13015 printf("Your PNG library (libpng-%s) is rather old.\n",
13016 PNG_LIBPNG_VER_STRING);
13022 assert(image_info != (const ImageInfo *) NULL);
13023 assert(image_info->signature == MagickSignature);
13024 assert(image != (Image *) NULL);
13025 assert(image->signature == MagickSignature);
13026 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13027 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13028 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13029 if (status == MagickFalse)
13033 Allocate a MngInfo structure.
13035 have_mng_structure=MagickFalse;
13036 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13037 if (mng_info == (MngInfo *) NULL)
13038 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13040 Initialize members of the MngInfo structure.
13042 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13043 mng_info->image=image;
13044 have_mng_structure=MagickTrue;
13045 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13048 * See if user has requested a specific PNG subformat to be used
13049 * for all of the PNGs in the MNG being written, e.g.,
13051 * convert *.png png8:animation.mng
13053 * To do: check -define png:bit_depth and png:color_type as well,
13054 * or perhaps use mng:bit_depth and mng:color_type instead for
13058 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13059 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13060 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13062 write_jng=MagickFalse;
13063 if (image_info->compression == JPEGCompression)
13064 write_jng=MagickTrue;
13066 mng_info->adjoin=image_info->adjoin &&
13067 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13069 if (logging != MagickFalse)
13071 /* Log some info about the input */
13075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13076 " Checking input image(s)");
13078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13079 " Image_info depth: %.20g",(double) image_info->depth);
13081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13082 " Type: %d",image_info->type);
13085 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13088 " Scene: %.20g",(double) scene++);
13090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13091 " Image depth: %.20g",(double) p->depth);
13093 if (p->alpha_trait == BlendPixelTrait)
13094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13101 if (p->storage_class == PseudoClass)
13102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13103 " Storage class: PseudoClass");
13106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13107 " Storage class: DirectClass");
13110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13111 " Number of colors: %.20g",(double) p->colors);
13114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13115 " Number of colors: unspecified");
13117 if (mng_info->adjoin == MagickFalse)
13122 use_global_plte=MagickFalse;
13123 all_images_are_gray=MagickFalse;
13124 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13125 need_local_plte=MagickTrue;
13127 need_defi=MagickFalse;
13128 need_matte=MagickFalse;
13129 mng_info->framing_mode=1;
13130 mng_info->old_framing_mode=1;
13133 if (image_info->page != (char *) NULL)
13136 Determine image bounding box.
13138 SetGeometry(image,&mng_info->page);
13139 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13140 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13152 mng_info->page=image->page;
13153 need_geom=MagickTrue;
13154 if (mng_info->page.width || mng_info->page.height)
13155 need_geom=MagickFalse;
13157 Check all the scenes.
13159 initial_delay=image->delay;
13160 need_iterations=MagickFalse;
13161 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13162 mng_info->equal_physs=MagickTrue,
13163 mng_info->equal_gammas=MagickTrue;
13164 mng_info->equal_srgbs=MagickTrue;
13165 mng_info->equal_backgrounds=MagickTrue;
13167 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13168 defined(PNG_MNG_FEATURES_SUPPORTED)
13169 all_images_are_gray=MagickTrue;
13170 mng_info->equal_palettes=MagickFalse;
13171 need_local_plte=MagickFalse;
13173 for (next_image=image; next_image != (Image *) NULL; )
13177 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13178 mng_info->page.width=next_image->columns+next_image->page.x;
13180 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13181 mng_info->page.height=next_image->rows+next_image->page.y;
13184 if (next_image->page.x || next_image->page.y)
13185 need_defi=MagickTrue;
13187 if (next_image->alpha_trait == BlendPixelTrait)
13188 need_matte=MagickTrue;
13190 if ((int) next_image->dispose >= BackgroundDispose)
13191 if ((next_image->alpha_trait == BlendPixelTrait) ||
13192 next_image->page.x || next_image->page.y ||
13193 ((next_image->columns < mng_info->page.width) &&
13194 (next_image->rows < mng_info->page.height)))
13195 mng_info->need_fram=MagickTrue;
13197 if (next_image->iterations)
13198 need_iterations=MagickTrue;
13200 final_delay=next_image->delay;
13202 if (final_delay != initial_delay || final_delay > 1UL*
13203 next_image->ticks_per_second)
13204 mng_info->need_fram=1;
13206 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13207 defined(PNG_MNG_FEATURES_SUPPORTED)
13209 check for global palette possibility.
13211 if (image->alpha_trait == BlendPixelTrait)
13212 need_local_plte=MagickTrue;
13214 if (need_local_plte == 0)
13216 if (IsImageGray(image,exception) == MagickFalse)
13217 all_images_are_gray=MagickFalse;
13218 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13219 if (use_global_plte == 0)
13220 use_global_plte=mng_info->equal_palettes;
13221 need_local_plte=!mng_info->equal_palettes;
13224 if (GetNextImageInList(next_image) != (Image *) NULL)
13226 if (next_image->background_color.red !=
13227 next_image->next->background_color.red ||
13228 next_image->background_color.green !=
13229 next_image->next->background_color.green ||
13230 next_image->background_color.blue !=
13231 next_image->next->background_color.blue)
13232 mng_info->equal_backgrounds=MagickFalse;
13234 if (next_image->gamma != next_image->next->gamma)
13235 mng_info->equal_gammas=MagickFalse;
13237 if (next_image->rendering_intent !=
13238 next_image->next->rendering_intent)
13239 mng_info->equal_srgbs=MagickFalse;
13241 if ((next_image->units != next_image->next->units) ||
13242 (next_image->resolution.x != next_image->next->resolution.x) ||
13243 (next_image->resolution.y != next_image->next->resolution.y))
13244 mng_info->equal_physs=MagickFalse;
13246 if (mng_info->equal_chrms)
13248 if (next_image->chromaticity.red_primary.x !=
13249 next_image->next->chromaticity.red_primary.x ||
13250 next_image->chromaticity.red_primary.y !=
13251 next_image->next->chromaticity.red_primary.y ||
13252 next_image->chromaticity.green_primary.x !=
13253 next_image->next->chromaticity.green_primary.x ||
13254 next_image->chromaticity.green_primary.y !=
13255 next_image->next->chromaticity.green_primary.y ||
13256 next_image->chromaticity.blue_primary.x !=
13257 next_image->next->chromaticity.blue_primary.x ||
13258 next_image->chromaticity.blue_primary.y !=
13259 next_image->next->chromaticity.blue_primary.y ||
13260 next_image->chromaticity.white_point.x !=
13261 next_image->next->chromaticity.white_point.x ||
13262 next_image->chromaticity.white_point.y !=
13263 next_image->next->chromaticity.white_point.y)
13264 mng_info->equal_chrms=MagickFalse;
13268 next_image=GetNextImageInList(next_image);
13270 if (image_count < 2)
13272 mng_info->equal_backgrounds=MagickFalse;
13273 mng_info->equal_chrms=MagickFalse;
13274 mng_info->equal_gammas=MagickFalse;
13275 mng_info->equal_srgbs=MagickFalse;
13276 mng_info->equal_physs=MagickFalse;
13277 use_global_plte=MagickFalse;
13278 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13279 need_local_plte=MagickTrue;
13281 need_iterations=MagickFalse;
13284 if (mng_info->need_fram == MagickFalse)
13287 Only certain framing rates 100/n are exactly representable without
13288 the FRAM chunk but we'll allow some slop in VLC files
13290 if (final_delay == 0)
13292 if (need_iterations != MagickFalse)
13295 It's probably a GIF with loop; don't run it *too* fast.
13297 if (mng_info->adjoin)
13300 (void) ThrowMagickException(exception,GetMagickModule(),
13302 "input has zero delay between all frames; assuming",
13307 mng_info->ticks_per_second=0;
13309 if (final_delay != 0)
13310 mng_info->ticks_per_second=(png_uint_32)
13311 (image->ticks_per_second/final_delay);
13312 if (final_delay > 50)
13313 mng_info->ticks_per_second=2;
13315 if (final_delay > 75)
13316 mng_info->ticks_per_second=1;
13318 if (final_delay > 125)
13319 mng_info->need_fram=MagickTrue;
13321 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13322 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13323 (final_delay != 25) && (final_delay != 50) && (final_delay !=
13324 1UL*image->ticks_per_second))
13325 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13328 if (mng_info->need_fram != MagickFalse)
13329 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13331 If pseudocolor, we should also check to see if all the
13332 palettes are identical and write a global PLTE if they are.
13336 Write the MNG version 1.0 signature and MHDR chunk.
13338 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13339 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13340 PNGType(chunk,mng_MHDR);
13341 LogPNGChunk(logging,mng_MHDR,28L);
13342 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13343 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13344 PNGLong(chunk+12,mng_info->ticks_per_second);
13345 PNGLong(chunk+16,0L); /* layer count=unknown */
13346 PNGLong(chunk+20,0L); /* frame count=unknown */
13347 PNGLong(chunk+24,0L); /* play time=unknown */
13352 if (need_defi || mng_info->need_fram || use_global_plte)
13353 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13356 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13361 if (need_defi || mng_info->need_fram || use_global_plte)
13362 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13365 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13373 if (need_defi || mng_info->need_fram || use_global_plte)
13374 PNGLong(chunk+28,11L); /* simplicity=LC */
13377 PNGLong(chunk+28,9L); /* simplicity=VLC */
13382 if (need_defi || mng_info->need_fram || use_global_plte)
13383 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13386 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13389 (void) WriteBlob(image,32,chunk);
13390 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13391 option=GetImageOption(image_info,"mng:need-cacheoff");
13392 if (option != (const char *) NULL)
13398 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13400 PNGType(chunk,mng_nEED);
13401 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13402 (void) WriteBlobMSBULong(image,(size_t) length);
13403 LogPNGChunk(logging,mng_nEED,(size_t) length);
13405 (void) WriteBlob(image,length,chunk);
13406 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13408 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13409 (GetNextImageInList(image) != (Image *) NULL) &&
13410 (image->iterations != 1))
13413 Write MNG TERM chunk
13415 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13416 PNGType(chunk,mng_TERM);
13417 LogPNGChunk(logging,mng_TERM,10L);
13418 chunk[4]=3; /* repeat animation */
13419 chunk[5]=0; /* show last frame when done */
13420 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13421 final_delay/MagickMax(image->ticks_per_second,1)));
13423 if (image->iterations == 0)
13424 PNGLong(chunk+10,PNG_UINT_31_MAX);
13427 PNGLong(chunk+10,(png_uint_32) image->iterations);
13429 if (logging != MagickFalse)
13431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13432 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13433 final_delay/MagickMax(image->ticks_per_second,1)));
13435 if (image->iterations == 0)
13436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13437 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13441 " Image iterations: %.20g",(double) image->iterations);
13443 (void) WriteBlob(image,14,chunk);
13444 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13447 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13449 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13450 mng_info->equal_srgbs)
13453 Write MNG sRGB chunk
13455 (void) WriteBlobMSBULong(image,1L);
13456 PNGType(chunk,mng_sRGB);
13457 LogPNGChunk(logging,mng_sRGB,1L);
13459 if (image->rendering_intent != UndefinedIntent)
13460 chunk[4]=(unsigned char)
13461 Magick_RenderingIntent_to_PNG_RenderingIntent(
13462 (image->rendering_intent));
13465 chunk[4]=(unsigned char)
13466 Magick_RenderingIntent_to_PNG_RenderingIntent(
13467 (PerceptualIntent));
13469 (void) WriteBlob(image,5,chunk);
13470 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13471 mng_info->have_write_global_srgb=MagickTrue;
13476 if (image->gamma && mng_info->equal_gammas)
13479 Write MNG gAMA chunk
13481 (void) WriteBlobMSBULong(image,4L);
13482 PNGType(chunk,mng_gAMA);
13483 LogPNGChunk(logging,mng_gAMA,4L);
13484 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13485 (void) WriteBlob(image,8,chunk);
13486 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13487 mng_info->have_write_global_gama=MagickTrue;
13489 if (mng_info->equal_chrms)
13495 Write MNG cHRM chunk
13497 (void) WriteBlobMSBULong(image,32L);
13498 PNGType(chunk,mng_cHRM);
13499 LogPNGChunk(logging,mng_cHRM,32L);
13500 primary=image->chromaticity.white_point;
13501 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13502 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13503 primary=image->chromaticity.red_primary;
13504 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13505 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13506 primary=image->chromaticity.green_primary;
13507 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13508 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13509 primary=image->chromaticity.blue_primary;
13510 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13511 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13512 (void) WriteBlob(image,36,chunk);
13513 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13514 mng_info->have_write_global_chrm=MagickTrue;
13517 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13520 Write MNG pHYs chunk
13522 (void) WriteBlobMSBULong(image,9L);
13523 PNGType(chunk,mng_pHYs);
13524 LogPNGChunk(logging,mng_pHYs,9L);
13526 if (image->units == PixelsPerInchResolution)
13528 PNGLong(chunk+4,(png_uint_32)
13529 (image->resolution.x*100.0/2.54+0.5));
13531 PNGLong(chunk+8,(png_uint_32)
13532 (image->resolution.y*100.0/2.54+0.5));
13539 if (image->units == PixelsPerCentimeterResolution)
13541 PNGLong(chunk+4,(png_uint_32)
13542 (image->resolution.x*100.0+0.5));
13544 PNGLong(chunk+8,(png_uint_32)
13545 (image->resolution.y*100.0+0.5));
13552 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13553 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13557 (void) WriteBlob(image,13,chunk);
13558 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13561 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13562 or does not cover the entire frame.
13564 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13565 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13566 (image->page.width+image->page.x < mng_info->page.width))
13567 || (image->page.height && (image->page.height+image->page.y
13568 < mng_info->page.height))))
13570 (void) WriteBlobMSBULong(image,6L);
13571 PNGType(chunk,mng_BACK);
13572 LogPNGChunk(logging,mng_BACK,6L);
13573 red=ScaleQuantumToShort(image->background_color.red);
13574 green=ScaleQuantumToShort(image->background_color.green);
13575 blue=ScaleQuantumToShort(image->background_color.blue);
13576 PNGShort(chunk+4,red);
13577 PNGShort(chunk+6,green);
13578 PNGShort(chunk+8,blue);
13579 (void) WriteBlob(image,10,chunk);
13580 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13581 if (mng_info->equal_backgrounds)
13583 (void) WriteBlobMSBULong(image,6L);
13584 PNGType(chunk,mng_bKGD);
13585 LogPNGChunk(logging,mng_bKGD,6L);
13586 (void) WriteBlob(image,10,chunk);
13587 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13591 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13592 if ((need_local_plte == MagickFalse) &&
13593 (image->storage_class == PseudoClass) &&
13594 (all_images_are_gray == MagickFalse))
13600 Write MNG PLTE chunk
13602 data_length=3*image->colors;
13603 (void) WriteBlobMSBULong(image,data_length);
13604 PNGType(chunk,mng_PLTE);
13605 LogPNGChunk(logging,mng_PLTE,data_length);
13607 for (i=0; i < (ssize_t) image->colors; i++)
13609 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13610 image->colormap[i].red) & 0xff);
13611 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13612 image->colormap[i].green) & 0xff);
13613 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13614 image->colormap[i].blue) & 0xff);
13617 (void) WriteBlob(image,data_length+4,chunk);
13618 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13619 mng_info->have_write_global_plte=MagickTrue;
13625 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13626 defined(PNG_MNG_FEATURES_SUPPORTED)
13627 mng_info->equal_palettes=MagickFalse;
13631 if (mng_info->adjoin)
13633 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13634 defined(PNG_MNG_FEATURES_SUPPORTED)
13636 If we aren't using a global palette for the entire MNG, check to
13637 see if we can use one for two or more consecutive images.
13639 if (need_local_plte && use_global_plte && !all_images_are_gray)
13641 if (mng_info->IsPalette)
13644 When equal_palettes is true, this image has the same palette
13645 as the previous PseudoClass image
13647 mng_info->have_write_global_plte=mng_info->equal_palettes;
13648 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13649 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13652 Write MNG PLTE chunk
13657 data_length=3*image->colors;
13658 (void) WriteBlobMSBULong(image,data_length);
13659 PNGType(chunk,mng_PLTE);
13660 LogPNGChunk(logging,mng_PLTE,data_length);
13662 for (i=0; i < (ssize_t) image->colors; i++)
13664 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13665 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13666 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13669 (void) WriteBlob(image,data_length+4,chunk);
13670 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13671 (uInt) (data_length+4)));
13672 mng_info->have_write_global_plte=MagickTrue;
13676 mng_info->have_write_global_plte=MagickFalse;
13687 previous_x=mng_info->page.x;
13688 previous_y=mng_info->page.y;
13695 mng_info->page=image->page;
13696 if ((mng_info->page.x != previous_x) ||
13697 (mng_info->page.y != previous_y))
13699 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13700 PNGType(chunk,mng_DEFI);
13701 LogPNGChunk(logging,mng_DEFI,12L);
13702 chunk[4]=0; /* object 0 MSB */
13703 chunk[5]=0; /* object 0 LSB */
13704 chunk[6]=0; /* visible */
13705 chunk[7]=0; /* abstract */
13706 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13707 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13708 (void) WriteBlob(image,16,chunk);
13709 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13714 mng_info->write_mng=write_mng;
13716 if ((int) image->dispose >= 3)
13717 mng_info->framing_mode=3;
13719 if (mng_info->need_fram && mng_info->adjoin &&
13720 ((image->delay != mng_info->delay) ||
13721 (mng_info->framing_mode != mng_info->old_framing_mode)))
13723 if (image->delay == mng_info->delay)
13726 Write a MNG FRAM chunk with the new framing mode.
13728 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13729 PNGType(chunk,mng_FRAM);
13730 LogPNGChunk(logging,mng_FRAM,1L);
13731 chunk[4]=(unsigned char) mng_info->framing_mode;
13732 (void) WriteBlob(image,5,chunk);
13733 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13738 Write a MNG FRAM chunk with the delay.
13740 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13741 PNGType(chunk,mng_FRAM);
13742 LogPNGChunk(logging,mng_FRAM,10L);
13743 chunk[4]=(unsigned char) mng_info->framing_mode;
13744 chunk[5]=0; /* frame name separator (no name) */
13745 chunk[6]=2; /* flag for changing default delay */
13746 chunk[7]=0; /* flag for changing frame timeout */
13747 chunk[8]=0; /* flag for changing frame clipping */
13748 chunk[9]=0; /* flag for changing frame sync_id */
13749 PNGLong(chunk+10,(png_uint_32)
13750 ((mng_info->ticks_per_second*
13751 image->delay)/MagickMax(image->ticks_per_second,1)));
13752 (void) WriteBlob(image,14,chunk);
13753 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13754 mng_info->delay=(png_uint_32) image->delay;
13756 mng_info->old_framing_mode=mng_info->framing_mode;
13759 #if defined(JNG_SUPPORTED)
13760 if (image_info->compression == JPEGCompression)
13765 if (logging != MagickFalse)
13766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13767 " Writing JNG object.");
13768 /* To do: specify the desired alpha compression method. */
13769 write_info=CloneImageInfo(image_info);
13770 write_info->compression=UndefinedCompression;
13771 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13772 write_info=DestroyImageInfo(write_info);
13777 if (logging != MagickFalse)
13778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13779 " Writing PNG object.");
13781 mng_info->need_blob = MagickFalse;
13782 mng_info->ping_preserve_colormap = MagickFalse;
13784 /* We don't want any ancillary chunks written */
13785 mng_info->ping_exclude_bKGD=MagickTrue;
13786 mng_info->ping_exclude_cHRM=MagickTrue;
13787 mng_info->ping_exclude_date=MagickTrue;
13788 mng_info->ping_exclude_EXIF=MagickTrue;
13789 mng_info->ping_exclude_gAMA=MagickTrue;
13790 mng_info->ping_exclude_iCCP=MagickTrue;
13791 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13792 mng_info->ping_exclude_oFFs=MagickTrue;
13793 mng_info->ping_exclude_pHYs=MagickTrue;
13794 mng_info->ping_exclude_sRGB=MagickTrue;
13795 mng_info->ping_exclude_tEXt=MagickTrue;
13796 mng_info->ping_exclude_tRNS=MagickTrue;
13797 mng_info->ping_exclude_vpAg=MagickTrue;
13798 mng_info->ping_exclude_zCCP=MagickTrue;
13799 mng_info->ping_exclude_zTXt=MagickTrue;
13801 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13804 if (status == MagickFalse)
13806 MngInfoFreeStruct(mng_info,&have_mng_structure);
13807 (void) CloseBlob(image);
13808 return(MagickFalse);
13810 (void) CatchImageException(image);
13811 if (GetNextImageInList(image) == (Image *) NULL)
13813 image=SyncNextImageInList(image);
13814 status=SetImageProgress(image,SaveImagesTag,scene++,
13815 GetImageListLength(image));
13817 if (status == MagickFalse)
13820 } while (mng_info->adjoin);
13824 while (GetPreviousImageInList(image) != (Image *) NULL)
13825 image=GetPreviousImageInList(image);
13827 Write the MEND chunk.
13829 (void) WriteBlobMSBULong(image,0x00000000L);
13830 PNGType(chunk,mng_MEND);
13831 LogPNGChunk(logging,mng_MEND,0L);
13832 (void) WriteBlob(image,4,chunk);
13833 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13836 Relinquish resources.
13838 (void) CloseBlob(image);
13839 MngInfoFreeStruct(mng_info,&have_mng_structure);
13841 if (logging != MagickFalse)
13842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13844 return(MagickTrue);
13846 #else /* PNG_LIBPNG_VER > 10011 */
13848 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13851 printf("Your PNG library is too old: You have libpng-%s\n",
13852 PNG_LIBPNG_VER_STRING);
13854 ThrowBinaryException(CoderError,"PNG library is too old",
13855 image_info->filename);
13858 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13860 return(WritePNGImage(image_info,image));
13862 #endif /* PNG_LIBPNG_VER > 10011 */