2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2014 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 /* Swap bytes if requested */
2465 if (ping_file_depth == 16)
2470 ResetImagePropertyIterator(image);
2472 while (property != (const char *) NULL)
2474 GetImageProperty(image,property,exception);
2476 if (LocaleNCompare(property,"png:swap-bytes",14) != 0)
2479 property=GetNextImageProperty(image);
2483 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2488 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2489 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2491 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2492 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2495 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2498 (void) png_get_bKGD(ping, ping_info, &ping_background);
2500 if (ping_bit_depth < 8)
2502 png_set_packing(ping);
2506 image->depth=ping_bit_depth;
2507 image->depth=GetImageQuantumDepth(image,MagickFalse);
2508 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2510 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2511 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2513 image->rendering_intent=UndefinedIntent;
2514 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2516 (void) ResetMagickMemory(&image->chromaticity,0,
2517 sizeof(image->chromaticity));
2520 if (logging != MagickFalse)
2522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2523 " PNG width: %.20g, height: %.20g",
2524 (double) ping_width, (double) ping_height);
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " PNG color_type: %d, bit_depth: %d",
2528 ping_color_type, ping_bit_depth);
2530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2531 " PNG compression_method: %d",
2532 ping_compression_method);
2534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2535 " PNG interlace_method: %d, filter_method: %d",
2536 ping_interlace_method,ping_filter_method);
2539 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2541 ping_found_iCCP=MagickTrue;
2542 if (logging != MagickFalse)
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " Found PNG iCCP chunk.");
2547 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2549 ping_found_gAMA=MagickTrue;
2550 if (logging != MagickFalse)
2551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2552 " Found PNG gAMA chunk.");
2555 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2557 ping_found_cHRM=MagickTrue;
2558 if (logging != MagickFalse)
2559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2560 " Found PNG cHRM chunk.");
2563 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2566 ping_found_sRGB=MagickTrue;
2567 if (logging != MagickFalse)
2568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2569 " Found PNG sRGB chunk.");
2572 #ifdef PNG_READ_iCCP_SUPPORTED
2573 if (ping_found_iCCP !=MagickTrue &&
2574 ping_found_sRGB != MagickTrue &&
2575 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2577 ping_found_iCCP=MagickTrue;
2578 if (logging != MagickFalse)
2579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2580 " Found PNG iCCP chunk.");
2583 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2588 #if (PNG_LIBPNG_VER < 10500)
2602 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2605 if (profile_length != 0)
2610 if (logging != MagickFalse)
2611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2612 " Reading PNG iCCP chunk.");
2614 profile=BlobToStringInfo(info,profile_length);
2616 if (profile == (StringInfo *) NULL)
2618 png_warning(ping, "ICC profile is NULL");
2619 profile=DestroyStringInfo(profile);
2623 if (ping_preserve_iCCP == MagickFalse)
2637 length=(png_uint_32) GetStringInfoLength(profile);
2639 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2641 if (length == sRGB_info[icheck].len)
2645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2646 " Got a %lu-byte ICC profile (potentially sRGB)",
2647 (unsigned long) length);
2649 data=GetStringInfoDatum(profile);
2650 profile_crc=crc32(0,data,length);
2652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2653 " with crc=%8x",(unsigned int) profile_crc);
2657 if (profile_crc == sRGB_info[icheck].crc)
2659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2660 " It is sRGB with rendering intent = %s",
2661 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2662 sRGB_info[icheck].intent));
2663 if (image->rendering_intent==UndefinedIntent)
2665 image->rendering_intent=
2666 Magick_RenderingIntent_from_PNG_RenderingIntent(
2667 sRGB_info[icheck].intent);
2673 if (sRGB_info[icheck].len == 0)
2675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2676 " Got a %lu-byte ICC profile not recognized as sRGB",
2677 (unsigned long) length);
2678 (void) SetImageProfile(image,"icc",profile,exception);
2681 else /* Preserve-iCCP */
2683 (void) SetImageProfile(image,"icc",profile,exception);
2686 profile=DestroyStringInfo(profile);
2692 #if defined(PNG_READ_sRGB_SUPPORTED)
2694 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2697 if (png_get_sRGB(ping,ping_info,&intent))
2699 if (image->rendering_intent == UndefinedIntent)
2700 image->rendering_intent=
2701 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2703 if (logging != MagickFalse)
2704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2705 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2709 else if (mng_info->have_global_srgb)
2711 if (image->rendering_intent == UndefinedIntent)
2712 image->rendering_intent=
2713 Magick_RenderingIntent_from_PNG_RenderingIntent
2714 (mng_info->global_srgb_intent);
2721 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2722 if (mng_info->have_global_gama)
2723 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2725 if (png_get_gAMA(ping,ping_info,&file_gamma))
2727 image->gamma=(float) file_gamma;
2728 if (logging != MagickFalse)
2729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2730 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2734 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2736 if (mng_info->have_global_chrm != MagickFalse)
2738 (void) png_set_cHRM(ping,ping_info,
2739 mng_info->global_chrm.white_point.x,
2740 mng_info->global_chrm.white_point.y,
2741 mng_info->global_chrm.red_primary.x,
2742 mng_info->global_chrm.red_primary.y,
2743 mng_info->global_chrm.green_primary.x,
2744 mng_info->global_chrm.green_primary.y,
2745 mng_info->global_chrm.blue_primary.x,
2746 mng_info->global_chrm.blue_primary.y);
2750 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2752 (void) png_get_cHRM(ping,ping_info,
2753 &image->chromaticity.white_point.x,
2754 &image->chromaticity.white_point.y,
2755 &image->chromaticity.red_primary.x,
2756 &image->chromaticity.red_primary.y,
2757 &image->chromaticity.green_primary.x,
2758 &image->chromaticity.green_primary.y,
2759 &image->chromaticity.blue_primary.x,
2760 &image->chromaticity.blue_primary.y);
2762 ping_found_cHRM=MagickTrue;
2764 if (image->chromaticity.red_primary.x>0.6399f &&
2765 image->chromaticity.red_primary.x<0.6401f &&
2766 image->chromaticity.red_primary.y>0.3299f &&
2767 image->chromaticity.red_primary.y<0.3301f &&
2768 image->chromaticity.green_primary.x>0.2999f &&
2769 image->chromaticity.green_primary.x<0.3001f &&
2770 image->chromaticity.green_primary.y>0.5999f &&
2771 image->chromaticity.green_primary.y<0.6001f &&
2772 image->chromaticity.blue_primary.x>0.1499f &&
2773 image->chromaticity.blue_primary.x<0.1501f &&
2774 image->chromaticity.blue_primary.y>0.0599f &&
2775 image->chromaticity.blue_primary.y<0.0601f &&
2776 image->chromaticity.white_point.x>0.3126f &&
2777 image->chromaticity.white_point.x<0.3128f &&
2778 image->chromaticity.white_point.y>0.3289f &&
2779 image->chromaticity.white_point.y<0.3291f)
2780 ping_found_sRGB_cHRM=MagickTrue;
2783 if (image->rendering_intent != UndefinedIntent)
2785 if (ping_found_sRGB != MagickTrue &&
2786 (ping_found_gAMA != MagickTrue ||
2787 (image->gamma > .45 && image->gamma < .46)) &&
2788 (ping_found_cHRM != MagickTrue ||
2789 ping_found_sRGB_cHRM != MagickFalse) &&
2790 ping_found_iCCP != MagickTrue)
2792 png_set_sRGB(ping,ping_info,
2793 Magick_RenderingIntent_to_PNG_RenderingIntent
2794 (image->rendering_intent));
2795 file_gamma=1.000f/2.200f;
2796 ping_found_sRGB=MagickTrue;
2797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798 " Setting sRGB as if in input");
2802 #if defined(PNG_oFFs_SUPPORTED)
2803 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2805 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2806 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2808 if (logging != MagickFalse)
2809 if (image->page.x || image->page.y)
2810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2811 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2812 image->page.x,(double) image->page.y);
2815 #if defined(PNG_pHYs_SUPPORTED)
2816 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2818 if (mng_info->have_global_phys)
2820 png_set_pHYs(ping,ping_info,
2821 mng_info->global_x_pixels_per_unit,
2822 mng_info->global_y_pixels_per_unit,
2823 mng_info->global_phys_unit_type);
2830 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2833 Set image resolution.
2835 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2837 image->resolution.x=(double) x_resolution;
2838 image->resolution.y=(double) y_resolution;
2840 if (unit_type == PNG_RESOLUTION_METER)
2842 image->units=PixelsPerCentimeterResolution;
2843 image->resolution.x=(double) x_resolution/100.0;
2844 image->resolution.y=(double) y_resolution/100.0;
2847 if (logging != MagickFalse)
2848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2849 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2850 (double) x_resolution,(double) y_resolution,unit_type);
2854 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2859 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2861 if ((number_colors == 0) &&
2862 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2864 if (mng_info->global_plte_length)
2866 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2867 (int) mng_info->global_plte_length);
2869 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2871 if (mng_info->global_trns_length)
2874 "global tRNS has more entries than global PLTE");
2878 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2879 (int) mng_info->global_trns_length,NULL);
2882 #ifdef PNG_READ_bKGD_SUPPORTED
2884 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2885 mng_info->have_saved_bkgd_index ||
2887 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2892 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2893 if (mng_info->have_saved_bkgd_index)
2894 background.index=mng_info->saved_bkgd_index;
2896 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2897 background.index=ping_background->index;
2899 background.red=(png_uint_16)
2900 mng_info->global_plte[background.index].red;
2902 background.green=(png_uint_16)
2903 mng_info->global_plte[background.index].green;
2905 background.blue=(png_uint_16)
2906 mng_info->global_plte[background.index].blue;
2908 background.gray=(png_uint_16)
2909 mng_info->global_plte[background.index].green;
2911 png_set_bKGD(ping,ping_info,&background);
2916 png_error(ping,"No global PLTE in file");
2920 #ifdef PNG_READ_bKGD_SUPPORTED
2921 if (mng_info->have_global_bkgd &&
2922 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2923 image->background_color=mng_info->mng_global_bkgd;
2925 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2931 Set image background color.
2933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " Reading PNG bKGD chunk.");
2937 /* Scale background components to 16-bit, then scale
2940 if (logging != MagickFalse)
2941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2942 " raw ping_background=(%d,%d,%d).",ping_background->red,
2943 ping_background->green,ping_background->blue);
2947 if (ping_file_depth == 1)
2950 else if (ping_file_depth == 2)
2953 else if (ping_file_depth == 4)
2956 if (ping_file_depth <= 8)
2959 ping_background->red *= bkgd_scale;
2960 ping_background->green *= bkgd_scale;
2961 ping_background->blue *= bkgd_scale;
2963 if (logging != MagickFalse)
2965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2966 " bkgd_scale=%d.",bkgd_scale);
2968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2969 " ping_background=(%d,%d,%d).",ping_background->red,
2970 ping_background->green,ping_background->blue);
2973 image->background_color.red=
2974 ScaleShortToQuantum(ping_background->red);
2976 image->background_color.green=
2977 ScaleShortToQuantum(ping_background->green);
2979 image->background_color.blue=
2980 ScaleShortToQuantum(ping_background->blue);
2982 image->background_color.alpha=OpaqueAlpha;
2984 if (logging != MagickFalse)
2985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2986 " image->background_color=(%.20g,%.20g,%.20g).",
2987 (double) image->background_color.red,
2988 (double) image->background_color.green,
2989 (double) image->background_color.blue);
2991 #endif /* PNG_READ_bKGD_SUPPORTED */
2993 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2996 Image has a tRNS chunk.
3004 if (logging != MagickFalse)
3005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3006 " Reading PNG tRNS chunk.");
3008 max_sample = (int) ((one << ping_file_depth) - 1);
3010 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3011 (int)ping_trans_color->gray > max_sample) ||
3012 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3013 ((int)ping_trans_color->red > max_sample ||
3014 (int)ping_trans_color->green > max_sample ||
3015 (int)ping_trans_color->blue > max_sample)))
3017 if (logging != MagickFalse)
3018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3019 " Ignoring PNG tRNS chunk with out-of-range sample.");
3020 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3021 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3022 image->alpha_trait=UndefinedPixelTrait;
3029 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3031 /* Scale transparent_color to short */
3032 transparent_color.red= scale_to_short*ping_trans_color->red;
3033 transparent_color.green= scale_to_short*ping_trans_color->green;
3034 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3035 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3037 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3039 if (logging != MagickFalse)
3041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3042 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
3044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3045 " scaled graylevel is %.20g.",transparent_color.alpha);
3047 transparent_color.red=transparent_color.alpha;
3048 transparent_color.green=transparent_color.alpha;
3049 transparent_color.blue=transparent_color.alpha;
3053 #if defined(PNG_READ_sBIT_SUPPORTED)
3054 if (mng_info->have_global_sbit)
3056 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3057 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3060 num_passes=png_set_interlace_handling(ping);
3062 png_read_update_info(ping,ping_info);
3064 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3067 Initialize image structure.
3069 mng_info->image_box.left=0;
3070 mng_info->image_box.right=(ssize_t) ping_width;
3071 mng_info->image_box.top=0;
3072 mng_info->image_box.bottom=(ssize_t) ping_height;
3073 if (mng_info->mng_type == 0)
3075 mng_info->mng_width=ping_width;
3076 mng_info->mng_height=ping_height;
3077 mng_info->frame=mng_info->image_box;
3078 mng_info->clip=mng_info->image_box;
3083 image->page.y=mng_info->y_off[mng_info->object_id];
3086 image->compression=ZipCompression;
3087 image->columns=ping_width;
3088 image->rows=ping_height;
3090 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3091 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3093 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
3094 image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
3096 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
3097 * image->colorspace to GRAY, and reset image->chromaticity.
3099 image->intensity = Rec709LuminancePixelIntensityMethod;
3100 SetImageColorspace(image,GRAYColorspace,exception);
3104 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3105 " image->colorspace=%d",(int) image->colorspace);
3107 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3108 ((int) ping_bit_depth < 16 &&
3109 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3114 image->storage_class=PseudoClass;
3116 image->colors=one << ping_file_depth;
3117 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3118 if (image->colors > 256)
3121 if (image->colors > 65536L)
3122 image->colors=65536L;
3124 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3129 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3130 image->colors=(size_t) number_colors;
3132 if (logging != MagickFalse)
3133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3134 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3138 if (image->storage_class == PseudoClass)
3141 Initialize image colormap.
3143 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3144 png_error(ping,"Memory allocation failed");
3146 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3151 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3153 for (i=0; i < (ssize_t) number_colors; i++)
3155 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3156 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3157 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3160 for ( ; i < (ssize_t) image->colors; i++)
3162 image->colormap[i].red=0;
3163 image->colormap[i].green=0;
3164 image->colormap[i].blue=0;
3173 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3178 for (i=0; i < (ssize_t) image->colors; i++)
3180 image->colormap[i].red=(Quantum) (i*scale);
3181 image->colormap[i].green=(Quantum) (i*scale);
3182 image->colormap[i].blue=(Quantum) (i*scale);
3187 /* Set some properties for reporting by "identify" */
3192 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3193 ping_interlace_method in value */
3195 (void) FormatLocaleString(msg,MaxTextExtent,
3196 "%d, %d",(int) ping_width, (int) ping_height);
3197 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3199 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3200 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3202 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3203 (int) ping_color_type,
3204 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3205 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3207 if (ping_interlace_method == 0)
3209 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3210 (int) ping_interlace_method);
3212 else if (ping_interlace_method == 1)
3214 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3215 (int) ping_interlace_method);
3219 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3220 (int) ping_interlace_method);
3222 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3224 if (number_colors != 0)
3226 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3227 (int) number_colors);
3228 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3234 Read image scanlines.
3236 if (image->delay != 0)
3237 mng_info->scenes_found++;
3239 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3240 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3241 (image_info->first_scene+image_info->number_scenes))))
3243 /* This happens later in non-ping decodes */
3244 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3245 image->storage_class=DirectClass;
3247 if (logging != MagickFalse)
3248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249 " Skipping PNG image data for scene %.20g",(double)
3250 mng_info->scenes_found-1);
3251 png_destroy_read_struct(&ping,&ping_info,&end_info);
3253 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3254 UnlockSemaphoreInfo(ping_semaphore);
3257 if (logging != MagickFalse)
3258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3259 " exit ReadOnePNGImage().");
3264 if (logging != MagickFalse)
3265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3266 " Reading PNG IDAT chunk(s)");
3269 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3270 sizeof(*ping_pixels));
3272 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3274 if (pixel_info == (MemoryInfo *) NULL)
3275 png_error(ping,"Memory allocation failed");
3276 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3278 if (logging != MagickFalse)
3279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3280 " Converting PNG pixels to pixel packets");
3282 Convert PNG pixels to pixel packets.
3284 quantum_info=AcquireQuantumInfo(image_info,image);
3286 if (quantum_info == (QuantumInfo *) NULL)
3287 png_error(ping,"Failed to allocate quantum_info");
3289 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3294 found_transparent_pixel;
3296 found_transparent_pixel=MagickFalse;
3298 if (image->storage_class == DirectClass)
3300 for (pass=0; pass < num_passes; pass++)
3303 Convert image to DirectClass pixel packets.
3305 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3306 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3307 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3308 BlendPixelTrait : UndefinedPixelTrait;
3310 for (y=0; y < (ssize_t) image->rows; y++)
3313 row_offset=ping_rowbytes*y;
3318 png_read_row(ping,ping_pixels+row_offset,NULL);
3320 if (pass < num_passes-1)
3323 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3325 if (q == (Quantum *) NULL)
3328 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3329 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3330 GrayQuantum,ping_pixels+row_offset,exception);
3332 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3333 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3334 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3336 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3337 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3338 RGBAQuantum,ping_pixels+row_offset,exception);
3340 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3341 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3342 IndexQuantum,ping_pixels+row_offset,exception);
3344 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3345 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3346 RGBQuantum,ping_pixels+row_offset,exception);
3348 if (found_transparent_pixel == MagickFalse)
3350 /* Is there a transparent pixel in the row? */
3351 if (y== 0 && logging != MagickFalse)
3352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3353 " Looking for cheap transparent pixel");
3355 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3357 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3358 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3359 (GetPixelAlpha(image,q) != OpaqueAlpha))
3361 if (logging != MagickFalse)
3362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3365 found_transparent_pixel = MagickTrue;
3368 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3369 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3370 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3371 transparent_color.red &&
3372 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3373 transparent_color.green &&
3374 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3375 transparent_color.blue))
3377 if (logging != MagickFalse)
3378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380 found_transparent_pixel = MagickTrue;
3383 q+=GetPixelChannels(image);
3387 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3389 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3392 if (status == MagickFalse)
3395 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3399 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3401 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3402 if (status == MagickFalse)
3408 else /* image->storage_class != DirectClass */
3410 for (pass=0; pass < num_passes; pass++)
3419 Convert grayscale image to PseudoClass pixel packets.
3421 if (logging != MagickFalse)
3422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3423 " Converting grayscale pixels to pixel packets");
3425 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3426 BlendPixelTrait : UndefinedPixelTrait;
3428 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3429 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3430 sizeof(*quantum_scanline));
3432 if (quantum_scanline == (Quantum *) NULL)
3433 png_error(ping,"Memory allocation failed");
3435 for (y=0; y < (ssize_t) image->rows; y++)
3441 row_offset=ping_rowbytes*y;
3446 png_read_row(ping,ping_pixels+row_offset,NULL);
3448 if (pass < num_passes-1)
3451 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3453 if (q == (Quantum *) NULL)
3456 p=ping_pixels+row_offset;
3459 switch (ping_bit_depth)
3464 if (ping_color_type == 4)
3465 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3469 alpha=ScaleCharToQuantum((unsigned char)*p++);
3471 SetPixelAlpha(image,alpha,q);
3473 if (alpha != OpaqueAlpha)
3474 found_transparent_pixel = MagickTrue;
3476 q+=GetPixelChannels(image);
3480 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3488 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3490 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3494 if (image->colors > 256)
3495 quantum=((*p++) << 8);
3501 *r=ScaleShortToQuantum(quantum);
3504 if (ping_color_type == 4)
3506 if (image->colors > 256)
3507 quantum=((*p++) << 8);
3513 alpha=ScaleShortToQuantum(quantum);
3514 SetPixelAlpha(image,alpha,q);
3516 if (alpha != OpaqueAlpha)
3517 found_transparent_pixel = MagickTrue;
3519 q+=GetPixelChannels(image);
3522 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3524 p++; /* strip low byte */
3526 if (ping_color_type == 4)
3528 SetPixelAlpha(image,*p++,q);
3530 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3531 found_transparent_pixel = MagickTrue;
3534 q+=GetPixelChannels(image);
3547 Transfer image scanline.
3551 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3553 if (q == (Quantum *) NULL)
3555 for (x=0; x < (ssize_t) image->columns; x++)
3557 SetPixelIndex(image,*r++,q);
3558 q+=GetPixelChannels(image);
3561 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3564 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3566 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3569 if (status == MagickFalse)
3574 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3576 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3578 if (status == MagickFalse)
3582 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3585 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3586 UndefinedPixelTrait;
3588 if (logging != MagickFalse)
3590 if (found_transparent_pixel != MagickFalse)
3591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3592 " Found transparent pixel");
3595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3596 " No transparent pixel was found");
3598 ping_color_type&=0x03;
3603 if (quantum_info != (QuantumInfo *) NULL)
3604 quantum_info=DestroyQuantumInfo(quantum_info);
3606 if (image->storage_class == PseudoClass)
3611 alpha_trait=image->alpha_trait;
3612 image->alpha_trait=UndefinedPixelTrait;
3613 (void) SyncImage(image,exception);
3614 image->alpha_trait=alpha_trait;
3617 png_read_end(ping,end_info);
3619 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3620 (ssize_t) image_info->first_scene && image->delay != 0)
3622 png_destroy_read_struct(&ping,&ping_info,&end_info);
3623 pixel_info=RelinquishVirtualMemory(pixel_info);
3625 (void) SetImageBackgroundColor(image,exception);
3626 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3627 UnlockSemaphoreInfo(ping_semaphore);
3629 if (logging != MagickFalse)
3630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3631 " exit ReadOnePNGImage() early.");
3635 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3641 Image has a transparent background.
3643 storage_class=image->storage_class;
3644 image->alpha_trait=BlendPixelTrait;
3646 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3648 if (storage_class == PseudoClass)
3650 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3652 for (x=0; x < ping_num_trans; x++)
3654 image->colormap[x].alpha_trait=BlendPixelTrait;
3655 image->colormap[x].alpha =
3656 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3660 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3662 for (x=0; x < (int) image->colors; x++)
3664 if (ScaleQuantumToShort(image->colormap[x].red) ==
3665 transparent_color.alpha)
3667 image->colormap[x].alpha_trait=BlendPixelTrait;
3668 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3672 (void) SyncImage(image,exception);
3675 #if 1 /* Should have already been done above, but glennrp problem P10
3680 for (y=0; y < (ssize_t) image->rows; y++)
3682 image->storage_class=storage_class;
3683 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3685 if (q == (Quantum *) NULL)
3689 /* Caution: on a Q8 build, this does not distinguish between
3690 * 16-bit colors that differ only in the low byte
3692 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3694 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3695 transparent_color.red &&
3696 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3697 transparent_color.green &&
3698 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3699 transparent_color.blue)
3701 SetPixelAlpha(image,TransparentAlpha,q);
3704 #if 0 /* I have not found a case where this is needed. */
3707 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3711 q+=GetPixelChannels(image);
3714 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3720 image->storage_class=DirectClass;
3723 for (j = 0; j < 2; j++)
3726 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3727 MagickTrue : MagickFalse;
3729 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3730 MagickTrue : MagickFalse;
3732 if (status != MagickFalse)
3733 for (i=0; i < (ssize_t) num_text; i++)
3735 /* Check for a profile */
3737 if (logging != MagickFalse)
3738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3739 " Reading PNG text chunk");
3741 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3743 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3753 length=text[i].text_length;
3754 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3756 if (value == (char *) NULL)
3758 png_error(ping,"Memory allocation failed");
3762 (void) ConcatenateMagickString(value,text[i].text,length+2);
3764 /* Don't save "density" or "units" property if we have a pHYs
3767 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3768 (LocaleCompare(text[i].key,"density") != 0 &&
3769 LocaleCompare(text[i].key,"units") != 0))
3770 (void) SetImageProperty(image,text[i].key,value,exception);
3772 if (logging != MagickFalse)
3774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3775 " length: %lu",(unsigned long) length);
3776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3777 " Keyword: %s",text[i].key);
3780 value=DestroyString(value);
3783 num_text_total += num_text;
3786 #ifdef MNG_OBJECT_BUFFERS
3788 Store the object if necessary.
3790 if (object_id && !mng_info->frozen[object_id])
3792 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3795 create a new object buffer.
3797 mng_info->ob[object_id]=(MngBuffer *)
3798 AcquireMagickMemory(sizeof(MngBuffer));
3800 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3802 mng_info->ob[object_id]->image=(Image *) NULL;
3803 mng_info->ob[object_id]->reference_count=1;
3807 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3808 mng_info->ob[object_id]->frozen)
3810 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3811 png_error(ping,"Memory allocation failed");
3813 if (mng_info->ob[object_id]->frozen)
3814 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3820 if (mng_info->ob[object_id]->image != (Image *) NULL)
3821 mng_info->ob[object_id]->image=DestroyImage
3822 (mng_info->ob[object_id]->image);
3824 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3827 if (mng_info->ob[object_id]->image != (Image *) NULL)
3828 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3831 png_error(ping, "Cloning image for object buffer failed");
3833 if (ping_width > 250000L || ping_height > 250000L)
3834 png_error(ping,"PNG Image dimensions are too large.");
3836 mng_info->ob[object_id]->width=ping_width;
3837 mng_info->ob[object_id]->height=ping_height;
3838 mng_info->ob[object_id]->color_type=ping_color_type;
3839 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3840 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3841 mng_info->ob[object_id]->compression_method=
3842 ping_compression_method;
3843 mng_info->ob[object_id]->filter_method=ping_filter_method;
3845 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3851 Copy the PLTE to the object buffer.
3853 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3854 mng_info->ob[object_id]->plte_length=number_colors;
3856 for (i=0; i < number_colors; i++)
3858 mng_info->ob[object_id]->plte[i]=plte[i];
3863 mng_info->ob[object_id]->plte_length=0;
3868 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3869 * alpha or if a valid tRNS chunk is present, no matter whether there
3870 * is actual transparency present.
3872 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3873 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3874 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3875 BlendPixelTrait : UndefinedPixelTrait;
3877 #if 0 /* I'm not sure what's wrong here but it does not work. */
3878 if (image->alpha_trait == BlendPixelTrait)
3880 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3881 (void) SetImageType(image,GrayscaleMatteType,exception);
3883 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3884 (void) SetImageType(image,PaletteMatteType,exception);
3887 (void) SetImageType(image,TrueColorMatteType,exception);
3892 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3893 (void) SetImageType(image,GrayscaleType,exception);
3895 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3896 (void) SetImageType(image,PaletteType,exception);
3899 (void) SetImageType(image,TrueColorType,exception);
3903 /* Set more properties for identify to retrieve */
3908 if (num_text_total != 0)
3910 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3911 (void) FormatLocaleString(msg,MaxTextExtent,
3912 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3913 (void) SetImageProperty(image,"png:text",msg,
3917 if (num_raw_profiles != 0)
3919 (void) FormatLocaleString(msg,MaxTextExtent,
3920 "%d were found", num_raw_profiles);
3921 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3925 if (ping_found_cHRM != MagickFalse)
3927 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3928 "chunk was found (see Chromaticity, above)");
3929 (void) SetImageProperty(image,"png:cHRM",msg,
3933 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3935 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3936 "chunk was found (see Background color, above)");
3937 (void) SetImageProperty(image,"png:bKGD",msg,
3941 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3944 #if defined(PNG_iCCP_SUPPORTED)
3945 if (ping_found_iCCP != MagickFalse)
3946 (void) SetImageProperty(image,"png:iCCP",msg,
3950 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3951 (void) SetImageProperty(image,"png:tRNS",msg,
3954 #if defined(PNG_sRGB_SUPPORTED)
3955 if (ping_found_sRGB != MagickFalse)
3957 (void) FormatLocaleString(msg,MaxTextExtent,
3960 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3961 (void) SetImageProperty(image,"png:sRGB",msg,
3966 if (ping_found_gAMA != MagickFalse)
3968 (void) FormatLocaleString(msg,MaxTextExtent,
3969 "gamma=%.8g (See Gamma, above)",
3971 (void) SetImageProperty(image,"png:gAMA",msg,
3975 #if defined(PNG_pHYs_SUPPORTED)
3976 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3978 (void) FormatLocaleString(msg,MaxTextExtent,
3979 "x_res=%.10g, y_res=%.10g, units=%d",
3980 (double) x_resolution,(double) y_resolution, unit_type);
3981 (void) SetImageProperty(image,"png:pHYs",msg,
3986 #if defined(PNG_oFFs_SUPPORTED)
3987 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3989 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3990 (double) image->page.x,(double) image->page.y);
3991 (void) SetImageProperty(image,"png:oFFs",msg,
3996 if ((image->page.width != 0 && image->page.width != image->columns) ||
3997 (image->page.height != 0 && image->page.height != image->rows))
3999 (void) FormatLocaleString(msg,MaxTextExtent,
4000 "width=%.20g, height=%.20g",
4001 (double) image->page.width,(double) image->page.height);
4002 (void) SetImageProperty(image,"png:vpAg",msg,
4008 Relinquish resources.
4010 png_destroy_read_struct(&ping,&ping_info,&end_info);
4012 pixel_info=RelinquishVirtualMemory(pixel_info);
4014 if (logging != MagickFalse)
4015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016 " exit ReadOnePNGImage()");
4018 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
4019 UnlockSemaphoreInfo(ping_semaphore);
4022 /* } for navigation to beginning of SETJMP-protected block, revert to
4023 * Throwing an Exception when an error occurs.
4028 /* end of reading one PNG image */
4031 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4046 magic_number[MaxTextExtent];
4054 assert(image_info != (const ImageInfo *) NULL);
4055 assert(image_info->signature == MagickSignature);
4057 if (image_info->debug != MagickFalse)
4058 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4059 image_info->filename);
4061 assert(exception != (ExceptionInfo *) NULL);
4062 assert(exception->signature == MagickSignature);
4063 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4064 image=AcquireImage(image_info,exception);
4065 mng_info=(MngInfo *) NULL;
4066 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4068 if (status == MagickFalse)
4069 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4072 Verify PNG signature.
4074 count=ReadBlob(image,8,(unsigned char *) magic_number);
4076 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4077 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4080 Allocate a MngInfo structure.
4082 have_mng_structure=MagickFalse;
4083 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4085 if (mng_info == (MngInfo *) NULL)
4086 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4089 Initialize members of the MngInfo structure.
4091 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4092 mng_info->image=image;
4093 have_mng_structure=MagickTrue;
4096 image=ReadOnePNGImage(mng_info,image_info,exception);
4097 MngInfoFreeStruct(mng_info,&have_mng_structure);
4099 if (image == (Image *) NULL)
4101 if (previous != (Image *) NULL)
4103 if (previous->signature != MagickSignature)
4104 ThrowReaderException(CorruptImageError,"CorruptImage");
4106 (void) CloseBlob(previous);
4107 (void) DestroyImageList(previous);
4110 if (logging != MagickFalse)
4111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4112 "exit ReadPNGImage() with error");
4114 return((Image *) NULL);
4117 (void) CloseBlob(image);
4119 if ((image->columns == 0) || (image->rows == 0))
4121 if (logging != MagickFalse)
4122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4123 "exit ReadPNGImage() with error.");
4125 ThrowReaderException(CorruptImageError,"CorruptImage");
4128 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4129 ((image->gamma < .45) || (image->gamma > .46)) &&
4130 !(image->chromaticity.red_primary.x>0.6399f &&
4131 image->chromaticity.red_primary.x<0.6401f &&
4132 image->chromaticity.red_primary.y>0.3299f &&
4133 image->chromaticity.red_primary.y<0.3301f &&
4134 image->chromaticity.green_primary.x>0.2999f &&
4135 image->chromaticity.green_primary.x<0.3001f &&
4136 image->chromaticity.green_primary.y>0.5999f &&
4137 image->chromaticity.green_primary.y<0.6001f &&
4138 image->chromaticity.blue_primary.x>0.1499f &&
4139 image->chromaticity.blue_primary.x<0.1501f &&
4140 image->chromaticity.blue_primary.y>0.0599f &&
4141 image->chromaticity.blue_primary.y<0.0601f &&
4142 image->chromaticity.white_point.x>0.3126f &&
4143 image->chromaticity.white_point.x<0.3128f &&
4144 image->chromaticity.white_point.y>0.3289f &&
4145 image->chromaticity.white_point.y<0.3291f))
4146 SetImageColorspace(image,RGBColorspace,exception);
4148 if (logging != MagickFalse)
4149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4150 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4151 (double) image->page.width,(double) image->page.height,
4152 (double) image->page.x,(double) image->page.y);
4154 if (logging != MagickFalse)
4155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4162 #if defined(JNG_SUPPORTED)
4164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4168 % R e a d O n e J N G I m a g e %
4172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4174 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4175 % (minus the 8-byte signature) and returns it. It allocates the memory
4176 % necessary for the new Image structure and returns a pointer to the new
4179 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4181 % The format of the ReadOneJNGImage method is:
4183 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4184 % ExceptionInfo *exception)
4186 % A description of each parameter follows:
4188 % o mng_info: Specifies a pointer to a MngInfo structure.
4190 % o image_info: the image info.
4192 % o exception: return any errors or warnings in this structure.
4195 static Image *ReadOneJNGImage(MngInfo *mng_info,
4196 const ImageInfo *image_info, ExceptionInfo *exception)
4223 jng_image_sample_depth,
4224 jng_image_compression_method,
4225 jng_image_interlace_method,
4226 jng_alpha_sample_depth,
4227 jng_alpha_compression_method,
4228 jng_alpha_filter_method,
4229 jng_alpha_interlace_method;
4231 register const Quantum
4241 register unsigned char
4252 jng_alpha_compression_method=0;
4253 jng_alpha_sample_depth=8;
4257 alpha_image=(Image *) NULL;
4258 color_image=(Image *) NULL;
4259 alpha_image_info=(ImageInfo *) NULL;
4260 color_image_info=(ImageInfo *) NULL;
4262 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4263 " Enter ReadOneJNGImage()");
4265 image=mng_info->image;
4267 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4270 Allocate next image structure.
4272 if (logging != MagickFalse)
4273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4274 " AcquireNextImage()");
4276 AcquireNextImage(image_info,image,exception);
4278 if (GetNextImageInList(image) == (Image *) NULL)
4279 return((Image *) NULL);
4281 image=SyncNextImageInList(image);
4283 mng_info->image=image;
4286 Signature bytes have already been read.
4289 read_JSEP=MagickFalse;
4290 reading_idat=MagickFalse;
4291 skip_to_iend=MagickFalse;
4295 type[MaxTextExtent];
4304 Read a new JNG chunk.
4306 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4307 2*GetBlobSize(image));
4309 if (status == MagickFalse)
4313 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4314 length=ReadBlobMSBLong(image);
4315 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4317 if (logging != MagickFalse)
4318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4319 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4320 type[0],type[1],type[2],type[3],(double) length);
4322 if (length > PNG_UINT_31_MAX || count == 0)
4323 ThrowReaderException(CorruptImageError,"CorruptImage");
4326 chunk=(unsigned char *) NULL;
4330 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4332 if (chunk == (unsigned char *) NULL)
4333 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4335 for (i=0; i < (ssize_t) length; i++)
4336 chunk[i]=(unsigned char) ReadBlobByte(image);
4341 (void) ReadBlobMSBLong(image); /* read crc word */
4346 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4351 if (memcmp(type,mng_JHDR,4) == 0)
4355 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4356 (p[2] << 8) | p[3]);
4357 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4358 (p[6] << 8) | p[7]);
4359 jng_color_type=p[8];
4360 jng_image_sample_depth=p[9];
4361 jng_image_compression_method=p[10];
4362 jng_image_interlace_method=p[11];
4364 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4367 jng_alpha_sample_depth=p[12];
4368 jng_alpha_compression_method=p[13];
4369 jng_alpha_filter_method=p[14];
4370 jng_alpha_interlace_method=p[15];
4372 if (logging != MagickFalse)
4374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4375 " jng_width: %16lu",(unsigned long) jng_width);
4377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4378 " jng_width: %16lu",(unsigned long) jng_height);
4380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4381 " jng_color_type: %16d",jng_color_type);
4383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4384 " jng_image_sample_depth: %3d",
4385 jng_image_sample_depth);
4387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4388 " jng_image_compression_method:%3d",
4389 jng_image_compression_method);
4391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392 " jng_image_interlace_method: %3d",
4393 jng_image_interlace_method);
4395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396 " jng_alpha_sample_depth: %3d",
4397 jng_alpha_sample_depth);
4399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400 " jng_alpha_compression_method:%3d",
4401 jng_alpha_compression_method);
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " jng_alpha_filter_method: %3d",
4405 jng_alpha_filter_method);
4407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4408 " jng_alpha_interlace_method: %3d",
4409 jng_alpha_interlace_method);
4414 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4420 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4421 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4422 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4425 o create color_image
4426 o open color_blob, attached to color_image
4427 o if (color type has alpha)
4428 open alpha_blob, attached to alpha_image
4431 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4433 if (color_image_info == (ImageInfo *) NULL)
4434 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4436 GetImageInfo(color_image_info);
4437 color_image=AcquireImage(color_image_info,exception);
4439 if (color_image == (Image *) NULL)
4440 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4442 if (logging != MagickFalse)
4443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4444 " Creating color_blob.");
4446 (void) AcquireUniqueFilename(color_image->filename);
4447 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4450 if (status == MagickFalse)
4451 return((Image *) NULL);
4453 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4455 alpha_image_info=(ImageInfo *)
4456 AcquireMagickMemory(sizeof(ImageInfo));
4458 if (alpha_image_info == (ImageInfo *) NULL)
4459 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4461 GetImageInfo(alpha_image_info);
4462 alpha_image=AcquireImage(alpha_image_info,exception);
4464 if (alpha_image == (Image *) NULL)
4466 alpha_image=DestroyImage(alpha_image);
4467 ThrowReaderException(ResourceLimitError,
4468 "MemoryAllocationFailed");
4471 if (logging != MagickFalse)
4472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4473 " Creating alpha_blob.");
4475 (void) AcquireUniqueFilename(alpha_image->filename);
4476 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4479 if (status == MagickFalse)
4480 return((Image *) NULL);
4482 if (jng_alpha_compression_method == 0)
4487 if (logging != MagickFalse)
4488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4489 " Writing IHDR chunk to alpha_blob.");
4491 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4492 "\211PNG\r\n\032\n");
4494 (void) WriteBlobMSBULong(alpha_image,13L);
4495 PNGType(data,mng_IHDR);
4496 LogPNGChunk(logging,mng_IHDR,13L);
4497 PNGLong(data+4,jng_width);
4498 PNGLong(data+8,jng_height);
4499 data[12]=jng_alpha_sample_depth;
4500 data[13]=0; /* color_type gray */
4501 data[14]=0; /* compression method 0 */
4502 data[15]=0; /* filter_method 0 */
4503 data[16]=0; /* interlace_method 0 */
4504 (void) WriteBlob(alpha_image,17,data);
4505 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4508 reading_idat=MagickTrue;
4511 if (memcmp(type,mng_JDAT,4) == 0)
4513 /* Copy chunk to color_image->blob */
4515 if (logging != MagickFalse)
4516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4517 " Copying JDAT chunk data to color_blob.");
4519 (void) WriteBlob(color_image,length,chunk);
4522 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4527 if (memcmp(type,mng_IDAT,4) == 0)
4532 /* Copy IDAT header and chunk data to alpha_image->blob */
4534 if (image_info->ping == MagickFalse)
4536 if (logging != MagickFalse)
4537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4538 " Copying IDAT chunk data to alpha_blob.");
4540 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4541 PNGType(data,mng_IDAT);
4542 LogPNGChunk(logging,mng_IDAT,length);
4543 (void) WriteBlob(alpha_image,4,data);
4544 (void) WriteBlob(alpha_image,length,chunk);
4545 (void) WriteBlobMSBULong(alpha_image,
4546 crc32(crc32(0,data,4),chunk,(uInt) length));
4550 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4555 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4557 /* Copy chunk data to alpha_image->blob */
4559 if (image_info->ping == MagickFalse)
4561 if (logging != MagickFalse)
4562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4563 " Copying JDAA chunk data to alpha_blob.");
4565 (void) WriteBlob(alpha_image,length,chunk);
4569 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4574 if (memcmp(type,mng_JSEP,4) == 0)
4576 read_JSEP=MagickTrue;
4579 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4584 if (memcmp(type,mng_bKGD,4) == 0)
4588 image->background_color.red=ScaleCharToQuantum(p[1]);
4589 image->background_color.green=image->background_color.red;
4590 image->background_color.blue=image->background_color.red;
4595 image->background_color.red=ScaleCharToQuantum(p[1]);
4596 image->background_color.green=ScaleCharToQuantum(p[3]);
4597 image->background_color.blue=ScaleCharToQuantum(p[5]);
4600 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4604 if (memcmp(type,mng_gAMA,4) == 0)
4607 image->gamma=((float) mng_get_long(p))*0.00001;
4609 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4613 if (memcmp(type,mng_cHRM,4) == 0)
4617 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4618 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4619 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4620 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4621 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4622 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4623 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4624 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4627 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4631 if (memcmp(type,mng_sRGB,4) == 0)
4635 image->rendering_intent=
4636 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4637 image->gamma=1.000f/2.200f;
4638 image->chromaticity.red_primary.x=0.6400f;
4639 image->chromaticity.red_primary.y=0.3300f;
4640 image->chromaticity.green_primary.x=0.3000f;
4641 image->chromaticity.green_primary.y=0.6000f;
4642 image->chromaticity.blue_primary.x=0.1500f;
4643 image->chromaticity.blue_primary.y=0.0600f;
4644 image->chromaticity.white_point.x=0.3127f;
4645 image->chromaticity.white_point.y=0.3290f;
4648 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4652 if (memcmp(type,mng_oFFs,4) == 0)
4656 image->page.x=(ssize_t) mng_get_long(p);
4657 image->page.y=(ssize_t) mng_get_long(&p[4]);
4659 if ((int) p[8] != 0)
4661 image->page.x/=10000;
4662 image->page.y/=10000;
4667 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4672 if (memcmp(type,mng_pHYs,4) == 0)
4676 image->resolution.x=(double) mng_get_long(p);
4677 image->resolution.y=(double) mng_get_long(&p[4]);
4678 if ((int) p[8] == PNG_RESOLUTION_METER)
4680 image->units=PixelsPerCentimeterResolution;
4681 image->resolution.x=image->resolution.x/100.0f;
4682 image->resolution.y=image->resolution.y/100.0f;
4686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4691 if (memcmp(type,mng_iCCP,4) == 0)
4695 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4702 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4704 if (memcmp(type,mng_IEND,4))
4714 Finish up reading image data:
4716 o read main image from color_blob.
4720 o if (color_type has alpha)
4721 if alpha_encoding is PNG
4722 read secondary image from alpha_blob via ReadPNG
4723 if alpha_encoding is JPEG
4724 read secondary image from alpha_blob via ReadJPEG
4728 o copy intensity of secondary image into
4729 alpha samples of main image.
4731 o destroy the secondary image.
4734 (void) CloseBlob(color_image);
4736 if (logging != MagickFalse)
4737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4738 " Reading jng_image from color_blob.");
4740 assert(color_image_info != (ImageInfo *) NULL);
4741 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4742 color_image->filename);
4744 color_image_info->ping=MagickFalse; /* To do: avoid this */
4745 jng_image=ReadImage(color_image_info,exception);
4747 if (jng_image == (Image *) NULL)
4748 return((Image *) NULL);
4750 (void) RelinquishUniqueFileResource(color_image->filename);
4751 color_image=DestroyImage(color_image);
4752 color_image_info=DestroyImageInfo(color_image_info);
4754 if (jng_image == (Image *) NULL)
4755 return((Image *) NULL);
4757 if (logging != MagickFalse)
4758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4759 " Copying jng_image pixels to main image.");
4761 image->rows=jng_height;
4762 image->columns=jng_width;
4764 for (y=0; y < (ssize_t) image->rows; y++)
4766 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4767 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4768 for (x=(ssize_t) image->columns; x != 0; x--)
4770 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4771 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4772 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4773 q+=GetPixelChannels(image);
4774 s+=GetPixelChannels(jng_image);
4777 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4781 jng_image=DestroyImage(jng_image);
4783 if (image_info->ping == MagickFalse)
4785 if (jng_color_type >= 12)
4787 if (jng_alpha_compression_method == 0)
4791 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4792 PNGType(data,mng_IEND);
4793 LogPNGChunk(logging,mng_IEND,0L);
4794 (void) WriteBlob(alpha_image,4,data);
4795 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4798 (void) CloseBlob(alpha_image);
4800 if (logging != MagickFalse)
4801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4802 " Reading alpha from alpha_blob.");
4804 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4805 "%s",alpha_image->filename);
4807 jng_image=ReadImage(alpha_image_info,exception);
4809 if (jng_image != (Image *) NULL)
4810 for (y=0; y < (ssize_t) image->rows; y++)
4812 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4814 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4816 if (image->alpha_trait == BlendPixelTrait)
4817 for (x=(ssize_t) image->columns; x != 0; x--)
4819 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4820 q+=GetPixelChannels(image);
4821 s+=GetPixelChannels(jng_image);
4825 for (x=(ssize_t) image->columns; x != 0; x--)
4827 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4828 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4829 image->alpha_trait=BlendPixelTrait;
4830 q+=GetPixelChannels(image);
4831 s+=GetPixelChannels(jng_image);
4834 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4837 (void) RelinquishUniqueFileResource(alpha_image->filename);
4838 alpha_image=DestroyImage(alpha_image);
4839 alpha_image_info=DestroyImageInfo(alpha_image_info);
4840 if (jng_image != (Image *) NULL)
4841 jng_image=DestroyImage(jng_image);
4845 /* Read the JNG image. */
4847 if (mng_info->mng_type == 0)
4849 mng_info->mng_width=jng_width;
4850 mng_info->mng_height=jng_height;
4853 if (image->page.width == 0 && image->page.height == 0)
4855 image->page.width=jng_width;
4856 image->page.height=jng_height;
4859 if (image->page.x == 0 && image->page.y == 0)
4861 image->page.x=mng_info->x_off[mng_info->object_id];
4862 image->page.y=mng_info->y_off[mng_info->object_id];
4867 image->page.y=mng_info->y_off[mng_info->object_id];
4870 mng_info->image_found++;
4871 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4872 2*GetBlobSize(image));
4874 if (logging != MagickFalse)
4875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4876 " exit ReadOneJNGImage()");
4882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4886 % R e a d J N G I m a g e %
4890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4892 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4893 % (including the 8-byte signature) and returns it. It allocates the memory
4894 % necessary for the new Image structure and returns a pointer to the new
4897 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4899 % The format of the ReadJNGImage method is:
4901 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4904 % A description of each parameter follows:
4906 % o image_info: the image info.
4908 % o exception: return any errors or warnings in this structure.
4912 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4927 magic_number[MaxTextExtent];
4935 assert(image_info != (const ImageInfo *) NULL);
4936 assert(image_info->signature == MagickSignature);
4937 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4938 assert(exception != (ExceptionInfo *) NULL);
4939 assert(exception->signature == MagickSignature);
4940 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4941 image=AcquireImage(image_info,exception);
4942 mng_info=(MngInfo *) NULL;
4943 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4945 if (status == MagickFalse)
4946 return((Image *) NULL);
4948 if (LocaleCompare(image_info->magick,"JNG") != 0)
4949 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4951 /* Verify JNG signature. */
4953 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4955 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4956 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4958 /* Allocate a MngInfo structure. */
4960 have_mng_structure=MagickFalse;
4961 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4963 if (mng_info == (MngInfo *) NULL)
4964 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4966 /* Initialize members of the MngInfo structure. */
4968 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4969 have_mng_structure=MagickTrue;
4971 mng_info->image=image;
4973 image=ReadOneJNGImage(mng_info,image_info,exception);
4974 MngInfoFreeStruct(mng_info,&have_mng_structure);
4976 if (image == (Image *) NULL)
4978 if (IsImageObject(previous) != MagickFalse)
4980 (void) CloseBlob(previous);
4981 (void) DestroyImageList(previous);
4984 if (logging != MagickFalse)
4985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4986 "exit ReadJNGImage() with error");
4988 return((Image *) NULL);
4990 (void) CloseBlob(image);
4992 if (image->columns == 0 || image->rows == 0)
4994 if (logging != MagickFalse)
4995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4996 "exit ReadJNGImage() with error");
4998 ThrowReaderException(CorruptImageError,"CorruptImage");
5001 if (logging != MagickFalse)
5002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5008 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5011 page_geometry[MaxTextExtent];
5044 #if defined(MNG_INSERT_LAYERS)
5046 mng_background_color;
5049 register unsigned char
5064 #if defined(MNG_INSERT_LAYERS)
5069 volatile unsigned int
5070 #ifdef MNG_OBJECT_BUFFERS
5071 mng_background_object=0,
5073 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5076 default_frame_timeout,
5078 #if defined(MNG_INSERT_LAYERS)
5084 /* These delays are all measured in image ticks_per_second,
5085 * not in MNG ticks_per_second
5088 default_frame_delay,
5092 #if defined(MNG_INSERT_LAYERS)
5101 previous_fb.bottom=0;
5103 previous_fb.right=0;
5105 default_fb.bottom=0;
5109 /* Open image file. */
5111 assert(image_info != (const ImageInfo *) NULL);
5112 assert(image_info->signature == MagickSignature);
5113 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5114 assert(exception != (ExceptionInfo *) NULL);
5115 assert(exception->signature == MagickSignature);
5116 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5117 image=AcquireImage(image_info,exception);
5118 mng_info=(MngInfo *) NULL;
5119 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5121 if (status == MagickFalse)
5122 return((Image *) NULL);
5124 first_mng_object=MagickFalse;
5126 have_mng_structure=MagickFalse;
5128 /* Allocate a MngInfo structure. */
5130 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5132 if (mng_info == (MngInfo *) NULL)
5133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5135 /* Initialize members of the MngInfo structure. */
5137 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5138 mng_info->image=image;
5139 have_mng_structure=MagickTrue;
5141 if (LocaleCompare(image_info->magick,"MNG") == 0)
5144 magic_number[MaxTextExtent];
5146 /* Verify MNG signature. */
5147 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5148 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5149 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5151 /* Initialize some nonzero members of the MngInfo structure. */
5152 for (i=0; i < MNG_MAX_OBJECTS; i++)
5154 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5155 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5157 mng_info->exists[0]=MagickTrue;
5160 first_mng_object=MagickTrue;
5162 #if defined(MNG_INSERT_LAYERS)
5163 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5165 default_frame_delay=0;
5166 default_frame_timeout=0;
5169 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5171 skip_to_iend=MagickFalse;
5172 term_chunk_found=MagickFalse;
5173 mng_info->framing_mode=1;
5174 #if defined(MNG_INSERT_LAYERS)
5175 mandatory_back=MagickFalse;
5177 #if defined(MNG_INSERT_LAYERS)
5178 mng_background_color=image->background_color;
5180 default_fb=mng_info->frame;
5181 previous_fb=mng_info->frame;
5185 type[MaxTextExtent];
5187 if (LocaleCompare(image_info->magick,"MNG") == 0)
5196 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5197 length=ReadBlobMSBLong(image);
5198 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5200 if (logging != MagickFalse)
5201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5202 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5203 type[0],type[1],type[2],type[3],(double) length);
5205 if (length > PNG_UINT_31_MAX)
5209 ThrowReaderException(CorruptImageError,"CorruptImage");
5212 chunk=(unsigned char *) NULL;
5216 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5218 if (chunk == (unsigned char *) NULL)
5219 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5221 for (i=0; i < (ssize_t) length; i++)
5222 chunk[i]=(unsigned char) ReadBlobByte(image);
5227 (void) ReadBlobMSBLong(image); /* read crc word */
5229 #if !defined(JNG_SUPPORTED)
5230 if (memcmp(type,mng_JHDR,4) == 0)
5232 skip_to_iend=MagickTrue;
5234 if (mng_info->jhdr_warning == 0)
5235 (void) ThrowMagickException(exception,GetMagickModule(),
5236 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5238 mng_info->jhdr_warning++;
5241 if (memcmp(type,mng_DHDR,4) == 0)
5243 skip_to_iend=MagickTrue;
5245 if (mng_info->dhdr_warning == 0)
5246 (void) ThrowMagickException(exception,GetMagickModule(),
5247 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5249 mng_info->dhdr_warning++;
5251 if (memcmp(type,mng_MEND,4) == 0)
5256 if (memcmp(type,mng_IEND,4) == 0)
5257 skip_to_iend=MagickFalse;
5260 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5262 if (logging != MagickFalse)
5263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5269 if (memcmp(type,mng_MHDR,4) == 0)
5271 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5272 (p[2] << 8) | p[3]);
5274 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5275 (p[6] << 8) | p[7]);
5277 if (logging != MagickFalse)
5279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5280 " MNG width: %.20g",(double) mng_info->mng_width);
5281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5282 " MNG height: %.20g",(double) mng_info->mng_height);
5286 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5288 if (mng_info->ticks_per_second == 0)
5289 default_frame_delay=0;
5292 default_frame_delay=1UL*image->ticks_per_second/
5293 mng_info->ticks_per_second;
5295 frame_delay=default_frame_delay;
5301 simplicity=(size_t) mng_get_long(p);
5304 mng_type=1; /* Full MNG */
5306 if ((simplicity != 0) && ((simplicity | 11) == 11))
5307 mng_type=2; /* LC */
5309 if ((simplicity != 0) && ((simplicity | 9) == 9))
5310 mng_type=3; /* VLC */
5312 #if defined(MNG_INSERT_LAYERS)
5314 insert_layers=MagickTrue;
5316 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5318 /* Allocate next image structure. */
5319 AcquireNextImage(image_info,image,exception);
5321 if (GetNextImageInList(image) == (Image *) NULL)
5322 return((Image *) NULL);
5324 image=SyncNextImageInList(image);
5325 mng_info->image=image;
5328 if ((mng_info->mng_width > 65535L) ||
5329 (mng_info->mng_height > 65535L))
5330 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5332 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5333 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5334 mng_info->mng_height);
5336 mng_info->frame.left=0;
5337 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5338 mng_info->frame.top=0;
5339 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5340 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5342 for (i=0; i < MNG_MAX_OBJECTS; i++)
5343 mng_info->object_clip[i]=mng_info->frame;
5345 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5349 if (memcmp(type,mng_TERM,4) == 0)
5360 final_delay=(png_uint_32) mng_get_long(&p[2]);
5361 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5363 if (mng_iterations == PNG_UINT_31_MAX)
5366 image->iterations=mng_iterations;
5367 term_chunk_found=MagickTrue;
5370 if (logging != MagickFalse)
5372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5373 " repeat=%d",repeat);
5375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5376 " final_delay=%.20g",(double) final_delay);
5378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5379 " image->iterations=%.20g",(double) image->iterations);
5382 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5385 if (memcmp(type,mng_DEFI,4) == 0)
5388 (void) ThrowMagickException(exception,GetMagickModule(),
5389 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5392 object_id=(p[0] << 8) | p[1];
5394 if (mng_type == 2 && object_id != 0)
5395 (void) ThrowMagickException(exception,GetMagickModule(),
5396 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5399 if (object_id > MNG_MAX_OBJECTS)
5402 Instead of using a warning we should allocate a larger
5403 MngInfo structure and continue.
5405 (void) ThrowMagickException(exception,GetMagickModule(),
5406 CoderError,"object id too large","`%s'",image->filename);
5407 object_id=MNG_MAX_OBJECTS;
5410 if (mng_info->exists[object_id])
5411 if (mng_info->frozen[object_id])
5413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5414 (void) ThrowMagickException(exception,
5415 GetMagickModule(),CoderError,
5416 "DEFI cannot redefine a frozen MNG object","`%s'",
5421 mng_info->exists[object_id]=MagickTrue;
5424 mng_info->invisible[object_id]=p[2];
5427 Extract object offset info.
5431 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5432 (p[5] << 16) | (p[6] << 8) | p[7]);
5434 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5435 (p[9] << 16) | (p[10] << 8) | p[11]);
5437 if (logging != MagickFalse)
5439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5440 " x_off[%d]: %.20g",object_id,(double)
5441 mng_info->x_off[object_id]);
5443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5444 " y_off[%d]: %.20g",object_id,(double)
5445 mng_info->y_off[object_id]);
5450 Extract object clipping info.
5453 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5459 if (memcmp(type,mng_bKGD,4) == 0)
5461 mng_info->have_global_bkgd=MagickFalse;
5465 mng_info->mng_global_bkgd.red=
5466 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5468 mng_info->mng_global_bkgd.green=
5469 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5471 mng_info->mng_global_bkgd.blue=
5472 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5474 mng_info->have_global_bkgd=MagickTrue;
5477 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5480 if (memcmp(type,mng_BACK,4) == 0)
5482 #if defined(MNG_INSERT_LAYERS)
5484 mandatory_back=p[6];
5489 if (mandatory_back && length > 5)
5491 mng_background_color.red=
5492 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5494 mng_background_color.green=
5495 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5497 mng_background_color.blue=
5498 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5500 mng_background_color.alpha=OpaqueAlpha;
5503 #ifdef MNG_OBJECT_BUFFERS
5505 mng_background_object=(p[7] << 8) | p[8];
5508 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5512 if (memcmp(type,mng_PLTE,4) == 0)
5514 /* Read global PLTE. */
5516 if (length && (length < 769))
5518 if (mng_info->global_plte == (png_colorp) NULL)
5519 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5520 sizeof(*mng_info->global_plte));
5522 for (i=0; i < (ssize_t) (length/3); i++)
5524 mng_info->global_plte[i].red=p[3*i];
5525 mng_info->global_plte[i].green=p[3*i+1];
5526 mng_info->global_plte[i].blue=p[3*i+2];
5529 mng_info->global_plte_length=(unsigned int) (length/3);
5532 for ( ; i < 256; i++)
5534 mng_info->global_plte[i].red=i;
5535 mng_info->global_plte[i].green=i;
5536 mng_info->global_plte[i].blue=i;
5540 mng_info->global_plte_length=256;
5543 mng_info->global_plte_length=0;
5545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5549 if (memcmp(type,mng_tRNS,4) == 0)
5551 /* read global tRNS */
5554 for (i=0; i < (ssize_t) length; i++)
5555 mng_info->global_trns[i]=p[i];
5558 for ( ; i < 256; i++)
5559 mng_info->global_trns[i]=255;
5561 mng_info->global_trns_length=(unsigned int) length;
5562 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5565 if (memcmp(type,mng_gAMA,4) == 0)
5572 igamma=mng_get_long(p);
5573 mng_info->global_gamma=((float) igamma)*0.00001;
5574 mng_info->have_global_gama=MagickTrue;
5578 mng_info->have_global_gama=MagickFalse;
5580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5584 if (memcmp(type,mng_cHRM,4) == 0)
5586 /* Read global cHRM */
5590 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5591 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5592 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5593 mng_info->global_chrm.red_primary.y=0.00001*
5594 mng_get_long(&p[12]);
5595 mng_info->global_chrm.green_primary.x=0.00001*
5596 mng_get_long(&p[16]);
5597 mng_info->global_chrm.green_primary.y=0.00001*
5598 mng_get_long(&p[20]);
5599 mng_info->global_chrm.blue_primary.x=0.00001*
5600 mng_get_long(&p[24]);
5601 mng_info->global_chrm.blue_primary.y=0.00001*
5602 mng_get_long(&p[28]);
5603 mng_info->have_global_chrm=MagickTrue;
5606 mng_info->have_global_chrm=MagickFalse;
5608 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5612 if (memcmp(type,mng_sRGB,4) == 0)
5619 mng_info->global_srgb_intent=
5620 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5621 mng_info->have_global_srgb=MagickTrue;
5624 mng_info->have_global_srgb=MagickFalse;
5626 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5630 if (memcmp(type,mng_iCCP,4) == 0)
5638 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5643 if (memcmp(type,mng_FRAM,4) == 0)
5646 (void) ThrowMagickException(exception,GetMagickModule(),
5647 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5650 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5651 image->delay=frame_delay;
5653 frame_delay=default_frame_delay;
5654 frame_timeout=default_frame_timeout;
5659 mng_info->framing_mode=p[0];
5661 if (logging != MagickFalse)
5662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5663 " Framing_mode=%d",mng_info->framing_mode);
5667 /* Note the delay and frame clipping boundaries. */
5669 p++; /* framing mode */
5671 while (*p && ((p-chunk) < (ssize_t) length))
5672 p++; /* frame name */
5674 p++; /* frame name terminator */
5676 if ((p-chunk) < (ssize_t) (length-4))
5683 change_delay=(*p++);
5684 change_timeout=(*p++);
5685 change_clipping=(*p++);
5686 p++; /* change_sync */
5690 frame_delay=1UL*image->ticks_per_second*
5693 if (mng_info->ticks_per_second != 0)
5694 frame_delay/=mng_info->ticks_per_second;
5697 frame_delay=PNG_UINT_31_MAX;
5699 if (change_delay == 2)
5700 default_frame_delay=frame_delay;
5704 if (logging != MagickFalse)
5705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5706 " Framing_delay=%.20g",(double) frame_delay);
5711 frame_timeout=1UL*image->ticks_per_second*
5714 if (mng_info->ticks_per_second != 0)
5715 frame_timeout/=mng_info->ticks_per_second;
5718 frame_timeout=PNG_UINT_31_MAX;
5720 if (change_delay == 2)
5721 default_frame_timeout=frame_timeout;
5725 if (logging != MagickFalse)
5726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5727 " Framing_timeout=%.20g",(double) frame_timeout);
5730 if (change_clipping)
5732 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5736 if (logging != MagickFalse)
5737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5738 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5739 (double) fb.left,(double) fb.right,(double) fb.top,
5740 (double) fb.bottom);
5742 if (change_clipping == 2)
5748 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5750 subframe_width=(size_t) (mng_info->clip.right
5751 -mng_info->clip.left);
5753 subframe_height=(size_t) (mng_info->clip.bottom
5754 -mng_info->clip.top);
5756 Insert a background layer behind the frame if framing_mode is 4.
5758 #if defined(MNG_INSERT_LAYERS)
5759 if (logging != MagickFalse)
5760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5761 " subframe_width=%.20g, subframe_height=%.20g",(double)
5762 subframe_width,(double) subframe_height);
5764 if (insert_layers && (mng_info->framing_mode == 4) &&
5765 (subframe_width) && (subframe_height))
5767 /* Allocate next image structure. */
5768 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5770 AcquireNextImage(image_info,image,exception);
5772 if (GetNextImageInList(image) == (Image *) NULL)
5774 image=DestroyImageList(image);
5775 MngInfoFreeStruct(mng_info,&have_mng_structure);
5776 return((Image *) NULL);
5779 image=SyncNextImageInList(image);
5782 mng_info->image=image;
5784 if (term_chunk_found)
5786 image->start_loop=MagickTrue;
5787 image->iterations=mng_iterations;
5788 term_chunk_found=MagickFalse;
5792 image->start_loop=MagickFalse;
5794 image->columns=subframe_width;
5795 image->rows=subframe_height;
5796 image->page.width=subframe_width;
5797 image->page.height=subframe_height;
5798 image->page.x=mng_info->clip.left;
5799 image->page.y=mng_info->clip.top;
5800 image->background_color=mng_background_color;
5801 image->alpha_trait=UndefinedPixelTrait;
5803 (void) SetImageBackgroundColor(image,exception);
5805 if (logging != MagickFalse)
5806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5807 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5808 (double) mng_info->clip.left,(double) mng_info->clip.right,
5809 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5812 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5815 if (memcmp(type,mng_CLIP,4) == 0)
5824 first_object=(p[0] << 8) | p[1];
5825 last_object=(p[2] << 8) | p[3];
5827 for (i=(int) first_object; i <= (int) last_object; i++)
5829 if (mng_info->exists[i] && !mng_info->frozen[i])
5834 box=mng_info->object_clip[i];
5835 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5839 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5842 if (memcmp(type,mng_SAVE,4) == 0)
5844 for (i=1; i < MNG_MAX_OBJECTS; i++)
5845 if (mng_info->exists[i])
5847 mng_info->frozen[i]=MagickTrue;
5848 #ifdef MNG_OBJECT_BUFFERS
5849 if (mng_info->ob[i] != (MngBuffer *) NULL)
5850 mng_info->ob[i]->frozen=MagickTrue;
5855 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5860 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5862 /* Read DISC or SEEK. */
5864 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5866 for (i=1; i < MNG_MAX_OBJECTS; i++)
5867 MngInfoDiscardObject(mng_info,i);
5875 for (j=0; j < (ssize_t) length; j+=2)
5877 i=p[j] << 8 | p[j+1];
5878 MngInfoDiscardObject(mng_info,i);
5883 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5888 if (memcmp(type,mng_MOVE,4) == 0)
5896 first_object=(p[0] << 8) | p[1];
5897 last_object=(p[2] << 8) | p[3];
5898 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5900 if (mng_info->exists[i] && !mng_info->frozen[i])
5908 old_pair.a=mng_info->x_off[i];
5909 old_pair.b=mng_info->y_off[i];
5910 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5911 mng_info->x_off[i]=new_pair.a;
5912 mng_info->y_off[i]=new_pair.b;
5916 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5920 if (memcmp(type,mng_LOOP,4) == 0)
5922 ssize_t loop_iters=1;
5923 loop_level=chunk[0];
5924 mng_info->loop_active[loop_level]=1; /* mark loop active */
5926 /* Record starting point. */
5927 loop_iters=mng_get_long(&chunk[1]);
5929 if (logging != MagickFalse)
5930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5931 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5932 (double) loop_iters);
5934 if (loop_iters == 0)
5935 skipping_loop=loop_level;
5939 mng_info->loop_jump[loop_level]=TellBlob(image);
5940 mng_info->loop_count[loop_level]=loop_iters;
5943 mng_info->loop_iteration[loop_level]=0;
5944 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5948 if (memcmp(type,mng_ENDL,4) == 0)
5950 loop_level=chunk[0];
5952 if (skipping_loop > 0)
5954 if (skipping_loop == loop_level)
5957 Found end of zero-iteration loop.
5960 mng_info->loop_active[loop_level]=0;
5966 if (mng_info->loop_active[loop_level] == 1)
5968 mng_info->loop_count[loop_level]--;
5969 mng_info->loop_iteration[loop_level]++;
5971 if (logging != MagickFalse)
5972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5973 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5974 (double) loop_level,(double)
5975 mng_info->loop_count[loop_level]);
5977 if (mng_info->loop_count[loop_level] != 0)
5979 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5983 ThrowReaderException(CorruptImageError,
5984 "ImproperImageHeader");
5995 mng_info->loop_active[loop_level]=0;
5997 for (i=0; i < loop_level; i++)
5998 if (mng_info->loop_active[i] == 1)
5999 last_level=(short) i;
6000 loop_level=last_level;
6005 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6009 if (memcmp(type,mng_CLON,4) == 0)
6011 if (mng_info->clon_warning == 0)
6012 (void) ThrowMagickException(exception,GetMagickModule(),
6013 CoderError,"CLON is not implemented yet","`%s'",
6016 mng_info->clon_warning++;
6019 if (memcmp(type,mng_MAGN,4) == 0)
6034 magn_first=(p[0] << 8) | p[1];
6040 magn_last=(p[2] << 8) | p[3];
6043 magn_last=magn_first;
6044 #ifndef MNG_OBJECT_BUFFERS
6045 if (magn_first || magn_last)
6046 if (mng_info->magn_warning == 0)
6048 (void) ThrowMagickException(exception,
6049 GetMagickModule(),CoderError,
6050 "MAGN is not implemented yet for nonzero objects",
6051 "`%s'",image->filename);
6053 mng_info->magn_warning++;
6063 magn_mx=(p[5] << 8) | p[6];
6072 magn_my=(p[7] << 8) | p[8];
6081 magn_ml=(p[9] << 8) | p[10];
6090 magn_mr=(p[11] << 8) | p[12];
6099 magn_mt=(p[13] << 8) | p[14];
6108 magn_mb=(p[15] << 8) | p[16];
6120 magn_methy=magn_methx;
6123 if (magn_methx > 5 || magn_methy > 5)
6124 if (mng_info->magn_warning == 0)
6126 (void) ThrowMagickException(exception,
6127 GetMagickModule(),CoderError,
6128 "Unknown MAGN method in MNG datastream","`%s'",
6131 mng_info->magn_warning++;
6133 #ifdef MNG_OBJECT_BUFFERS
6134 /* Magnify existing objects in the range magn_first to magn_last */
6136 if (magn_first == 0 || magn_last == 0)
6138 /* Save the magnification factors for object 0 */
6139 mng_info->magn_mb=magn_mb;
6140 mng_info->magn_ml=magn_ml;
6141 mng_info->magn_mr=magn_mr;
6142 mng_info->magn_mt=magn_mt;
6143 mng_info->magn_mx=magn_mx;
6144 mng_info->magn_my=magn_my;
6145 mng_info->magn_methx=magn_methx;
6146 mng_info->magn_methy=magn_methy;
6150 if (memcmp(type,mng_PAST,4) == 0)
6152 if (mng_info->past_warning == 0)
6153 (void) ThrowMagickException(exception,GetMagickModule(),
6154 CoderError,"PAST is not implemented yet","`%s'",
6157 mng_info->past_warning++;
6160 if (memcmp(type,mng_SHOW,4) == 0)
6162 if (mng_info->show_warning == 0)
6163 (void) ThrowMagickException(exception,GetMagickModule(),
6164 CoderError,"SHOW is not implemented yet","`%s'",
6167 mng_info->show_warning++;
6170 if (memcmp(type,mng_sBIT,4) == 0)
6173 mng_info->have_global_sbit=MagickFalse;
6177 mng_info->global_sbit.gray=p[0];
6178 mng_info->global_sbit.red=p[0];
6179 mng_info->global_sbit.green=p[1];
6180 mng_info->global_sbit.blue=p[2];
6181 mng_info->global_sbit.alpha=p[3];
6182 mng_info->have_global_sbit=MagickTrue;
6185 if (memcmp(type,mng_pHYs,4) == 0)
6189 mng_info->global_x_pixels_per_unit=
6190 (size_t) mng_get_long(p);
6191 mng_info->global_y_pixels_per_unit=
6192 (size_t) mng_get_long(&p[4]);
6193 mng_info->global_phys_unit_type=p[8];
6194 mng_info->have_global_phys=MagickTrue;
6198 mng_info->have_global_phys=MagickFalse;
6200 if (memcmp(type,mng_pHYg,4) == 0)
6202 if (mng_info->phyg_warning == 0)
6203 (void) ThrowMagickException(exception,GetMagickModule(),
6204 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6206 mng_info->phyg_warning++;
6208 if (memcmp(type,mng_BASI,4) == 0)
6210 skip_to_iend=MagickTrue;
6212 if (mng_info->basi_warning == 0)
6213 (void) ThrowMagickException(exception,GetMagickModule(),
6214 CoderError,"BASI is not implemented yet","`%s'",
6217 mng_info->basi_warning++;
6218 #ifdef MNG_BASI_SUPPORTED
6219 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6220 (p[2] << 8) | p[3]);
6221 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6222 (p[6] << 8) | p[7]);
6223 basi_color_type=p[8];
6224 basi_compression_method=p[9];
6225 basi_filter_type=p[10];
6226 basi_interlace_method=p[11];
6228 basi_red=(p[12] << 8) & p[13];
6234 basi_green=(p[14] << 8) & p[15];
6240 basi_blue=(p[16] << 8) & p[17];
6246 basi_alpha=(p[18] << 8) & p[19];
6250 if (basi_sample_depth == 16)
6257 basi_viewable=p[20];
6263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6267 if (memcmp(type,mng_IHDR,4)
6268 #if defined(JNG_SUPPORTED)
6269 && memcmp(type,mng_JHDR,4)
6273 /* Not an IHDR or JHDR chunk */
6275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6280 if (logging != MagickFalse)
6281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6282 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6284 mng_info->exists[object_id]=MagickTrue;
6285 mng_info->viewable[object_id]=MagickTrue;
6287 if (mng_info->invisible[object_id])
6289 if (logging != MagickFalse)
6290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6291 " Skipping invisible object");
6293 skip_to_iend=MagickTrue;
6294 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6297 #if defined(MNG_INSERT_LAYERS)
6299 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6301 image_width=(size_t) mng_get_long(p);
6302 image_height=(size_t) mng_get_long(&p[4]);
6304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6307 Insert a transparent background layer behind the entire animation
6308 if it is not full screen.
6310 #if defined(MNG_INSERT_LAYERS)
6311 if (insert_layers && mng_type && first_mng_object)
6313 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6314 (image_width < mng_info->mng_width) ||
6315 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6316 (image_height < mng_info->mng_height) ||
6317 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6319 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6322 Allocate next image structure.
6324 AcquireNextImage(image_info,image,exception);
6326 if (GetNextImageInList(image) == (Image *) NULL)
6328 image=DestroyImageList(image);
6329 MngInfoFreeStruct(mng_info,&have_mng_structure);
6330 return((Image *) NULL);
6333 image=SyncNextImageInList(image);
6335 mng_info->image=image;
6337 if (term_chunk_found)
6339 image->start_loop=MagickTrue;
6340 image->iterations=mng_iterations;
6341 term_chunk_found=MagickFalse;
6345 image->start_loop=MagickFalse;
6347 /* Make a background rectangle. */
6350 image->columns=mng_info->mng_width;
6351 image->rows=mng_info->mng_height;
6352 image->page.width=mng_info->mng_width;
6353 image->page.height=mng_info->mng_height;
6356 image->background_color=mng_background_color;
6357 (void) SetImageBackgroundColor(image,exception);
6358 if (logging != MagickFalse)
6359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6360 " Inserted transparent background layer, W=%.20g, H=%.20g",
6361 (double) mng_info->mng_width,(double) mng_info->mng_height);
6365 Insert a background layer behind the upcoming image if
6366 framing_mode is 3, and we haven't already inserted one.
6368 if (insert_layers && (mng_info->framing_mode == 3) &&
6369 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6370 (simplicity & 0x08)))
6372 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6375 Allocate next image structure.
6377 AcquireNextImage(image_info,image,exception);
6379 if (GetNextImageInList(image) == (Image *) NULL)
6381 image=DestroyImageList(image);
6382 MngInfoFreeStruct(mng_info,&have_mng_structure);
6383 return((Image *) NULL);
6386 image=SyncNextImageInList(image);
6389 mng_info->image=image;
6391 if (term_chunk_found)
6393 image->start_loop=MagickTrue;
6394 image->iterations=mng_iterations;
6395 term_chunk_found=MagickFalse;
6399 image->start_loop=MagickFalse;
6402 image->columns=subframe_width;
6403 image->rows=subframe_height;
6404 image->page.width=subframe_width;
6405 image->page.height=subframe_height;
6406 image->page.x=mng_info->clip.left;
6407 image->page.y=mng_info->clip.top;
6408 image->background_color=mng_background_color;
6409 image->alpha_trait=UndefinedPixelTrait;
6410 (void) SetImageBackgroundColor(image,exception);
6412 if (logging != MagickFalse)
6413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6414 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6415 (double) mng_info->clip.left,(double) mng_info->clip.right,
6416 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6418 #endif /* MNG_INSERT_LAYERS */
6419 first_mng_object=MagickFalse;
6421 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6424 Allocate next image structure.
6426 AcquireNextImage(image_info,image,exception);
6428 if (GetNextImageInList(image) == (Image *) NULL)
6430 image=DestroyImageList(image);
6431 MngInfoFreeStruct(mng_info,&have_mng_structure);
6432 return((Image *) NULL);
6435 image=SyncNextImageInList(image);
6437 mng_info->image=image;
6438 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6439 GetBlobSize(image));
6441 if (status == MagickFalse)
6444 if (term_chunk_found)
6446 image->start_loop=MagickTrue;
6447 term_chunk_found=MagickFalse;
6451 image->start_loop=MagickFalse;
6453 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6455 image->delay=frame_delay;
6456 frame_delay=default_frame_delay;
6462 image->page.width=mng_info->mng_width;
6463 image->page.height=mng_info->mng_height;
6464 image->page.x=mng_info->x_off[object_id];
6465 image->page.y=mng_info->y_off[object_id];
6466 image->iterations=mng_iterations;
6469 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6472 if (logging != MagickFalse)
6473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6474 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6477 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6480 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6484 mng_info->image=image;
6485 mng_info->mng_type=mng_type;
6486 mng_info->object_id=object_id;
6488 if (memcmp(type,mng_IHDR,4) == 0)
6489 image=ReadOnePNGImage(mng_info,image_info,exception);
6491 #if defined(JNG_SUPPORTED)
6493 image=ReadOneJNGImage(mng_info,image_info,exception);
6496 if (image == (Image *) NULL)
6498 if (IsImageObject(previous) != MagickFalse)
6500 (void) DestroyImageList(previous);
6501 (void) CloseBlob(previous);
6504 MngInfoFreeStruct(mng_info,&have_mng_structure);
6505 return((Image *) NULL);
6508 if (image->columns == 0 || image->rows == 0)
6510 (void) CloseBlob(image);
6511 image=DestroyImageList(image);
6512 MngInfoFreeStruct(mng_info,&have_mng_structure);
6513 return((Image *) NULL);
6516 mng_info->image=image;
6523 if (mng_info->magn_methx || mng_info->magn_methy)
6529 if (logging != MagickFalse)
6530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6531 " Processing MNG MAGN chunk");
6533 if (mng_info->magn_methx == 1)
6535 magnified_width=mng_info->magn_ml;
6537 if (image->columns > 1)
6538 magnified_width += mng_info->magn_mr;
6540 if (image->columns > 2)
6541 magnified_width += (png_uint_32)
6542 ((image->columns-2)*(mng_info->magn_mx));
6547 magnified_width=(png_uint_32) image->columns;
6549 if (image->columns > 1)
6550 magnified_width += mng_info->magn_ml-1;
6552 if (image->columns > 2)
6553 magnified_width += mng_info->magn_mr-1;
6555 if (image->columns > 3)
6556 magnified_width += (png_uint_32)
6557 ((image->columns-3)*(mng_info->magn_mx-1));
6560 if (mng_info->magn_methy == 1)
6562 magnified_height=mng_info->magn_mt;
6564 if (image->rows > 1)
6565 magnified_height += mng_info->magn_mb;
6567 if (image->rows > 2)
6568 magnified_height += (png_uint_32)
6569 ((image->rows-2)*(mng_info->magn_my));
6574 magnified_height=(png_uint_32) image->rows;
6576 if (image->rows > 1)
6577 magnified_height += mng_info->magn_mt-1;
6579 if (image->rows > 2)
6580 magnified_height += mng_info->magn_mb-1;
6582 if (image->rows > 3)
6583 magnified_height += (png_uint_32)
6584 ((image->rows-3)*(mng_info->magn_my-1));
6587 if (magnified_height > image->rows ||
6588 magnified_width > image->columns)
6615 /* Allocate next image structure. */
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Allocate magnified image");
6621 AcquireNextImage(image_info,image,exception);
6623 if (GetNextImageInList(image) == (Image *) NULL)
6625 image=DestroyImageList(image);
6626 MngInfoFreeStruct(mng_info,&have_mng_structure);
6627 return((Image *) NULL);
6630 large_image=SyncNextImageInList(image);
6632 large_image->columns=magnified_width;
6633 large_image->rows=magnified_height;
6635 magn_methx=mng_info->magn_methx;
6636 magn_methy=mng_info->magn_methy;
6638 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6639 #define QM unsigned short
6640 if (magn_methx != 1 || magn_methy != 1)
6643 Scale pixels to unsigned shorts to prevent
6644 overflow of intermediate values of interpolations
6646 for (y=0; y < (ssize_t) image->rows; y++)
6648 q=GetAuthenticPixels(image,0,y,image->columns,1,
6651 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6653 SetPixelRed(image,ScaleQuantumToShort(
6654 GetPixelRed(image,q)),q);
6655 SetPixelGreen(image,ScaleQuantumToShort(
6656 GetPixelGreen(image,q)),q);
6657 SetPixelBlue(image,ScaleQuantumToShort(
6658 GetPixelBlue(image,q)),q);
6659 SetPixelAlpha(image,ScaleQuantumToShort(
6660 GetPixelAlpha(image,q)),q);
6661 q+=GetPixelChannels(image);
6664 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6672 if (image->alpha_trait == BlendPixelTrait)
6673 (void) SetImageBackgroundColor(large_image,exception);
6677 large_image->background_color.alpha=OpaqueAlpha;
6678 (void) SetImageBackgroundColor(large_image,exception);
6680 if (magn_methx == 4)
6683 if (magn_methx == 5)
6686 if (magn_methy == 4)
6689 if (magn_methy == 5)
6693 /* magnify the rows into the right side of the large image */
6695 if (logging != MagickFalse)
6696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6697 " Magnify the rows to %.20g",(double) large_image->rows);
6698 m=(ssize_t) mng_info->magn_mt;
6700 length=(size_t) image->columns*GetPixelChannels(image);
6701 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6702 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6704 if ((prev == (Quantum *) NULL) ||
6705 (next == (Quantum *) NULL))
6707 image=DestroyImageList(image);
6708 MngInfoFreeStruct(mng_info,&have_mng_structure);
6709 ThrowReaderException(ResourceLimitError,
6710 "MemoryAllocationFailed");
6713 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6714 (void) CopyMagickMemory(next,n,length);
6716 for (y=0; y < (ssize_t) image->rows; y++)
6719 m=(ssize_t) mng_info->magn_mt;
6721 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6722 m=(ssize_t) mng_info->magn_mb;
6724 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6725 m=(ssize_t) mng_info->magn_mb;
6727 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6731 m=(ssize_t) mng_info->magn_my;
6737 if (y < (ssize_t) image->rows-1)
6739 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6741 (void) CopyMagickMemory(next,n,length);
6744 for (i=0; i < m; i++, yy++)
6749 assert(yy < (ssize_t) large_image->rows);
6752 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6754 q+=(large_image->columns-image->columns)*
6755 GetPixelChannels(large_image);
6757 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6759 /* To do: get color as function of indexes[x] */
6761 if (image->storage_class == PseudoClass)
6766 if (magn_methy <= 1)
6768 /* replicate previous */
6769 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6770 SetPixelGreen(large_image,GetPixelGreen(image,
6772 SetPixelBlue(large_image,GetPixelBlue(image,
6774 SetPixelAlpha(large_image,GetPixelAlpha(image,
6778 else if (magn_methy == 2 || magn_methy == 4)
6782 SetPixelRed(large_image,GetPixelRed(image,
6784 SetPixelGreen(large_image,GetPixelGreen(image,
6786 SetPixelBlue(large_image,GetPixelBlue(image,
6788 SetPixelAlpha(large_image,GetPixelAlpha(image,
6795 SetPixelRed(large_image,((QM) (((ssize_t)
6796 (2*i*(GetPixelRed(image,n)
6797 -GetPixelRed(image,pixels)+m))/
6799 +GetPixelRed(image,pixels)))),q);
6800 SetPixelGreen(large_image,((QM) (((ssize_t)
6801 (2*i*(GetPixelGreen(image,n)
6802 -GetPixelGreen(image,pixels)+m))/
6804 +GetPixelGreen(image,pixels)))),q);
6805 SetPixelBlue(large_image,((QM) (((ssize_t)
6806 (2*i*(GetPixelBlue(image,n)
6807 -GetPixelBlue(image,pixels)+m))/
6809 +GetPixelBlue(image,pixels)))),q);
6811 if (image->alpha_trait == BlendPixelTrait)
6812 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6813 (2*i*(GetPixelAlpha(image,n)
6814 -GetPixelAlpha(image,pixels)+m))
6816 GetPixelAlpha(image,pixels)))),q);
6819 if (magn_methy == 4)
6821 /* Replicate nearest */
6822 if (i <= ((m+1) << 1))
6823 SetPixelAlpha(large_image,GetPixelAlpha(image,
6826 SetPixelAlpha(large_image,GetPixelAlpha(image,
6831 else /* if (magn_methy == 3 || magn_methy == 5) */
6833 /* Replicate nearest */
6834 if (i <= ((m+1) << 1))
6836 SetPixelRed(large_image,GetPixelRed(image,
6838 SetPixelGreen(large_image,GetPixelGreen(image,
6840 SetPixelBlue(large_image,GetPixelBlue(image,
6842 SetPixelAlpha(large_image,GetPixelAlpha(image,
6848 SetPixelRed(large_image,GetPixelRed(image,n),q);
6849 SetPixelGreen(large_image,GetPixelGreen(image,n),
6851 SetPixelBlue(large_image,GetPixelBlue(image,n),
6853 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6857 if (magn_methy == 5)
6859 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6860 (GetPixelAlpha(image,n)
6861 -GetPixelAlpha(image,pixels))
6862 +m))/((ssize_t) (m*2))
6863 +GetPixelAlpha(image,pixels)),q);
6866 n+=GetPixelChannels(image);
6867 q+=GetPixelChannels(large_image);
6868 pixels+=GetPixelChannels(image);
6871 if (SyncAuthenticPixels(large_image,exception) == 0)
6877 prev=(Quantum *) RelinquishMagickMemory(prev);
6878 next=(Quantum *) RelinquishMagickMemory(next);
6880 length=image->columns;
6882 if (logging != MagickFalse)
6883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6884 " Delete original image");
6886 DeleteImageFromList(&image);
6890 mng_info->image=image;
6892 /* magnify the columns */
6893 if (logging != MagickFalse)
6894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6895 " Magnify the columns to %.20g",(double) image->columns);
6897 for (y=0; y < (ssize_t) image->rows; y++)
6902 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6903 pixels=q+(image->columns-length)*GetPixelChannels(image);
6904 n=pixels+GetPixelChannels(image);
6906 for (x=(ssize_t) (image->columns-length);
6907 x < (ssize_t) image->columns; x++)
6909 /* To do: Rewrite using Get/Set***PixelChannel() */
6911 if (x == (ssize_t) (image->columns-length))
6912 m=(ssize_t) mng_info->magn_ml;
6914 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6915 m=(ssize_t) mng_info->magn_mr;
6917 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6918 m=(ssize_t) mng_info->magn_mr;
6920 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6924 m=(ssize_t) mng_info->magn_mx;
6926 for (i=0; i < m; i++)
6928 if (magn_methx <= 1)
6930 /* replicate previous */
6931 SetPixelRed(image,GetPixelRed(image,pixels),q);
6932 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6933 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6934 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6937 else if (magn_methx == 2 || magn_methx == 4)
6941 SetPixelRed(image,GetPixelRed(image,pixels),q);
6942 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6943 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6944 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6947 /* To do: Rewrite using Get/Set***PixelChannel() */
6951 SetPixelRed(image,(QM) ((2*i*(
6952 GetPixelRed(image,n)
6953 -GetPixelRed(image,pixels))+m)
6955 GetPixelRed(image,pixels)),q);
6957 SetPixelGreen(image,(QM) ((2*i*(
6958 GetPixelGreen(image,n)
6959 -GetPixelGreen(image,pixels))+m)
6961 GetPixelGreen(image,pixels)),q);
6963 SetPixelBlue(image,(QM) ((2*i*(
6964 GetPixelBlue(image,n)
6965 -GetPixelBlue(image,pixels))+m)
6967 GetPixelBlue(image,pixels)),q);
6968 if (image->alpha_trait == BlendPixelTrait)
6969 SetPixelAlpha(image,(QM) ((2*i*(
6970 GetPixelAlpha(image,n)
6971 -GetPixelAlpha(image,pixels))+m)
6973 GetPixelAlpha(image,pixels)),q);
6976 if (magn_methx == 4)
6978 /* Replicate nearest */
6979 if (i <= ((m+1) << 1))
6981 SetPixelAlpha(image,
6982 GetPixelAlpha(image,pixels)+0,q);
6986 SetPixelAlpha(image,
6987 GetPixelAlpha(image,n)+0,q);
6992 else /* if (magn_methx == 3 || magn_methx == 5) */
6994 /* Replicate nearest */
6995 if (i <= ((m+1) << 1))
6997 SetPixelRed(image,GetPixelRed(image,pixels),q);
6998 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6999 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7000 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7005 SetPixelRed(image,GetPixelRed(image,n),q);
7006 SetPixelGreen(image,GetPixelGreen(image,n),q);
7007 SetPixelBlue(image,GetPixelBlue(image,n),q);
7008 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7011 if (magn_methx == 5)
7014 SetPixelAlpha(image,
7015 (QM) ((2*i*( GetPixelAlpha(image,n)
7016 -GetPixelAlpha(image,pixels))+m)/
7018 +GetPixelAlpha(image,pixels)),q);
7021 q+=GetPixelChannels(image);
7023 n+=GetPixelChannels(image);
7024 p+=GetPixelChannels(image);
7027 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7030 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7031 if (magn_methx != 1 || magn_methy != 1)
7034 Rescale pixels to Quantum
7036 for (y=0; y < (ssize_t) image->rows; y++)
7038 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7040 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7042 SetPixelRed(image,ScaleShortToQuantum(
7043 GetPixelRed(image,q)),q);
7044 SetPixelGreen(image,ScaleShortToQuantum(
7045 GetPixelGreen(image,q)),q);
7046 SetPixelBlue(image,ScaleShortToQuantum(
7047 GetPixelBlue(image,q)),q);
7048 SetPixelAlpha(image,ScaleShortToQuantum(
7049 GetPixelAlpha(image,q)),q);
7050 q+=GetPixelChannels(image);
7053 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7058 if (logging != MagickFalse)
7059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7060 " Finished MAGN processing");
7065 Crop_box is with respect to the upper left corner of the MNG.
7067 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7068 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7069 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7070 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7071 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7072 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7073 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7074 if ((crop_box.left != (mng_info->image_box.left
7075 +mng_info->x_off[object_id])) ||
7076 (crop_box.right != (mng_info->image_box.right
7077 +mng_info->x_off[object_id])) ||
7078 (crop_box.top != (mng_info->image_box.top
7079 +mng_info->y_off[object_id])) ||
7080 (crop_box.bottom != (mng_info->image_box.bottom
7081 +mng_info->y_off[object_id])))
7083 if (logging != MagickFalse)
7084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085 " Crop the PNG image");
7087 if ((crop_box.left < crop_box.right) &&
7088 (crop_box.top < crop_box.bottom))
7097 Crop_info is with respect to the upper left corner of
7100 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7101 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7102 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7103 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7104 image->page.width=image->columns;
7105 image->page.height=image->rows;
7108 im=CropImage(image,&crop_info,exception);
7110 if (im != (Image *) NULL)
7112 image->columns=im->columns;
7113 image->rows=im->rows;
7114 im=DestroyImage(im);
7115 image->page.width=image->columns;
7116 image->page.height=image->rows;
7117 image->page.x=crop_box.left;
7118 image->page.y=crop_box.top;
7125 No pixels in crop area. The MNG spec still requires
7126 a layer, though, so make a single transparent pixel in
7127 the top left corner.
7132 (void) SetImageBackgroundColor(image,exception);
7133 image->page.width=1;
7134 image->page.height=1;
7139 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7140 image=mng_info->image;
7144 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7145 /* PNG does not handle depths greater than 16 so reduce it even
7148 if (image->depth > 16)
7152 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7153 if (image->depth > 8)
7155 /* To do: fill low byte properly */
7159 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7163 if (image_info->number_scenes != 0)
7165 if (mng_info->scenes_found >
7166 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7170 if (logging != MagickFalse)
7171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7172 " Finished reading image datastream.");
7174 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7176 (void) CloseBlob(image);
7178 if (logging != MagickFalse)
7179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7180 " Finished reading all image datastreams.");
7182 #if defined(MNG_INSERT_LAYERS)
7183 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7184 (mng_info->mng_height))
7187 Insert a background layer if nothing else was found.
7189 if (logging != MagickFalse)
7190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7191 " No images found. Inserting a background layer.");
7193 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7196 Allocate next image structure.
7198 AcquireNextImage(image_info,image,exception);
7199 if (GetNextImageInList(image) == (Image *) NULL)
7201 image=DestroyImageList(image);
7202 MngInfoFreeStruct(mng_info,&have_mng_structure);
7204 if (logging != MagickFalse)
7205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7206 " Allocation failed, returning NULL.");
7208 return((Image *) NULL);
7210 image=SyncNextImageInList(image);
7212 image->columns=mng_info->mng_width;
7213 image->rows=mng_info->mng_height;
7214 image->page.width=mng_info->mng_width;
7215 image->page.height=mng_info->mng_height;
7218 image->background_color=mng_background_color;
7219 image->alpha_trait=UndefinedPixelTrait;
7221 if (image_info->ping == MagickFalse)
7222 (void) SetImageBackgroundColor(image,exception);
7224 mng_info->image_found++;
7227 image->iterations=mng_iterations;
7229 if (mng_iterations == 1)
7230 image->start_loop=MagickTrue;
7232 while (GetPreviousImageInList(image) != (Image *) NULL)
7235 if (image_count > 10*mng_info->image_found)
7237 if (logging != MagickFalse)
7238 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7240 (void) ThrowMagickException(exception,GetMagickModule(),
7241 CoderError,"Linked list is corrupted, beginning of list not found",
7242 "`%s'",image_info->filename);
7244 return((Image *) NULL);
7247 image=GetPreviousImageInList(image);
7249 if (GetNextImageInList(image) == (Image *) NULL)
7251 if (logging != MagickFalse)
7252 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7254 (void) ThrowMagickException(exception,GetMagickModule(),
7255 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7256 image_info->filename);
7260 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7261 GetNextImageInList(image) ==
7264 if (logging != MagickFalse)
7265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7266 " First image null");
7268 (void) ThrowMagickException(exception,GetMagickModule(),
7269 CoderError,"image->next for first image is NULL but shouldn't be.",
7270 "`%s'",image_info->filename);
7273 if (mng_info->image_found == 0)
7275 if (logging != MagickFalse)
7276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7277 " No visible images found.");
7279 (void) ThrowMagickException(exception,GetMagickModule(),
7280 CoderError,"No visible images in file","`%s'",image_info->filename);
7282 if (image != (Image *) NULL)
7283 image=DestroyImageList(image);
7285 MngInfoFreeStruct(mng_info,&have_mng_structure);
7286 return((Image *) NULL);
7289 if (mng_info->ticks_per_second)
7290 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7291 final_delay/mng_info->ticks_per_second;
7294 image->start_loop=MagickTrue;
7296 /* Find final nonzero image delay */
7297 final_image_delay=0;
7299 while (GetNextImageInList(image) != (Image *) NULL)
7302 final_image_delay=image->delay;
7304 image=GetNextImageInList(image);
7307 if (final_delay < final_image_delay)
7308 final_delay=final_image_delay;
7310 image->delay=final_delay;
7312 if (logging != MagickFalse)
7313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7314 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7315 (double) final_delay);
7317 if (logging != MagickFalse)
7323 image=GetFirstImageInList(image);
7325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7326 " Before coalesce:");
7328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7329 " scene 0 delay=%.20g",(double) image->delay);
7331 while (GetNextImageInList(image) != (Image *) NULL)
7333 image=GetNextImageInList(image);
7334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7335 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7339 image=GetFirstImageInList(image);
7340 #ifdef MNG_COALESCE_LAYERS
7350 if (logging != MagickFalse)
7351 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7354 next_image=CoalesceImages(image,exception);
7356 if (next_image == (Image *) NULL)
7357 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7359 image=DestroyImageList(image);
7362 for (next=image; next != (Image *) NULL; next=next_image)
7364 next->page.width=mng_info->mng_width;
7365 next->page.height=mng_info->mng_height;
7368 next->scene=scene++;
7369 next_image=GetNextImageInList(next);
7371 if (next_image == (Image *) NULL)
7374 if (next->delay == 0)
7377 next_image->previous=GetPreviousImageInList(next);
7378 if (GetPreviousImageInList(next) == (Image *) NULL)
7381 next->previous->next=next_image;
7382 next=DestroyImage(next);
7388 while (GetNextImageInList(image) != (Image *) NULL)
7389 image=GetNextImageInList(image);
7391 image->dispose=BackgroundDispose;
7393 if (logging != MagickFalse)
7399 image=GetFirstImageInList(image);
7401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7402 " After coalesce:");
7404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7406 (double) image->dispose);
7408 while (GetNextImageInList(image) != (Image *) NULL)
7410 image=GetNextImageInList(image);
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7413 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7414 (double) image->delay,(double) image->dispose);
7418 image=GetFirstImageInList(image);
7419 MngInfoFreeStruct(mng_info,&have_mng_structure);
7420 have_mng_structure=MagickFalse;
7422 if (logging != MagickFalse)
7423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7425 return(GetFirstImageInList(image));
7427 #else /* PNG_LIBPNG_VER > 10011 */
7428 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7430 printf("Your PNG library is too old: You have libpng-%s\n",
7431 PNG_LIBPNG_VER_STRING);
7433 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7434 "PNG library is too old","`%s'",image_info->filename);
7436 return(Image *) NULL;
7439 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7441 return(ReadPNGImage(image_info,exception));
7443 #endif /* PNG_LIBPNG_VER > 10011 */
7447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7451 % R e g i s t e r P N G I m a g e %
7455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7457 % RegisterPNGImage() adds properties for the PNG image format to
7458 % the list of supported formats. The properties include the image format
7459 % tag, a method to read and/or write the format, whether the format
7460 % supports the saving of more than one frame to the same file or blob,
7461 % whether the format supports native in-memory I/O, and a brief
7462 % description of the format.
7464 % The format of the RegisterPNGImage method is:
7466 % size_t RegisterPNGImage(void)
7469 ModuleExport size_t RegisterPNGImage(void)
7472 version[MaxTextExtent];
7480 "See http://www.libpng.org/ for details about the PNG format."
7485 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7491 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7497 #if defined(PNG_LIBPNG_VER_STRING)
7498 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7499 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7501 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7503 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7504 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7509 entry=SetMagickInfo("MNG");
7510 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7512 #if defined(MAGICKCORE_PNG_DELEGATE)
7513 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7514 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7517 entry->magick=(IsImageFormatHandler *) IsMNG;
7518 entry->description=ConstantString("Multiple-image Network Graphics");
7520 if (*version != '\0')
7521 entry->version=ConstantString(version);
7523 entry->mime_type=ConstantString("video/x-mng");
7524 entry->module=ConstantString("PNG");
7525 entry->note=ConstantString(MNGNote);
7526 (void) RegisterMagickInfo(entry);
7528 entry=SetMagickInfo("PNG");
7530 #if defined(MAGICKCORE_PNG_DELEGATE)
7531 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7532 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7535 entry->magick=(IsImageFormatHandler *) IsPNG;
7536 entry->adjoin=MagickFalse;
7537 entry->description=ConstantString("Portable Network Graphics");
7538 entry->mime_type=ConstantString("image/png");
7539 entry->module=ConstantString("PNG");
7541 if (*version != '\0')
7542 entry->version=ConstantString(version);
7544 entry->note=ConstantString(PNGNote);
7545 (void) RegisterMagickInfo(entry);
7547 entry=SetMagickInfo("PNG8");
7549 #if defined(MAGICKCORE_PNG_DELEGATE)
7550 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7551 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7554 entry->magick=(IsImageFormatHandler *) IsPNG;
7555 entry->adjoin=MagickFalse;
7556 entry->description=ConstantString(
7557 "8-bit indexed with optional binary transparency");
7558 entry->mime_type=ConstantString("image/png");
7559 entry->module=ConstantString("PNG");
7560 (void) RegisterMagickInfo(entry);
7562 entry=SetMagickInfo("PNG24");
7565 #if defined(ZLIB_VERSION)
7566 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7567 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7569 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7571 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7572 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7576 if (*version != '\0')
7577 entry->version=ConstantString(version);
7579 #if defined(MAGICKCORE_PNG_DELEGATE)
7580 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7581 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7584 entry->magick=(IsImageFormatHandler *) IsPNG;
7585 entry->adjoin=MagickFalse;
7586 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7587 entry->mime_type=ConstantString("image/png");
7588 entry->module=ConstantString("PNG");
7589 (void) RegisterMagickInfo(entry);
7591 entry=SetMagickInfo("PNG32");
7593 #if defined(MAGICKCORE_PNG_DELEGATE)
7594 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7595 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7598 entry->magick=(IsImageFormatHandler *) IsPNG;
7599 entry->adjoin=MagickFalse;
7600 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7601 entry->mime_type=ConstantString("image/png");
7602 entry->module=ConstantString("PNG");
7603 (void) RegisterMagickInfo(entry);
7605 entry=SetMagickInfo("PNG48");
7607 #if defined(MAGICKCORE_PNG_DELEGATE)
7608 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7609 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7612 entry->magick=(IsImageFormatHandler *) IsPNG;
7613 entry->adjoin=MagickFalse;
7614 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7615 entry->mime_type=ConstantString("image/png");
7616 entry->module=ConstantString("PNG");
7617 (void) RegisterMagickInfo(entry);
7619 entry=SetMagickInfo("PNG64");
7621 #if defined(MAGICKCORE_PNG_DELEGATE)
7622 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7623 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7626 entry->magick=(IsImageFormatHandler *) IsPNG;
7627 entry->adjoin=MagickFalse;
7628 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7629 entry->mime_type=ConstantString("image/png");
7630 entry->module=ConstantString("PNG");
7631 (void) RegisterMagickInfo(entry);
7633 entry=SetMagickInfo("PNG00");
7635 #if defined(MAGICKCORE_PNG_DELEGATE)
7636 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7637 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7640 entry->magick=(IsImageFormatHandler *) IsPNG;
7641 entry->adjoin=MagickFalse;
7642 entry->description=ConstantString(
7643 "PNG inheriting bit-depth and color-type from original");
7644 entry->mime_type=ConstantString("image/png");
7645 entry->module=ConstantString("PNG");
7646 (void) RegisterMagickInfo(entry);
7648 entry=SetMagickInfo("JNG");
7650 #if defined(JNG_SUPPORTED)
7651 #if defined(MAGICKCORE_PNG_DELEGATE)
7652 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7653 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7657 entry->magick=(IsImageFormatHandler *) IsJNG;
7658 entry->adjoin=MagickFalse;
7659 entry->description=ConstantString("JPEG Network Graphics");
7660 entry->mime_type=ConstantString("image/x-jng");
7661 entry->module=ConstantString("PNG");
7662 entry->note=ConstantString(JNGNote);
7663 (void) RegisterMagickInfo(entry);
7665 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7666 ping_semaphore=AllocateSemaphoreInfo();
7669 return(MagickImageCoderSignature);
7673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7677 % U n r e g i s t e r P N G I m a g e %
7681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7683 % UnregisterPNGImage() removes format registrations made by the
7684 % PNG module from the list of supported formats.
7686 % The format of the UnregisterPNGImage method is:
7688 % UnregisterPNGImage(void)
7691 ModuleExport void UnregisterPNGImage(void)
7693 (void) UnregisterMagickInfo("MNG");
7694 (void) UnregisterMagickInfo("PNG");
7695 (void) UnregisterMagickInfo("PNG8");
7696 (void) UnregisterMagickInfo("PNG24");
7697 (void) UnregisterMagickInfo("PNG32");
7698 (void) UnregisterMagickInfo("PNG48");
7699 (void) UnregisterMagickInfo("PNG64");
7700 (void) UnregisterMagickInfo("PNG00");
7701 (void) UnregisterMagickInfo("JNG");
7703 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7704 if (ping_semaphore != (SemaphoreInfo *) NULL)
7705 DestroySemaphoreInfo(&ping_semaphore);
7709 #if defined(MAGICKCORE_PNG_DELEGATE)
7710 #if PNG_LIBPNG_VER > 10011
7712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7716 % W r i t e M N G I m a g e %
7720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7722 % WriteMNGImage() writes an image in the Portable Network Graphics
7723 % Group's "Multiple-image Network Graphics" encoded image format.
7725 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7727 % The format of the WriteMNGImage method is:
7729 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7730 % Image *image,ExceptionInfo *exception)
7732 % A description of each parameter follows.
7734 % o image_info: the image info.
7736 % o image: The image.
7738 % o exception: return any errors or warnings in this structure.
7740 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7741 % "To do" under ReadPNGImage):
7743 % Preserve all unknown and not-yet-handled known chunks found in input
7744 % PNG file and copy them into output PNG files according to the PNG
7747 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7749 % Improve selection of color type (use indexed-colour or indexed-colour
7750 % with tRNS when 256 or fewer unique RGBA values are present).
7752 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7753 % This will be complicated if we limit ourselves to generating MNG-LC
7754 % files. For now we ignore disposal method 3 and simply overlay the next
7757 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7758 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7759 % [mostly done 15 June 1999 but still need to take care of tRNS]
7761 % Check for identical sRGB and replace with a global sRGB (and remove
7762 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7763 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7764 % local gAMA/cHRM with local sRGB if appropriate).
7766 % Check for identical sBIT chunks and write global ones.
7768 % Provide option to skip writing the signature tEXt chunks.
7770 % Use signatures to detect identical objects and reuse the first
7771 % instance of such objects instead of writing duplicate objects.
7773 % Use a smaller-than-32k value of compression window size when
7776 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7777 % ancillary text chunks and save profiles.
7779 % Provide an option to force LC files (to ensure exact framing rate)
7782 % Provide an option to force VLC files instead of LC, even when offsets
7783 % are present. This will involve expanding the embedded images with a
7784 % transparent region at the top and/or left.
7788 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7789 png_info *ping_info, unsigned char *profile_type, unsigned char
7790 *profile_description, unsigned char *profile_data, png_uint_32 length)
7809 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7811 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7814 if (image_info->verbose)
7816 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7817 (char *) profile_type, (double) length);
7820 #if PNG_LIBPNG_VER >= 10400
7821 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7823 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7825 description_length=(png_uint_32) strlen((const char *) profile_description);
7826 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7827 + description_length);
7828 #if PNG_LIBPNG_VER >= 10400
7829 text[0].text=(png_charp) png_malloc(ping,
7830 (png_alloc_size_t) allocated_length);
7831 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7833 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7834 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7836 text[0].key[0]='\0';
7837 (void) ConcatenateMagickString(text[0].key,
7838 "Raw profile type ",MaxTextExtent);
7839 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7843 (void) CopyMagickString(dp,(const char *) profile_description,
7845 dp+=description_length;
7847 (void) FormatLocaleString(dp,allocated_length-
7848 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7851 for (i=0; i < (ssize_t) length; i++)
7855 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7856 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7861 text[0].text_length=(png_size_t) (dp-text[0].text);
7862 text[0].compression=image_info->compression == NoCompression ||
7863 (image_info->compression == UndefinedCompression &&
7864 text[0].text_length < 128) ? -1 : 0;
7866 if (text[0].text_length <= allocated_length)
7867 png_set_text(ping,ping_info,text,1);
7869 png_free(ping,text[0].text);
7870 png_free(ping,text[0].key);
7871 png_free(ping,text);
7874 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7875 const char *string, MagickBooleanType logging)
7888 ResetImageProfileIterator(image);
7890 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7892 profile=GetImageProfile(image,name);
7894 if (profile != (const StringInfo *) NULL)
7899 if (LocaleNCompare(name,string,11) == 0)
7901 if (logging != MagickFalse)
7902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7903 " Found %s profile",name);
7905 ping_profile=CloneStringInfo(profile);
7906 data=GetStringInfoDatum(ping_profile),
7907 length=(png_uint_32) GetStringInfoLength(ping_profile);
7912 (void) WriteBlobMSBULong(image,length-5); /* data length */
7913 (void) WriteBlob(image,length-1,data+1);
7914 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7915 ping_profile=DestroyStringInfo(ping_profile);
7919 name=GetNextImageProfile(image);
7926 /* Write one PNG image */
7927 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7928 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7959 ping_trans_alpha[256];
7987 ping_have_cheap_transparency,
8000 /* ping_exclude_EXIF, */
8003 /* ping_exclude_iTXt, */
8008 /* ping_exclude_tRNS, */
8010 ping_exclude_zCCP, /* hex-encoded iCCP */
8013 ping_preserve_colormap,
8015 ping_need_colortype_warning,
8023 *volatile pixel_info;
8042 ping_interlace_method,
8043 ping_compression_method,
8060 number_semitransparent,
8062 ping_pHYs_unit_type;
8065 ping_pHYs_x_resolution,
8066 ping_pHYs_y_resolution;
8068 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8069 " Enter WriteOnePNGImage()");
8071 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8072 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8073 if (image_info == (ImageInfo *) NULL)
8074 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8076 /* Define these outside of the following "if logging()" block so they will
8077 * show in debuggers.
8080 (void) ConcatenateMagickString(im_vers,
8081 MagickLibVersionText,MaxTextExtent);
8082 (void) ConcatenateMagickString(im_vers,
8083 MagickLibAddendum,MaxTextExtent);
8086 (void) ConcatenateMagickString(libpng_vers,
8087 PNG_LIBPNG_VER_STRING,32);
8089 (void) ConcatenateMagickString(libpng_runv,
8090 png_get_libpng_ver(NULL),32);
8093 (void) ConcatenateMagickString(zlib_vers,
8096 (void) ConcatenateMagickString(zlib_runv,
8101 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8103 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8105 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8107 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8110 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8112 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8114 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8119 /* Initialize some stuff */
8122 ping_interlace_method=0,
8123 ping_compression_method=0,
8124 ping_filter_method=0,
8127 ping_background.red = 0;
8128 ping_background.green = 0;
8129 ping_background.blue = 0;
8130 ping_background.gray = 0;
8131 ping_background.index = 0;
8133 ping_trans_color.red=0;
8134 ping_trans_color.green=0;
8135 ping_trans_color.blue=0;
8136 ping_trans_color.gray=0;
8138 ping_pHYs_unit_type = 0;
8139 ping_pHYs_x_resolution = 0;
8140 ping_pHYs_y_resolution = 0;
8142 ping_have_blob=MagickFalse;
8143 ping_have_cheap_transparency=MagickFalse;
8144 ping_have_color=MagickTrue;
8145 ping_have_non_bw=MagickTrue;
8146 ping_have_PLTE=MagickFalse;
8147 ping_have_bKGD=MagickFalse;
8148 ping_have_iCCP=MagickFalse;
8149 ping_have_pHYs=MagickFalse;
8150 ping_have_sRGB=MagickFalse;
8151 ping_have_tRNS=MagickFalse;
8153 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8154 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8155 ping_exclude_date=mng_info->ping_exclude_date;
8156 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8157 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8158 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8159 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8160 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8161 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8162 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8163 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8164 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8165 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8166 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8167 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8169 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8170 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8171 ping_need_colortype_warning = MagickFalse;
8173 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8174 * i.e., eliminate the ICC profile and set image->rendering_intent.
8175 * Note that this will not involve any changes to the actual pixels
8176 * but merely passes information to applications that read the resulting
8179 * To do: recognize other variants of the sRGB profile, using the CRC to
8180 * verify all recognized variants including the 7 already known.
8182 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8184 * Use something other than image->rendering_intent to record the fact
8185 * that the sRGB profile was found.
8187 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8188 * profile. Record the Blackpoint Compensation, if any.
8190 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8198 ResetImageProfileIterator(image);
8199 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8201 profile=GetImageProfile(image,name);
8203 if (profile != (StringInfo *) NULL)
8205 if ((LocaleCompare(name,"ICC") == 0) ||
8206 (LocaleCompare(name,"ICM") == 0))
8221 length=(png_uint_32) GetStringInfoLength(profile);
8223 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8225 if (length == sRGB_info[icheck].len)
8229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8230 " Got a %lu-byte ICC profile (potentially sRGB)",
8231 (unsigned long) length);
8233 data=GetStringInfoDatum(profile);
8234 profile_crc=crc32(0,data,length);
8236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8237 " with crc=%8x",(unsigned int) profile_crc);
8241 if (profile_crc == sRGB_info[icheck].crc)
8243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8244 " It is sRGB with rendering intent = %s",
8245 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8246 sRGB_info[icheck].intent));
8247 if (image->rendering_intent==UndefinedIntent)
8249 image->rendering_intent=
8250 Magick_RenderingIntent_from_PNG_RenderingIntent(
8251 sRGB_info[icheck].intent);
8253 ping_exclude_iCCP = MagickTrue;
8254 ping_exclude_zCCP = MagickTrue;
8255 ping_have_sRGB = MagickTrue;
8260 if (sRGB_info[icheck].len == 0)
8261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8262 " Got a %lu-byte ICC profile not recognized as sRGB",
8263 (unsigned long) length);
8266 name=GetNextImageProfile(image);
8271 number_semitransparent = 0;
8272 number_transparent = 0;
8274 if (logging != MagickFalse)
8276 if (image->storage_class == UndefinedClass)
8277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8278 " storage_class=UndefinedClass");
8279 if (image->storage_class == DirectClass)
8280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8281 " storage_class=DirectClass");
8282 if (image->storage_class == PseudoClass)
8283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8284 " storage_class=PseudoClass");
8287 if (image->storage_class == PseudoClass &&
8288 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8289 mng_info->write_png48 || mng_info->write_png64 ||
8290 (mng_info->write_png_colortype != 1 &&
8291 mng_info->write_png_colortype != 5)))
8293 (void) SyncImage(image,exception);
8294 image->storage_class = DirectClass;
8297 if (ping_preserve_colormap == MagickFalse)
8299 if (image->storage_class != PseudoClass && image->colormap != NULL)
8301 /* Free the bogus colormap; it can cause trouble later */
8302 if (logging != MagickFalse)
8303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8304 " Freeing bogus colormap");
8305 (void) RelinquishMagickMemory(image->colormap);
8306 image->colormap=NULL;
8310 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8311 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8314 Sometimes we get PseudoClass images whose RGB values don't match
8315 the colors in the colormap. This code syncs the RGB values.
8317 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8318 (void) SyncImage(image,exception);
8320 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8321 if (image->depth > 8)
8323 if (logging != MagickFalse)
8324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8325 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8331 /* Respect the -depth option */
8332 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8337 if (image->depth > 8)
8339 #if MAGICKCORE_QUANTUM_DEPTH > 16
8340 /* Scale to 16-bit */
8341 LBR16PacketRGBO(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 LBR16PacketRGBO(image->colormap[i]);
8367 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8370 else if (image->depth > 4)
8372 #if MAGICKCORE_QUANTUM_DEPTH > 8
8373 /* Scale to 8-bit */
8374 LBR08PacketRGBO(image->background_color);
8376 for (y=0; y < (ssize_t) image->rows; y++)
8378 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8380 if (r == (Quantum *) NULL)
8383 for (x=0; x < (ssize_t) image->columns; x++)
8386 r+=GetPixelChannels(image);
8389 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8393 if (image->storage_class == PseudoClass && image->colormap != NULL)
8395 for (i=0; i < (ssize_t) image->colors; i++)
8397 LBR08PacketRGBO(image->colormap[i]);
8400 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8403 if (image->depth > 2)
8405 /* Scale to 4-bit */
8406 LBR04PacketRGBO(image->background_color);
8408 for (y=0; y < (ssize_t) image->rows; y++)
8410 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8412 if (r == (Quantum *) NULL)
8415 for (x=0; x < (ssize_t) image->columns; x++)
8418 r+=GetPixelChannels(image);
8421 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8425 if (image->storage_class == PseudoClass && image->colormap != NULL)
8427 for (i=0; i < (ssize_t) image->colors; i++)
8429 LBR04PacketRGBO(image->colormap[i]);
8434 else if (image->depth > 1)
8436 /* Scale to 2-bit */
8437 LBR02PacketRGBO(image->background_color);
8439 for (y=0; y < (ssize_t) image->rows; y++)
8441 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8443 if (r == (Quantum *) NULL)
8446 for (x=0; x < (ssize_t) image->columns; x++)
8449 r+=GetPixelChannels(image);
8452 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8456 if (image->storage_class == PseudoClass && image->colormap != NULL)
8458 for (i=0; i < (ssize_t) image->colors; i++)
8460 LBR02PacketRGBO(image->colormap[i]);
8466 /* Scale to 1-bit */
8467 LBR01PacketRGBO(image->background_color);
8469 for (y=0; y < (ssize_t) image->rows; y++)
8471 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8473 if (r == (Quantum *) NULL)
8476 for (x=0; x < (ssize_t) image->columns; x++)
8479 r+=GetPixelChannels(image);
8482 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8486 if (image->storage_class == PseudoClass && image->colormap != NULL)
8488 for (i=0; i < (ssize_t) image->colors; i++)
8490 LBR01PacketRGBO(image->colormap[i]);
8496 /* To do: set to next higher multiple of 8 */
8497 if (image->depth < 8)
8500 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8501 /* PNG does not handle depths greater than 16 so reduce it even
8504 if (image->depth > 8)
8508 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8509 if (image->depth > 8)
8511 /* To do: fill low byte properly */
8515 if (image->depth == 16 && mng_info->write_png_depth != 16)
8516 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8520 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8521 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8522 mng_info->write_png_colortype < 4 &&
8523 image->alpha_trait != BlendPixelTrait)))
8525 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8526 * are not going to need the result.
8528 image_colors = (int) image->colors;
8529 number_opaque = (int) image->colors;
8530 if (mng_info->write_png_colortype == 1 ||
8531 mng_info->write_png_colortype == 5)
8532 ping_have_color=MagickFalse;
8534 ping_have_color=MagickTrue;
8535 ping_have_non_bw=MagickFalse;
8537 if (image->alpha_trait == BlendPixelTrait)
8539 number_transparent = 2;
8540 number_semitransparent = 1;
8545 number_transparent = 0;
8546 number_semitransparent = 0;
8554 * Normally we run this just once, but in the case of writing PNG8
8555 * we reduce the transparency to binary and run again, then if there
8556 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8557 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8558 * palette. Then (To do) we take care of a final reduction that is only
8559 * needed if there are still 256 colors present and one of them has both
8560 * transparent and opaque instances.
8563 tried_332 = MagickFalse;
8564 tried_333 = MagickFalse;
8565 tried_444 = MagickFalse;
8570 * Sometimes we get DirectClass images that have 256 colors or fewer.
8571 * This code will build a colormap.
8573 * Also, sometimes we get PseudoClass images with an out-of-date
8574 * colormap. This code will replace the colormap with a new one.
8575 * Sometimes we get PseudoClass images that have more than 256 colors.
8576 * This code will delete the colormap and change the image to
8579 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8580 * even though it sometimes contains left-over non-opaque values.
8582 * Also we gather some information (number of opaque, transparent,
8583 * and semitransparent pixels, and whether the image has any non-gray
8584 * pixels or only black-and-white pixels) that we might need later.
8586 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8587 * we need to check for bogus non-opaque values, at least.
8595 semitransparent[260],
8598 register const Quantum
8605 if (logging != MagickFalse)
8606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8607 " Enter BUILD_PALETTE:");
8609 if (logging != MagickFalse)
8611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8612 " image->columns=%.20g",(double) image->columns);
8613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8614 " image->rows=%.20g",(double) image->rows);
8615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8616 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8618 " image->depth=%.20g",(double) image->depth);
8620 if (image->storage_class == PseudoClass && image->colormap != NULL)
8622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8623 " Original colormap:");
8624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625 " i (red,green,blue,alpha)");
8627 for (i=0; i < 256; i++)
8629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8630 " %d (%d,%d,%d,%d)",
8632 (int) image->colormap[i].red,
8633 (int) image->colormap[i].green,
8634 (int) image->colormap[i].blue,
8635 (int) image->colormap[i].alpha);
8638 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8643 " %d (%d,%d,%d,%d)",
8645 (int) image->colormap[i].red,
8646 (int) image->colormap[i].green,
8647 (int) image->colormap[i].blue,
8648 (int) image->colormap[i].alpha);
8653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8654 " image->colors=%d",(int) image->colors);
8656 if (image->colors == 0)
8657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8658 " (zero means unknown)");
8660 if (ping_preserve_colormap == MagickFalse)
8661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8662 " Regenerate the colormap");
8667 number_semitransparent = 0;
8668 number_transparent = 0;
8670 for (y=0; y < (ssize_t) image->rows; y++)
8672 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8674 if (q == (Quantum *) NULL)
8677 for (x=0; x < (ssize_t) image->columns; x++)
8679 if (image->alpha_trait != BlendPixelTrait ||
8680 GetPixelAlpha(image,q) == OpaqueAlpha)
8682 if (number_opaque < 259)
8684 if (number_opaque == 0)
8686 GetPixelInfoPixel(image, q, opaque);
8687 opaque[0].alpha=OpaqueAlpha;
8691 for (i=0; i< (ssize_t) number_opaque; i++)
8693 if (IsPixelEquivalent(image,q, opaque+i))
8697 if (i == (ssize_t) number_opaque && number_opaque < 259)
8700 GetPixelInfoPixel(image, q, opaque+i);
8701 opaque[i].alpha=OpaqueAlpha;
8705 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8707 if (number_transparent < 259)
8709 if (number_transparent == 0)
8711 GetPixelInfoPixel(image, q, transparent);
8712 ping_trans_color.red=(unsigned short)
8713 GetPixelRed(image,q);
8714 ping_trans_color.green=(unsigned short)
8715 GetPixelGreen(image,q);
8716 ping_trans_color.blue=(unsigned short)
8717 GetPixelBlue(image,q);
8718 ping_trans_color.gray=(unsigned short)
8719 GetPixelGray(image,q);
8720 number_transparent = 1;
8723 for (i=0; i< (ssize_t) number_transparent; i++)
8725 if (IsPixelEquivalent(image,q, transparent+i))
8729 if (i == (ssize_t) number_transparent &&
8730 number_transparent < 259)
8732 number_transparent++;
8733 GetPixelInfoPixel(image,q,transparent+i);
8739 if (number_semitransparent < 259)
8741 if (number_semitransparent == 0)
8743 GetPixelInfoPixel(image,q,semitransparent);
8744 number_semitransparent = 1;
8747 for (i=0; i< (ssize_t) number_semitransparent; i++)
8749 if (IsPixelEquivalent(image,q, semitransparent+i)
8750 && GetPixelAlpha(image,q) ==
8751 semitransparent[i].alpha)
8755 if (i == (ssize_t) number_semitransparent &&
8756 number_semitransparent < 259)
8758 number_semitransparent++;
8759 GetPixelInfoPixel(image, q, semitransparent+i);
8763 q+=GetPixelChannels(image);
8767 if (mng_info->write_png8 == MagickFalse &&
8768 ping_exclude_bKGD == MagickFalse)
8770 /* Add the background color to the palette, if it
8771 * isn't already there.
8773 if (logging != MagickFalse)
8775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8776 " Check colormap for background (%d,%d,%d)",
8777 (int) image->background_color.red,
8778 (int) image->background_color.green,
8779 (int) image->background_color.blue);
8781 for (i=0; i<number_opaque; i++)
8783 if (opaque[i].red == image->background_color.red &&
8784 opaque[i].green == image->background_color.green &&
8785 opaque[i].blue == image->background_color.blue)
8788 if (number_opaque < 259 && i == number_opaque)
8790 opaque[i] = image->background_color;
8791 ping_background.index = i;
8793 if (logging != MagickFalse)
8795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8796 " background_color index is %d",(int) i);
8800 else if (logging != MagickFalse)
8801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8802 " No room in the colormap to add background color");
8805 image_colors=number_opaque+number_transparent+number_semitransparent;
8807 if (logging != MagickFalse)
8809 if (image_colors > 256)
8810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8811 " image has more than 256 colors");
8814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8815 " image has %d colors",image_colors);
8818 if (ping_preserve_colormap != MagickFalse)
8821 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8823 ping_have_color=MagickFalse;
8824 ping_have_non_bw=MagickFalse;
8826 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8829 "incompatible colorspace");
8830 ping_have_color=MagickTrue;
8831 ping_have_non_bw=MagickTrue;
8834 if(image_colors > 256)
8836 for (y=0; y < (ssize_t) image->rows; y++)
8838 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8840 if (q == (Quantum *) NULL)
8844 for (x=0; x < (ssize_t) image->columns; x++)
8846 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8847 GetPixelRed(image,s) != GetPixelBlue(image,s))
8849 ping_have_color=MagickTrue;
8850 ping_have_non_bw=MagickTrue;
8853 s+=GetPixelChannels(image);
8856 if (ping_have_color != MagickFalse)
8859 /* Worst case is black-and-white; we are looking at every
8863 if (ping_have_non_bw == MagickFalse)
8866 for (x=0; x < (ssize_t) image->columns; x++)
8868 if (GetPixelRed(image,s) != 0 &&
8869 GetPixelRed(image,s) != QuantumRange)
8871 ping_have_non_bw=MagickTrue;
8874 s+=GetPixelChannels(image);
8881 if (image_colors < 257)
8887 * Initialize image colormap.
8890 if (logging != MagickFalse)
8891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8892 " Sort the new colormap");
8894 /* Sort palette, transparent first */;
8898 for (i=0; i<number_transparent; i++)
8899 colormap[n++] = transparent[i];
8901 for (i=0; i<number_semitransparent; i++)
8902 colormap[n++] = semitransparent[i];
8904 for (i=0; i<number_opaque; i++)
8905 colormap[n++] = opaque[i];
8907 ping_background.index +=
8908 (number_transparent + number_semitransparent);
8910 /* image_colors < 257; search the colormap instead of the pixels
8911 * to get ping_have_color and ping_have_non_bw
8915 if (ping_have_color == MagickFalse)
8917 if (colormap[i].red != colormap[i].green ||
8918 colormap[i].red != colormap[i].blue)
8920 ping_have_color=MagickTrue;
8921 ping_have_non_bw=MagickTrue;
8926 if (ping_have_non_bw == MagickFalse)
8928 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8929 ping_have_non_bw=MagickTrue;
8933 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8934 (number_transparent == 0 && number_semitransparent == 0)) &&
8935 (((mng_info->write_png_colortype-1) ==
8936 PNG_COLOR_TYPE_PALETTE) ||
8937 (mng_info->write_png_colortype == 0)))
8939 if (logging != MagickFalse)
8941 if (n != (ssize_t) image_colors)
8942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8943 " image_colors (%d) and n (%d) don't match",
8946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8947 " AcquireImageColormap");
8950 image->colors = image_colors;
8952 if (AcquireImageColormap(image,image_colors,exception) ==
8954 ThrowWriterException(ResourceLimitError,
8955 "MemoryAllocationFailed");
8957 for (i=0; i< (ssize_t) image_colors; i++)
8958 image->colormap[i] = colormap[i];
8960 if (logging != MagickFalse)
8962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963 " image->colors=%d (%d)",
8964 (int) image->colors, image_colors);
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967 " Update the pixel indexes");
8970 /* Sync the pixel indices with the new colormap */
8972 for (y=0; y < (ssize_t) image->rows; y++)
8974 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8976 if (q == (Quantum *) NULL)
8979 for (x=0; x < (ssize_t) image->columns; x++)
8981 for (i=0; i< (ssize_t) image_colors; i++)
8983 if ((image->alpha_trait != BlendPixelTrait ||
8984 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8985 image->colormap[i].red == GetPixelRed(image,q) &&
8986 image->colormap[i].green == GetPixelGreen(image,q) &&
8987 image->colormap[i].blue == GetPixelBlue(image,q))
8989 SetPixelIndex(image,i,q);
8993 q+=GetPixelChannels(image);
8996 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9002 if (logging != MagickFalse)
9004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9005 " image->colors=%d", (int) image->colors);
9007 if (image->colormap != NULL)
9009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9010 " i (red,green,blue,alpha)");
9012 for (i=0; i < (ssize_t) image->colors; i++)
9014 if (i < 300 || i >= (ssize_t) image->colors - 10)
9016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017 " %d (%d,%d,%d,%d)",
9019 (int) image->colormap[i].red,
9020 (int) image->colormap[i].green,
9021 (int) image->colormap[i].blue,
9022 (int) image->colormap[i].alpha);
9027 if (number_transparent < 257)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " number_transparent = %d",
9030 number_transparent);
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " number_transparent > 256");
9036 if (number_opaque < 257)
9037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9038 " number_opaque = %d",
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " number_opaque > 256");
9045 if (number_semitransparent < 257)
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " number_semitransparent = %d",
9048 number_semitransparent);
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " number_semitransparent > 256");
9054 if (ping_have_non_bw == MagickFalse)
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " All pixels and the background are black or white");
9058 else if (ping_have_color == MagickFalse)
9059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " All pixels and the background are gray");
9063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 " At least one pixel or the background is non-gray");
9066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9067 " Exit BUILD_PALETTE:");
9070 if (mng_info->write_png8 == MagickFalse)
9073 /* Make any reductions necessary for the PNG8 format */
9074 if (image_colors <= 256 &&
9075 image_colors != 0 && image->colormap != NULL &&
9076 number_semitransparent == 0 &&
9077 number_transparent <= 1)
9080 /* PNG8 can't have semitransparent colors so we threshold the
9081 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9082 * transparent color so if more than one is transparent we merge
9083 * them into image->background_color.
9085 if (number_semitransparent != 0 || number_transparent > 1)
9087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9088 " Thresholding the alpha channel to binary");
9090 for (y=0; y < (ssize_t) image->rows; y++)
9092 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9094 if (r == (Quantum *) NULL)
9097 for (x=0; x < (ssize_t) image->columns; x++)
9099 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9101 SetPixelInfoPixel(image,&image->background_color,r);
9102 SetPixelAlpha(image,TransparentAlpha,r);
9105 SetPixelAlpha(image,OpaqueAlpha,r);
9106 r+=GetPixelChannels(image);
9109 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9112 if (image_colors != 0 && image_colors <= 256 &&
9113 image->colormap != NULL)
9114 for (i=0; i<image_colors; i++)
9115 image->colormap[i].alpha =
9116 (image->colormap[i].alpha > TransparentAlpha/2 ?
9117 TransparentAlpha : OpaqueAlpha);
9122 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9123 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9124 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9127 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9129 if (logging != MagickFalse)
9130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9131 " Quantizing the background color to 4-4-4");
9133 tried_444 = MagickTrue;
9135 LBR04PacketRGB(image->background_color);
9137 if (logging != MagickFalse)
9138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9139 " Quantizing the pixel colors to 4-4-4");
9141 if (image->colormap == NULL)
9143 for (y=0; y < (ssize_t) image->rows; y++)
9145 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9147 if (r == (Quantum *) NULL)
9150 for (x=0; x < (ssize_t) image->columns; x++)
9152 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9154 r+=GetPixelChannels(image);
9157 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9162 else /* Should not reach this; colormap already exists and
9165 if (logging != MagickFalse)
9166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9167 " Quantizing the colormap to 4-4-4");
9169 for (i=0; i<image_colors; i++)
9171 LBR04PacketRGB(image->colormap[i]);
9177 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9179 if (logging != MagickFalse)
9180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9181 " Quantizing the background color to 3-3-3");
9183 tried_333 = MagickTrue;
9185 LBR03PacketRGB(image->background_color);
9187 if (logging != MagickFalse)
9188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9189 " Quantizing the pixel colors to 3-3-3-1");
9191 if (image->colormap == NULL)
9193 for (y=0; y < (ssize_t) image->rows; y++)
9195 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9197 if (r == (Quantum *) NULL)
9200 for (x=0; x < (ssize_t) image->columns; x++)
9202 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9204 r+=GetPixelChannels(image);
9207 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9212 else /* Should not reach this; colormap already exists and
9215 if (logging != MagickFalse)
9216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9217 " Quantizing the colormap to 3-3-3-1");
9218 for (i=0; i<image_colors; i++)
9220 LBR03PacketRGB(image->colormap[i]);
9226 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9228 if (logging != MagickFalse)
9229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9230 " Quantizing the background color to 3-3-2");
9232 tried_332 = MagickTrue;
9234 /* Red and green were already done so we only quantize the blue
9238 LBR02PacketBlue(image->background_color);
9240 if (logging != MagickFalse)
9241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9242 " Quantizing the pixel colors to 3-3-2-1");
9244 if (image->colormap == NULL)
9246 for (y=0; y < (ssize_t) image->rows; y++)
9248 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9250 if (r == (Quantum *) NULL)
9253 for (x=0; x < (ssize_t) image->columns; x++)
9255 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9257 r+=GetPixelChannels(image);
9260 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9265 else /* Should not reach this; colormap already exists and
9268 if (logging != MagickFalse)
9269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9270 " Quantizing the colormap to 3-3-2-1");
9271 for (i=0; i<image_colors; i++)
9273 LBR02PacketBlue(image->colormap[i]);
9279 if (image_colors == 0 || image_colors > 256)
9281 /* Take care of special case with 256 opaque colors + 1 transparent
9282 * color. We don't need to quantize to 2-3-2-1; we only need to
9283 * eliminate one color, so we'll merge the two darkest red
9284 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9286 if (logging != MagickFalse)
9287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9288 " Merging two dark red background colors to 3-3-2-1");
9290 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9291 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9292 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9294 image->background_color.red=ScaleCharToQuantum(0x24);
9297 if (logging != MagickFalse)
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " Merging two dark red pixel colors to 3-3-2-1");
9301 if (image->colormap == NULL)
9303 for (y=0; y < (ssize_t) image->rows; y++)
9305 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9307 if (r == (Quantum *) NULL)
9310 for (x=0; x < (ssize_t) image->columns; x++)
9312 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9313 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9314 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9315 GetPixelAlpha(image,r) == OpaqueAlpha)
9317 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9319 r+=GetPixelChannels(image);
9322 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9330 for (i=0; i<image_colors; i++)
9332 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9333 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9334 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9336 image->colormap[i].red=ScaleCharToQuantum(0x24);
9343 /* END OF BUILD_PALETTE */
9345 /* If we are excluding the tRNS chunk and there is transparency,
9346 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9349 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9350 (number_transparent != 0 || number_semitransparent != 0))
9352 unsigned int colortype=mng_info->write_png_colortype;
9354 if (ping_have_color == MagickFalse)
9355 mng_info->write_png_colortype = 5;
9358 mng_info->write_png_colortype = 7;
9360 if (colortype != 0 &&
9361 mng_info->write_png_colortype != colortype)
9362 ping_need_colortype_warning=MagickTrue;
9366 /* See if cheap transparency is possible. It is only possible
9367 * when there is a single transparent color, no semitransparent
9368 * color, and no opaque color that has the same RGB components
9369 * as the transparent color. We only need this information if
9370 * we are writing a PNG with colortype 0 or 2, and we have not
9371 * excluded the tRNS chunk.
9373 if (number_transparent == 1 &&
9374 mng_info->write_png_colortype < 4)
9376 ping_have_cheap_transparency = MagickTrue;
9378 if (number_semitransparent != 0)
9379 ping_have_cheap_transparency = MagickFalse;
9381 else if (image_colors == 0 || image_colors > 256 ||
9382 image->colormap == NULL)
9384 register const Quantum
9387 for (y=0; y < (ssize_t) image->rows; y++)
9389 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9391 if (q == (Quantum *) NULL)
9394 for (x=0; x < (ssize_t) image->columns; x++)
9396 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9397 (unsigned short) GetPixelRed(image,q) ==
9398 ping_trans_color.red &&
9399 (unsigned short) GetPixelGreen(image,q) ==
9400 ping_trans_color.green &&
9401 (unsigned short) GetPixelBlue(image,q) ==
9402 ping_trans_color.blue)
9404 ping_have_cheap_transparency = MagickFalse;
9408 q+=GetPixelChannels(image);
9411 if (ping_have_cheap_transparency == MagickFalse)
9417 /* Assuming that image->colormap[0] is the one transparent color
9418 * and that all others are opaque.
9420 if (image_colors > 1)
9421 for (i=1; i<image_colors; i++)
9422 if (image->colormap[i].red == image->colormap[0].red &&
9423 image->colormap[i].green == image->colormap[0].green &&
9424 image->colormap[i].blue == image->colormap[0].blue)
9426 ping_have_cheap_transparency = MagickFalse;
9431 if (logging != MagickFalse)
9433 if (ping_have_cheap_transparency == MagickFalse)
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435 " Cheap transparency is not possible.");
9438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9439 " Cheap transparency is possible.");
9443 ping_have_cheap_transparency = MagickFalse;
9445 image_depth=image->depth;
9447 quantum_info = (QuantumInfo *) NULL;
9449 image_colors=(int) image->colors;
9450 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9452 mng_info->IsPalette=image->storage_class == PseudoClass &&
9453 image_colors <= 256 && image->colormap != NULL;
9455 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9456 (image->colors == 0 || image->colormap == NULL))
9458 image_info=DestroyImageInfo(image_info);
9459 image=DestroyImage(image);
9460 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9461 "Cannot write PNG8 or color-type 3; colormap is NULL",
9462 "`%s'",IMimage->filename);
9463 return(MagickFalse);
9467 Allocate the PNG structures
9469 #ifdef PNG_USER_MEM_SUPPORTED
9470 error_info.image=image;
9471 error_info.exception=exception;
9472 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9473 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9474 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9477 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9478 MagickPNGErrorHandler,MagickPNGWarningHandler);
9481 if (ping == (png_struct *) NULL)
9482 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9484 ping_info=png_create_info_struct(ping);
9486 if (ping_info == (png_info *) NULL)
9488 png_destroy_write_struct(&ping,(png_info **) NULL);
9489 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9492 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9493 pixel_info=(MemoryInfo *) NULL;
9495 if (setjmp(png_jmpbuf(ping)))
9501 if (image_info->verbose)
9502 (void) printf("PNG write has failed.\n");
9504 png_destroy_write_struct(&ping,&ping_info);
9505 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9506 UnlockSemaphoreInfo(ping_semaphore);
9509 if (pixel_info != (MemoryInfo *) NULL)
9510 pixel_info=RelinquishVirtualMemory(pixel_info);
9512 if (quantum_info != (QuantumInfo *) NULL)
9513 quantum_info=DestroyQuantumInfo(quantum_info);
9515 if (ping_have_blob != MagickFalse)
9516 (void) CloseBlob(image);
9517 image_info=DestroyImageInfo(image_info);
9518 image=DestroyImage(image);
9519 return(MagickFalse);
9522 /* { For navigation to end of SETJMP-protected block. Within this
9523 * block, use png_error() instead of Throwing an Exception, to ensure
9524 * that libpng is able to clean up, and that the semaphore is unlocked.
9527 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9528 LockSemaphoreInfo(ping_semaphore);
9531 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9532 /* Allow benign errors */
9533 png_set_benign_errors(ping, 1);
9537 Prepare PNG for writing.
9540 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9541 if (mng_info->write_mng)
9543 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9544 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9545 /* Disable new libpng-1.5.10 feature when writing a MNG because
9546 * zero-length PLTE is OK
9548 png_set_check_for_invalid_index (ping, 0);
9553 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9554 if (mng_info->write_mng)
9555 png_permit_empty_plte(ping,MagickTrue);
9562 ping_width=(png_uint_32) image->columns;
9563 ping_height=(png_uint_32) image->rows;
9565 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9568 if (mng_info->write_png48 || mng_info->write_png64)
9571 if (mng_info->write_png_depth != 0)
9572 image_depth=mng_info->write_png_depth;
9574 /* Adjust requested depth to next higher valid depth if necessary */
9575 if (image_depth > 8)
9578 if ((image_depth > 4) && (image_depth < 8))
9581 if (image_depth == 3)
9584 if (logging != MagickFalse)
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " width=%.20g",(double) ping_width);
9588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589 " height=%.20g",(double) ping_height);
9590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9591 " image_matte=%.20g",(double) image->alpha_trait);
9592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9593 " image->depth=%.20g",(double) image->depth);
9594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9595 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9598 save_image_depth=image_depth;
9599 ping_bit_depth=(png_byte) save_image_depth;
9602 #if defined(PNG_pHYs_SUPPORTED)
9603 if (ping_exclude_pHYs == MagickFalse)
9605 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9606 (!mng_info->write_mng || !mng_info->equal_physs))
9608 if (logging != MagickFalse)
9609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9610 " Setting up pHYs chunk");
9612 if (image->units == PixelsPerInchResolution)
9614 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9615 ping_pHYs_x_resolution=
9616 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9617 ping_pHYs_y_resolution=
9618 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9621 else if (image->units == PixelsPerCentimeterResolution)
9623 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9624 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9625 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9630 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9631 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9632 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9635 if (logging != MagickFalse)
9636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9637 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9638 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9639 (int) ping_pHYs_unit_type);
9640 ping_have_pHYs = MagickTrue;
9645 if (ping_exclude_bKGD == MagickFalse)
9647 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9653 if (ping_bit_depth == 8)
9656 if (ping_bit_depth == 4)
9659 if (ping_bit_depth == 2)
9662 if (ping_bit_depth == 1)
9665 ping_background.red=(png_uint_16)
9666 (ScaleQuantumToShort(image->background_color.red) & mask);
9668 ping_background.green=(png_uint_16)
9669 (ScaleQuantumToShort(image->background_color.green) & mask);
9671 ping_background.blue=(png_uint_16)
9672 (ScaleQuantumToShort(image->background_color.blue) & mask);
9674 ping_background.gray=(png_uint_16) ping_background.green;
9677 if (logging != MagickFalse)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9680 " Setting up bKGD chunk (1)");
9681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9682 " background_color index is %d",
9683 (int) ping_background.index);
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " ping_bit_depth=%d",ping_bit_depth);
9689 ping_have_bKGD = MagickTrue;
9693 Select the color type.
9698 if (mng_info->IsPalette && mng_info->write_png8)
9700 /* To do: make this a function cause it's used twice, except
9701 for reducing the sample depth from 8. */
9703 number_colors=image_colors;
9705 ping_have_tRNS=MagickFalse;
9710 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9712 if (logging != MagickFalse)
9713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9714 " Setting up PLTE chunk with %d colors (%d)",
9715 number_colors, image_colors);
9717 for (i=0; i < (ssize_t) number_colors; i++)
9719 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9720 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9721 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9722 if (logging != MagickFalse)
9723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724 #if MAGICKCORE_QUANTUM_DEPTH == 8
9725 " %3ld (%3d,%3d,%3d)",
9727 " %5ld (%5d,%5d,%5d)",
9729 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9733 ping_have_PLTE=MagickTrue;
9734 image_depth=ping_bit_depth;
9737 if (matte != MagickFalse)
9740 Identify which colormap entry is transparent.
9742 assert(number_colors <= 256);
9743 assert(image->colormap != NULL);
9745 for (i=0; i < (ssize_t) number_transparent; i++)
9746 ping_trans_alpha[i]=0;
9749 ping_num_trans=(unsigned short) (number_transparent +
9750 number_semitransparent);
9752 if (ping_num_trans == 0)
9753 ping_have_tRNS=MagickFalse;
9756 ping_have_tRNS=MagickTrue;
9759 if (ping_exclude_bKGD == MagickFalse)
9762 * Identify which colormap entry is the background color.
9765 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9766 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9769 ping_background.index=(png_byte) i;
9771 if (logging != MagickFalse)
9773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9774 " background_color index is %d",
9775 (int) ping_background.index);
9778 } /* end of write_png8 */
9780 else if (mng_info->write_png_colortype == 1)
9782 image_matte=MagickFalse;
9783 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9786 else if (mng_info->write_png24 || mng_info->write_png48 ||
9787 mng_info->write_png_colortype == 3)
9789 image_matte=MagickFalse;
9790 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9793 else if (mng_info->write_png32 || mng_info->write_png64 ||
9794 mng_info->write_png_colortype == 7)
9796 image_matte=MagickTrue;
9797 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9800 else /* mng_info->write_pngNN not specified */
9802 image_depth=ping_bit_depth;
9804 if (mng_info->write_png_colortype != 0)
9806 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9808 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9809 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9810 image_matte=MagickTrue;
9813 image_matte=MagickFalse;
9815 if (logging != MagickFalse)
9816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9817 " PNG colortype %d was specified:",(int) ping_color_type);
9820 else /* write_png_colortype not specified */
9822 if (logging != MagickFalse)
9823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9824 " Selecting PNG colortype:");
9826 ping_color_type=(png_byte) ((matte != MagickFalse)?
9827 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9829 if (image_info->type == TrueColorType)
9831 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9832 image_matte=MagickFalse;
9835 if (image_info->type == TrueColorMatteType)
9837 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9838 image_matte=MagickTrue;
9841 if (image_info->type == PaletteType ||
9842 image_info->type == PaletteMatteType)
9843 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9845 if (mng_info->write_png_colortype == 0 &&
9846 (image_info->type == UndefinedType ||
9847 image_info->type == OptimizeType))
9849 if (ping_have_color == MagickFalse)
9851 if (image_matte == MagickFalse)
9853 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9854 image_matte=MagickFalse;
9859 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9860 image_matte=MagickTrue;
9865 if (image_matte == MagickFalse)
9867 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9868 image_matte=MagickFalse;
9873 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9874 image_matte=MagickTrue;
9881 if (logging != MagickFalse)
9882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9883 " Selected PNG colortype=%d",ping_color_type);
9885 if (ping_bit_depth < 8)
9887 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9888 ping_color_type == PNG_COLOR_TYPE_RGB ||
9889 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9893 old_bit_depth=ping_bit_depth;
9895 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9897 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9901 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9906 if (image->colors == 0)
9909 png_error(ping,"image has 0 colors");
9912 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9913 ping_bit_depth <<= 1;
9916 if (logging != MagickFalse)
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919 " Number of colors: %.20g",(double) image_colors);
9921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9922 " Tentative PNG bit depth: %d",ping_bit_depth);
9925 if (ping_bit_depth < (int) mng_info->write_png_depth)
9926 ping_bit_depth = mng_info->write_png_depth;
9929 image_depth=ping_bit_depth;
9931 if (logging != MagickFalse)
9933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9934 " Tentative PNG color type: %s (%.20g)",
9935 PngColorTypeToString(ping_color_type),
9936 (double) ping_color_type);
9938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9939 " image_info->type: %.20g",(double) image_info->type);
9941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " image_depth: %.20g",(double) image_depth);
9944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9946 " image->depth: %.20g",(double) image->depth);
9948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9949 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9952 if (matte != MagickFalse)
9954 if (mng_info->IsPalette)
9956 if (mng_info->write_png_colortype == 0)
9958 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9960 if (ping_have_color != MagickFalse)
9961 ping_color_type=PNG_COLOR_TYPE_RGBA;
9965 * Determine if there is any transparent color.
9967 if (number_transparent + number_semitransparent == 0)
9970 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9973 image_matte=MagickFalse;
9975 if (mng_info->write_png_colortype == 0)
9976 ping_color_type&=0x03;
9986 if (ping_bit_depth == 8)
9989 if (ping_bit_depth == 4)
9992 if (ping_bit_depth == 2)
9995 if (ping_bit_depth == 1)
9998 ping_trans_color.red=(png_uint_16)
9999 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10001 ping_trans_color.green=(png_uint_16)
10002 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10004 ping_trans_color.blue=(png_uint_16)
10005 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10007 ping_trans_color.gray=(png_uint_16)
10008 (ScaleQuantumToShort(GetPixelInfoIntensity(
10009 image->colormap)) & mask);
10011 ping_trans_color.index=(png_byte) 0;
10013 ping_have_tRNS=MagickTrue;
10016 if (ping_have_tRNS != MagickFalse)
10019 * Determine if there is one and only one transparent color
10020 * and if so if it is fully transparent.
10022 if (ping_have_cheap_transparency == MagickFalse)
10023 ping_have_tRNS=MagickFalse;
10026 if (ping_have_tRNS != MagickFalse)
10028 if (mng_info->write_png_colortype == 0)
10029 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10031 if (image_depth == 8)
10033 ping_trans_color.red&=0xff;
10034 ping_trans_color.green&=0xff;
10035 ping_trans_color.blue&=0xff;
10036 ping_trans_color.gray&=0xff;
10042 if (image_depth == 8)
10044 ping_trans_color.red&=0xff;
10045 ping_trans_color.green&=0xff;
10046 ping_trans_color.blue&=0xff;
10047 ping_trans_color.gray&=0xff;
10054 if (ping_have_tRNS != MagickFalse)
10055 image_matte=MagickFalse;
10057 if ((mng_info->IsPalette) &&
10058 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10059 ping_have_color == MagickFalse &&
10060 (image_matte == MagickFalse || image_depth >= 8))
10064 if (image_matte != MagickFalse)
10065 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10067 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10069 ping_color_type=PNG_COLOR_TYPE_GRAY;
10071 if (save_image_depth == 16 && image_depth == 8)
10073 if (logging != MagickFalse)
10075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10076 " Scaling ping_trans_color (0)");
10078 ping_trans_color.gray*=0x0101;
10082 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10083 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10085 if ((image_colors == 0) ||
10086 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10087 image_colors=(int) (one << image_depth);
10089 if (image_depth > 8)
10095 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10097 if(!mng_info->write_png_depth)
10101 while ((int) (one << ping_bit_depth)
10102 < (ssize_t) image_colors)
10103 ping_bit_depth <<= 1;
10107 else if (ping_color_type ==
10108 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10109 mng_info->IsPalette)
10111 /* Check if grayscale is reducible */
10114 depth_4_ok=MagickTrue,
10115 depth_2_ok=MagickTrue,
10116 depth_1_ok=MagickTrue;
10118 for (i=0; i < (ssize_t) image_colors; i++)
10123 intensity=ScaleQuantumToChar(image->colormap[i].red);
10125 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10126 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10127 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10128 depth_2_ok=depth_1_ok=MagickFalse;
10129 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10130 depth_1_ok=MagickFalse;
10133 if (depth_1_ok && mng_info->write_png_depth <= 1)
10136 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10139 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10144 image_depth=ping_bit_depth;
10149 if (mng_info->IsPalette)
10151 number_colors=image_colors;
10153 if (image_depth <= 8)
10158 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10160 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10162 for (i=0; i < (ssize_t) number_colors; i++)
10164 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10165 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10166 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10169 if (logging != MagickFalse)
10170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10171 " Setting up PLTE chunk with %d colors",
10174 ping_have_PLTE=MagickTrue;
10177 /* color_type is PNG_COLOR_TYPE_PALETTE */
10178 if (mng_info->write_png_depth == 0)
10186 while ((one << ping_bit_depth) < (size_t) number_colors)
10187 ping_bit_depth <<= 1;
10192 if (matte != MagickFalse)
10195 * Set up trans_colors array.
10197 assert(number_colors <= 256);
10199 ping_num_trans=(unsigned short) (number_transparent +
10200 number_semitransparent);
10202 if (ping_num_trans == 0)
10203 ping_have_tRNS=MagickFalse;
10207 if (logging != MagickFalse)
10209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10210 " Scaling ping_trans_color (1)");
10212 ping_have_tRNS=MagickTrue;
10214 for (i=0; i < ping_num_trans; i++)
10216 ping_trans_alpha[i]= (png_byte)
10217 ScaleQuantumToChar(image->colormap[i].alpha);
10227 if (image_depth < 8)
10230 if ((save_image_depth == 16) && (image_depth == 8))
10232 if (logging != MagickFalse)
10234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10235 " Scaling ping_trans_color from (%d,%d,%d)",
10236 (int) ping_trans_color.red,
10237 (int) ping_trans_color.green,
10238 (int) ping_trans_color.blue);
10241 ping_trans_color.red*=0x0101;
10242 ping_trans_color.green*=0x0101;
10243 ping_trans_color.blue*=0x0101;
10244 ping_trans_color.gray*=0x0101;
10246 if (logging != MagickFalse)
10248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 (int) ping_trans_color.red,
10251 (int) ping_trans_color.green,
10252 (int) ping_trans_color.blue);
10257 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10258 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10261 Adjust background and transparency samples in sub-8-bit grayscale files.
10263 if (ping_bit_depth < 8 && ping_color_type ==
10264 PNG_COLOR_TYPE_GRAY)
10272 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10274 if (ping_exclude_bKGD == MagickFalse)
10277 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10278 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10279 &image->background_color))) +.5)));
10281 if (logging != MagickFalse)
10282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283 " Setting up bKGD chunk (2)");
10284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10285 " background_color index is %d",
10286 (int) ping_background.index);
10288 ping_have_bKGD = MagickTrue;
10291 if (logging != MagickFalse)
10292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10293 " Scaling ping_trans_color.gray from %d",
10294 (int)ping_trans_color.gray);
10296 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10297 ping_trans_color.gray)+.5);
10299 if (logging != MagickFalse)
10300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10301 " to %d", (int)ping_trans_color.gray);
10304 if (ping_exclude_bKGD == MagickFalse)
10306 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10309 Identify which colormap entry is the background color.
10312 number_colors=image_colors;
10314 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10315 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10318 ping_background.index=(png_byte) i;
10320 if (logging != MagickFalse)
10322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10323 " Setting up bKGD chunk with index=%d",(int) i);
10326 if (i < (ssize_t) number_colors)
10328 ping_have_bKGD = MagickTrue;
10330 if (logging != MagickFalse)
10332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10333 " background =(%d,%d,%d)",
10334 (int) ping_background.red,
10335 (int) ping_background.green,
10336 (int) ping_background.blue);
10340 else /* Can't happen */
10342 if (logging != MagickFalse)
10343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10344 " No room in PLTE to add bKGD color");
10345 ping_have_bKGD = MagickFalse;
10350 if (logging != MagickFalse)
10351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10352 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10355 Initialize compression level and filtering.
10357 if (logging != MagickFalse)
10359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " Setting up deflate compression");
10362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363 " Compression buffer size: 32768");
10366 png_set_compression_buffer_size(ping,32768L);
10368 if (logging != MagickFalse)
10369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10370 " Compression mem level: 9");
10372 png_set_compression_mem_level(ping, 9);
10374 /* Untangle the "-quality" setting:
10376 Undefined is 0; the default is used.
10381 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10382 zlib default compression level
10384 1-9: the zlib compression level
10388 0-4: the PNG filter method
10390 5: libpng adaptive filtering if compression level > 5
10391 libpng filter type "none" if compression level <= 5
10392 or if image is grayscale or palette
10394 6: libpng adaptive filtering
10396 7: "LOCO" filtering (intrapixel differing) if writing
10397 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10398 and earlier because of a missing "else".
10400 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10401 filtering. Unused prior to IM-6.7.0-10, was same as 6
10403 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10404 Unused prior to IM-6.7.0-10, was same as 6
10406 Note that using the -quality option, not all combinations of
10407 PNG filter type, zlib compression level, and zlib compression
10408 strategy are possible. This will be addressed soon in a
10409 release that accomodates "-define png:compression-strategy", etc.
10413 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10414 image_info->quality;
10418 if (mng_info->write_png_compression_strategy == 0)
10419 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10422 else if (mng_info->write_png_compression_level == 0)
10427 level=(int) MagickMin((ssize_t) quality/10,9);
10429 mng_info->write_png_compression_level = level+1;
10432 if (mng_info->write_png_compression_strategy == 0)
10434 if ((quality %10) == 8 || (quality %10) == 9)
10435 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10436 mng_info->write_png_compression_strategy=Z_RLE+1;
10438 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10442 if (mng_info->write_png_compression_filter == 0)
10443 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10445 if (logging != MagickFalse)
10447 if (mng_info->write_png_compression_level)
10448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10449 " Compression level: %d",
10450 (int) mng_info->write_png_compression_level-1);
10452 if (mng_info->write_png_compression_strategy)
10453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10454 " Compression strategy: %d",
10455 (int) mng_info->write_png_compression_strategy-1);
10457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10458 " Setting up filtering");
10460 if (mng_info->write_png_compression_filter == 6)
10461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462 " Base filter method: ADAPTIVE");
10463 else if (mng_info->write_png_compression_filter == 0 ||
10464 mng_info->write_png_compression_filter == 1)
10465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10466 " Base filter method: NONE");
10468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10469 " Base filter method: %d",
10470 (int) mng_info->write_png_compression_filter-1);
10473 if (mng_info->write_png_compression_level != 0)
10474 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10476 if (mng_info->write_png_compression_filter == 6)
10478 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10479 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10481 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10483 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10485 else if (mng_info->write_png_compression_filter == 7 ||
10486 mng_info->write_png_compression_filter == 10)
10487 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10489 else if (mng_info->write_png_compression_filter == 8)
10491 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10492 if (mng_info->write_mng)
10494 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10495 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10496 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10499 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10502 else if (mng_info->write_png_compression_filter == 9)
10503 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10505 else if (mng_info->write_png_compression_filter != 0)
10506 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10507 mng_info->write_png_compression_filter-1);
10509 if (mng_info->write_png_compression_strategy != 0)
10510 png_set_compression_strategy(ping,
10511 mng_info->write_png_compression_strategy-1);
10513 ping_interlace_method=image_info->interlace != NoInterlace;
10515 if (mng_info->write_mng)
10516 png_set_sig_bytes(ping,8);
10518 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10520 if (mng_info->write_png_colortype != 0)
10522 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10523 if (ping_have_color != MagickFalse)
10525 ping_color_type = PNG_COLOR_TYPE_RGB;
10527 if (ping_bit_depth < 8)
10531 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10532 if (ping_have_color != MagickFalse)
10533 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10536 if (ping_need_colortype_warning != MagickFalse ||
10537 ((mng_info->write_png_depth &&
10538 (int) mng_info->write_png_depth != ping_bit_depth) ||
10539 (mng_info->write_png_colortype &&
10540 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10541 mng_info->write_png_colortype != 7 &&
10542 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10544 if (logging != MagickFalse)
10546 if (ping_need_colortype_warning != MagickFalse)
10548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10549 " Image has transparency but tRNS chunk was excluded");
10552 if (mng_info->write_png_depth)
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " Defined png:bit-depth=%u, Computed depth=%u",
10556 mng_info->write_png_depth,
10560 if (mng_info->write_png_colortype)
10562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10563 " Defined png:color-type=%u, Computed color type=%u",
10564 mng_info->write_png_colortype-1,
10570 "Cannot write image with defined png:bit-depth or png:color-type.");
10573 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10575 /* Add an opaque matte channel */
10576 image->alpha_trait = BlendPixelTrait;
10577 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10579 if (logging != MagickFalse)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " Added an opaque matte channel");
10584 if (number_transparent != 0 || number_semitransparent != 0)
10586 if (ping_color_type < 4)
10588 ping_have_tRNS=MagickTrue;
10589 if (logging != MagickFalse)
10590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10591 " Setting ping_have_tRNS=MagickTrue.");
10595 if (logging != MagickFalse)
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10597 " Writing PNG header chunks");
10599 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10600 ping_bit_depth,ping_color_type,
10601 ping_interlace_method,ping_compression_method,
10602 ping_filter_method);
10604 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10606 png_set_PLTE(ping,ping_info,palette,number_colors);
10608 if (logging != MagickFalse)
10610 for (i=0; i< (ssize_t) number_colors; i++)
10612 if (i < ping_num_trans)
10613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10614 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10616 (int) palette[i].red,
10617 (int) palette[i].green,
10618 (int) palette[i].blue,
10620 (int) ping_trans_alpha[i]);
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " PLTE[%d] = (%d,%d,%d)",
10625 (int) palette[i].red,
10626 (int) palette[i].green,
10627 (int) palette[i].blue);
10632 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10633 if (ping_exclude_sRGB != MagickFalse ||
10634 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10636 if ((ping_exclude_tEXt == MagickFalse ||
10637 ping_exclude_zTXt == MagickFalse) &&
10638 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10640 ResetImageProfileIterator(image);
10641 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10643 profile=GetImageProfile(image,name);
10645 if (profile != (StringInfo *) NULL)
10647 #ifdef PNG_WRITE_iCCP_SUPPORTED
10648 if ((LocaleCompare(name,"ICC") == 0) ||
10649 (LocaleCompare(name,"ICM") == 0))
10652 if (ping_exclude_iCCP == MagickFalse)
10654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 " Setting up iCCP chunk");
10657 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10658 #if (PNG_LIBPNG_VER < 10500)
10659 (png_charp) GetStringInfoDatum(profile),
10661 (png_const_bytep) GetStringInfoDatum(profile),
10663 (png_uint_32) GetStringInfoLength(profile));
10664 ping_have_iCCP = MagickTrue;
10670 if (ping_exclude_zCCP == MagickFalse)
10672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10673 " Setting up zTXT chunk with uuencoded ICC");
10674 Magick_png_write_raw_profile(image_info,ping,ping_info,
10675 (unsigned char *) name,(unsigned char *) name,
10676 GetStringInfoDatum(profile),
10677 (png_uint_32) GetStringInfoLength(profile));
10678 ping_have_iCCP = MagickTrue;
10682 if (logging != MagickFalse)
10683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684 " Setting up text chunk with %s profile",name);
10686 name=GetNextImageProfile(image);
10691 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10692 if ((mng_info->have_write_global_srgb == 0) &&
10693 ping_have_iCCP != MagickTrue &&
10694 (ping_have_sRGB != MagickFalse ||
10695 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10697 if (ping_exclude_sRGB == MagickFalse)
10700 Note image rendering intent.
10702 if (logging != MagickFalse)
10703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10704 " Setting up sRGB chunk");
10706 (void) png_set_sRGB(ping,ping_info,(
10707 Magick_RenderingIntent_to_PNG_RenderingIntent(
10708 image->rendering_intent)));
10710 ping_have_sRGB = MagickTrue;
10714 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10717 if (ping_exclude_gAMA == MagickFalse &&
10718 ping_have_iCCP == MagickFalse &&
10719 ping_have_sRGB == MagickFalse &&
10720 (ping_exclude_sRGB == MagickFalse ||
10721 (image->gamma < .45 || image->gamma > .46)))
10723 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10727 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10729 if (logging != MagickFalse)
10730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10731 " Setting up gAMA chunk");
10733 png_set_gAMA(ping,ping_info,image->gamma);
10737 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10739 if ((mng_info->have_write_global_chrm == 0) &&
10740 (image->chromaticity.red_primary.x != 0.0))
10743 Note image chromaticity.
10744 Note: if cHRM+gAMA == sRGB write sRGB instead.
10752 wp=image->chromaticity.white_point;
10753 rp=image->chromaticity.red_primary;
10754 gp=image->chromaticity.green_primary;
10755 bp=image->chromaticity.blue_primary;
10757 if (logging != MagickFalse)
10758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10759 " Setting up cHRM chunk");
10761 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10767 if (ping_exclude_bKGD == MagickFalse)
10769 if (ping_have_bKGD != MagickFalse)
10771 png_set_bKGD(ping,ping_info,&ping_background);
10774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10775 " Setting up bKGD chunk");
10776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10777 " background color = (%d,%d,%d)",
10778 (int) ping_background.red,
10779 (int) ping_background.green,
10780 (int) ping_background.blue);
10781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10782 " index = %d, gray=%d",
10783 (int) ping_background.index,
10784 (int) ping_background.gray);
10789 if (ping_exclude_pHYs == MagickFalse)
10791 if (ping_have_pHYs != MagickFalse)
10793 png_set_pHYs(ping,ping_info,
10794 ping_pHYs_x_resolution,
10795 ping_pHYs_y_resolution,
10796 ping_pHYs_unit_type);
10800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10801 " Setting up pHYs chunk");
10802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803 " x_resolution=%lu",
10804 (unsigned long) ping_pHYs_x_resolution);
10805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10806 " y_resolution=%lu",
10807 (unsigned long) ping_pHYs_y_resolution);
10808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10810 (unsigned long) ping_pHYs_unit_type);
10815 #if defined(PNG_oFFs_SUPPORTED)
10816 if (ping_exclude_oFFs == MagickFalse)
10818 if (image->page.x || image->page.y)
10820 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10821 (png_int_32) image->page.y, 0);
10823 if (logging != MagickFalse)
10824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10825 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10826 (int) image->page.x, (int) image->page.y);
10831 if (mng_info->need_blob != MagickFalse)
10833 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10835 png_error(ping,"WriteBlob Failed");
10837 ping_have_blob=MagickTrue;
10840 png_write_info_before_PLTE(ping, ping_info);
10842 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10844 if (logging != MagickFalse)
10846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10847 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10850 if (ping_color_type == 3)
10851 (void) png_set_tRNS(ping, ping_info,
10858 (void) png_set_tRNS(ping, ping_info,
10861 &ping_trans_color);
10863 if (logging != MagickFalse)
10865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10866 " tRNS color =(%d,%d,%d)",
10867 (int) ping_trans_color.red,
10868 (int) ping_trans_color.green,
10869 (int) ping_trans_color.blue);
10874 /* write any png-chunk-b profiles */
10875 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10877 png_write_info(ping,ping_info);
10879 /* write any PNG-chunk-m profiles */
10880 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10882 if (ping_exclude_vpAg == MagickFalse)
10884 if ((image->page.width != 0 && image->page.width != image->columns) ||
10885 (image->page.height != 0 && image->page.height != image->rows))
10890 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10891 PNGType(chunk,mng_vpAg);
10892 LogPNGChunk(logging,mng_vpAg,9L);
10893 PNGLong(chunk+4,(png_uint_32) image->page.width);
10894 PNGLong(chunk+8,(png_uint_32) image->page.height);
10895 chunk[12]=0; /* unit = pixels */
10896 (void) WriteBlob(image,13,chunk);
10897 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10901 #if (PNG_LIBPNG_VER == 10206)
10902 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10903 #define PNG_HAVE_IDAT 0x04
10904 ping->mode |= PNG_HAVE_IDAT;
10905 #undef PNG_HAVE_IDAT
10908 png_set_packing(ping);
10912 rowbytes=image->columns;
10913 if (image_depth > 8)
10915 switch (ping_color_type)
10917 case PNG_COLOR_TYPE_RGB:
10921 case PNG_COLOR_TYPE_GRAY_ALPHA:
10925 case PNG_COLOR_TYPE_RGBA:
10933 if (logging != MagickFalse)
10935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10936 " Writing PNG image data");
10938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10941 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10942 if (pixel_info == (MemoryInfo *) NULL)
10943 png_error(ping,"Allocation of memory for pixels failed");
10944 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10947 Initialize image scanlines.
10949 quantum_info=AcquireQuantumInfo(image_info,image);
10950 if (quantum_info == (QuantumInfo *) NULL)
10951 png_error(ping,"Memory allocation for quantum_info failed");
10952 quantum_info->format=UndefinedQuantumFormat;
10953 quantum_info->depth=image_depth;
10954 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10955 num_passes=png_set_interlace_handling(ping);
10957 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10958 !mng_info->write_png48 && !mng_info->write_png64 &&
10959 !mng_info->write_png32) &&
10960 (mng_info->IsPalette ||
10961 (image_info->type == BilevelType)) &&
10962 image_matte == MagickFalse &&
10963 ping_have_non_bw == MagickFalse)
10965 /* Palette, Bilevel, or Opaque Monochrome */
10966 register const Quantum
10969 quantum_info->depth=8;
10970 for (pass=0; pass < num_passes; pass++)
10973 Convert PseudoClass image to a PNG monochrome image.
10975 for (y=0; y < (ssize_t) image->rows; y++)
10977 if (logging != MagickFalse && y == 0)
10978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10979 " Writing row of pixels (0)");
10981 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10983 if (p == (const Quantum *) NULL)
10986 if (mng_info->IsPalette)
10988 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10989 quantum_info,GrayQuantum,ping_pixels,exception);
10990 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10991 mng_info->write_png_depth &&
10992 mng_info->write_png_depth != old_bit_depth)
10994 /* Undo pixel scaling */
10995 for (i=0; i < (ssize_t) image->columns; i++)
10996 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10997 >> (8-old_bit_depth));
11003 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11004 quantum_info,RedQuantum,ping_pixels,exception);
11007 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11008 for (i=0; i < (ssize_t) image->columns; i++)
11009 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11012 if (logging != MagickFalse && y == 0)
11013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11014 " Writing row of pixels (1)");
11016 png_write_row(ping,ping_pixels);
11018 if (image->previous == (Image *) NULL)
11020 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11021 if (status == MagickFalse)
11027 else /* Not Palette, Bilevel, or Opaque Monochrome */
11029 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11030 !mng_info->write_png48 && !mng_info->write_png64 &&
11031 !mng_info->write_png32) && (image_matte != MagickFalse ||
11032 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11033 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11035 register const Quantum
11038 for (pass=0; pass < num_passes; pass++)
11041 for (y=0; y < (ssize_t) image->rows; y++)
11043 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11045 if (p == (const Quantum *) NULL)
11048 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11050 if (mng_info->IsPalette)
11051 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11052 quantum_info,GrayQuantum,ping_pixels,exception);
11055 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056 quantum_info,RedQuantum,ping_pixels,exception);
11058 if (logging != MagickFalse && y == 0)
11059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11060 " Writing GRAY PNG pixels (2)");
11063 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11065 if (logging != MagickFalse && y == 0)
11066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11067 " Writing GRAY_ALPHA PNG pixels (2)");
11069 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11070 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11073 if (logging != MagickFalse && y == 0)
11074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11075 " Writing row of pixels (2)");
11077 png_write_row(ping,ping_pixels);
11080 if (image->previous == (Image *) NULL)
11082 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11083 if (status == MagickFalse)
11091 register const Quantum
11094 for (pass=0; pass < num_passes; pass++)
11096 if ((image_depth > 8) ||
11097 mng_info->write_png24 ||
11098 mng_info->write_png32 ||
11099 mng_info->write_png48 ||
11100 mng_info->write_png64 ||
11101 (!mng_info->write_png8 && !mng_info->IsPalette))
11103 for (y=0; y < (ssize_t) image->rows; y++)
11105 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11107 if (p == (const Quantum *) NULL)
11110 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11112 if (image->storage_class == DirectClass)
11113 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11114 quantum_info,RedQuantum,ping_pixels,exception);
11117 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11118 quantum_info,GrayQuantum,ping_pixels,exception);
11121 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11123 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11124 quantum_info,GrayAlphaQuantum,ping_pixels,
11127 if (logging != MagickFalse && y == 0)
11128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11129 " Writing GRAY_ALPHA PNG pixels (3)");
11132 else if (image_matte != MagickFalse)
11133 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11134 quantum_info,RGBAQuantum,ping_pixels,exception);
11137 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11138 quantum_info,RGBQuantum,ping_pixels,exception);
11140 if (logging != MagickFalse && y == 0)
11141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11142 " Writing row of pixels (3)");
11144 png_write_row(ping,ping_pixels);
11149 /* not ((image_depth > 8) ||
11150 mng_info->write_png24 || mng_info->write_png32 ||
11151 mng_info->write_png48 || mng_info->write_png64 ||
11152 (!mng_info->write_png8 && !mng_info->IsPalette))
11155 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11156 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11158 if (logging != MagickFalse)
11159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11160 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11162 quantum_info->depth=8;
11166 for (y=0; y < (ssize_t) image->rows; y++)
11168 if (logging != MagickFalse && y == 0)
11169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11170 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11172 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11174 if (p == (const Quantum *) NULL)
11177 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11179 quantum_info->depth=image->depth;
11181 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11182 quantum_info,GrayQuantum,ping_pixels,exception);
11185 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11187 if (logging != MagickFalse && y == 0)
11188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11189 " Writing GRAY_ALPHA PNG pixels (4)");
11191 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11192 quantum_info,GrayAlphaQuantum,ping_pixels,
11198 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11199 quantum_info,IndexQuantum,ping_pixels,exception);
11201 if (logging != MagickFalse && y <= 2)
11203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11204 " Writing row of non-gray pixels (4)");
11206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11207 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11208 (int)ping_pixels[0],(int)ping_pixels[1]);
11211 png_write_row(ping,ping_pixels);
11215 if (image->previous == (Image *) NULL)
11217 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11218 if (status == MagickFalse)
11225 if (quantum_info != (QuantumInfo *) NULL)
11226 quantum_info=DestroyQuantumInfo(quantum_info);
11228 if (logging != MagickFalse)
11230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11231 " Wrote PNG image data");
11233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11234 " Width: %.20g",(double) ping_width);
11236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11237 " Height: %.20g",(double) ping_height);
11239 if (mng_info->write_png_depth)
11241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11242 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11246 " PNG bit-depth written: %d",ping_bit_depth);
11248 if (mng_info->write_png_colortype)
11250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11255 " PNG color-type written: %d",ping_color_type);
11257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11258 " PNG Interlace method: %d",ping_interlace_method);
11261 Generate text chunks after IDAT.
11263 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11265 ResetImagePropertyIterator(image);
11266 property=GetNextImageProperty(image);
11267 while (property != (const char *) NULL)
11272 value=GetImageProperty(image,property,exception);
11274 /* Don't write any "png:" or "jpeg:" properties; those are just for
11275 * "identify" or for passing through to another JPEG
11277 if ((LocaleNCompare(property,"png:",4) != 0 &&
11278 LocaleNCompare(property,"jpeg:",5)) &&
11281 /* Suppress density and units if we wrote a pHYs chunk */
11282 (ping_exclude_pHYs != MagickFalse ||
11283 LocaleCompare(property,"density") != 0 ||
11284 LocaleCompare(property,"units") != 0) &&
11286 /* Suppress the IM-generated Date:create and Date:modify */
11287 (ping_exclude_date == MagickFalse ||
11288 LocaleNCompare(property, "Date:",5) != 0))
11290 if (value != (const char *) NULL)
11293 #if PNG_LIBPNG_VER >= 10400
11294 text=(png_textp) png_malloc(ping,
11295 (png_alloc_size_t) sizeof(png_text));
11297 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11299 text[0].key=(char *) property;
11300 text[0].text=(char *) value;
11301 text[0].text_length=strlen(value);
11303 if (ping_exclude_tEXt != MagickFalse)
11304 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11306 else if (ping_exclude_zTXt != MagickFalse)
11307 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11311 text[0].compression=image_info->compression == NoCompression ||
11312 (image_info->compression == UndefinedCompression &&
11313 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11314 PNG_TEXT_COMPRESSION_zTXt ;
11317 if (logging != MagickFalse)
11319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11320 " Setting up text chunk");
11322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11323 " keyword: '%s'",text[0].key);
11326 png_set_text(ping,ping_info,text,1);
11327 png_free(ping,text);
11330 property=GetNextImageProperty(image);
11334 /* write any PNG-chunk-e profiles */
11335 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11337 if (logging != MagickFalse)
11338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11339 " Writing PNG end info");
11341 png_write_end(ping,ping_info);
11343 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11345 if (mng_info->page.x || mng_info->page.y ||
11346 (ping_width != mng_info->page.width) ||
11347 (ping_height != mng_info->page.height))
11353 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11355 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11356 PNGType(chunk,mng_FRAM);
11357 LogPNGChunk(logging,mng_FRAM,27L);
11359 chunk[5]=0; /* frame name separator (no name) */
11360 chunk[6]=1; /* flag for changing delay, for next frame only */
11361 chunk[7]=0; /* flag for changing frame timeout */
11362 chunk[8]=1; /* flag for changing frame clipping for next frame */
11363 chunk[9]=0; /* flag for changing frame sync_id */
11364 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11365 chunk[14]=0; /* clipping boundaries delta type */
11366 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11368 (png_uint_32) (mng_info->page.x + ping_width));
11369 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11371 (png_uint_32) (mng_info->page.y + ping_height));
11372 (void) WriteBlob(image,31,chunk);
11373 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11374 mng_info->old_framing_mode=4;
11375 mng_info->framing_mode=1;
11379 mng_info->framing_mode=3;
11381 if (mng_info->write_mng && !mng_info->need_fram &&
11382 ((int) image->dispose == 3))
11383 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11386 Free PNG resources.
11389 png_destroy_write_struct(&ping,&ping_info);
11391 pixel_info=RelinquishVirtualMemory(pixel_info);
11393 if (ping_have_blob != MagickFalse)
11394 (void) CloseBlob(image);
11396 image_info=DestroyImageInfo(image_info);
11397 image=DestroyImage(image);
11399 /* Store bit depth actually written */
11400 s[0]=(char) ping_bit_depth;
11403 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11405 if (logging != MagickFalse)
11406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11407 " exit WriteOnePNGImage()");
11409 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11410 UnlockSemaphoreInfo(ping_semaphore);
11413 /* } for navigation to beginning of SETJMP-protected block. Revert to
11414 * Throwing an Exception when an error occurs.
11417 return(MagickTrue);
11418 /* End write one PNG image */
11423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11427 % W r i t e P N G I m a g e %
11431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11433 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11434 % Multiple-image Network Graphics (MNG) image file.
11436 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11438 % The format of the WritePNGImage method is:
11440 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11441 % Image *image,ExceptionInfo *exception)
11443 % A description of each parameter follows:
11445 % o image_info: the image info.
11447 % o image: The image.
11449 % o exception: return any errors or warnings in this structure.
11451 % Returns MagickTrue on success, MagickFalse on failure.
11453 % Communicating with the PNG encoder:
11455 % While the datastream written is always in PNG format and normally would
11456 % be given the "png" file extension, this method also writes the following
11457 % pseudo-formats which are subsets of png:
11459 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11460 % a depth greater than 8, the depth is reduced. If transparency
11461 % is present, the tRNS chunk must only have values 0 and 255
11462 % (i.e., transparency is binary: fully opaque or fully
11463 % transparent). If other values are present they will be
11464 % 50%-thresholded to binary transparency. If more than 256
11465 % colors are present, they will be quantized to the 4-4-4-1,
11466 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11467 % of any resulting fully-transparent pixels is changed to
11468 % the image's background color.
11470 % If you want better quantization or dithering of the colors
11471 % or alpha than that, you need to do it before calling the
11472 % PNG encoder. The pixels contain 8-bit indices even if
11473 % they could be represented with 1, 2, or 4 bits. Grayscale
11474 % images will be written as indexed PNG files even though the
11475 % PNG grayscale type might be slightly more efficient. Please
11476 % note that writing to the PNG8 format may result in loss
11477 % of color and alpha data.
11479 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11480 % chunk can be present to convey binary transparency by naming
11481 % one of the colors as transparent. The only loss incurred
11482 % is reduction of sample depth to 8. If the image has more
11483 % than one transparent color, has semitransparent pixels, or
11484 % has an opaque pixel with the same RGB components as the
11485 % transparent color, an image is not written.
11487 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11488 % transparency is permitted, i.e., the alpha sample for
11489 % each pixel can have any value from 0 to 255. The alpha
11490 % channel is present even if the image is fully opaque.
11491 % The only loss in data is the reduction of the sample depth
11494 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11495 % chunk can be present to convey binary transparency by naming
11496 % one of the colors as transparent. If the image has more
11497 % than one transparent color, has semitransparent pixels, or
11498 % has an opaque pixel with the same RGB components as the
11499 % transparent color, an image is not written.
11501 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11502 % transparency is permitted, i.e., the alpha sample for
11503 % each pixel can have any value from 0 to 65535. The alpha
11504 % channel is present even if the image is fully opaque.
11506 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11507 % image, if the input was a PNG, is written. If these values
11508 % cannot be found, then "PNG00" falls back to the regular "PNG"
11511 % o -define: For more precise control of the PNG output, you can use the
11512 % Image options "png:bit-depth" and "png:color-type". These
11513 % can be set from the commandline with "-define" and also
11514 % from the application programming interfaces. The options
11515 % are case-independent and are converted to lowercase before
11516 % being passed to this encoder.
11518 % png:color-type can be 0, 2, 3, 4, or 6.
11520 % When png:color-type is 0 (Grayscale), png:bit-depth can
11521 % be 1, 2, 4, 8, or 16.
11523 % When png:color-type is 2 (RGB), png:bit-depth can
11526 % When png:color-type is 3 (Indexed), png:bit-depth can
11527 % be 1, 2, 4, or 8. This refers to the number of bits
11528 % used to store the index. The color samples always have
11529 % bit-depth 8 in indexed PNG files.
11531 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11532 % png:bit-depth can be 8 or 16.
11534 % If the image cannot be written without loss with the
11535 % requested bit-depth and color-type, a PNG file will not
11536 % be written, a warning will be issued, and the encoder will
11537 % return MagickFalse.
11539 % Since image encoders should not be responsible for the "heavy lifting",
11540 % the user should make sure that ImageMagick has already reduced the
11541 % image depth and number of colors and limit transparency to binary
11542 % transparency prior to attempting to write the image with depth, color,
11543 % or transparency limitations.
11545 % Note that another definition, "png:bit-depth-written" exists, but it
11546 % is not intended for external use. It is only used internally by the
11547 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11549 % It is possible to request that the PNG encoder write previously-formatted
11550 % ancillary chunks in the output PNG file, using the "-profile" commandline
11551 % option as shown below or by setting the profile via a programming
11554 % -profile PNG-chunk-x:<file>
11556 % where x is a location flag and <file> is a file containing the chunk
11557 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11558 % This encoder will compute the chunk length and CRC, so those must not
11559 % be included in the file.
11561 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11562 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11563 % of the same type, then add a short unique string after the "x" to prevent
11564 % subsequent profiles from overwriting the preceding ones, e.g.,
11566 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11568 % As of version 6.6.6 the following optimizations are always done:
11570 % o 32-bit depth is reduced to 16.
11571 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11572 % high byte and low byte are identical.
11573 % o Palette is sorted to remove unused entries and to put a
11574 % transparent color first, if BUILD_PNG_PALETTE is defined.
11575 % o Opaque matte channel is removed when writing an indexed PNG.
11576 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11577 % this can be done without loss and a larger bit depth N was not
11578 % requested via the "-define png:bit-depth=N" option.
11579 % o If matte channel is present but only one transparent color is
11580 % present, RGB+tRNS is written instead of RGBA
11581 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11582 % was requested when converting an opaque image).
11584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11586 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11587 Image *image,ExceptionInfo *exception)
11592 have_mng_structure,
11608 assert(image_info != (const ImageInfo *) NULL);
11609 assert(image_info->signature == MagickSignature);
11610 assert(image != (Image *) NULL);
11611 assert(image->signature == MagickSignature);
11612 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11613 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11615 Allocate a MngInfo structure.
11617 have_mng_structure=MagickFalse;
11618 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11620 if (mng_info == (MngInfo *) NULL)
11621 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11624 Initialize members of the MngInfo structure.
11626 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11627 mng_info->image=image;
11628 mng_info->equal_backgrounds=MagickTrue;
11629 have_mng_structure=MagickTrue;
11631 /* See if user has requested a specific PNG subformat */
11633 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11634 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11635 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11636 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11637 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11639 value=GetImageOption(image_info,"png:format");
11641 if (value != (char *) NULL)
11643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11644 " Format=%s",value);
11646 mng_info->write_png8 = MagickFalse;
11647 mng_info->write_png24 = MagickFalse;
11648 mng_info->write_png32 = MagickFalse;
11649 mng_info->write_png48 = MagickFalse;
11650 mng_info->write_png64 = MagickFalse;
11652 if (LocaleCompare(value,"png8") == 0)
11653 mng_info->write_png8 = MagickTrue;
11655 else if (LocaleCompare(value,"png24") == 0)
11656 mng_info->write_png24 = MagickTrue;
11658 else if (LocaleCompare(value,"png32") == 0)
11659 mng_info->write_png32 = MagickTrue;
11661 else if (LocaleCompare(value,"png48") == 0)
11662 mng_info->write_png48 = MagickTrue;
11664 else if (LocaleCompare(value,"png64") == 0)
11665 mng_info->write_png64 = MagickTrue;
11667 else if (LocaleCompare(value,"png00") == 0)
11669 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11670 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11672 if (value != (char *) NULL)
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675 " png00 inherited bit depth=%s",value);
11677 if (LocaleCompare(value,"1") == 0)
11678 mng_info->write_png_depth = 1;
11680 else if (LocaleCompare(value,"1") == 0)
11681 mng_info->write_png_depth = 2;
11683 else if (LocaleCompare(value,"2") == 0)
11684 mng_info->write_png_depth = 4;
11686 else if (LocaleCompare(value,"8") == 0)
11687 mng_info->write_png_depth = 8;
11689 else if (LocaleCompare(value,"16") == 0)
11690 mng_info->write_png_depth = 16;
11693 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11695 if (value != (char *) NULL)
11697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11698 " png00 inherited color type=%s",value);
11700 if (LocaleCompare(value,"0") == 0)
11701 mng_info->write_png_colortype = 1;
11703 else if (LocaleCompare(value,"2") == 0)
11704 mng_info->write_png_colortype = 3;
11706 else if (LocaleCompare(value,"3") == 0)
11707 mng_info->write_png_colortype = 4;
11709 else if (LocaleCompare(value,"4") == 0)
11710 mng_info->write_png_colortype = 5;
11712 else if (LocaleCompare(value,"6") == 0)
11713 mng_info->write_png_colortype = 7;
11718 if (mng_info->write_png8)
11720 mng_info->write_png_colortype = /* 3 */ 4;
11721 mng_info->write_png_depth = 8;
11725 if (mng_info->write_png24)
11727 mng_info->write_png_colortype = /* 2 */ 3;
11728 mng_info->write_png_depth = 8;
11731 if (image->alpha_trait == BlendPixelTrait)
11732 (void) SetImageType(image,TrueColorMatteType,exception);
11735 (void) SetImageType(image,TrueColorType,exception);
11737 (void) SyncImage(image,exception);
11740 if (mng_info->write_png32)
11742 mng_info->write_png_colortype = /* 6 */ 7;
11743 mng_info->write_png_depth = 8;
11746 if (image->alpha_trait == BlendPixelTrait)
11747 (void) SetImageType(image,TrueColorMatteType,exception);
11750 (void) SetImageType(image,TrueColorType,exception);
11752 (void) SyncImage(image,exception);
11755 if (mng_info->write_png48)
11757 mng_info->write_png_colortype = /* 2 */ 3;
11758 mng_info->write_png_depth = 16;
11761 if (image->alpha_trait == BlendPixelTrait)
11762 (void) SetImageType(image,TrueColorMatteType,exception);
11765 (void) SetImageType(image,TrueColorType,exception);
11767 (void) SyncImage(image,exception);
11770 if (mng_info->write_png64)
11772 mng_info->write_png_colortype = /* 6 */ 7;
11773 mng_info->write_png_depth = 16;
11776 if (image->alpha_trait == BlendPixelTrait)
11777 (void) SetImageType(image,TrueColorMatteType,exception);
11780 (void) SetImageType(image,TrueColorType,exception);
11782 (void) SyncImage(image,exception);
11785 value=GetImageOption(image_info,"png:bit-depth");
11787 if (value != (char *) NULL)
11789 if (LocaleCompare(value,"1") == 0)
11790 mng_info->write_png_depth = 1;
11792 else if (LocaleCompare(value,"2") == 0)
11793 mng_info->write_png_depth = 2;
11795 else if (LocaleCompare(value,"4") == 0)
11796 mng_info->write_png_depth = 4;
11798 else if (LocaleCompare(value,"8") == 0)
11799 mng_info->write_png_depth = 8;
11801 else if (LocaleCompare(value,"16") == 0)
11802 mng_info->write_png_depth = 16;
11805 (void) ThrowMagickException(exception,
11806 GetMagickModule(),CoderWarning,
11807 "ignoring invalid defined png:bit-depth",
11810 if (logging != MagickFalse)
11811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11812 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11815 value=GetImageOption(image_info,"png:color-type");
11817 if (value != (char *) NULL)
11819 /* We must store colortype+1 because 0 is a valid colortype */
11820 if (LocaleCompare(value,"0") == 0)
11821 mng_info->write_png_colortype = 1;
11823 else if (LocaleCompare(value,"1") == 0)
11824 mng_info->write_png_colortype = 2;
11826 else if (LocaleCompare(value,"2") == 0)
11827 mng_info->write_png_colortype = 3;
11829 else if (LocaleCompare(value,"3") == 0)
11830 mng_info->write_png_colortype = 4;
11832 else if (LocaleCompare(value,"4") == 0)
11833 mng_info->write_png_colortype = 5;
11835 else if (LocaleCompare(value,"6") == 0)
11836 mng_info->write_png_colortype = 7;
11839 (void) ThrowMagickException(exception,
11840 GetMagickModule(),CoderWarning,
11841 "ignoring invalid defined png:color-type",
11844 if (logging != MagickFalse)
11845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11846 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11849 /* Check for chunks to be excluded:
11851 * The default is to not exclude any known chunks except for any
11852 * listed in the "unused_chunks" array, above.
11854 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11855 * define (in the image properties or in the image artifacts)
11856 * or via a mng_info member. For convenience, in addition
11857 * to or instead of a comma-separated list of chunks, the
11858 * "exclude-chunk" string can be simply "all" or "none".
11860 * The exclude-chunk define takes priority over the mng_info.
11862 * A "png:include-chunk" define takes priority over both the
11863 * mng_info and the "png:exclude-chunk" define. Like the
11864 * "exclude-chunk" string, it can define "all" or "none" as
11865 * well as a comma-separated list. Chunks that are unknown to
11866 * ImageMagick are always excluded, regardless of their "copy-safe"
11867 * status according to the PNG specification, and even if they
11868 * appear in the "include-chunk" list. Such defines appearing among
11869 * the image options take priority over those found among the image
11872 * Finally, all chunks listed in the "unused_chunks" array are
11873 * automatically excluded, regardless of the other instructions
11876 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11877 * will not be written and the gAMA chunk will only be written if it
11878 * is not between .45 and .46, or approximately (1.0/2.2).
11880 * If you exclude tRNS and the image has transparency, the colortype
11881 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11883 * The -strip option causes StripImage() to set the png:include-chunk
11884 * artifact to "none,trns,gama".
11887 mng_info->ping_exclude_bKGD=MagickFalse;
11888 mng_info->ping_exclude_cHRM=MagickFalse;
11889 mng_info->ping_exclude_date=MagickFalse;
11890 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11891 mng_info->ping_exclude_gAMA=MagickFalse;
11892 mng_info->ping_exclude_iCCP=MagickFalse;
11893 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11894 mng_info->ping_exclude_oFFs=MagickFalse;
11895 mng_info->ping_exclude_pHYs=MagickFalse;
11896 mng_info->ping_exclude_sRGB=MagickFalse;
11897 mng_info->ping_exclude_tEXt=MagickFalse;
11898 mng_info->ping_exclude_tRNS=MagickFalse;
11899 mng_info->ping_exclude_vpAg=MagickFalse;
11900 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11901 mng_info->ping_exclude_zTXt=MagickFalse;
11903 mng_info->ping_preserve_colormap=MagickFalse;
11905 value=GetImageOption(image_info,"png:preserve-colormap");
11907 value=GetImageArtifact(image,"png:preserve-colormap");
11909 mng_info->ping_preserve_colormap=MagickTrue;
11911 mng_info->ping_preserve_iCCP=MagickFalse;
11913 value=GetImageOption(image_info,"png:preserve-iCCP");
11915 value=GetImageArtifact(image,"png:preserve-iCCP");
11917 mng_info->ping_preserve_iCCP=MagickTrue;
11919 /* These compression-level, compression-strategy, and compression-filter
11920 * defines take precedence over values from the -quality option.
11922 value=GetImageOption(image_info,"png:compression-level");
11924 value=GetImageArtifact(image,"png:compression-level");
11927 /* We have to add 1 to everything because 0 is a valid input,
11928 * and we want to use 0 (the default) to mean undefined.
11930 if (LocaleCompare(value,"0") == 0)
11931 mng_info->write_png_compression_level = 1;
11933 else if (LocaleCompare(value,"1") == 0)
11934 mng_info->write_png_compression_level = 2;
11936 else if (LocaleCompare(value,"2") == 0)
11937 mng_info->write_png_compression_level = 3;
11939 else if (LocaleCompare(value,"3") == 0)
11940 mng_info->write_png_compression_level = 4;
11942 else if (LocaleCompare(value,"4") == 0)
11943 mng_info->write_png_compression_level = 5;
11945 else if (LocaleCompare(value,"5") == 0)
11946 mng_info->write_png_compression_level = 6;
11948 else if (LocaleCompare(value,"6") == 0)
11949 mng_info->write_png_compression_level = 7;
11951 else if (LocaleCompare(value,"7") == 0)
11952 mng_info->write_png_compression_level = 8;
11954 else if (LocaleCompare(value,"8") == 0)
11955 mng_info->write_png_compression_level = 9;
11957 else if (LocaleCompare(value,"9") == 0)
11958 mng_info->write_png_compression_level = 10;
11961 (void) ThrowMagickException(exception,
11962 GetMagickModule(),CoderWarning,
11963 "ignoring invalid defined png:compression-level",
11967 value=GetImageOption(image_info,"png:compression-strategy");
11969 value=GetImageArtifact(image,"png:compression-strategy");
11973 if (LocaleCompare(value,"0") == 0)
11974 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11976 else if (LocaleCompare(value,"1") == 0)
11977 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11979 else if (LocaleCompare(value,"2") == 0)
11980 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11982 else if (LocaleCompare(value,"3") == 0)
11983 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11984 mng_info->write_png_compression_strategy = Z_RLE+1;
11986 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11989 else if (LocaleCompare(value,"4") == 0)
11990 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11991 mng_info->write_png_compression_strategy = Z_FIXED+1;
11993 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11997 (void) ThrowMagickException(exception,
11998 GetMagickModule(),CoderWarning,
11999 "ignoring invalid defined png:compression-strategy",
12003 value=GetImageOption(image_info,"png:compression-filter");
12005 value=GetImageArtifact(image,"png:compression-filter");
12009 /* To do: combinations of filters allowed by libpng
12010 * masks 0x08 through 0xf8
12012 * Implement this as a comma-separated list of 0,1,2,3,4,5
12013 * where 5 is a special case meaning PNG_ALL_FILTERS.
12016 if (LocaleCompare(value,"0") == 0)
12017 mng_info->write_png_compression_filter = 1;
12019 else if (LocaleCompare(value,"1") == 0)
12020 mng_info->write_png_compression_filter = 2;
12022 else if (LocaleCompare(value,"2") == 0)
12023 mng_info->write_png_compression_filter = 3;
12025 else if (LocaleCompare(value,"3") == 0)
12026 mng_info->write_png_compression_filter = 4;
12028 else if (LocaleCompare(value,"4") == 0)
12029 mng_info->write_png_compression_filter = 5;
12031 else if (LocaleCompare(value,"5") == 0)
12032 mng_info->write_png_compression_filter = 6;
12035 (void) ThrowMagickException(exception,
12036 GetMagickModule(),CoderWarning,
12037 "ignoring invalid defined png:compression-filter",
12041 excluding=MagickFalse;
12043 for (source=0; source<1; source++)
12047 value=GetImageOption(image_info,"png:exclude-chunk");
12050 value=GetImageArtifact(image,"png:exclude-chunks");
12054 value=GetImageOption(image_info,"png:exclude-chunk");
12057 value=GetImageArtifact(image,"png:exclude-chunks");
12066 excluding=MagickTrue;
12068 if (logging != MagickFalse)
12071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12072 " png:exclude-chunk=%s found in image artifacts.\n", value);
12074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12075 " png:exclude-chunk=%s found in image properties.\n", value);
12078 last=strlen(value);
12080 for (i=0; i<(int) last; i+=5)
12083 if (LocaleNCompare(value+i,"none",4) == 0)
12085 mng_info->ping_exclude_bKGD=MagickFalse;
12086 mng_info->ping_exclude_cHRM=MagickFalse;
12087 mng_info->ping_exclude_date=MagickFalse;
12088 mng_info->ping_exclude_EXIF=MagickFalse;
12089 mng_info->ping_exclude_gAMA=MagickFalse;
12090 mng_info->ping_exclude_iCCP=MagickFalse;
12091 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12092 mng_info->ping_exclude_oFFs=MagickFalse;
12093 mng_info->ping_exclude_pHYs=MagickFalse;
12094 mng_info->ping_exclude_sRGB=MagickFalse;
12095 mng_info->ping_exclude_tEXt=MagickFalse;
12096 mng_info->ping_exclude_tRNS=MagickFalse;
12097 mng_info->ping_exclude_vpAg=MagickFalse;
12098 mng_info->ping_exclude_zCCP=MagickFalse;
12099 mng_info->ping_exclude_zTXt=MagickFalse;
12102 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12103 mng_info->ping_exclude_bKGD=MagickTrue;
12105 if (LocaleNCompare(value+i,"chrm",4) == 0)
12106 mng_info->ping_exclude_cHRM=MagickTrue;
12108 if (LocaleNCompare(value+i,"date",4) == 0)
12109 mng_info->ping_exclude_date=MagickTrue;
12111 if (LocaleNCompare(value+i,"exif",4) == 0)
12112 mng_info->ping_exclude_EXIF=MagickTrue;
12114 if (LocaleNCompare(value+i,"gama",4) == 0)
12115 mng_info->ping_exclude_gAMA=MagickTrue;
12117 if (LocaleNCompare(value+i,"iccp",4) == 0)
12118 mng_info->ping_exclude_iCCP=MagickTrue;
12121 if (LocaleNCompare(value+i,"itxt",4) == 0)
12122 mng_info->ping_exclude_iTXt=MagickTrue;
12125 if (LocaleNCompare(value+i,"gama",4) == 0)
12126 mng_info->ping_exclude_gAMA=MagickTrue;
12128 if (LocaleNCompare(value+i,"offs",4) == 0)
12129 mng_info->ping_exclude_oFFs=MagickTrue;
12131 if (LocaleNCompare(value+i,"phys",4) == 0)
12132 mng_info->ping_exclude_pHYs=MagickTrue;
12134 if (LocaleNCompare(value+i,"srgb",4) == 0)
12135 mng_info->ping_exclude_sRGB=MagickTrue;
12137 if (LocaleNCompare(value+i,"text",4) == 0)
12138 mng_info->ping_exclude_tEXt=MagickTrue;
12140 if (LocaleNCompare(value+i,"trns",4) == 0)
12141 mng_info->ping_exclude_tRNS=MagickTrue;
12143 if (LocaleNCompare(value+i,"vpag",4) == 0)
12144 mng_info->ping_exclude_vpAg=MagickTrue;
12146 if (LocaleNCompare(value+i,"zccp",4) == 0)
12147 mng_info->ping_exclude_zCCP=MagickTrue;
12149 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12150 mng_info->ping_exclude_zTXt=MagickTrue;
12152 if (LocaleNCompare(value+i,"all",3) == 0)
12154 mng_info->ping_exclude_bKGD=MagickTrue;
12155 mng_info->ping_exclude_cHRM=MagickTrue;
12156 mng_info->ping_exclude_date=MagickTrue;
12157 mng_info->ping_exclude_EXIF=MagickTrue;
12158 mng_info->ping_exclude_gAMA=MagickTrue;
12159 mng_info->ping_exclude_iCCP=MagickTrue;
12160 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12161 mng_info->ping_exclude_oFFs=MagickTrue;
12162 mng_info->ping_exclude_pHYs=MagickTrue;
12163 mng_info->ping_exclude_sRGB=MagickTrue;
12164 mng_info->ping_exclude_tEXt=MagickTrue;
12165 mng_info->ping_exclude_tRNS=MagickTrue;
12166 mng_info->ping_exclude_vpAg=MagickTrue;
12167 mng_info->ping_exclude_zCCP=MagickTrue;
12168 mng_info->ping_exclude_zTXt=MagickTrue;
12175 for (source=0; source<1; source++)
12179 value=GetImageOption(image_info,"png:include-chunk");
12182 value=GetImageArtifact(image,"png:include-chunks");
12186 value=GetImageOption(image_info,"png:include-chunk");
12189 value=GetImageArtifact(image,"png:include-chunks");
12197 excluding=MagickTrue;
12199 if (logging != MagickFalse)
12202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12203 " png:include-chunk=%s found in image artifacts.\n", value);
12205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12206 " png:include-chunk=%s found in image properties.\n", value);
12209 last=strlen(value);
12211 for (i=0; i<(int) last; i+=5)
12213 if (LocaleNCompare(value+i,"none",4) == 0)
12215 mng_info->ping_exclude_bKGD=MagickTrue;
12216 mng_info->ping_exclude_cHRM=MagickTrue;
12217 mng_info->ping_exclude_date=MagickTrue;
12218 mng_info->ping_exclude_EXIF=MagickTrue;
12219 mng_info->ping_exclude_gAMA=MagickTrue;
12220 mng_info->ping_exclude_iCCP=MagickTrue;
12221 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12222 mng_info->ping_exclude_oFFs=MagickTrue;
12223 mng_info->ping_exclude_pHYs=MagickTrue;
12224 mng_info->ping_exclude_sRGB=MagickTrue;
12225 mng_info->ping_exclude_tEXt=MagickTrue;
12226 mng_info->ping_exclude_tRNS=MagickTrue;
12227 mng_info->ping_exclude_vpAg=MagickTrue;
12228 mng_info->ping_exclude_zCCP=MagickTrue;
12229 mng_info->ping_exclude_zTXt=MagickTrue;
12232 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12233 mng_info->ping_exclude_bKGD=MagickFalse;
12235 if (LocaleNCompare(value+i,"chrm",4) == 0)
12236 mng_info->ping_exclude_cHRM=MagickFalse;
12238 if (LocaleNCompare(value+i,"date",4) == 0)
12239 mng_info->ping_exclude_date=MagickFalse;
12241 if (LocaleNCompare(value+i,"exif",4) == 0)
12242 mng_info->ping_exclude_EXIF=MagickFalse;
12244 if (LocaleNCompare(value+i,"gama",4) == 0)
12245 mng_info->ping_exclude_gAMA=MagickFalse;
12247 if (LocaleNCompare(value+i,"iccp",4) == 0)
12248 mng_info->ping_exclude_iCCP=MagickFalse;
12251 if (LocaleNCompare(value+i,"itxt",4) == 0)
12252 mng_info->ping_exclude_iTXt=MagickFalse;
12255 if (LocaleNCompare(value+i,"gama",4) == 0)
12256 mng_info->ping_exclude_gAMA=MagickFalse;
12258 if (LocaleNCompare(value+i,"offs",4) == 0)
12259 mng_info->ping_exclude_oFFs=MagickFalse;
12261 if (LocaleNCompare(value+i,"phys",4) == 0)
12262 mng_info->ping_exclude_pHYs=MagickFalse;
12264 if (LocaleNCompare(value+i,"srgb",4) == 0)
12265 mng_info->ping_exclude_sRGB=MagickFalse;
12267 if (LocaleNCompare(value+i,"text",4) == 0)
12268 mng_info->ping_exclude_tEXt=MagickFalse;
12270 if (LocaleNCompare(value+i,"trns",4) == 0)
12271 mng_info->ping_exclude_tRNS=MagickFalse;
12273 if (LocaleNCompare(value+i,"vpag",4) == 0)
12274 mng_info->ping_exclude_vpAg=MagickFalse;
12276 if (LocaleNCompare(value+i,"zccp",4) == 0)
12277 mng_info->ping_exclude_zCCP=MagickFalse;
12279 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12280 mng_info->ping_exclude_zTXt=MagickFalse;
12282 if (LocaleNCompare(value+i,"all",3) == 0)
12284 mng_info->ping_exclude_bKGD=MagickFalse;
12285 mng_info->ping_exclude_cHRM=MagickFalse;
12286 mng_info->ping_exclude_date=MagickFalse;
12287 mng_info->ping_exclude_EXIF=MagickFalse;
12288 mng_info->ping_exclude_gAMA=MagickFalse;
12289 mng_info->ping_exclude_iCCP=MagickFalse;
12290 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12291 mng_info->ping_exclude_oFFs=MagickFalse;
12292 mng_info->ping_exclude_pHYs=MagickFalse;
12293 mng_info->ping_exclude_sRGB=MagickFalse;
12294 mng_info->ping_exclude_tEXt=MagickFalse;
12295 mng_info->ping_exclude_tRNS=MagickFalse;
12296 mng_info->ping_exclude_vpAg=MagickFalse;
12297 mng_info->ping_exclude_zCCP=MagickFalse;
12298 mng_info->ping_exclude_zTXt=MagickFalse;
12305 if (excluding != MagickFalse && logging != MagickFalse)
12307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12308 " Chunks to be excluded from the output png:");
12309 if (mng_info->ping_exclude_bKGD != MagickFalse)
12310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12312 if (mng_info->ping_exclude_cHRM != MagickFalse)
12313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12315 if (mng_info->ping_exclude_date != MagickFalse)
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12318 if (mng_info->ping_exclude_EXIF != MagickFalse)
12319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12321 if (mng_info->ping_exclude_gAMA != MagickFalse)
12322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 if (mng_info->ping_exclude_iCCP != MagickFalse)
12325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12328 if (mng_info->ping_exclude_iTXt != MagickFalse)
12329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 if (mng_info->ping_exclude_oFFs != MagickFalse)
12333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12335 if (mng_info->ping_exclude_pHYs != MagickFalse)
12336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 if (mng_info->ping_exclude_sRGB != MagickFalse)
12339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12341 if (mng_info->ping_exclude_tEXt != MagickFalse)
12342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 if (mng_info->ping_exclude_tRNS != MagickFalse)
12345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12347 if (mng_info->ping_exclude_vpAg != MagickFalse)
12348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12350 if (mng_info->ping_exclude_zCCP != MagickFalse)
12351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12353 if (mng_info->ping_exclude_zTXt != MagickFalse)
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12358 mng_info->need_blob = MagickTrue;
12360 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12362 MngInfoFreeStruct(mng_info,&have_mng_structure);
12364 if (logging != MagickFalse)
12365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12370 #if defined(JNG_SUPPORTED)
12372 /* Write one JNG image */
12373 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12374 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12395 jng_alpha_compression_method,
12396 jng_alpha_sample_depth,
12404 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12405 " Enter WriteOneJNGImage()");
12407 blob=(unsigned char *) NULL;
12408 jpeg_image=(Image *) NULL;
12409 jpeg_image_info=(ImageInfo *) NULL;
12412 transparent=image_info->type==GrayscaleMatteType ||
12413 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12415 jng_alpha_sample_depth = 0;
12417 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12419 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12421 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12422 image_info->quality;
12424 if (jng_alpha_quality >= 1000)
12425 jng_alpha_quality /= 1000;
12431 /* Create JPEG blob, image, and image_info */
12432 if (logging != MagickFalse)
12433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12434 " Creating jpeg_image_info for alpha.");
12436 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12438 if (jpeg_image_info == (ImageInfo *) NULL)
12439 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12441 if (logging != MagickFalse)
12442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12443 " Creating jpeg_image.");
12445 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12446 if (jpeg_image == (Image *) NULL)
12447 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12448 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12449 jpeg_image->alpha_trait=UndefinedPixelTrait;
12450 jpeg_image->quality=jng_alpha_quality;
12451 jpeg_image_info->type=GrayscaleType;
12452 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12453 (void) AcquireUniqueFilename(jpeg_image->filename);
12454 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12455 "%s",jpeg_image->filename);
12459 jng_alpha_compression_method=0;
12461 jng_alpha_sample_depth=0;
12464 /* To do: check bit depth of PNG alpha channel */
12466 /* Check if image is grayscale. */
12467 if (image_info->type != TrueColorMatteType && image_info->type !=
12468 TrueColorType && IsImageGray(image,exception))
12471 if (logging != MagickFalse)
12473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12474 " JNG Quality = %d",(int) jng_quality);
12475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12476 " JNG Color Type = %d",jng_color_type);
12479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12480 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12482 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12484 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12490 if (jng_alpha_compression_method==0)
12495 /* Encode alpha as a grayscale PNG blob */
12496 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12498 if (logging != MagickFalse)
12499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12500 " Creating PNG blob.");
12503 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12504 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12505 jpeg_image_info->interlace=NoInterlace;
12507 /* Exclude all ancillary chunks */
12508 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12510 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12513 /* Retrieve sample depth used */
12514 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12515 if (value != (char *) NULL)
12516 jng_alpha_sample_depth= (unsigned int) value[0];
12520 /* Encode alpha as a grayscale JPEG blob */
12522 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12525 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12526 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12527 jpeg_image_info->interlace=NoInterlace;
12528 if (logging != MagickFalse)
12529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12530 " Creating blob.");
12531 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12533 jng_alpha_sample_depth=8;
12535 if (logging != MagickFalse)
12536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12537 " Successfully read jpeg_image into a blob, length=%.20g.",
12541 /* Destroy JPEG image and image_info */
12542 jpeg_image=DestroyImage(jpeg_image);
12543 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12544 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12547 /* Write JHDR chunk */
12548 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12549 PNGType(chunk,mng_JHDR);
12550 LogPNGChunk(logging,mng_JHDR,16L);
12551 PNGLong(chunk+4,(png_uint_32) image->columns);
12552 PNGLong(chunk+8,(png_uint_32) image->rows);
12553 chunk[12]=jng_color_type;
12554 chunk[13]=8; /* sample depth */
12555 chunk[14]=8; /*jng_image_compression_method */
12556 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12557 chunk[16]=jng_alpha_sample_depth;
12558 chunk[17]=jng_alpha_compression_method;
12559 chunk[18]=0; /*jng_alpha_filter_method */
12560 chunk[19]=0; /*jng_alpha_interlace_method */
12561 (void) WriteBlob(image,20,chunk);
12562 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12563 if (logging != MagickFalse)
12565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12566 " JNG width:%15lu",(unsigned long) image->columns);
12568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12569 " JNG height:%14lu",(unsigned long) image->rows);
12571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12572 " JNG color type:%10d",jng_color_type);
12574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12575 " JNG sample depth:%8d",8);
12577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12578 " JNG compression:%9d",8);
12580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12581 " JNG interlace:%11d",0);
12583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12584 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12587 " JNG alpha compression:%3d",jng_alpha_compression_method);
12589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12590 " JNG alpha filter:%8d",0);
12592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12593 " JNG alpha interlace:%5d",0);
12596 /* Write any JNG-chunk-b profiles */
12597 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12600 Write leading ancillary chunks
12606 Write JNG bKGD chunk
12617 if (jng_color_type == 8 || jng_color_type == 12)
12621 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12622 PNGType(chunk,mng_bKGD);
12623 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12624 red=ScaleQuantumToChar(image->background_color.red);
12625 green=ScaleQuantumToChar(image->background_color.green);
12626 blue=ScaleQuantumToChar(image->background_color.blue);
12633 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12634 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12637 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12640 Write JNG sRGB chunk
12642 (void) WriteBlobMSBULong(image,1L);
12643 PNGType(chunk,mng_sRGB);
12644 LogPNGChunk(logging,mng_sRGB,1L);
12646 if (image->rendering_intent != UndefinedIntent)
12647 chunk[4]=(unsigned char)
12648 Magick_RenderingIntent_to_PNG_RenderingIntent(
12649 (image->rendering_intent));
12652 chunk[4]=(unsigned char)
12653 Magick_RenderingIntent_to_PNG_RenderingIntent(
12654 (PerceptualIntent));
12656 (void) WriteBlob(image,5,chunk);
12657 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12661 if (image->gamma != 0.0)
12664 Write JNG gAMA chunk
12666 (void) WriteBlobMSBULong(image,4L);
12667 PNGType(chunk,mng_gAMA);
12668 LogPNGChunk(logging,mng_gAMA,4L);
12669 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12670 (void) WriteBlob(image,8,chunk);
12671 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12674 if ((mng_info->equal_chrms == MagickFalse) &&
12675 (image->chromaticity.red_primary.x != 0.0))
12681 Write JNG cHRM chunk
12683 (void) WriteBlobMSBULong(image,32L);
12684 PNGType(chunk,mng_cHRM);
12685 LogPNGChunk(logging,mng_cHRM,32L);
12686 primary=image->chromaticity.white_point;
12687 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12688 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12689 primary=image->chromaticity.red_primary;
12690 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12691 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12692 primary=image->chromaticity.green_primary;
12693 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12694 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12695 primary=image->chromaticity.blue_primary;
12696 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12697 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12698 (void) WriteBlob(image,36,chunk);
12699 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12703 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12706 Write JNG pHYs chunk
12708 (void) WriteBlobMSBULong(image,9L);
12709 PNGType(chunk,mng_pHYs);
12710 LogPNGChunk(logging,mng_pHYs,9L);
12711 if (image->units == PixelsPerInchResolution)
12713 PNGLong(chunk+4,(png_uint_32)
12714 (image->resolution.x*100.0/2.54+0.5));
12716 PNGLong(chunk+8,(png_uint_32)
12717 (image->resolution.y*100.0/2.54+0.5));
12724 if (image->units == PixelsPerCentimeterResolution)
12726 PNGLong(chunk+4,(png_uint_32)
12727 (image->resolution.x*100.0+0.5));
12729 PNGLong(chunk+8,(png_uint_32)
12730 (image->resolution.y*100.0+0.5));
12737 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12738 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12742 (void) WriteBlob(image,13,chunk);
12743 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12746 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12749 Write JNG oFFs chunk
12751 (void) WriteBlobMSBULong(image,9L);
12752 PNGType(chunk,mng_oFFs);
12753 LogPNGChunk(logging,mng_oFFs,9L);
12754 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12755 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12757 (void) WriteBlob(image,13,chunk);
12758 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12760 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12762 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12763 PNGType(chunk,mng_vpAg);
12764 LogPNGChunk(logging,mng_vpAg,9L);
12765 PNGLong(chunk+4,(png_uint_32) image->page.width);
12766 PNGLong(chunk+8,(png_uint_32) image->page.height);
12767 chunk[12]=0; /* unit = pixels */
12768 (void) WriteBlob(image,13,chunk);
12769 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12775 if (jng_alpha_compression_method==0)
12783 /* Write IDAT chunk header */
12784 if (logging != MagickFalse)
12785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12786 " Write IDAT chunks from blob, length=%.20g.",(double)
12789 /* Copy IDAT chunks */
12792 for (i=8; i<(ssize_t) length; i+=len+12)
12794 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12797 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12799 /* Found an IDAT chunk. */
12800 (void) WriteBlobMSBULong(image,(size_t) len);
12801 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12802 (void) WriteBlob(image,(size_t) len+4,p);
12803 (void) WriteBlobMSBULong(image,
12804 crc32(0,p,(uInt) len+4));
12809 if (logging != MagickFalse)
12810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12811 " Skipping %c%c%c%c chunk, length=%.20g.",
12812 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12819 /* Write JDAA chunk header */
12820 if (logging != MagickFalse)
12821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12822 " Write JDAA chunk, length=%.20g.",(double) length);
12823 (void) WriteBlobMSBULong(image,(size_t) length);
12824 PNGType(chunk,mng_JDAA);
12825 LogPNGChunk(logging,mng_JDAA,length);
12826 /* Write JDAT chunk(s) data */
12827 (void) WriteBlob(image,4,chunk);
12828 (void) WriteBlob(image,length,blob);
12829 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12832 blob=(unsigned char *) RelinquishMagickMemory(blob);
12835 /* Encode image as a JPEG blob */
12836 if (logging != MagickFalse)
12837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12838 " Creating jpeg_image_info.");
12839 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12840 if (jpeg_image_info == (ImageInfo *) NULL)
12841 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12843 if (logging != MagickFalse)
12844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12845 " Creating jpeg_image.");
12847 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12848 if (jpeg_image == (Image *) NULL)
12849 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12850 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12852 (void) AcquireUniqueFilename(jpeg_image->filename);
12853 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12854 jpeg_image->filename);
12856 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12859 if (logging != MagickFalse)
12860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12861 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12862 (double) jpeg_image->rows);
12864 if (jng_color_type == 8 || jng_color_type == 12)
12865 jpeg_image_info->type=GrayscaleType;
12867 jpeg_image_info->quality=jng_quality;
12868 jpeg_image->quality=jng_quality;
12869 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12870 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12872 if (logging != MagickFalse)
12873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874 " Creating blob.");
12876 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12878 if (logging != MagickFalse)
12880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12881 " Successfully read jpeg_image into a blob, length=%.20g.",
12884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12885 " Write JDAT chunk, length=%.20g.",(double) length);
12888 /* Write JDAT chunk(s) */
12889 (void) WriteBlobMSBULong(image,(size_t) length);
12890 PNGType(chunk,mng_JDAT);
12891 LogPNGChunk(logging,mng_JDAT,length);
12892 (void) WriteBlob(image,4,chunk);
12893 (void) WriteBlob(image,length,blob);
12894 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12896 jpeg_image=DestroyImage(jpeg_image);
12897 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12898 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12899 blob=(unsigned char *) RelinquishMagickMemory(blob);
12901 /* Write any JNG-chunk-e profiles */
12902 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12904 /* Write IEND chunk */
12905 (void) WriteBlobMSBULong(image,0L);
12906 PNGType(chunk,mng_IEND);
12907 LogPNGChunk(logging,mng_IEND,0);
12908 (void) WriteBlob(image,4,chunk);
12909 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12911 if (logging != MagickFalse)
12912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12913 " exit WriteOneJNGImage()");
12920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12924 % W r i t e J N G I m a g e %
12928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12930 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12932 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12934 % The format of the WriteJNGImage method is:
12936 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12937 % Image *image,ExceptionInfo *exception)
12939 % A description of each parameter follows:
12941 % o image_info: the image info.
12943 % o image: The image.
12945 % o exception: return any errors or warnings in this structure.
12947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12949 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12950 ExceptionInfo *exception)
12953 have_mng_structure,
12963 assert(image_info != (const ImageInfo *) NULL);
12964 assert(image_info->signature == MagickSignature);
12965 assert(image != (Image *) NULL);
12966 assert(image->signature == MagickSignature);
12967 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12968 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12969 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12970 if (status == MagickFalse)
12974 Allocate a MngInfo structure.
12976 have_mng_structure=MagickFalse;
12977 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12978 if (mng_info == (MngInfo *) NULL)
12979 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12981 Initialize members of the MngInfo structure.
12983 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12984 mng_info->image=image;
12985 have_mng_structure=MagickTrue;
12987 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12989 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12990 (void) CloseBlob(image);
12992 (void) CatchImageException(image);
12993 MngInfoFreeStruct(mng_info,&have_mng_structure);
12994 if (logging != MagickFalse)
12995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13000 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
13001 ExceptionInfo *exception)
13010 have_mng_structure,
13013 volatile MagickBooleanType
13025 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13026 defined(PNG_MNG_FEATURES_SUPPORTED)
13029 all_images_are_gray,
13039 volatile unsigned int
13050 #if (PNG_LIBPNG_VER < 10200)
13051 if (image_info->verbose)
13052 printf("Your PNG library (libpng-%s) is rather old.\n",
13053 PNG_LIBPNG_VER_STRING);
13059 assert(image_info != (const ImageInfo *) NULL);
13060 assert(image_info->signature == MagickSignature);
13061 assert(image != (Image *) NULL);
13062 assert(image->signature == MagickSignature);
13063 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13064 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13065 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13066 if (status == MagickFalse)
13070 Allocate a MngInfo structure.
13072 have_mng_structure=MagickFalse;
13073 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13074 if (mng_info == (MngInfo *) NULL)
13075 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13077 Initialize members of the MngInfo structure.
13079 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13080 mng_info->image=image;
13081 have_mng_structure=MagickTrue;
13082 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13085 * See if user has requested a specific PNG subformat to be used
13086 * for all of the PNGs in the MNG being written, e.g.,
13088 * convert *.png png8:animation.mng
13090 * To do: check -define png:bit_depth and png:color_type as well,
13091 * or perhaps use mng:bit_depth and mng:color_type instead for
13095 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13096 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13097 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13099 write_jng=MagickFalse;
13100 if (image_info->compression == JPEGCompression)
13101 write_jng=MagickTrue;
13103 mng_info->adjoin=image_info->adjoin &&
13104 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13106 if (logging != MagickFalse)
13108 /* Log some info about the input */
13112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13113 " Checking input image(s)");
13115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13116 " Image_info depth: %.20g",(double) image_info->depth);
13118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13119 " Type: %d",image_info->type);
13122 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13125 " Scene: %.20g",(double) scene++);
13127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13128 " Image depth: %.20g",(double) p->depth);
13130 if (p->alpha_trait == BlendPixelTrait)
13131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13138 if (p->storage_class == PseudoClass)
13139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13140 " Storage class: PseudoClass");
13143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13144 " Storage class: DirectClass");
13147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13148 " Number of colors: %.20g",(double) p->colors);
13151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13152 " Number of colors: unspecified");
13154 if (mng_info->adjoin == MagickFalse)
13159 use_global_plte=MagickFalse;
13160 all_images_are_gray=MagickFalse;
13161 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13162 need_local_plte=MagickTrue;
13164 need_defi=MagickFalse;
13165 need_matte=MagickFalse;
13166 mng_info->framing_mode=1;
13167 mng_info->old_framing_mode=1;
13170 if (image_info->page != (char *) NULL)
13173 Determine image bounding box.
13175 SetGeometry(image,&mng_info->page);
13176 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13177 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13189 mng_info->page=image->page;
13190 need_geom=MagickTrue;
13191 if (mng_info->page.width || mng_info->page.height)
13192 need_geom=MagickFalse;
13194 Check all the scenes.
13196 initial_delay=image->delay;
13197 need_iterations=MagickFalse;
13198 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13199 mng_info->equal_physs=MagickTrue,
13200 mng_info->equal_gammas=MagickTrue;
13201 mng_info->equal_srgbs=MagickTrue;
13202 mng_info->equal_backgrounds=MagickTrue;
13204 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13205 defined(PNG_MNG_FEATURES_SUPPORTED)
13206 all_images_are_gray=MagickTrue;
13207 mng_info->equal_palettes=MagickFalse;
13208 need_local_plte=MagickFalse;
13210 for (next_image=image; next_image != (Image *) NULL; )
13214 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13215 mng_info->page.width=next_image->columns+next_image->page.x;
13217 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13218 mng_info->page.height=next_image->rows+next_image->page.y;
13221 if (next_image->page.x || next_image->page.y)
13222 need_defi=MagickTrue;
13224 if (next_image->alpha_trait == BlendPixelTrait)
13225 need_matte=MagickTrue;
13227 if ((int) next_image->dispose >= BackgroundDispose)
13228 if ((next_image->alpha_trait == BlendPixelTrait) ||
13229 next_image->page.x || next_image->page.y ||
13230 ((next_image->columns < mng_info->page.width) &&
13231 (next_image->rows < mng_info->page.height)))
13232 mng_info->need_fram=MagickTrue;
13234 if (next_image->iterations)
13235 need_iterations=MagickTrue;
13237 final_delay=next_image->delay;
13239 if (final_delay != initial_delay || final_delay > 1UL*
13240 next_image->ticks_per_second)
13241 mng_info->need_fram=1;
13243 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13244 defined(PNG_MNG_FEATURES_SUPPORTED)
13246 check for global palette possibility.
13248 if (image->alpha_trait == BlendPixelTrait)
13249 need_local_plte=MagickTrue;
13251 if (need_local_plte == 0)
13253 if (IsImageGray(image,exception) == MagickFalse)
13254 all_images_are_gray=MagickFalse;
13255 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13256 if (use_global_plte == 0)
13257 use_global_plte=mng_info->equal_palettes;
13258 need_local_plte=!mng_info->equal_palettes;
13261 if (GetNextImageInList(next_image) != (Image *) NULL)
13263 if (next_image->background_color.red !=
13264 next_image->next->background_color.red ||
13265 next_image->background_color.green !=
13266 next_image->next->background_color.green ||
13267 next_image->background_color.blue !=
13268 next_image->next->background_color.blue)
13269 mng_info->equal_backgrounds=MagickFalse;
13271 if (next_image->gamma != next_image->next->gamma)
13272 mng_info->equal_gammas=MagickFalse;
13274 if (next_image->rendering_intent !=
13275 next_image->next->rendering_intent)
13276 mng_info->equal_srgbs=MagickFalse;
13278 if ((next_image->units != next_image->next->units) ||
13279 (next_image->resolution.x != next_image->next->resolution.x) ||
13280 (next_image->resolution.y != next_image->next->resolution.y))
13281 mng_info->equal_physs=MagickFalse;
13283 if (mng_info->equal_chrms)
13285 if (next_image->chromaticity.red_primary.x !=
13286 next_image->next->chromaticity.red_primary.x ||
13287 next_image->chromaticity.red_primary.y !=
13288 next_image->next->chromaticity.red_primary.y ||
13289 next_image->chromaticity.green_primary.x !=
13290 next_image->next->chromaticity.green_primary.x ||
13291 next_image->chromaticity.green_primary.y !=
13292 next_image->next->chromaticity.green_primary.y ||
13293 next_image->chromaticity.blue_primary.x !=
13294 next_image->next->chromaticity.blue_primary.x ||
13295 next_image->chromaticity.blue_primary.y !=
13296 next_image->next->chromaticity.blue_primary.y ||
13297 next_image->chromaticity.white_point.x !=
13298 next_image->next->chromaticity.white_point.x ||
13299 next_image->chromaticity.white_point.y !=
13300 next_image->next->chromaticity.white_point.y)
13301 mng_info->equal_chrms=MagickFalse;
13305 next_image=GetNextImageInList(next_image);
13307 if (image_count < 2)
13309 mng_info->equal_backgrounds=MagickFalse;
13310 mng_info->equal_chrms=MagickFalse;
13311 mng_info->equal_gammas=MagickFalse;
13312 mng_info->equal_srgbs=MagickFalse;
13313 mng_info->equal_physs=MagickFalse;
13314 use_global_plte=MagickFalse;
13315 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13316 need_local_plte=MagickTrue;
13318 need_iterations=MagickFalse;
13321 if (mng_info->need_fram == MagickFalse)
13324 Only certain framing rates 100/n are exactly representable without
13325 the FRAM chunk but we'll allow some slop in VLC files
13327 if (final_delay == 0)
13329 if (need_iterations != MagickFalse)
13332 It's probably a GIF with loop; don't run it *too* fast.
13334 if (mng_info->adjoin)
13337 (void) ThrowMagickException(exception,GetMagickModule(),
13339 "input has zero delay between all frames; assuming",
13344 mng_info->ticks_per_second=0;
13346 if (final_delay != 0)
13347 mng_info->ticks_per_second=(png_uint_32)
13348 (image->ticks_per_second/final_delay);
13349 if (final_delay > 50)
13350 mng_info->ticks_per_second=2;
13352 if (final_delay > 75)
13353 mng_info->ticks_per_second=1;
13355 if (final_delay > 125)
13356 mng_info->need_fram=MagickTrue;
13358 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13359 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13360 (final_delay != 25) && (final_delay != 50) && (final_delay !=
13361 1UL*image->ticks_per_second))
13362 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13365 if (mng_info->need_fram != MagickFalse)
13366 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13368 If pseudocolor, we should also check to see if all the
13369 palettes are identical and write a global PLTE if they are.
13373 Write the MNG version 1.0 signature and MHDR chunk.
13375 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13376 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13377 PNGType(chunk,mng_MHDR);
13378 LogPNGChunk(logging,mng_MHDR,28L);
13379 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13380 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13381 PNGLong(chunk+12,mng_info->ticks_per_second);
13382 PNGLong(chunk+16,0L); /* layer count=unknown */
13383 PNGLong(chunk+20,0L); /* frame count=unknown */
13384 PNGLong(chunk+24,0L); /* play time=unknown */
13389 if (need_defi || mng_info->need_fram || use_global_plte)
13390 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13393 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13398 if (need_defi || mng_info->need_fram || use_global_plte)
13399 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13402 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13410 if (need_defi || mng_info->need_fram || use_global_plte)
13411 PNGLong(chunk+28,11L); /* simplicity=LC */
13414 PNGLong(chunk+28,9L); /* simplicity=VLC */
13419 if (need_defi || mng_info->need_fram || use_global_plte)
13420 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13423 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13426 (void) WriteBlob(image,32,chunk);
13427 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13428 option=GetImageOption(image_info,"mng:need-cacheoff");
13429 if (option != (const char *) NULL)
13435 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13437 PNGType(chunk,mng_nEED);
13438 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13439 (void) WriteBlobMSBULong(image,(size_t) length);
13440 LogPNGChunk(logging,mng_nEED,(size_t) length);
13442 (void) WriteBlob(image,length,chunk);
13443 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13445 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13446 (GetNextImageInList(image) != (Image *) NULL) &&
13447 (image->iterations != 1))
13450 Write MNG TERM chunk
13452 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13453 PNGType(chunk,mng_TERM);
13454 LogPNGChunk(logging,mng_TERM,10L);
13455 chunk[4]=3; /* repeat animation */
13456 chunk[5]=0; /* show last frame when done */
13457 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13458 final_delay/MagickMax(image->ticks_per_second,1)));
13460 if (image->iterations == 0)
13461 PNGLong(chunk+10,PNG_UINT_31_MAX);
13464 PNGLong(chunk+10,(png_uint_32) image->iterations);
13466 if (logging != MagickFalse)
13468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13469 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13470 final_delay/MagickMax(image->ticks_per_second,1)));
13472 if (image->iterations == 0)
13473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13474 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13478 " Image iterations: %.20g",(double) image->iterations);
13480 (void) WriteBlob(image,14,chunk);
13481 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13484 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13486 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13487 mng_info->equal_srgbs)
13490 Write MNG sRGB chunk
13492 (void) WriteBlobMSBULong(image,1L);
13493 PNGType(chunk,mng_sRGB);
13494 LogPNGChunk(logging,mng_sRGB,1L);
13496 if (image->rendering_intent != UndefinedIntent)
13497 chunk[4]=(unsigned char)
13498 Magick_RenderingIntent_to_PNG_RenderingIntent(
13499 (image->rendering_intent));
13502 chunk[4]=(unsigned char)
13503 Magick_RenderingIntent_to_PNG_RenderingIntent(
13504 (PerceptualIntent));
13506 (void) WriteBlob(image,5,chunk);
13507 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13508 mng_info->have_write_global_srgb=MagickTrue;
13513 if (image->gamma && mng_info->equal_gammas)
13516 Write MNG gAMA chunk
13518 (void) WriteBlobMSBULong(image,4L);
13519 PNGType(chunk,mng_gAMA);
13520 LogPNGChunk(logging,mng_gAMA,4L);
13521 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13522 (void) WriteBlob(image,8,chunk);
13523 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13524 mng_info->have_write_global_gama=MagickTrue;
13526 if (mng_info->equal_chrms)
13532 Write MNG cHRM chunk
13534 (void) WriteBlobMSBULong(image,32L);
13535 PNGType(chunk,mng_cHRM);
13536 LogPNGChunk(logging,mng_cHRM,32L);
13537 primary=image->chromaticity.white_point;
13538 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13539 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13540 primary=image->chromaticity.red_primary;
13541 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13542 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13543 primary=image->chromaticity.green_primary;
13544 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13545 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13546 primary=image->chromaticity.blue_primary;
13547 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13548 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13549 (void) WriteBlob(image,36,chunk);
13550 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13551 mng_info->have_write_global_chrm=MagickTrue;
13554 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13557 Write MNG pHYs chunk
13559 (void) WriteBlobMSBULong(image,9L);
13560 PNGType(chunk,mng_pHYs);
13561 LogPNGChunk(logging,mng_pHYs,9L);
13563 if (image->units == PixelsPerInchResolution)
13565 PNGLong(chunk+4,(png_uint_32)
13566 (image->resolution.x*100.0/2.54+0.5));
13568 PNGLong(chunk+8,(png_uint_32)
13569 (image->resolution.y*100.0/2.54+0.5));
13576 if (image->units == PixelsPerCentimeterResolution)
13578 PNGLong(chunk+4,(png_uint_32)
13579 (image->resolution.x*100.0+0.5));
13581 PNGLong(chunk+8,(png_uint_32)
13582 (image->resolution.y*100.0+0.5));
13589 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13590 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13594 (void) WriteBlob(image,13,chunk);
13595 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13598 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13599 or does not cover the entire frame.
13601 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13602 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13603 (image->page.width+image->page.x < mng_info->page.width))
13604 || (image->page.height && (image->page.height+image->page.y
13605 < mng_info->page.height))))
13607 (void) WriteBlobMSBULong(image,6L);
13608 PNGType(chunk,mng_BACK);
13609 LogPNGChunk(logging,mng_BACK,6L);
13610 red=ScaleQuantumToShort(image->background_color.red);
13611 green=ScaleQuantumToShort(image->background_color.green);
13612 blue=ScaleQuantumToShort(image->background_color.blue);
13613 PNGShort(chunk+4,red);
13614 PNGShort(chunk+6,green);
13615 PNGShort(chunk+8,blue);
13616 (void) WriteBlob(image,10,chunk);
13617 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13618 if (mng_info->equal_backgrounds)
13620 (void) WriteBlobMSBULong(image,6L);
13621 PNGType(chunk,mng_bKGD);
13622 LogPNGChunk(logging,mng_bKGD,6L);
13623 (void) WriteBlob(image,10,chunk);
13624 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13628 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13629 if ((need_local_plte == MagickFalse) &&
13630 (image->storage_class == PseudoClass) &&
13631 (all_images_are_gray == MagickFalse))
13637 Write MNG PLTE chunk
13639 data_length=3*image->colors;
13640 (void) WriteBlobMSBULong(image,data_length);
13641 PNGType(chunk,mng_PLTE);
13642 LogPNGChunk(logging,mng_PLTE,data_length);
13644 for (i=0; i < (ssize_t) image->colors; i++)
13646 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13647 image->colormap[i].red) & 0xff);
13648 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13649 image->colormap[i].green) & 0xff);
13650 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13651 image->colormap[i].blue) & 0xff);
13654 (void) WriteBlob(image,data_length+4,chunk);
13655 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13656 mng_info->have_write_global_plte=MagickTrue;
13662 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13663 defined(PNG_MNG_FEATURES_SUPPORTED)
13664 mng_info->equal_palettes=MagickFalse;
13668 if (mng_info->adjoin)
13670 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13671 defined(PNG_MNG_FEATURES_SUPPORTED)
13673 If we aren't using a global palette for the entire MNG, check to
13674 see if we can use one for two or more consecutive images.
13676 if (need_local_plte && use_global_plte && !all_images_are_gray)
13678 if (mng_info->IsPalette)
13681 When equal_palettes is true, this image has the same palette
13682 as the previous PseudoClass image
13684 mng_info->have_write_global_plte=mng_info->equal_palettes;
13685 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13686 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13689 Write MNG PLTE chunk
13694 data_length=3*image->colors;
13695 (void) WriteBlobMSBULong(image,data_length);
13696 PNGType(chunk,mng_PLTE);
13697 LogPNGChunk(logging,mng_PLTE,data_length);
13699 for (i=0; i < (ssize_t) image->colors; i++)
13701 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13702 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13703 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13706 (void) WriteBlob(image,data_length+4,chunk);
13707 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13708 (uInt) (data_length+4)));
13709 mng_info->have_write_global_plte=MagickTrue;
13713 mng_info->have_write_global_plte=MagickFalse;
13724 previous_x=mng_info->page.x;
13725 previous_y=mng_info->page.y;
13732 mng_info->page=image->page;
13733 if ((mng_info->page.x != previous_x) ||
13734 (mng_info->page.y != previous_y))
13736 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13737 PNGType(chunk,mng_DEFI);
13738 LogPNGChunk(logging,mng_DEFI,12L);
13739 chunk[4]=0; /* object 0 MSB */
13740 chunk[5]=0; /* object 0 LSB */
13741 chunk[6]=0; /* visible */
13742 chunk[7]=0; /* abstract */
13743 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13744 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13745 (void) WriteBlob(image,16,chunk);
13746 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13751 mng_info->write_mng=write_mng;
13753 if ((int) image->dispose >= 3)
13754 mng_info->framing_mode=3;
13756 if (mng_info->need_fram && mng_info->adjoin &&
13757 ((image->delay != mng_info->delay) ||
13758 (mng_info->framing_mode != mng_info->old_framing_mode)))
13760 if (image->delay == mng_info->delay)
13763 Write a MNG FRAM chunk with the new framing mode.
13765 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13766 PNGType(chunk,mng_FRAM);
13767 LogPNGChunk(logging,mng_FRAM,1L);
13768 chunk[4]=(unsigned char) mng_info->framing_mode;
13769 (void) WriteBlob(image,5,chunk);
13770 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13775 Write a MNG FRAM chunk with the delay.
13777 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13778 PNGType(chunk,mng_FRAM);
13779 LogPNGChunk(logging,mng_FRAM,10L);
13780 chunk[4]=(unsigned char) mng_info->framing_mode;
13781 chunk[5]=0; /* frame name separator (no name) */
13782 chunk[6]=2; /* flag for changing default delay */
13783 chunk[7]=0; /* flag for changing frame timeout */
13784 chunk[8]=0; /* flag for changing frame clipping */
13785 chunk[9]=0; /* flag for changing frame sync_id */
13786 PNGLong(chunk+10,(png_uint_32)
13787 ((mng_info->ticks_per_second*
13788 image->delay)/MagickMax(image->ticks_per_second,1)));
13789 (void) WriteBlob(image,14,chunk);
13790 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13791 mng_info->delay=(png_uint_32) image->delay;
13793 mng_info->old_framing_mode=mng_info->framing_mode;
13796 #if defined(JNG_SUPPORTED)
13797 if (image_info->compression == JPEGCompression)
13802 if (logging != MagickFalse)
13803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13804 " Writing JNG object.");
13805 /* To do: specify the desired alpha compression method. */
13806 write_info=CloneImageInfo(image_info);
13807 write_info->compression=UndefinedCompression;
13808 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13809 write_info=DestroyImageInfo(write_info);
13814 if (logging != MagickFalse)
13815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13816 " Writing PNG object.");
13818 mng_info->need_blob = MagickFalse;
13819 mng_info->ping_preserve_colormap = MagickFalse;
13821 /* We don't want any ancillary chunks written */
13822 mng_info->ping_exclude_bKGD=MagickTrue;
13823 mng_info->ping_exclude_cHRM=MagickTrue;
13824 mng_info->ping_exclude_date=MagickTrue;
13825 mng_info->ping_exclude_EXIF=MagickTrue;
13826 mng_info->ping_exclude_gAMA=MagickTrue;
13827 mng_info->ping_exclude_iCCP=MagickTrue;
13828 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13829 mng_info->ping_exclude_oFFs=MagickTrue;
13830 mng_info->ping_exclude_pHYs=MagickTrue;
13831 mng_info->ping_exclude_sRGB=MagickTrue;
13832 mng_info->ping_exclude_tEXt=MagickTrue;
13833 mng_info->ping_exclude_tRNS=MagickTrue;
13834 mng_info->ping_exclude_vpAg=MagickTrue;
13835 mng_info->ping_exclude_zCCP=MagickTrue;
13836 mng_info->ping_exclude_zTXt=MagickTrue;
13838 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13841 if (status == MagickFalse)
13843 MngInfoFreeStruct(mng_info,&have_mng_structure);
13844 (void) CloseBlob(image);
13845 return(MagickFalse);
13847 (void) CatchImageException(image);
13848 if (GetNextImageInList(image) == (Image *) NULL)
13850 image=SyncNextImageInList(image);
13851 status=SetImageProgress(image,SaveImagesTag,scene++,
13852 GetImageListLength(image));
13854 if (status == MagickFalse)
13857 } while (mng_info->adjoin);
13861 while (GetPreviousImageInList(image) != (Image *) NULL)
13862 image=GetPreviousImageInList(image);
13864 Write the MEND chunk.
13866 (void) WriteBlobMSBULong(image,0x00000000L);
13867 PNGType(chunk,mng_MEND);
13868 LogPNGChunk(logging,mng_MEND,0L);
13869 (void) WriteBlob(image,4,chunk);
13870 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13873 Relinquish resources.
13875 (void) CloseBlob(image);
13876 MngInfoFreeStruct(mng_info,&have_mng_structure);
13878 if (logging != MagickFalse)
13879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13881 return(MagickTrue);
13883 #else /* PNG_LIBPNG_VER > 10011 */
13885 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13888 printf("Your PNG library is too old: You have libpng-%s\n",
13889 PNG_LIBPNG_VER_STRING);
13891 ThrowBinaryException(CoderError,"PNG library is too old",
13892 image_info->filename);
13895 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13897 return(WritePNGImage(image_info,image));
13899 #endif /* PNG_LIBPNG_VER > 10011 */