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 value=GetImageOption(image_info,"png:swap-bytes");
2473 value=GetImageArtifact(image,"png:swap-bytes");
2479 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2484 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2485 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2487 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2488 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2491 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2494 (void) png_get_bKGD(ping, ping_info, &ping_background);
2496 if (ping_bit_depth < 8)
2498 png_set_packing(ping);
2502 image->depth=ping_bit_depth;
2503 image->depth=GetImageQuantumDepth(image,MagickFalse);
2504 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2506 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2507 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2509 image->rendering_intent=UndefinedIntent;
2510 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2512 (void) ResetMagickMemory(&image->chromaticity,0,
2513 sizeof(image->chromaticity));
2516 if (logging != MagickFalse)
2518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2519 " PNG width: %.20g, height: %.20g",
2520 (double) ping_width, (double) ping_height);
2522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2523 " PNG color_type: %d, bit_depth: %d",
2524 ping_color_type, ping_bit_depth);
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " PNG compression_method: %d",
2528 ping_compression_method);
2530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2531 " PNG interlace_method: %d, filter_method: %d",
2532 ping_interlace_method,ping_filter_method);
2535 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2537 ping_found_iCCP=MagickTrue;
2538 if (logging != MagickFalse)
2539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2540 " Found PNG iCCP chunk.");
2543 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2545 ping_found_gAMA=MagickTrue;
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Found PNG gAMA chunk.");
2551 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2553 ping_found_cHRM=MagickTrue;
2554 if (logging != MagickFalse)
2555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2556 " Found PNG cHRM chunk.");
2559 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2562 ping_found_sRGB=MagickTrue;
2563 if (logging != MagickFalse)
2564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2565 " Found PNG sRGB chunk.");
2568 #ifdef PNG_READ_iCCP_SUPPORTED
2569 if (ping_found_iCCP !=MagickTrue &&
2570 ping_found_sRGB != MagickTrue &&
2571 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2573 ping_found_iCCP=MagickTrue;
2574 if (logging != MagickFalse)
2575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2576 " Found PNG iCCP chunk.");
2579 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2584 #if (PNG_LIBPNG_VER < 10500)
2598 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2601 if (profile_length != 0)
2606 if (logging != MagickFalse)
2607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2608 " Reading PNG iCCP chunk.");
2610 profile=BlobToStringInfo(info,profile_length);
2612 if (profile == (StringInfo *) NULL)
2614 png_warning(ping, "ICC profile is NULL");
2615 profile=DestroyStringInfo(profile);
2619 if (ping_preserve_iCCP == MagickFalse)
2633 length=(png_uint_32) GetStringInfoLength(profile);
2635 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2637 if (length == sRGB_info[icheck].len)
2641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2642 " Got a %lu-byte ICC profile (potentially sRGB)",
2643 (unsigned long) length);
2645 data=GetStringInfoDatum(profile);
2646 profile_crc=crc32(0,data,length);
2648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2649 " with crc=%8x",(unsigned int) profile_crc);
2653 if (profile_crc == sRGB_info[icheck].crc)
2655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2656 " It is sRGB with rendering intent = %s",
2657 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2658 sRGB_info[icheck].intent));
2659 if (image->rendering_intent==UndefinedIntent)
2661 image->rendering_intent=
2662 Magick_RenderingIntent_from_PNG_RenderingIntent(
2663 sRGB_info[icheck].intent);
2669 if (sRGB_info[icheck].len == 0)
2671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2672 " Got a %lu-byte ICC profile not recognized as sRGB",
2673 (unsigned long) length);
2674 (void) SetImageProfile(image,"icc",profile,exception);
2677 else /* Preserve-iCCP */
2679 (void) SetImageProfile(image,"icc",profile,exception);
2682 profile=DestroyStringInfo(profile);
2688 #if defined(PNG_READ_sRGB_SUPPORTED)
2690 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2693 if (png_get_sRGB(ping,ping_info,&intent))
2695 if (image->rendering_intent == UndefinedIntent)
2696 image->rendering_intent=
2697 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2699 if (logging != MagickFalse)
2700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2701 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2705 else if (mng_info->have_global_srgb)
2707 if (image->rendering_intent == UndefinedIntent)
2708 image->rendering_intent=
2709 Magick_RenderingIntent_from_PNG_RenderingIntent
2710 (mng_info->global_srgb_intent);
2717 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2718 if (mng_info->have_global_gama)
2719 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2721 if (png_get_gAMA(ping,ping_info,&file_gamma))
2723 image->gamma=(float) file_gamma;
2724 if (logging != MagickFalse)
2725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2726 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2730 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2732 if (mng_info->have_global_chrm != MagickFalse)
2734 (void) png_set_cHRM(ping,ping_info,
2735 mng_info->global_chrm.white_point.x,
2736 mng_info->global_chrm.white_point.y,
2737 mng_info->global_chrm.red_primary.x,
2738 mng_info->global_chrm.red_primary.y,
2739 mng_info->global_chrm.green_primary.x,
2740 mng_info->global_chrm.green_primary.y,
2741 mng_info->global_chrm.blue_primary.x,
2742 mng_info->global_chrm.blue_primary.y);
2746 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2748 (void) png_get_cHRM(ping,ping_info,
2749 &image->chromaticity.white_point.x,
2750 &image->chromaticity.white_point.y,
2751 &image->chromaticity.red_primary.x,
2752 &image->chromaticity.red_primary.y,
2753 &image->chromaticity.green_primary.x,
2754 &image->chromaticity.green_primary.y,
2755 &image->chromaticity.blue_primary.x,
2756 &image->chromaticity.blue_primary.y);
2758 ping_found_cHRM=MagickTrue;
2760 if (image->chromaticity.red_primary.x>0.6399f &&
2761 image->chromaticity.red_primary.x<0.6401f &&
2762 image->chromaticity.red_primary.y>0.3299f &&
2763 image->chromaticity.red_primary.y<0.3301f &&
2764 image->chromaticity.green_primary.x>0.2999f &&
2765 image->chromaticity.green_primary.x<0.3001f &&
2766 image->chromaticity.green_primary.y>0.5999f &&
2767 image->chromaticity.green_primary.y<0.6001f &&
2768 image->chromaticity.blue_primary.x>0.1499f &&
2769 image->chromaticity.blue_primary.x<0.1501f &&
2770 image->chromaticity.blue_primary.y>0.0599f &&
2771 image->chromaticity.blue_primary.y<0.0601f &&
2772 image->chromaticity.white_point.x>0.3126f &&
2773 image->chromaticity.white_point.x<0.3128f &&
2774 image->chromaticity.white_point.y>0.3289f &&
2775 image->chromaticity.white_point.y<0.3291f)
2776 ping_found_sRGB_cHRM=MagickTrue;
2779 if (image->rendering_intent != UndefinedIntent)
2781 if (ping_found_sRGB != MagickTrue &&
2782 (ping_found_gAMA != MagickTrue ||
2783 (image->gamma > .45 && image->gamma < .46)) &&
2784 (ping_found_cHRM != MagickTrue ||
2785 ping_found_sRGB_cHRM != MagickFalse) &&
2786 ping_found_iCCP != MagickTrue)
2788 png_set_sRGB(ping,ping_info,
2789 Magick_RenderingIntent_to_PNG_RenderingIntent
2790 (image->rendering_intent));
2791 file_gamma=1.000f/2.200f;
2792 ping_found_sRGB=MagickTrue;
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2794 " Setting sRGB as if in input");
2798 #if defined(PNG_oFFs_SUPPORTED)
2799 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2801 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2802 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2804 if (logging != MagickFalse)
2805 if (image->page.x || image->page.y)
2806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2807 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2808 image->page.x,(double) image->page.y);
2811 #if defined(PNG_pHYs_SUPPORTED)
2812 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2814 if (mng_info->have_global_phys)
2816 png_set_pHYs(ping,ping_info,
2817 mng_info->global_x_pixels_per_unit,
2818 mng_info->global_y_pixels_per_unit,
2819 mng_info->global_phys_unit_type);
2826 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2829 Set image resolution.
2831 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2833 image->resolution.x=(double) x_resolution;
2834 image->resolution.y=(double) y_resolution;
2836 if (unit_type == PNG_RESOLUTION_METER)
2838 image->units=PixelsPerCentimeterResolution;
2839 image->resolution.x=(double) x_resolution/100.0;
2840 image->resolution.y=(double) y_resolution/100.0;
2843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2846 (double) x_resolution,(double) y_resolution,unit_type);
2850 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2855 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2857 if ((number_colors == 0) &&
2858 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2860 if (mng_info->global_plte_length)
2862 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2863 (int) mng_info->global_plte_length);
2865 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2867 if (mng_info->global_trns_length)
2870 "global tRNS has more entries than global PLTE");
2874 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2875 (int) mng_info->global_trns_length,NULL);
2878 #ifdef PNG_READ_bKGD_SUPPORTED
2880 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2881 mng_info->have_saved_bkgd_index ||
2883 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2888 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2889 if (mng_info->have_saved_bkgd_index)
2890 background.index=mng_info->saved_bkgd_index;
2892 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2893 background.index=ping_background->index;
2895 background.red=(png_uint_16)
2896 mng_info->global_plte[background.index].red;
2898 background.green=(png_uint_16)
2899 mng_info->global_plte[background.index].green;
2901 background.blue=(png_uint_16)
2902 mng_info->global_plte[background.index].blue;
2904 background.gray=(png_uint_16)
2905 mng_info->global_plte[background.index].green;
2907 png_set_bKGD(ping,ping_info,&background);
2912 png_error(ping,"No global PLTE in file");
2916 #ifdef PNG_READ_bKGD_SUPPORTED
2917 if (mng_info->have_global_bkgd &&
2918 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2919 image->background_color=mng_info->mng_global_bkgd;
2921 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2927 Set image background color.
2929 if (logging != MagickFalse)
2930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2931 " Reading PNG bKGD chunk.");
2933 /* Scale background components to 16-bit, then scale
2936 if (logging != MagickFalse)
2937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2938 " raw ping_background=(%d,%d,%d).",ping_background->red,
2939 ping_background->green,ping_background->blue);
2943 if (ping_file_depth == 1)
2946 else if (ping_file_depth == 2)
2949 else if (ping_file_depth == 4)
2952 if (ping_file_depth <= 8)
2955 ping_background->red *= bkgd_scale;
2956 ping_background->green *= bkgd_scale;
2957 ping_background->blue *= bkgd_scale;
2959 if (logging != MagickFalse)
2961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2962 " bkgd_scale=%d.",bkgd_scale);
2964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2965 " ping_background=(%d,%d,%d).",ping_background->red,
2966 ping_background->green,ping_background->blue);
2969 image->background_color.red=
2970 ScaleShortToQuantum(ping_background->red);
2972 image->background_color.green=
2973 ScaleShortToQuantum(ping_background->green);
2975 image->background_color.blue=
2976 ScaleShortToQuantum(ping_background->blue);
2978 image->background_color.alpha=OpaqueAlpha;
2980 if (logging != MagickFalse)
2981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2982 " image->background_color=(%.20g,%.20g,%.20g).",
2983 (double) image->background_color.red,
2984 (double) image->background_color.green,
2985 (double) image->background_color.blue);
2987 #endif /* PNG_READ_bKGD_SUPPORTED */
2989 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2992 Image has a tRNS chunk.
3000 if (logging != MagickFalse)
3001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3002 " Reading PNG tRNS chunk.");
3004 max_sample = (int) ((one << ping_file_depth) - 1);
3006 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3007 (int)ping_trans_color->gray > max_sample) ||
3008 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3009 ((int)ping_trans_color->red > max_sample ||
3010 (int)ping_trans_color->green > max_sample ||
3011 (int)ping_trans_color->blue > max_sample)))
3013 if (logging != MagickFalse)
3014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3015 " Ignoring PNG tRNS chunk with out-of-range sample.");
3016 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3017 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3018 image->alpha_trait=UndefinedPixelTrait;
3025 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3027 /* Scale transparent_color to short */
3028 transparent_color.red= scale_to_short*ping_trans_color->red;
3029 transparent_color.green= scale_to_short*ping_trans_color->green;
3030 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3031 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3033 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3035 if (logging != MagickFalse)
3037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3038 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
3040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3041 " scaled graylevel is %.20g.",transparent_color.alpha);
3043 transparent_color.red=transparent_color.alpha;
3044 transparent_color.green=transparent_color.alpha;
3045 transparent_color.blue=transparent_color.alpha;
3049 #if defined(PNG_READ_sBIT_SUPPORTED)
3050 if (mng_info->have_global_sbit)
3052 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3053 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3056 num_passes=png_set_interlace_handling(ping);
3058 png_read_update_info(ping,ping_info);
3060 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3063 Initialize image structure.
3065 mng_info->image_box.left=0;
3066 mng_info->image_box.right=(ssize_t) ping_width;
3067 mng_info->image_box.top=0;
3068 mng_info->image_box.bottom=(ssize_t) ping_height;
3069 if (mng_info->mng_type == 0)
3071 mng_info->mng_width=ping_width;
3072 mng_info->mng_height=ping_height;
3073 mng_info->frame=mng_info->image_box;
3074 mng_info->clip=mng_info->image_box;
3079 image->page.y=mng_info->y_off[mng_info->object_id];
3082 image->compression=ZipCompression;
3083 image->columns=ping_width;
3084 image->rows=ping_height;
3086 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3087 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3089 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
3090 image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
3092 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
3093 * image->colorspace to GRAY, and reset image->chromaticity.
3095 image->intensity = Rec709LuminancePixelIntensityMethod;
3096 SetImageColorspace(image,GRAYColorspace,exception);
3100 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3101 " image->colorspace=%d",(int) image->colorspace);
3103 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3104 ((int) ping_bit_depth < 16 &&
3105 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3110 image->storage_class=PseudoClass;
3112 image->colors=one << ping_file_depth;
3113 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3114 if (image->colors > 256)
3117 if (image->colors > 65536L)
3118 image->colors=65536L;
3120 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3125 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3126 image->colors=(size_t) number_colors;
3128 if (logging != MagickFalse)
3129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3134 if (image->storage_class == PseudoClass)
3137 Initialize image colormap.
3139 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3140 png_error(ping,"Memory allocation failed");
3142 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3147 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3149 for (i=0; i < (ssize_t) number_colors; i++)
3151 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3152 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3153 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3156 for ( ; i < (ssize_t) image->colors; i++)
3158 image->colormap[i].red=0;
3159 image->colormap[i].green=0;
3160 image->colormap[i].blue=0;
3169 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3174 for (i=0; i < (ssize_t) image->colors; i++)
3176 image->colormap[i].red=(Quantum) (i*scale);
3177 image->colormap[i].green=(Quantum) (i*scale);
3178 image->colormap[i].blue=(Quantum) (i*scale);
3183 /* Set some properties for reporting by "identify" */
3188 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3189 ping_interlace_method in value */
3191 (void) FormatLocaleString(msg,MaxTextExtent,
3192 "%d, %d",(int) ping_width, (int) ping_height);
3193 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3195 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3196 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3198 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3199 (int) ping_color_type,
3200 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3201 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3203 if (ping_interlace_method == 0)
3205 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3206 (int) ping_interlace_method);
3208 else if (ping_interlace_method == 1)
3210 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3211 (int) ping_interlace_method);
3215 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3216 (int) ping_interlace_method);
3218 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3220 if (number_colors != 0)
3222 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3223 (int) number_colors);
3224 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3230 Read image scanlines.
3232 if (image->delay != 0)
3233 mng_info->scenes_found++;
3235 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3236 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3237 (image_info->first_scene+image_info->number_scenes))))
3239 /* This happens later in non-ping decodes */
3240 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3241 image->storage_class=DirectClass;
3243 if (logging != MagickFalse)
3244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3245 " Skipping PNG image data for scene %.20g",(double)
3246 mng_info->scenes_found-1);
3247 png_destroy_read_struct(&ping,&ping_info,&end_info);
3249 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3250 UnlockSemaphoreInfo(ping_semaphore);
3253 if (logging != MagickFalse)
3254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3255 " exit ReadOnePNGImage().");
3260 if (logging != MagickFalse)
3261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3262 " Reading PNG IDAT chunk(s)");
3265 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3266 sizeof(*ping_pixels));
3268 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3270 if (pixel_info == (MemoryInfo *) NULL)
3271 png_error(ping,"Memory allocation failed");
3272 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3274 if (logging != MagickFalse)
3275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3276 " Converting PNG pixels to pixel packets");
3278 Convert PNG pixels to pixel packets.
3280 quantum_info=AcquireQuantumInfo(image_info,image);
3282 if (quantum_info == (QuantumInfo *) NULL)
3283 png_error(ping,"Failed to allocate quantum_info");
3285 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3290 found_transparent_pixel;
3292 found_transparent_pixel=MagickFalse;
3294 if (image->storage_class == DirectClass)
3296 for (pass=0; pass < num_passes; pass++)
3299 Convert image to DirectClass pixel packets.
3301 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3302 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3303 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3304 BlendPixelTrait : UndefinedPixelTrait;
3306 for (y=0; y < (ssize_t) image->rows; y++)
3309 row_offset=ping_rowbytes*y;
3314 png_read_row(ping,ping_pixels+row_offset,NULL);
3316 if (pass < num_passes-1)
3319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3321 if (q == (Quantum *) NULL)
3324 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3325 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3326 GrayQuantum,ping_pixels+row_offset,exception);
3328 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3329 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3330 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3332 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3333 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3334 RGBAQuantum,ping_pixels+row_offset,exception);
3336 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3337 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3338 IndexQuantum,ping_pixels+row_offset,exception);
3340 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3341 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3342 RGBQuantum,ping_pixels+row_offset,exception);
3344 if (found_transparent_pixel == MagickFalse)
3346 /* Is there a transparent pixel in the row? */
3347 if (y== 0 && logging != MagickFalse)
3348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3349 " Looking for cheap transparent pixel");
3351 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3353 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3354 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3355 (GetPixelAlpha(image,q) != OpaqueAlpha))
3357 if (logging != MagickFalse)
3358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3361 found_transparent_pixel = MagickTrue;
3364 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3365 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3366 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3367 transparent_color.red &&
3368 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3369 transparent_color.green &&
3370 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3371 transparent_color.blue))
3373 if (logging != MagickFalse)
3374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 found_transparent_pixel = MagickTrue;
3379 q+=GetPixelChannels(image);
3383 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3385 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3388 if (status == MagickFalse)
3391 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3395 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3397 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3398 if (status == MagickFalse)
3404 else /* image->storage_class != DirectClass */
3406 for (pass=0; pass < num_passes; pass++)
3415 Convert grayscale image to PseudoClass pixel packets.
3417 if (logging != MagickFalse)
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " Converting grayscale pixels to pixel packets");
3421 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3422 BlendPixelTrait : UndefinedPixelTrait;
3424 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3425 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3426 sizeof(*quantum_scanline));
3428 if (quantum_scanline == (Quantum *) NULL)
3429 png_error(ping,"Memory allocation failed");
3431 for (y=0; y < (ssize_t) image->rows; y++)
3437 row_offset=ping_rowbytes*y;
3442 png_read_row(ping,ping_pixels+row_offset,NULL);
3444 if (pass < num_passes-1)
3447 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3449 if (q == (Quantum *) NULL)
3452 p=ping_pixels+row_offset;
3455 switch (ping_bit_depth)
3460 if (ping_color_type == 4)
3461 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3465 alpha=ScaleCharToQuantum((unsigned char)*p++);
3467 SetPixelAlpha(image,alpha,q);
3469 if (alpha != OpaqueAlpha)
3470 found_transparent_pixel = MagickTrue;
3472 q+=GetPixelChannels(image);
3476 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3484 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3486 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3490 if (image->colors > 256)
3491 quantum=((*p++) << 8);
3497 *r=ScaleShortToQuantum(quantum);
3500 if (ping_color_type == 4)
3502 if (image->colors > 256)
3503 quantum=((*p++) << 8);
3509 alpha=ScaleShortToQuantum(quantum);
3510 SetPixelAlpha(image,alpha,q);
3512 if (alpha != OpaqueAlpha)
3513 found_transparent_pixel = MagickTrue;
3515 q+=GetPixelChannels(image);
3518 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3520 p++; /* strip low byte */
3522 if (ping_color_type == 4)
3524 SetPixelAlpha(image,*p++,q);
3526 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3527 found_transparent_pixel = MagickTrue;
3530 q+=GetPixelChannels(image);
3543 Transfer image scanline.
3547 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3549 if (q == (Quantum *) NULL)
3551 for (x=0; x < (ssize_t) image->columns; x++)
3553 SetPixelIndex(image,*r++,q);
3554 q+=GetPixelChannels(image);
3557 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3560 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3562 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3565 if (status == MagickFalse)
3570 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3572 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3574 if (status == MagickFalse)
3578 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3581 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3582 UndefinedPixelTrait;
3584 if (logging != MagickFalse)
3586 if (found_transparent_pixel != MagickFalse)
3587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3588 " Found transparent pixel");
3591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3592 " No transparent pixel was found");
3594 ping_color_type&=0x03;
3599 if (quantum_info != (QuantumInfo *) NULL)
3600 quantum_info=DestroyQuantumInfo(quantum_info);
3602 if (image->storage_class == PseudoClass)
3607 alpha_trait=image->alpha_trait;
3608 image->alpha_trait=UndefinedPixelTrait;
3609 (void) SyncImage(image,exception);
3610 image->alpha_trait=alpha_trait;
3613 png_read_end(ping,end_info);
3615 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3616 (ssize_t) image_info->first_scene && image->delay != 0)
3618 png_destroy_read_struct(&ping,&ping_info,&end_info);
3619 pixel_info=RelinquishVirtualMemory(pixel_info);
3621 (void) SetImageBackgroundColor(image,exception);
3622 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3623 UnlockSemaphoreInfo(ping_semaphore);
3625 if (logging != MagickFalse)
3626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3627 " exit ReadOnePNGImage() early.");
3631 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3637 Image has a transparent background.
3639 storage_class=image->storage_class;
3640 image->alpha_trait=BlendPixelTrait;
3642 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3644 if (storage_class == PseudoClass)
3646 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3648 for (x=0; x < ping_num_trans; x++)
3650 image->colormap[x].alpha_trait=BlendPixelTrait;
3651 image->colormap[x].alpha =
3652 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3656 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3658 for (x=0; x < (int) image->colors; x++)
3660 if (ScaleQuantumToShort(image->colormap[x].red) ==
3661 transparent_color.alpha)
3663 image->colormap[x].alpha_trait=BlendPixelTrait;
3664 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3668 (void) SyncImage(image,exception);
3671 #if 1 /* Should have already been done above, but glennrp problem P10
3676 for (y=0; y < (ssize_t) image->rows; y++)
3678 image->storage_class=storage_class;
3679 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3681 if (q == (Quantum *) NULL)
3685 /* Caution: on a Q8 build, this does not distinguish between
3686 * 16-bit colors that differ only in the low byte
3688 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3690 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3691 transparent_color.red &&
3692 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3693 transparent_color.green &&
3694 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3695 transparent_color.blue)
3697 SetPixelAlpha(image,TransparentAlpha,q);
3700 #if 0 /* I have not found a case where this is needed. */
3703 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3707 q+=GetPixelChannels(image);
3710 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3716 image->storage_class=DirectClass;
3719 for (j = 0; j < 2; j++)
3722 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3723 MagickTrue : MagickFalse;
3725 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3726 MagickTrue : MagickFalse;
3728 if (status != MagickFalse)
3729 for (i=0; i < (ssize_t) num_text; i++)
3731 /* Check for a profile */
3733 if (logging != MagickFalse)
3734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3735 " Reading PNG text chunk");
3737 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3739 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3749 length=text[i].text_length;
3750 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3752 if (value == (char *) NULL)
3754 png_error(ping,"Memory allocation failed");
3758 (void) ConcatenateMagickString(value,text[i].text,length+2);
3760 /* Don't save "density" or "units" property if we have a pHYs
3763 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3764 (LocaleCompare(text[i].key,"density") != 0 &&
3765 LocaleCompare(text[i].key,"units") != 0))
3766 (void) SetImageProperty(image,text[i].key,value,exception);
3768 if (logging != MagickFalse)
3770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3771 " length: %lu",(unsigned long) length);
3772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3773 " Keyword: %s",text[i].key);
3776 value=DestroyString(value);
3779 num_text_total += num_text;
3782 #ifdef MNG_OBJECT_BUFFERS
3784 Store the object if necessary.
3786 if (object_id && !mng_info->frozen[object_id])
3788 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3791 create a new object buffer.
3793 mng_info->ob[object_id]=(MngBuffer *)
3794 AcquireMagickMemory(sizeof(MngBuffer));
3796 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3798 mng_info->ob[object_id]->image=(Image *) NULL;
3799 mng_info->ob[object_id]->reference_count=1;
3803 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3804 mng_info->ob[object_id]->frozen)
3806 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3807 png_error(ping,"Memory allocation failed");
3809 if (mng_info->ob[object_id]->frozen)
3810 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3816 if (mng_info->ob[object_id]->image != (Image *) NULL)
3817 mng_info->ob[object_id]->image=DestroyImage
3818 (mng_info->ob[object_id]->image);
3820 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3823 if (mng_info->ob[object_id]->image != (Image *) NULL)
3824 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3827 png_error(ping, "Cloning image for object buffer failed");
3829 if (ping_width > 250000L || ping_height > 250000L)
3830 png_error(ping,"PNG Image dimensions are too large.");
3832 mng_info->ob[object_id]->width=ping_width;
3833 mng_info->ob[object_id]->height=ping_height;
3834 mng_info->ob[object_id]->color_type=ping_color_type;
3835 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3836 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3837 mng_info->ob[object_id]->compression_method=
3838 ping_compression_method;
3839 mng_info->ob[object_id]->filter_method=ping_filter_method;
3841 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3847 Copy the PLTE to the object buffer.
3849 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3850 mng_info->ob[object_id]->plte_length=number_colors;
3852 for (i=0; i < number_colors; i++)
3854 mng_info->ob[object_id]->plte[i]=plte[i];
3859 mng_info->ob[object_id]->plte_length=0;
3864 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3865 * alpha or if a valid tRNS chunk is present, no matter whether there
3866 * is actual transparency present.
3868 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3869 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3870 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3871 BlendPixelTrait : UndefinedPixelTrait;
3873 #if 0 /* I'm not sure what's wrong here but it does not work. */
3874 if (image->alpha_trait == BlendPixelTrait)
3876 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3877 (void) SetImageType(image,GrayscaleMatteType,exception);
3879 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3880 (void) SetImageType(image,PaletteMatteType,exception);
3883 (void) SetImageType(image,TrueColorMatteType,exception);
3888 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3889 (void) SetImageType(image,GrayscaleType,exception);
3891 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3892 (void) SetImageType(image,PaletteType,exception);
3895 (void) SetImageType(image,TrueColorType,exception);
3899 /* Set more properties for identify to retrieve */
3904 if (num_text_total != 0)
3906 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3907 (void) FormatLocaleString(msg,MaxTextExtent,
3908 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3909 (void) SetImageProperty(image,"png:text",msg,
3913 if (num_raw_profiles != 0)
3915 (void) FormatLocaleString(msg,MaxTextExtent,
3916 "%d were found", num_raw_profiles);
3917 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3921 if (ping_found_cHRM != MagickFalse)
3923 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3924 "chunk was found (see Chromaticity, above)");
3925 (void) SetImageProperty(image,"png:cHRM",msg,
3929 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3931 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3932 "chunk was found (see Background color, above)");
3933 (void) SetImageProperty(image,"png:bKGD",msg,
3937 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3940 #if defined(PNG_iCCP_SUPPORTED)
3941 if (ping_found_iCCP != MagickFalse)
3942 (void) SetImageProperty(image,"png:iCCP",msg,
3946 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3947 (void) SetImageProperty(image,"png:tRNS",msg,
3950 #if defined(PNG_sRGB_SUPPORTED)
3951 if (ping_found_sRGB != MagickFalse)
3953 (void) FormatLocaleString(msg,MaxTextExtent,
3956 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3957 (void) SetImageProperty(image,"png:sRGB",msg,
3962 if (ping_found_gAMA != MagickFalse)
3964 (void) FormatLocaleString(msg,MaxTextExtent,
3965 "gamma=%.8g (See Gamma, above)",
3967 (void) SetImageProperty(image,"png:gAMA",msg,
3971 #if defined(PNG_pHYs_SUPPORTED)
3972 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3974 (void) FormatLocaleString(msg,MaxTextExtent,
3975 "x_res=%.10g, y_res=%.10g, units=%d",
3976 (double) x_resolution,(double) y_resolution, unit_type);
3977 (void) SetImageProperty(image,"png:pHYs",msg,
3982 #if defined(PNG_oFFs_SUPPORTED)
3983 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3985 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3986 (double) image->page.x,(double) image->page.y);
3987 (void) SetImageProperty(image,"png:oFFs",msg,
3992 if ((image->page.width != 0 && image->page.width != image->columns) ||
3993 (image->page.height != 0 && image->page.height != image->rows))
3995 (void) FormatLocaleString(msg,MaxTextExtent,
3996 "width=%.20g, height=%.20g",
3997 (double) image->page.width,(double) image->page.height);
3998 (void) SetImageProperty(image,"png:vpAg",msg,
4004 Relinquish resources.
4006 png_destroy_read_struct(&ping,&ping_info,&end_info);
4008 pixel_info=RelinquishVirtualMemory(pixel_info);
4010 if (logging != MagickFalse)
4011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4012 " exit ReadOnePNGImage()");
4014 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
4015 UnlockSemaphoreInfo(ping_semaphore);
4018 /* } for navigation to beginning of SETJMP-protected block, revert to
4019 * Throwing an Exception when an error occurs.
4024 /* end of reading one PNG image */
4027 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4042 magic_number[MaxTextExtent];
4050 assert(image_info != (const ImageInfo *) NULL);
4051 assert(image_info->signature == MagickSignature);
4053 if (image_info->debug != MagickFalse)
4054 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4055 image_info->filename);
4057 assert(exception != (ExceptionInfo *) NULL);
4058 assert(exception->signature == MagickSignature);
4059 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4060 image=AcquireImage(image_info,exception);
4061 mng_info=(MngInfo *) NULL;
4062 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4064 if (status == MagickFalse)
4065 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4068 Verify PNG signature.
4070 count=ReadBlob(image,8,(unsigned char *) magic_number);
4072 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4073 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4076 Allocate a MngInfo structure.
4078 have_mng_structure=MagickFalse;
4079 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4081 if (mng_info == (MngInfo *) NULL)
4082 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4085 Initialize members of the MngInfo structure.
4087 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4088 mng_info->image=image;
4089 have_mng_structure=MagickTrue;
4092 image=ReadOnePNGImage(mng_info,image_info,exception);
4093 MngInfoFreeStruct(mng_info,&have_mng_structure);
4095 if (image == (Image *) NULL)
4097 if (previous != (Image *) NULL)
4099 if (previous->signature != MagickSignature)
4100 ThrowReaderException(CorruptImageError,"CorruptImage");
4102 (void) CloseBlob(previous);
4103 (void) DestroyImageList(previous);
4106 if (logging != MagickFalse)
4107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4108 "exit ReadPNGImage() with error");
4110 return((Image *) NULL);
4113 (void) CloseBlob(image);
4115 if ((image->columns == 0) || (image->rows == 0))
4117 if (logging != MagickFalse)
4118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4119 "exit ReadPNGImage() with error.");
4121 ThrowReaderException(CorruptImageError,"CorruptImage");
4124 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4125 ((image->gamma < .45) || (image->gamma > .46)) &&
4126 !(image->chromaticity.red_primary.x>0.6399f &&
4127 image->chromaticity.red_primary.x<0.6401f &&
4128 image->chromaticity.red_primary.y>0.3299f &&
4129 image->chromaticity.red_primary.y<0.3301f &&
4130 image->chromaticity.green_primary.x>0.2999f &&
4131 image->chromaticity.green_primary.x<0.3001f &&
4132 image->chromaticity.green_primary.y>0.5999f &&
4133 image->chromaticity.green_primary.y<0.6001f &&
4134 image->chromaticity.blue_primary.x>0.1499f &&
4135 image->chromaticity.blue_primary.x<0.1501f &&
4136 image->chromaticity.blue_primary.y>0.0599f &&
4137 image->chromaticity.blue_primary.y<0.0601f &&
4138 image->chromaticity.white_point.x>0.3126f &&
4139 image->chromaticity.white_point.x<0.3128f &&
4140 image->chromaticity.white_point.y>0.3289f &&
4141 image->chromaticity.white_point.y<0.3291f))
4142 SetImageColorspace(image,RGBColorspace,exception);
4144 if (logging != MagickFalse)
4145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4146 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4147 (double) image->page.width,(double) image->page.height,
4148 (double) image->page.x,(double) image->page.y);
4150 if (logging != MagickFalse)
4151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4158 #if defined(JNG_SUPPORTED)
4160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4164 % R e a d O n e J N G I m a g e %
4168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4170 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4171 % (minus the 8-byte signature) and returns it. It allocates the memory
4172 % necessary for the new Image structure and returns a pointer to the new
4175 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4177 % The format of the ReadOneJNGImage method is:
4179 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4180 % ExceptionInfo *exception)
4182 % A description of each parameter follows:
4184 % o mng_info: Specifies a pointer to a MngInfo structure.
4186 % o image_info: the image info.
4188 % o exception: return any errors or warnings in this structure.
4191 static Image *ReadOneJNGImage(MngInfo *mng_info,
4192 const ImageInfo *image_info, ExceptionInfo *exception)
4219 jng_image_sample_depth,
4220 jng_image_compression_method,
4221 jng_image_interlace_method,
4222 jng_alpha_sample_depth,
4223 jng_alpha_compression_method,
4224 jng_alpha_filter_method,
4225 jng_alpha_interlace_method;
4227 register const Quantum
4237 register unsigned char
4248 jng_alpha_compression_method=0;
4249 jng_alpha_sample_depth=8;
4253 alpha_image=(Image *) NULL;
4254 color_image=(Image *) NULL;
4255 alpha_image_info=(ImageInfo *) NULL;
4256 color_image_info=(ImageInfo *) NULL;
4258 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4259 " Enter ReadOneJNGImage()");
4261 image=mng_info->image;
4263 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4266 Allocate next image structure.
4268 if (logging != MagickFalse)
4269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4270 " AcquireNextImage()");
4272 AcquireNextImage(image_info,image,exception);
4274 if (GetNextImageInList(image) == (Image *) NULL)
4275 return((Image *) NULL);
4277 image=SyncNextImageInList(image);
4279 mng_info->image=image;
4282 Signature bytes have already been read.
4285 read_JSEP=MagickFalse;
4286 reading_idat=MagickFalse;
4287 skip_to_iend=MagickFalse;
4291 type[MaxTextExtent];
4300 Read a new JNG chunk.
4302 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4303 2*GetBlobSize(image));
4305 if (status == MagickFalse)
4309 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4310 length=ReadBlobMSBLong(image);
4311 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4313 if (logging != MagickFalse)
4314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4315 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4316 type[0],type[1],type[2],type[3],(double) length);
4318 if (length > PNG_UINT_31_MAX || count == 0)
4319 ThrowReaderException(CorruptImageError,"CorruptImage");
4322 chunk=(unsigned char *) NULL;
4326 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4328 if (chunk == (unsigned char *) NULL)
4329 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4331 for (i=0; i < (ssize_t) length; i++)
4332 chunk[i]=(unsigned char) ReadBlobByte(image);
4337 (void) ReadBlobMSBLong(image); /* read crc word */
4342 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4347 if (memcmp(type,mng_JHDR,4) == 0)
4351 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4352 (p[2] << 8) | p[3]);
4353 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4354 (p[6] << 8) | p[7]);
4355 jng_color_type=p[8];
4356 jng_image_sample_depth=p[9];
4357 jng_image_compression_method=p[10];
4358 jng_image_interlace_method=p[11];
4360 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4363 jng_alpha_sample_depth=p[12];
4364 jng_alpha_compression_method=p[13];
4365 jng_alpha_filter_method=p[14];
4366 jng_alpha_interlace_method=p[15];
4368 if (logging != MagickFalse)
4370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4371 " jng_width: %16lu",(unsigned long) jng_width);
4373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4374 " jng_width: %16lu",(unsigned long) jng_height);
4376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4377 " jng_color_type: %16d",jng_color_type);
4379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4380 " jng_image_sample_depth: %3d",
4381 jng_image_sample_depth);
4383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4384 " jng_image_compression_method:%3d",
4385 jng_image_compression_method);
4387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4388 " jng_image_interlace_method: %3d",
4389 jng_image_interlace_method);
4391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392 " jng_alpha_sample_depth: %3d",
4393 jng_alpha_sample_depth);
4395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396 " jng_alpha_compression_method:%3d",
4397 jng_alpha_compression_method);
4399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400 " jng_alpha_filter_method: %3d",
4401 jng_alpha_filter_method);
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " jng_alpha_interlace_method: %3d",
4405 jng_alpha_interlace_method);
4410 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4416 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4417 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4418 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4421 o create color_image
4422 o open color_blob, attached to color_image
4423 o if (color type has alpha)
4424 open alpha_blob, attached to alpha_image
4427 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4429 if (color_image_info == (ImageInfo *) NULL)
4430 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4432 GetImageInfo(color_image_info);
4433 color_image=AcquireImage(color_image_info,exception);
4435 if (color_image == (Image *) NULL)
4436 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4438 if (logging != MagickFalse)
4439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4440 " Creating color_blob.");
4442 (void) AcquireUniqueFilename(color_image->filename);
4443 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4446 if (status == MagickFalse)
4447 return((Image *) NULL);
4449 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4451 alpha_image_info=(ImageInfo *)
4452 AcquireMagickMemory(sizeof(ImageInfo));
4454 if (alpha_image_info == (ImageInfo *) NULL)
4455 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4457 GetImageInfo(alpha_image_info);
4458 alpha_image=AcquireImage(alpha_image_info,exception);
4460 if (alpha_image == (Image *) NULL)
4462 alpha_image=DestroyImage(alpha_image);
4463 ThrowReaderException(ResourceLimitError,
4464 "MemoryAllocationFailed");
4467 if (logging != MagickFalse)
4468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4469 " Creating alpha_blob.");
4471 (void) AcquireUniqueFilename(alpha_image->filename);
4472 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4475 if (status == MagickFalse)
4476 return((Image *) NULL);
4478 if (jng_alpha_compression_method == 0)
4483 if (logging != MagickFalse)
4484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4485 " Writing IHDR chunk to alpha_blob.");
4487 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4488 "\211PNG\r\n\032\n");
4490 (void) WriteBlobMSBULong(alpha_image,13L);
4491 PNGType(data,mng_IHDR);
4492 LogPNGChunk(logging,mng_IHDR,13L);
4493 PNGLong(data+4,jng_width);
4494 PNGLong(data+8,jng_height);
4495 data[12]=jng_alpha_sample_depth;
4496 data[13]=0; /* color_type gray */
4497 data[14]=0; /* compression method 0 */
4498 data[15]=0; /* filter_method 0 */
4499 data[16]=0; /* interlace_method 0 */
4500 (void) WriteBlob(alpha_image,17,data);
4501 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4504 reading_idat=MagickTrue;
4507 if (memcmp(type,mng_JDAT,4) == 0)
4509 /* Copy chunk to color_image->blob */
4511 if (logging != MagickFalse)
4512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4513 " Copying JDAT chunk data to color_blob.");
4515 (void) WriteBlob(color_image,length,chunk);
4518 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4523 if (memcmp(type,mng_IDAT,4) == 0)
4528 /* Copy IDAT header and chunk data to alpha_image->blob */
4530 if (image_info->ping == MagickFalse)
4532 if (logging != MagickFalse)
4533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4534 " Copying IDAT chunk data to alpha_blob.");
4536 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4537 PNGType(data,mng_IDAT);
4538 LogPNGChunk(logging,mng_IDAT,length);
4539 (void) WriteBlob(alpha_image,4,data);
4540 (void) WriteBlob(alpha_image,length,chunk);
4541 (void) WriteBlobMSBULong(alpha_image,
4542 crc32(crc32(0,data,4),chunk,(uInt) length));
4546 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4551 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4553 /* Copy chunk data to alpha_image->blob */
4555 if (image_info->ping == MagickFalse)
4557 if (logging != MagickFalse)
4558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4559 " Copying JDAA chunk data to alpha_blob.");
4561 (void) WriteBlob(alpha_image,length,chunk);
4565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4570 if (memcmp(type,mng_JSEP,4) == 0)
4572 read_JSEP=MagickTrue;
4575 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4580 if (memcmp(type,mng_bKGD,4) == 0)
4584 image->background_color.red=ScaleCharToQuantum(p[1]);
4585 image->background_color.green=image->background_color.red;
4586 image->background_color.blue=image->background_color.red;
4591 image->background_color.red=ScaleCharToQuantum(p[1]);
4592 image->background_color.green=ScaleCharToQuantum(p[3]);
4593 image->background_color.blue=ScaleCharToQuantum(p[5]);
4596 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4600 if (memcmp(type,mng_gAMA,4) == 0)
4603 image->gamma=((float) mng_get_long(p))*0.00001;
4605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4609 if (memcmp(type,mng_cHRM,4) == 0)
4613 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4614 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4615 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4616 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4617 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4618 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4619 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4620 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4623 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4627 if (memcmp(type,mng_sRGB,4) == 0)
4631 image->rendering_intent=
4632 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4633 image->gamma=1.000f/2.200f;
4634 image->chromaticity.red_primary.x=0.6400f;
4635 image->chromaticity.red_primary.y=0.3300f;
4636 image->chromaticity.green_primary.x=0.3000f;
4637 image->chromaticity.green_primary.y=0.6000f;
4638 image->chromaticity.blue_primary.x=0.1500f;
4639 image->chromaticity.blue_primary.y=0.0600f;
4640 image->chromaticity.white_point.x=0.3127f;
4641 image->chromaticity.white_point.y=0.3290f;
4644 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4648 if (memcmp(type,mng_oFFs,4) == 0)
4652 image->page.x=(ssize_t) mng_get_long(p);
4653 image->page.y=(ssize_t) mng_get_long(&p[4]);
4655 if ((int) p[8] != 0)
4657 image->page.x/=10000;
4658 image->page.y/=10000;
4663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4668 if (memcmp(type,mng_pHYs,4) == 0)
4672 image->resolution.x=(double) mng_get_long(p);
4673 image->resolution.y=(double) mng_get_long(&p[4]);
4674 if ((int) p[8] == PNG_RESOLUTION_METER)
4676 image->units=PixelsPerCentimeterResolution;
4677 image->resolution.x=image->resolution.x/100.0f;
4678 image->resolution.y=image->resolution.y/100.0f;
4682 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4687 if (memcmp(type,mng_iCCP,4) == 0)
4691 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4698 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4700 if (memcmp(type,mng_IEND,4))
4710 Finish up reading image data:
4712 o read main image from color_blob.
4716 o if (color_type has alpha)
4717 if alpha_encoding is PNG
4718 read secondary image from alpha_blob via ReadPNG
4719 if alpha_encoding is JPEG
4720 read secondary image from alpha_blob via ReadJPEG
4724 o copy intensity of secondary image into
4725 alpha samples of main image.
4727 o destroy the secondary image.
4730 (void) CloseBlob(color_image);
4732 if (logging != MagickFalse)
4733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4734 " Reading jng_image from color_blob.");
4736 assert(color_image_info != (ImageInfo *) NULL);
4737 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4738 color_image->filename);
4740 color_image_info->ping=MagickFalse; /* To do: avoid this */
4741 jng_image=ReadImage(color_image_info,exception);
4743 if (jng_image == (Image *) NULL)
4744 return((Image *) NULL);
4746 (void) RelinquishUniqueFileResource(color_image->filename);
4747 color_image=DestroyImage(color_image);
4748 color_image_info=DestroyImageInfo(color_image_info);
4750 if (jng_image == (Image *) NULL)
4751 return((Image *) NULL);
4753 if (logging != MagickFalse)
4754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4755 " Copying jng_image pixels to main image.");
4757 image->rows=jng_height;
4758 image->columns=jng_width;
4760 for (y=0; y < (ssize_t) image->rows; y++)
4762 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4763 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4764 for (x=(ssize_t) image->columns; x != 0; x--)
4766 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4767 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4768 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4769 q+=GetPixelChannels(image);
4770 s+=GetPixelChannels(jng_image);
4773 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4777 jng_image=DestroyImage(jng_image);
4779 if (image_info->ping == MagickFalse)
4781 if (jng_color_type >= 12)
4783 if (jng_alpha_compression_method == 0)
4787 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4788 PNGType(data,mng_IEND);
4789 LogPNGChunk(logging,mng_IEND,0L);
4790 (void) WriteBlob(alpha_image,4,data);
4791 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4794 (void) CloseBlob(alpha_image);
4796 if (logging != MagickFalse)
4797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4798 " Reading alpha from alpha_blob.");
4800 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4801 "%s",alpha_image->filename);
4803 jng_image=ReadImage(alpha_image_info,exception);
4805 if (jng_image != (Image *) NULL)
4806 for (y=0; y < (ssize_t) image->rows; y++)
4808 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4810 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4812 if (image->alpha_trait == BlendPixelTrait)
4813 for (x=(ssize_t) image->columns; x != 0; x--)
4815 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4816 q+=GetPixelChannels(image);
4817 s+=GetPixelChannels(jng_image);
4821 for (x=(ssize_t) image->columns; x != 0; x--)
4823 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4824 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4825 image->alpha_trait=BlendPixelTrait;
4826 q+=GetPixelChannels(image);
4827 s+=GetPixelChannels(jng_image);
4830 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4833 (void) RelinquishUniqueFileResource(alpha_image->filename);
4834 alpha_image=DestroyImage(alpha_image);
4835 alpha_image_info=DestroyImageInfo(alpha_image_info);
4836 if (jng_image != (Image *) NULL)
4837 jng_image=DestroyImage(jng_image);
4841 /* Read the JNG image. */
4843 if (mng_info->mng_type == 0)
4845 mng_info->mng_width=jng_width;
4846 mng_info->mng_height=jng_height;
4849 if (image->page.width == 0 && image->page.height == 0)
4851 image->page.width=jng_width;
4852 image->page.height=jng_height;
4855 if (image->page.x == 0 && image->page.y == 0)
4857 image->page.x=mng_info->x_off[mng_info->object_id];
4858 image->page.y=mng_info->y_off[mng_info->object_id];
4863 image->page.y=mng_info->y_off[mng_info->object_id];
4866 mng_info->image_found++;
4867 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4868 2*GetBlobSize(image));
4870 if (logging != MagickFalse)
4871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4872 " exit ReadOneJNGImage()");
4878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4882 % R e a d J N G I m a g e %
4886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4888 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4889 % (including the 8-byte signature) and returns it. It allocates the memory
4890 % necessary for the new Image structure and returns a pointer to the new
4893 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4895 % The format of the ReadJNGImage method is:
4897 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4900 % A description of each parameter follows:
4902 % o image_info: the image info.
4904 % o exception: return any errors or warnings in this structure.
4908 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4923 magic_number[MaxTextExtent];
4931 assert(image_info != (const ImageInfo *) NULL);
4932 assert(image_info->signature == MagickSignature);
4933 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4934 assert(exception != (ExceptionInfo *) NULL);
4935 assert(exception->signature == MagickSignature);
4936 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4937 image=AcquireImage(image_info,exception);
4938 mng_info=(MngInfo *) NULL;
4939 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4941 if (status == MagickFalse)
4942 return((Image *) NULL);
4944 if (LocaleCompare(image_info->magick,"JNG") != 0)
4945 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4947 /* Verify JNG signature. */
4949 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4951 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4952 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4954 /* Allocate a MngInfo structure. */
4956 have_mng_structure=MagickFalse;
4957 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4959 if (mng_info == (MngInfo *) NULL)
4960 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4962 /* Initialize members of the MngInfo structure. */
4964 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4965 have_mng_structure=MagickTrue;
4967 mng_info->image=image;
4969 image=ReadOneJNGImage(mng_info,image_info,exception);
4970 MngInfoFreeStruct(mng_info,&have_mng_structure);
4972 if (image == (Image *) NULL)
4974 if (IsImageObject(previous) != MagickFalse)
4976 (void) CloseBlob(previous);
4977 (void) DestroyImageList(previous);
4980 if (logging != MagickFalse)
4981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4982 "exit ReadJNGImage() with error");
4984 return((Image *) NULL);
4986 (void) CloseBlob(image);
4988 if (image->columns == 0 || image->rows == 0)
4990 if (logging != MagickFalse)
4991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4992 "exit ReadJNGImage() with error");
4994 ThrowReaderException(CorruptImageError,"CorruptImage");
4997 if (logging != MagickFalse)
4998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5004 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5007 page_geometry[MaxTextExtent];
5040 #if defined(MNG_INSERT_LAYERS)
5042 mng_background_color;
5045 register unsigned char
5060 #if defined(MNG_INSERT_LAYERS)
5065 volatile unsigned int
5066 #ifdef MNG_OBJECT_BUFFERS
5067 mng_background_object=0,
5069 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5072 default_frame_timeout,
5074 #if defined(MNG_INSERT_LAYERS)
5080 /* These delays are all measured in image ticks_per_second,
5081 * not in MNG ticks_per_second
5084 default_frame_delay,
5088 #if defined(MNG_INSERT_LAYERS)
5097 previous_fb.bottom=0;
5099 previous_fb.right=0;
5101 default_fb.bottom=0;
5105 /* Open image file. */
5107 assert(image_info != (const ImageInfo *) NULL);
5108 assert(image_info->signature == MagickSignature);
5109 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5110 assert(exception != (ExceptionInfo *) NULL);
5111 assert(exception->signature == MagickSignature);
5112 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5113 image=AcquireImage(image_info,exception);
5114 mng_info=(MngInfo *) NULL;
5115 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5117 if (status == MagickFalse)
5118 return((Image *) NULL);
5120 first_mng_object=MagickFalse;
5122 have_mng_structure=MagickFalse;
5124 /* Allocate a MngInfo structure. */
5126 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5128 if (mng_info == (MngInfo *) NULL)
5129 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5131 /* Initialize members of the MngInfo structure. */
5133 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5134 mng_info->image=image;
5135 have_mng_structure=MagickTrue;
5137 if (LocaleCompare(image_info->magick,"MNG") == 0)
5140 magic_number[MaxTextExtent];
5142 /* Verify MNG signature. */
5143 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5144 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5145 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5147 /* Initialize some nonzero members of the MngInfo structure. */
5148 for (i=0; i < MNG_MAX_OBJECTS; i++)
5150 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5151 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5153 mng_info->exists[0]=MagickTrue;
5156 first_mng_object=MagickTrue;
5158 #if defined(MNG_INSERT_LAYERS)
5159 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5161 default_frame_delay=0;
5162 default_frame_timeout=0;
5165 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5167 skip_to_iend=MagickFalse;
5168 term_chunk_found=MagickFalse;
5169 mng_info->framing_mode=1;
5170 #if defined(MNG_INSERT_LAYERS)
5171 mandatory_back=MagickFalse;
5173 #if defined(MNG_INSERT_LAYERS)
5174 mng_background_color=image->background_color;
5176 default_fb=mng_info->frame;
5177 previous_fb=mng_info->frame;
5181 type[MaxTextExtent];
5183 if (LocaleCompare(image_info->magick,"MNG") == 0)
5192 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5193 length=ReadBlobMSBLong(image);
5194 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5196 if (logging != MagickFalse)
5197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5198 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5199 type[0],type[1],type[2],type[3],(double) length);
5201 if (length > PNG_UINT_31_MAX)
5205 ThrowReaderException(CorruptImageError,"CorruptImage");
5208 chunk=(unsigned char *) NULL;
5212 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5214 if (chunk == (unsigned char *) NULL)
5215 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5217 for (i=0; i < (ssize_t) length; i++)
5218 chunk[i]=(unsigned char) ReadBlobByte(image);
5223 (void) ReadBlobMSBLong(image); /* read crc word */
5225 #if !defined(JNG_SUPPORTED)
5226 if (memcmp(type,mng_JHDR,4) == 0)
5228 skip_to_iend=MagickTrue;
5230 if (mng_info->jhdr_warning == 0)
5231 (void) ThrowMagickException(exception,GetMagickModule(),
5232 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5234 mng_info->jhdr_warning++;
5237 if (memcmp(type,mng_DHDR,4) == 0)
5239 skip_to_iend=MagickTrue;
5241 if (mng_info->dhdr_warning == 0)
5242 (void) ThrowMagickException(exception,GetMagickModule(),
5243 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5245 mng_info->dhdr_warning++;
5247 if (memcmp(type,mng_MEND,4) == 0)
5252 if (memcmp(type,mng_IEND,4) == 0)
5253 skip_to_iend=MagickFalse;
5256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5258 if (logging != MagickFalse)
5259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5265 if (memcmp(type,mng_MHDR,4) == 0)
5267 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5268 (p[2] << 8) | p[3]);
5270 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5271 (p[6] << 8) | p[7]);
5273 if (logging != MagickFalse)
5275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5276 " MNG width: %.20g",(double) mng_info->mng_width);
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5278 " MNG height: %.20g",(double) mng_info->mng_height);
5282 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5284 if (mng_info->ticks_per_second == 0)
5285 default_frame_delay=0;
5288 default_frame_delay=1UL*image->ticks_per_second/
5289 mng_info->ticks_per_second;
5291 frame_delay=default_frame_delay;
5297 simplicity=(size_t) mng_get_long(p);
5300 mng_type=1; /* Full MNG */
5302 if ((simplicity != 0) && ((simplicity | 11) == 11))
5303 mng_type=2; /* LC */
5305 if ((simplicity != 0) && ((simplicity | 9) == 9))
5306 mng_type=3; /* VLC */
5308 #if defined(MNG_INSERT_LAYERS)
5310 insert_layers=MagickTrue;
5312 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5314 /* Allocate next image structure. */
5315 AcquireNextImage(image_info,image,exception);
5317 if (GetNextImageInList(image) == (Image *) NULL)
5318 return((Image *) NULL);
5320 image=SyncNextImageInList(image);
5321 mng_info->image=image;
5324 if ((mng_info->mng_width > 65535L) ||
5325 (mng_info->mng_height > 65535L))
5326 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5328 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5329 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5330 mng_info->mng_height);
5332 mng_info->frame.left=0;
5333 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5334 mng_info->frame.top=0;
5335 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5336 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5338 for (i=0; i < MNG_MAX_OBJECTS; i++)
5339 mng_info->object_clip[i]=mng_info->frame;
5341 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5345 if (memcmp(type,mng_TERM,4) == 0)
5356 final_delay=(png_uint_32) mng_get_long(&p[2]);
5357 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5359 if (mng_iterations == PNG_UINT_31_MAX)
5362 image->iterations=mng_iterations;
5363 term_chunk_found=MagickTrue;
5366 if (logging != MagickFalse)
5368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5369 " repeat=%d",repeat);
5371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5372 " final_delay=%.20g",(double) final_delay);
5374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5375 " image->iterations=%.20g",(double) image->iterations);
5378 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5381 if (memcmp(type,mng_DEFI,4) == 0)
5384 (void) ThrowMagickException(exception,GetMagickModule(),
5385 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5388 object_id=(p[0] << 8) | p[1];
5390 if (mng_type == 2 && object_id != 0)
5391 (void) ThrowMagickException(exception,GetMagickModule(),
5392 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5395 if (object_id > MNG_MAX_OBJECTS)
5398 Instead of using a warning we should allocate a larger
5399 MngInfo structure and continue.
5401 (void) ThrowMagickException(exception,GetMagickModule(),
5402 CoderError,"object id too large","`%s'",image->filename);
5403 object_id=MNG_MAX_OBJECTS;
5406 if (mng_info->exists[object_id])
5407 if (mng_info->frozen[object_id])
5409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5410 (void) ThrowMagickException(exception,
5411 GetMagickModule(),CoderError,
5412 "DEFI cannot redefine a frozen MNG object","`%s'",
5417 mng_info->exists[object_id]=MagickTrue;
5420 mng_info->invisible[object_id]=p[2];
5423 Extract object offset info.
5427 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5428 (p[5] << 16) | (p[6] << 8) | p[7]);
5430 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5431 (p[9] << 16) | (p[10] << 8) | p[11]);
5433 if (logging != MagickFalse)
5435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5436 " x_off[%d]: %.20g",object_id,(double)
5437 mng_info->x_off[object_id]);
5439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5440 " y_off[%d]: %.20g",object_id,(double)
5441 mng_info->y_off[object_id]);
5446 Extract object clipping info.
5449 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5452 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5455 if (memcmp(type,mng_bKGD,4) == 0)
5457 mng_info->have_global_bkgd=MagickFalse;
5461 mng_info->mng_global_bkgd.red=
5462 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5464 mng_info->mng_global_bkgd.green=
5465 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5467 mng_info->mng_global_bkgd.blue=
5468 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5470 mng_info->have_global_bkgd=MagickTrue;
5473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5476 if (memcmp(type,mng_BACK,4) == 0)
5478 #if defined(MNG_INSERT_LAYERS)
5480 mandatory_back=p[6];
5485 if (mandatory_back && length > 5)
5487 mng_background_color.red=
5488 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5490 mng_background_color.green=
5491 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5493 mng_background_color.blue=
5494 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5496 mng_background_color.alpha=OpaqueAlpha;
5499 #ifdef MNG_OBJECT_BUFFERS
5501 mng_background_object=(p[7] << 8) | p[8];
5504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5508 if (memcmp(type,mng_PLTE,4) == 0)
5510 /* Read global PLTE. */
5512 if (length && (length < 769))
5514 if (mng_info->global_plte == (png_colorp) NULL)
5515 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5516 sizeof(*mng_info->global_plte));
5518 for (i=0; i < (ssize_t) (length/3); i++)
5520 mng_info->global_plte[i].red=p[3*i];
5521 mng_info->global_plte[i].green=p[3*i+1];
5522 mng_info->global_plte[i].blue=p[3*i+2];
5525 mng_info->global_plte_length=(unsigned int) (length/3);
5528 for ( ; i < 256; i++)
5530 mng_info->global_plte[i].red=i;
5531 mng_info->global_plte[i].green=i;
5532 mng_info->global_plte[i].blue=i;
5536 mng_info->global_plte_length=256;
5539 mng_info->global_plte_length=0;
5541 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5545 if (memcmp(type,mng_tRNS,4) == 0)
5547 /* read global tRNS */
5550 for (i=0; i < (ssize_t) length; i++)
5551 mng_info->global_trns[i]=p[i];
5554 for ( ; i < 256; i++)
5555 mng_info->global_trns[i]=255;
5557 mng_info->global_trns_length=(unsigned int) length;
5558 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5561 if (memcmp(type,mng_gAMA,4) == 0)
5568 igamma=mng_get_long(p);
5569 mng_info->global_gamma=((float) igamma)*0.00001;
5570 mng_info->have_global_gama=MagickTrue;
5574 mng_info->have_global_gama=MagickFalse;
5576 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5580 if (memcmp(type,mng_cHRM,4) == 0)
5582 /* Read global cHRM */
5586 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5587 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5588 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5589 mng_info->global_chrm.red_primary.y=0.00001*
5590 mng_get_long(&p[12]);
5591 mng_info->global_chrm.green_primary.x=0.00001*
5592 mng_get_long(&p[16]);
5593 mng_info->global_chrm.green_primary.y=0.00001*
5594 mng_get_long(&p[20]);
5595 mng_info->global_chrm.blue_primary.x=0.00001*
5596 mng_get_long(&p[24]);
5597 mng_info->global_chrm.blue_primary.y=0.00001*
5598 mng_get_long(&p[28]);
5599 mng_info->have_global_chrm=MagickTrue;
5602 mng_info->have_global_chrm=MagickFalse;
5604 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5608 if (memcmp(type,mng_sRGB,4) == 0)
5615 mng_info->global_srgb_intent=
5616 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5617 mng_info->have_global_srgb=MagickTrue;
5620 mng_info->have_global_srgb=MagickFalse;
5622 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5626 if (memcmp(type,mng_iCCP,4) == 0)
5634 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5639 if (memcmp(type,mng_FRAM,4) == 0)
5642 (void) ThrowMagickException(exception,GetMagickModule(),
5643 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5646 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5647 image->delay=frame_delay;
5649 frame_delay=default_frame_delay;
5650 frame_timeout=default_frame_timeout;
5655 mng_info->framing_mode=p[0];
5657 if (logging != MagickFalse)
5658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5659 " Framing_mode=%d",mng_info->framing_mode);
5663 /* Note the delay and frame clipping boundaries. */
5665 p++; /* framing mode */
5667 while (*p && ((p-chunk) < (ssize_t) length))
5668 p++; /* frame name */
5670 p++; /* frame name terminator */
5672 if ((p-chunk) < (ssize_t) (length-4))
5679 change_delay=(*p++);
5680 change_timeout=(*p++);
5681 change_clipping=(*p++);
5682 p++; /* change_sync */
5686 frame_delay=1UL*image->ticks_per_second*
5689 if (mng_info->ticks_per_second != 0)
5690 frame_delay/=mng_info->ticks_per_second;
5693 frame_delay=PNG_UINT_31_MAX;
5695 if (change_delay == 2)
5696 default_frame_delay=frame_delay;
5700 if (logging != MagickFalse)
5701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5702 " Framing_delay=%.20g",(double) frame_delay);
5707 frame_timeout=1UL*image->ticks_per_second*
5710 if (mng_info->ticks_per_second != 0)
5711 frame_timeout/=mng_info->ticks_per_second;
5714 frame_timeout=PNG_UINT_31_MAX;
5716 if (change_delay == 2)
5717 default_frame_timeout=frame_timeout;
5721 if (logging != MagickFalse)
5722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5723 " Framing_timeout=%.20g",(double) frame_timeout);
5726 if (change_clipping)
5728 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5732 if (logging != MagickFalse)
5733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5734 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5735 (double) fb.left,(double) fb.right,(double) fb.top,
5736 (double) fb.bottom);
5738 if (change_clipping == 2)
5744 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5746 subframe_width=(size_t) (mng_info->clip.right
5747 -mng_info->clip.left);
5749 subframe_height=(size_t) (mng_info->clip.bottom
5750 -mng_info->clip.top);
5752 Insert a background layer behind the frame if framing_mode is 4.
5754 #if defined(MNG_INSERT_LAYERS)
5755 if (logging != MagickFalse)
5756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5757 " subframe_width=%.20g, subframe_height=%.20g",(double)
5758 subframe_width,(double) subframe_height);
5760 if (insert_layers && (mng_info->framing_mode == 4) &&
5761 (subframe_width) && (subframe_height))
5763 /* Allocate next image structure. */
5764 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5766 AcquireNextImage(image_info,image,exception);
5768 if (GetNextImageInList(image) == (Image *) NULL)
5770 image=DestroyImageList(image);
5771 MngInfoFreeStruct(mng_info,&have_mng_structure);
5772 return((Image *) NULL);
5775 image=SyncNextImageInList(image);
5778 mng_info->image=image;
5780 if (term_chunk_found)
5782 image->start_loop=MagickTrue;
5783 image->iterations=mng_iterations;
5784 term_chunk_found=MagickFalse;
5788 image->start_loop=MagickFalse;
5790 image->columns=subframe_width;
5791 image->rows=subframe_height;
5792 image->page.width=subframe_width;
5793 image->page.height=subframe_height;
5794 image->page.x=mng_info->clip.left;
5795 image->page.y=mng_info->clip.top;
5796 image->background_color=mng_background_color;
5797 image->alpha_trait=UndefinedPixelTrait;
5799 (void) SetImageBackgroundColor(image,exception);
5801 if (logging != MagickFalse)
5802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5803 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5804 (double) mng_info->clip.left,(double) mng_info->clip.right,
5805 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5808 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5811 if (memcmp(type,mng_CLIP,4) == 0)
5820 first_object=(p[0] << 8) | p[1];
5821 last_object=(p[2] << 8) | p[3];
5823 for (i=(int) first_object; i <= (int) last_object; i++)
5825 if (mng_info->exists[i] && !mng_info->frozen[i])
5830 box=mng_info->object_clip[i];
5831 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5838 if (memcmp(type,mng_SAVE,4) == 0)
5840 for (i=1; i < MNG_MAX_OBJECTS; i++)
5841 if (mng_info->exists[i])
5843 mng_info->frozen[i]=MagickTrue;
5844 #ifdef MNG_OBJECT_BUFFERS
5845 if (mng_info->ob[i] != (MngBuffer *) NULL)
5846 mng_info->ob[i]->frozen=MagickTrue;
5851 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5856 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5858 /* Read DISC or SEEK. */
5860 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5862 for (i=1; i < MNG_MAX_OBJECTS; i++)
5863 MngInfoDiscardObject(mng_info,i);
5871 for (j=0; j < (ssize_t) length; j+=2)
5873 i=p[j] << 8 | p[j+1];
5874 MngInfoDiscardObject(mng_info,i);
5879 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5884 if (memcmp(type,mng_MOVE,4) == 0)
5892 first_object=(p[0] << 8) | p[1];
5893 last_object=(p[2] << 8) | p[3];
5894 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5896 if (mng_info->exists[i] && !mng_info->frozen[i])
5904 old_pair.a=mng_info->x_off[i];
5905 old_pair.b=mng_info->y_off[i];
5906 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5907 mng_info->x_off[i]=new_pair.a;
5908 mng_info->y_off[i]=new_pair.b;
5912 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5916 if (memcmp(type,mng_LOOP,4) == 0)
5918 ssize_t loop_iters=1;
5919 loop_level=chunk[0];
5920 mng_info->loop_active[loop_level]=1; /* mark loop active */
5922 /* Record starting point. */
5923 loop_iters=mng_get_long(&chunk[1]);
5925 if (logging != MagickFalse)
5926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5927 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5928 (double) loop_iters);
5930 if (loop_iters == 0)
5931 skipping_loop=loop_level;
5935 mng_info->loop_jump[loop_level]=TellBlob(image);
5936 mng_info->loop_count[loop_level]=loop_iters;
5939 mng_info->loop_iteration[loop_level]=0;
5940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5944 if (memcmp(type,mng_ENDL,4) == 0)
5946 loop_level=chunk[0];
5948 if (skipping_loop > 0)
5950 if (skipping_loop == loop_level)
5953 Found end of zero-iteration loop.
5956 mng_info->loop_active[loop_level]=0;
5962 if (mng_info->loop_active[loop_level] == 1)
5964 mng_info->loop_count[loop_level]--;
5965 mng_info->loop_iteration[loop_level]++;
5967 if (logging != MagickFalse)
5968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5969 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5970 (double) loop_level,(double)
5971 mng_info->loop_count[loop_level]);
5973 if (mng_info->loop_count[loop_level] != 0)
5975 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5979 ThrowReaderException(CorruptImageError,
5980 "ImproperImageHeader");
5991 mng_info->loop_active[loop_level]=0;
5993 for (i=0; i < loop_level; i++)
5994 if (mng_info->loop_active[i] == 1)
5995 last_level=(short) i;
5996 loop_level=last_level;
6001 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6005 if (memcmp(type,mng_CLON,4) == 0)
6007 if (mng_info->clon_warning == 0)
6008 (void) ThrowMagickException(exception,GetMagickModule(),
6009 CoderError,"CLON is not implemented yet","`%s'",
6012 mng_info->clon_warning++;
6015 if (memcmp(type,mng_MAGN,4) == 0)
6030 magn_first=(p[0] << 8) | p[1];
6036 magn_last=(p[2] << 8) | p[3];
6039 magn_last=magn_first;
6040 #ifndef MNG_OBJECT_BUFFERS
6041 if (magn_first || magn_last)
6042 if (mng_info->magn_warning == 0)
6044 (void) ThrowMagickException(exception,
6045 GetMagickModule(),CoderError,
6046 "MAGN is not implemented yet for nonzero objects",
6047 "`%s'",image->filename);
6049 mng_info->magn_warning++;
6059 magn_mx=(p[5] << 8) | p[6];
6068 magn_my=(p[7] << 8) | p[8];
6077 magn_ml=(p[9] << 8) | p[10];
6086 magn_mr=(p[11] << 8) | p[12];
6095 magn_mt=(p[13] << 8) | p[14];
6104 magn_mb=(p[15] << 8) | p[16];
6116 magn_methy=magn_methx;
6119 if (magn_methx > 5 || magn_methy > 5)
6120 if (mng_info->magn_warning == 0)
6122 (void) ThrowMagickException(exception,
6123 GetMagickModule(),CoderError,
6124 "Unknown MAGN method in MNG datastream","`%s'",
6127 mng_info->magn_warning++;
6129 #ifdef MNG_OBJECT_BUFFERS
6130 /* Magnify existing objects in the range magn_first to magn_last */
6132 if (magn_first == 0 || magn_last == 0)
6134 /* Save the magnification factors for object 0 */
6135 mng_info->magn_mb=magn_mb;
6136 mng_info->magn_ml=magn_ml;
6137 mng_info->magn_mr=magn_mr;
6138 mng_info->magn_mt=magn_mt;
6139 mng_info->magn_mx=magn_mx;
6140 mng_info->magn_my=magn_my;
6141 mng_info->magn_methx=magn_methx;
6142 mng_info->magn_methy=magn_methy;
6146 if (memcmp(type,mng_PAST,4) == 0)
6148 if (mng_info->past_warning == 0)
6149 (void) ThrowMagickException(exception,GetMagickModule(),
6150 CoderError,"PAST is not implemented yet","`%s'",
6153 mng_info->past_warning++;
6156 if (memcmp(type,mng_SHOW,4) == 0)
6158 if (mng_info->show_warning == 0)
6159 (void) ThrowMagickException(exception,GetMagickModule(),
6160 CoderError,"SHOW is not implemented yet","`%s'",
6163 mng_info->show_warning++;
6166 if (memcmp(type,mng_sBIT,4) == 0)
6169 mng_info->have_global_sbit=MagickFalse;
6173 mng_info->global_sbit.gray=p[0];
6174 mng_info->global_sbit.red=p[0];
6175 mng_info->global_sbit.green=p[1];
6176 mng_info->global_sbit.blue=p[2];
6177 mng_info->global_sbit.alpha=p[3];
6178 mng_info->have_global_sbit=MagickTrue;
6181 if (memcmp(type,mng_pHYs,4) == 0)
6185 mng_info->global_x_pixels_per_unit=
6186 (size_t) mng_get_long(p);
6187 mng_info->global_y_pixels_per_unit=
6188 (size_t) mng_get_long(&p[4]);
6189 mng_info->global_phys_unit_type=p[8];
6190 mng_info->have_global_phys=MagickTrue;
6194 mng_info->have_global_phys=MagickFalse;
6196 if (memcmp(type,mng_pHYg,4) == 0)
6198 if (mng_info->phyg_warning == 0)
6199 (void) ThrowMagickException(exception,GetMagickModule(),
6200 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6202 mng_info->phyg_warning++;
6204 if (memcmp(type,mng_BASI,4) == 0)
6206 skip_to_iend=MagickTrue;
6208 if (mng_info->basi_warning == 0)
6209 (void) ThrowMagickException(exception,GetMagickModule(),
6210 CoderError,"BASI is not implemented yet","`%s'",
6213 mng_info->basi_warning++;
6214 #ifdef MNG_BASI_SUPPORTED
6215 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6216 (p[2] << 8) | p[3]);
6217 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6218 (p[6] << 8) | p[7]);
6219 basi_color_type=p[8];
6220 basi_compression_method=p[9];
6221 basi_filter_type=p[10];
6222 basi_interlace_method=p[11];
6224 basi_red=(p[12] << 8) & p[13];
6230 basi_green=(p[14] << 8) & p[15];
6236 basi_blue=(p[16] << 8) & p[17];
6242 basi_alpha=(p[18] << 8) & p[19];
6246 if (basi_sample_depth == 16)
6253 basi_viewable=p[20];
6259 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6263 if (memcmp(type,mng_IHDR,4)
6264 #if defined(JNG_SUPPORTED)
6265 && memcmp(type,mng_JHDR,4)
6269 /* Not an IHDR or JHDR chunk */
6271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6276 if (logging != MagickFalse)
6277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6278 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6280 mng_info->exists[object_id]=MagickTrue;
6281 mng_info->viewable[object_id]=MagickTrue;
6283 if (mng_info->invisible[object_id])
6285 if (logging != MagickFalse)
6286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6287 " Skipping invisible object");
6289 skip_to_iend=MagickTrue;
6290 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6293 #if defined(MNG_INSERT_LAYERS)
6295 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6297 image_width=(size_t) mng_get_long(p);
6298 image_height=(size_t) mng_get_long(&p[4]);
6300 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6303 Insert a transparent background layer behind the entire animation
6304 if it is not full screen.
6306 #if defined(MNG_INSERT_LAYERS)
6307 if (insert_layers && mng_type && first_mng_object)
6309 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6310 (image_width < mng_info->mng_width) ||
6311 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6312 (image_height < mng_info->mng_height) ||
6313 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6315 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6318 Allocate next image structure.
6320 AcquireNextImage(image_info,image,exception);
6322 if (GetNextImageInList(image) == (Image *) NULL)
6324 image=DestroyImageList(image);
6325 MngInfoFreeStruct(mng_info,&have_mng_structure);
6326 return((Image *) NULL);
6329 image=SyncNextImageInList(image);
6331 mng_info->image=image;
6333 if (term_chunk_found)
6335 image->start_loop=MagickTrue;
6336 image->iterations=mng_iterations;
6337 term_chunk_found=MagickFalse;
6341 image->start_loop=MagickFalse;
6343 /* Make a background rectangle. */
6346 image->columns=mng_info->mng_width;
6347 image->rows=mng_info->mng_height;
6348 image->page.width=mng_info->mng_width;
6349 image->page.height=mng_info->mng_height;
6352 image->background_color=mng_background_color;
6353 (void) SetImageBackgroundColor(image,exception);
6354 if (logging != MagickFalse)
6355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6356 " Inserted transparent background layer, W=%.20g, H=%.20g",
6357 (double) mng_info->mng_width,(double) mng_info->mng_height);
6361 Insert a background layer behind the upcoming image if
6362 framing_mode is 3, and we haven't already inserted one.
6364 if (insert_layers && (mng_info->framing_mode == 3) &&
6365 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6366 (simplicity & 0x08)))
6368 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6371 Allocate next image structure.
6373 AcquireNextImage(image_info,image,exception);
6375 if (GetNextImageInList(image) == (Image *) NULL)
6377 image=DestroyImageList(image);
6378 MngInfoFreeStruct(mng_info,&have_mng_structure);
6379 return((Image *) NULL);
6382 image=SyncNextImageInList(image);
6385 mng_info->image=image;
6387 if (term_chunk_found)
6389 image->start_loop=MagickTrue;
6390 image->iterations=mng_iterations;
6391 term_chunk_found=MagickFalse;
6395 image->start_loop=MagickFalse;
6398 image->columns=subframe_width;
6399 image->rows=subframe_height;
6400 image->page.width=subframe_width;
6401 image->page.height=subframe_height;
6402 image->page.x=mng_info->clip.left;
6403 image->page.y=mng_info->clip.top;
6404 image->background_color=mng_background_color;
6405 image->alpha_trait=UndefinedPixelTrait;
6406 (void) SetImageBackgroundColor(image,exception);
6408 if (logging != MagickFalse)
6409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6410 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6411 (double) mng_info->clip.left,(double) mng_info->clip.right,
6412 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6414 #endif /* MNG_INSERT_LAYERS */
6415 first_mng_object=MagickFalse;
6417 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6420 Allocate next image structure.
6422 AcquireNextImage(image_info,image,exception);
6424 if (GetNextImageInList(image) == (Image *) NULL)
6426 image=DestroyImageList(image);
6427 MngInfoFreeStruct(mng_info,&have_mng_structure);
6428 return((Image *) NULL);
6431 image=SyncNextImageInList(image);
6433 mng_info->image=image;
6434 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6435 GetBlobSize(image));
6437 if (status == MagickFalse)
6440 if (term_chunk_found)
6442 image->start_loop=MagickTrue;
6443 term_chunk_found=MagickFalse;
6447 image->start_loop=MagickFalse;
6449 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6451 image->delay=frame_delay;
6452 frame_delay=default_frame_delay;
6458 image->page.width=mng_info->mng_width;
6459 image->page.height=mng_info->mng_height;
6460 image->page.x=mng_info->x_off[object_id];
6461 image->page.y=mng_info->y_off[object_id];
6462 image->iterations=mng_iterations;
6465 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6468 if (logging != MagickFalse)
6469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6470 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6473 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6476 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6480 mng_info->image=image;
6481 mng_info->mng_type=mng_type;
6482 mng_info->object_id=object_id;
6484 if (memcmp(type,mng_IHDR,4) == 0)
6485 image=ReadOnePNGImage(mng_info,image_info,exception);
6487 #if defined(JNG_SUPPORTED)
6489 image=ReadOneJNGImage(mng_info,image_info,exception);
6492 if (image == (Image *) NULL)
6494 if (IsImageObject(previous) != MagickFalse)
6496 (void) DestroyImageList(previous);
6497 (void) CloseBlob(previous);
6500 MngInfoFreeStruct(mng_info,&have_mng_structure);
6501 return((Image *) NULL);
6504 if (image->columns == 0 || image->rows == 0)
6506 (void) CloseBlob(image);
6507 image=DestroyImageList(image);
6508 MngInfoFreeStruct(mng_info,&have_mng_structure);
6509 return((Image *) NULL);
6512 mng_info->image=image;
6519 if (mng_info->magn_methx || mng_info->magn_methy)
6525 if (logging != MagickFalse)
6526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6527 " Processing MNG MAGN chunk");
6529 if (mng_info->magn_methx == 1)
6531 magnified_width=mng_info->magn_ml;
6533 if (image->columns > 1)
6534 magnified_width += mng_info->magn_mr;
6536 if (image->columns > 2)
6537 magnified_width += (png_uint_32)
6538 ((image->columns-2)*(mng_info->magn_mx));
6543 magnified_width=(png_uint_32) image->columns;
6545 if (image->columns > 1)
6546 magnified_width += mng_info->magn_ml-1;
6548 if (image->columns > 2)
6549 magnified_width += mng_info->magn_mr-1;
6551 if (image->columns > 3)
6552 magnified_width += (png_uint_32)
6553 ((image->columns-3)*(mng_info->magn_mx-1));
6556 if (mng_info->magn_methy == 1)
6558 magnified_height=mng_info->magn_mt;
6560 if (image->rows > 1)
6561 magnified_height += mng_info->magn_mb;
6563 if (image->rows > 2)
6564 magnified_height += (png_uint_32)
6565 ((image->rows-2)*(mng_info->magn_my));
6570 magnified_height=(png_uint_32) image->rows;
6572 if (image->rows > 1)
6573 magnified_height += mng_info->magn_mt-1;
6575 if (image->rows > 2)
6576 magnified_height += mng_info->magn_mb-1;
6578 if (image->rows > 3)
6579 magnified_height += (png_uint_32)
6580 ((image->rows-3)*(mng_info->magn_my-1));
6583 if (magnified_height > image->rows ||
6584 magnified_width > image->columns)
6611 /* Allocate next image structure. */
6613 if (logging != MagickFalse)
6614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6615 " Allocate magnified image");
6617 AcquireNextImage(image_info,image,exception);
6619 if (GetNextImageInList(image) == (Image *) NULL)
6621 image=DestroyImageList(image);
6622 MngInfoFreeStruct(mng_info,&have_mng_structure);
6623 return((Image *) NULL);
6626 large_image=SyncNextImageInList(image);
6628 large_image->columns=magnified_width;
6629 large_image->rows=magnified_height;
6631 magn_methx=mng_info->magn_methx;
6632 magn_methy=mng_info->magn_methy;
6634 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6635 #define QM unsigned short
6636 if (magn_methx != 1 || magn_methy != 1)
6639 Scale pixels to unsigned shorts to prevent
6640 overflow of intermediate values of interpolations
6642 for (y=0; y < (ssize_t) image->rows; y++)
6644 q=GetAuthenticPixels(image,0,y,image->columns,1,
6647 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6649 SetPixelRed(image,ScaleQuantumToShort(
6650 GetPixelRed(image,q)),q);
6651 SetPixelGreen(image,ScaleQuantumToShort(
6652 GetPixelGreen(image,q)),q);
6653 SetPixelBlue(image,ScaleQuantumToShort(
6654 GetPixelBlue(image,q)),q);
6655 SetPixelAlpha(image,ScaleQuantumToShort(
6656 GetPixelAlpha(image,q)),q);
6657 q+=GetPixelChannels(image);
6660 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6668 if (image->alpha_trait == BlendPixelTrait)
6669 (void) SetImageBackgroundColor(large_image,exception);
6673 large_image->background_color.alpha=OpaqueAlpha;
6674 (void) SetImageBackgroundColor(large_image,exception);
6676 if (magn_methx == 4)
6679 if (magn_methx == 5)
6682 if (magn_methy == 4)
6685 if (magn_methy == 5)
6689 /* magnify the rows into the right side of the large image */
6691 if (logging != MagickFalse)
6692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6693 " Magnify the rows to %.20g",(double) large_image->rows);
6694 m=(ssize_t) mng_info->magn_mt;
6696 length=(size_t) image->columns*GetPixelChannels(image);
6697 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6698 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6700 if ((prev == (Quantum *) NULL) ||
6701 (next == (Quantum *) NULL))
6703 image=DestroyImageList(image);
6704 MngInfoFreeStruct(mng_info,&have_mng_structure);
6705 ThrowReaderException(ResourceLimitError,
6706 "MemoryAllocationFailed");
6709 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6710 (void) CopyMagickMemory(next,n,length);
6712 for (y=0; y < (ssize_t) image->rows; y++)
6715 m=(ssize_t) mng_info->magn_mt;
6717 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6718 m=(ssize_t) mng_info->magn_mb;
6720 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6721 m=(ssize_t) mng_info->magn_mb;
6723 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6727 m=(ssize_t) mng_info->magn_my;
6733 if (y < (ssize_t) image->rows-1)
6735 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6737 (void) CopyMagickMemory(next,n,length);
6740 for (i=0; i < m; i++, yy++)
6745 assert(yy < (ssize_t) large_image->rows);
6748 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6750 q+=(large_image->columns-image->columns)*
6751 GetPixelChannels(large_image);
6753 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6755 /* To do: get color as function of indexes[x] */
6757 if (image->storage_class == PseudoClass)
6762 if (magn_methy <= 1)
6764 /* replicate previous */
6765 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6766 SetPixelGreen(large_image,GetPixelGreen(image,
6768 SetPixelBlue(large_image,GetPixelBlue(image,
6770 SetPixelAlpha(large_image,GetPixelAlpha(image,
6774 else if (magn_methy == 2 || magn_methy == 4)
6778 SetPixelRed(large_image,GetPixelRed(image,
6780 SetPixelGreen(large_image,GetPixelGreen(image,
6782 SetPixelBlue(large_image,GetPixelBlue(image,
6784 SetPixelAlpha(large_image,GetPixelAlpha(image,
6791 SetPixelRed(large_image,((QM) (((ssize_t)
6792 (2*i*(GetPixelRed(image,n)
6793 -GetPixelRed(image,pixels)+m))/
6795 +GetPixelRed(image,pixels)))),q);
6796 SetPixelGreen(large_image,((QM) (((ssize_t)
6797 (2*i*(GetPixelGreen(image,n)
6798 -GetPixelGreen(image,pixels)+m))/
6800 +GetPixelGreen(image,pixels)))),q);
6801 SetPixelBlue(large_image,((QM) (((ssize_t)
6802 (2*i*(GetPixelBlue(image,n)
6803 -GetPixelBlue(image,pixels)+m))/
6805 +GetPixelBlue(image,pixels)))),q);
6807 if (image->alpha_trait == BlendPixelTrait)
6808 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6809 (2*i*(GetPixelAlpha(image,n)
6810 -GetPixelAlpha(image,pixels)+m))
6812 GetPixelAlpha(image,pixels)))),q);
6815 if (magn_methy == 4)
6817 /* Replicate nearest */
6818 if (i <= ((m+1) << 1))
6819 SetPixelAlpha(large_image,GetPixelAlpha(image,
6822 SetPixelAlpha(large_image,GetPixelAlpha(image,
6827 else /* if (magn_methy == 3 || magn_methy == 5) */
6829 /* Replicate nearest */
6830 if (i <= ((m+1) << 1))
6832 SetPixelRed(large_image,GetPixelRed(image,
6834 SetPixelGreen(large_image,GetPixelGreen(image,
6836 SetPixelBlue(large_image,GetPixelBlue(image,
6838 SetPixelAlpha(large_image,GetPixelAlpha(image,
6844 SetPixelRed(large_image,GetPixelRed(image,n),q);
6845 SetPixelGreen(large_image,GetPixelGreen(image,n),
6847 SetPixelBlue(large_image,GetPixelBlue(image,n),
6849 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6853 if (magn_methy == 5)
6855 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6856 (GetPixelAlpha(image,n)
6857 -GetPixelAlpha(image,pixels))
6858 +m))/((ssize_t) (m*2))
6859 +GetPixelAlpha(image,pixels)),q);
6862 n+=GetPixelChannels(image);
6863 q+=GetPixelChannels(large_image);
6864 pixels+=GetPixelChannels(image);
6867 if (SyncAuthenticPixels(large_image,exception) == 0)
6873 prev=(Quantum *) RelinquishMagickMemory(prev);
6874 next=(Quantum *) RelinquishMagickMemory(next);
6876 length=image->columns;
6878 if (logging != MagickFalse)
6879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6880 " Delete original image");
6882 DeleteImageFromList(&image);
6886 mng_info->image=image;
6888 /* magnify the columns */
6889 if (logging != MagickFalse)
6890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6891 " Magnify the columns to %.20g",(double) image->columns);
6893 for (y=0; y < (ssize_t) image->rows; y++)
6898 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6899 pixels=q+(image->columns-length)*GetPixelChannels(image);
6900 n=pixels+GetPixelChannels(image);
6902 for (x=(ssize_t) (image->columns-length);
6903 x < (ssize_t) image->columns; x++)
6905 /* To do: Rewrite using Get/Set***PixelChannel() */
6907 if (x == (ssize_t) (image->columns-length))
6908 m=(ssize_t) mng_info->magn_ml;
6910 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6911 m=(ssize_t) mng_info->magn_mr;
6913 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6914 m=(ssize_t) mng_info->magn_mr;
6916 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6920 m=(ssize_t) mng_info->magn_mx;
6922 for (i=0; i < m; i++)
6924 if (magn_methx <= 1)
6926 /* replicate previous */
6927 SetPixelRed(image,GetPixelRed(image,pixels),q);
6928 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6929 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6930 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6933 else if (magn_methx == 2 || magn_methx == 4)
6937 SetPixelRed(image,GetPixelRed(image,pixels),q);
6938 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6939 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6940 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6943 /* To do: Rewrite using Get/Set***PixelChannel() */
6947 SetPixelRed(image,(QM) ((2*i*(
6948 GetPixelRed(image,n)
6949 -GetPixelRed(image,pixels))+m)
6951 GetPixelRed(image,pixels)),q);
6953 SetPixelGreen(image,(QM) ((2*i*(
6954 GetPixelGreen(image,n)
6955 -GetPixelGreen(image,pixels))+m)
6957 GetPixelGreen(image,pixels)),q);
6959 SetPixelBlue(image,(QM) ((2*i*(
6960 GetPixelBlue(image,n)
6961 -GetPixelBlue(image,pixels))+m)
6963 GetPixelBlue(image,pixels)),q);
6964 if (image->alpha_trait == BlendPixelTrait)
6965 SetPixelAlpha(image,(QM) ((2*i*(
6966 GetPixelAlpha(image,n)
6967 -GetPixelAlpha(image,pixels))+m)
6969 GetPixelAlpha(image,pixels)),q);
6972 if (magn_methx == 4)
6974 /* Replicate nearest */
6975 if (i <= ((m+1) << 1))
6977 SetPixelAlpha(image,
6978 GetPixelAlpha(image,pixels)+0,q);
6982 SetPixelAlpha(image,
6983 GetPixelAlpha(image,n)+0,q);
6988 else /* if (magn_methx == 3 || magn_methx == 5) */
6990 /* Replicate nearest */
6991 if (i <= ((m+1) << 1))
6993 SetPixelRed(image,GetPixelRed(image,pixels),q);
6994 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6995 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6996 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7001 SetPixelRed(image,GetPixelRed(image,n),q);
7002 SetPixelGreen(image,GetPixelGreen(image,n),q);
7003 SetPixelBlue(image,GetPixelBlue(image,n),q);
7004 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7007 if (magn_methx == 5)
7010 SetPixelAlpha(image,
7011 (QM) ((2*i*( GetPixelAlpha(image,n)
7012 -GetPixelAlpha(image,pixels))+m)/
7014 +GetPixelAlpha(image,pixels)),q);
7017 q+=GetPixelChannels(image);
7019 n+=GetPixelChannels(image);
7020 p+=GetPixelChannels(image);
7023 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7026 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7027 if (magn_methx != 1 || magn_methy != 1)
7030 Rescale pixels to Quantum
7032 for (y=0; y < (ssize_t) image->rows; y++)
7034 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7036 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7038 SetPixelRed(image,ScaleShortToQuantum(
7039 GetPixelRed(image,q)),q);
7040 SetPixelGreen(image,ScaleShortToQuantum(
7041 GetPixelGreen(image,q)),q);
7042 SetPixelBlue(image,ScaleShortToQuantum(
7043 GetPixelBlue(image,q)),q);
7044 SetPixelAlpha(image,ScaleShortToQuantum(
7045 GetPixelAlpha(image,q)),q);
7046 q+=GetPixelChannels(image);
7049 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7054 if (logging != MagickFalse)
7055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7056 " Finished MAGN processing");
7061 Crop_box is with respect to the upper left corner of the MNG.
7063 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7064 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7065 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7066 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7067 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7068 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7069 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7070 if ((crop_box.left != (mng_info->image_box.left
7071 +mng_info->x_off[object_id])) ||
7072 (crop_box.right != (mng_info->image_box.right
7073 +mng_info->x_off[object_id])) ||
7074 (crop_box.top != (mng_info->image_box.top
7075 +mng_info->y_off[object_id])) ||
7076 (crop_box.bottom != (mng_info->image_box.bottom
7077 +mng_info->y_off[object_id])))
7079 if (logging != MagickFalse)
7080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7081 " Crop the PNG image");
7083 if ((crop_box.left < crop_box.right) &&
7084 (crop_box.top < crop_box.bottom))
7093 Crop_info is with respect to the upper left corner of
7096 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7097 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7098 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7099 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7100 image->page.width=image->columns;
7101 image->page.height=image->rows;
7104 im=CropImage(image,&crop_info,exception);
7106 if (im != (Image *) NULL)
7108 image->columns=im->columns;
7109 image->rows=im->rows;
7110 im=DestroyImage(im);
7111 image->page.width=image->columns;
7112 image->page.height=image->rows;
7113 image->page.x=crop_box.left;
7114 image->page.y=crop_box.top;
7121 No pixels in crop area. The MNG spec still requires
7122 a layer, though, so make a single transparent pixel in
7123 the top left corner.
7128 (void) SetImageBackgroundColor(image,exception);
7129 image->page.width=1;
7130 image->page.height=1;
7135 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7136 image=mng_info->image;
7140 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7141 /* PNG does not handle depths greater than 16 so reduce it even
7144 if (image->depth > 16)
7148 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7149 if (image->depth > 8)
7151 /* To do: fill low byte properly */
7155 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7159 if (image_info->number_scenes != 0)
7161 if (mng_info->scenes_found >
7162 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7166 if (logging != MagickFalse)
7167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7168 " Finished reading image datastream.");
7170 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7172 (void) CloseBlob(image);
7174 if (logging != MagickFalse)
7175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7176 " Finished reading all image datastreams.");
7178 #if defined(MNG_INSERT_LAYERS)
7179 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7180 (mng_info->mng_height))
7183 Insert a background layer if nothing else was found.
7185 if (logging != MagickFalse)
7186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7187 " No images found. Inserting a background layer.");
7189 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7192 Allocate next image structure.
7194 AcquireNextImage(image_info,image,exception);
7195 if (GetNextImageInList(image) == (Image *) NULL)
7197 image=DestroyImageList(image);
7198 MngInfoFreeStruct(mng_info,&have_mng_structure);
7200 if (logging != MagickFalse)
7201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7202 " Allocation failed, returning NULL.");
7204 return((Image *) NULL);
7206 image=SyncNextImageInList(image);
7208 image->columns=mng_info->mng_width;
7209 image->rows=mng_info->mng_height;
7210 image->page.width=mng_info->mng_width;
7211 image->page.height=mng_info->mng_height;
7214 image->background_color=mng_background_color;
7215 image->alpha_trait=UndefinedPixelTrait;
7217 if (image_info->ping == MagickFalse)
7218 (void) SetImageBackgroundColor(image,exception);
7220 mng_info->image_found++;
7223 image->iterations=mng_iterations;
7225 if (mng_iterations == 1)
7226 image->start_loop=MagickTrue;
7228 while (GetPreviousImageInList(image) != (Image *) NULL)
7231 if (image_count > 10*mng_info->image_found)
7233 if (logging != MagickFalse)
7234 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7236 (void) ThrowMagickException(exception,GetMagickModule(),
7237 CoderError,"Linked list is corrupted, beginning of list not found",
7238 "`%s'",image_info->filename);
7240 return((Image *) NULL);
7243 image=GetPreviousImageInList(image);
7245 if (GetNextImageInList(image) == (Image *) NULL)
7247 if (logging != MagickFalse)
7248 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7250 (void) ThrowMagickException(exception,GetMagickModule(),
7251 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7252 image_info->filename);
7256 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7257 GetNextImageInList(image) ==
7260 if (logging != MagickFalse)
7261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7262 " First image null");
7264 (void) ThrowMagickException(exception,GetMagickModule(),
7265 CoderError,"image->next for first image is NULL but shouldn't be.",
7266 "`%s'",image_info->filename);
7269 if (mng_info->image_found == 0)
7271 if (logging != MagickFalse)
7272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7273 " No visible images found.");
7275 (void) ThrowMagickException(exception,GetMagickModule(),
7276 CoderError,"No visible images in file","`%s'",image_info->filename);
7278 if (image != (Image *) NULL)
7279 image=DestroyImageList(image);
7281 MngInfoFreeStruct(mng_info,&have_mng_structure);
7282 return((Image *) NULL);
7285 if (mng_info->ticks_per_second)
7286 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7287 final_delay/mng_info->ticks_per_second;
7290 image->start_loop=MagickTrue;
7292 /* Find final nonzero image delay */
7293 final_image_delay=0;
7295 while (GetNextImageInList(image) != (Image *) NULL)
7298 final_image_delay=image->delay;
7300 image=GetNextImageInList(image);
7303 if (final_delay < final_image_delay)
7304 final_delay=final_image_delay;
7306 image->delay=final_delay;
7308 if (logging != MagickFalse)
7309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7310 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7311 (double) final_delay);
7313 if (logging != MagickFalse)
7319 image=GetFirstImageInList(image);
7321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7322 " Before coalesce:");
7324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7325 " scene 0 delay=%.20g",(double) image->delay);
7327 while (GetNextImageInList(image) != (Image *) NULL)
7329 image=GetNextImageInList(image);
7330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7331 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7335 image=GetFirstImageInList(image);
7336 #ifdef MNG_COALESCE_LAYERS
7346 if (logging != MagickFalse)
7347 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7350 next_image=CoalesceImages(image,exception);
7352 if (next_image == (Image *) NULL)
7353 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7355 image=DestroyImageList(image);
7358 for (next=image; next != (Image *) NULL; next=next_image)
7360 next->page.width=mng_info->mng_width;
7361 next->page.height=mng_info->mng_height;
7364 next->scene=scene++;
7365 next_image=GetNextImageInList(next);
7367 if (next_image == (Image *) NULL)
7370 if (next->delay == 0)
7373 next_image->previous=GetPreviousImageInList(next);
7374 if (GetPreviousImageInList(next) == (Image *) NULL)
7377 next->previous->next=next_image;
7378 next=DestroyImage(next);
7384 while (GetNextImageInList(image) != (Image *) NULL)
7385 image=GetNextImageInList(image);
7387 image->dispose=BackgroundDispose;
7389 if (logging != MagickFalse)
7395 image=GetFirstImageInList(image);
7397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7398 " After coalesce:");
7400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7401 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7402 (double) image->dispose);
7404 while (GetNextImageInList(image) != (Image *) NULL)
7406 image=GetNextImageInList(image);
7408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7410 (double) image->delay,(double) image->dispose);
7414 image=GetFirstImageInList(image);
7415 MngInfoFreeStruct(mng_info,&have_mng_structure);
7416 have_mng_structure=MagickFalse;
7418 if (logging != MagickFalse)
7419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7421 return(GetFirstImageInList(image));
7423 #else /* PNG_LIBPNG_VER > 10011 */
7424 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7426 printf("Your PNG library is too old: You have libpng-%s\n",
7427 PNG_LIBPNG_VER_STRING);
7429 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7430 "PNG library is too old","`%s'",image_info->filename);
7432 return(Image *) NULL;
7435 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7437 return(ReadPNGImage(image_info,exception));
7439 #endif /* PNG_LIBPNG_VER > 10011 */
7443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7447 % R e g i s t e r P N G I m a g e %
7451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7453 % RegisterPNGImage() adds properties for the PNG image format to
7454 % the list of supported formats. The properties include the image format
7455 % tag, a method to read and/or write the format, whether the format
7456 % supports the saving of more than one frame to the same file or blob,
7457 % whether the format supports native in-memory I/O, and a brief
7458 % description of the format.
7460 % The format of the RegisterPNGImage method is:
7462 % size_t RegisterPNGImage(void)
7465 ModuleExport size_t RegisterPNGImage(void)
7468 version[MaxTextExtent];
7476 "See http://www.libpng.org/ for details about the PNG format."
7481 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7487 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7493 #if defined(PNG_LIBPNG_VER_STRING)
7494 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7495 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7497 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7499 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7500 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7505 entry=SetMagickInfo("MNG");
7506 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7508 #if defined(MAGICKCORE_PNG_DELEGATE)
7509 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7510 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7513 entry->magick=(IsImageFormatHandler *) IsMNG;
7514 entry->description=ConstantString("Multiple-image Network Graphics");
7516 if (*version != '\0')
7517 entry->version=ConstantString(version);
7519 entry->mime_type=ConstantString("video/x-mng");
7520 entry->module=ConstantString("PNG");
7521 entry->note=ConstantString(MNGNote);
7522 (void) RegisterMagickInfo(entry);
7524 entry=SetMagickInfo("PNG");
7526 #if defined(MAGICKCORE_PNG_DELEGATE)
7527 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7528 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7531 entry->magick=(IsImageFormatHandler *) IsPNG;
7532 entry->adjoin=MagickFalse;
7533 entry->description=ConstantString("Portable Network Graphics");
7534 entry->mime_type=ConstantString("image/png");
7535 entry->module=ConstantString("PNG");
7537 if (*version != '\0')
7538 entry->version=ConstantString(version);
7540 entry->note=ConstantString(PNGNote);
7541 (void) RegisterMagickInfo(entry);
7543 entry=SetMagickInfo("PNG8");
7545 #if defined(MAGICKCORE_PNG_DELEGATE)
7546 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7547 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7550 entry->magick=(IsImageFormatHandler *) IsPNG;
7551 entry->adjoin=MagickFalse;
7552 entry->description=ConstantString(
7553 "8-bit indexed with optional binary transparency");
7554 entry->mime_type=ConstantString("image/png");
7555 entry->module=ConstantString("PNG");
7556 (void) RegisterMagickInfo(entry);
7558 entry=SetMagickInfo("PNG24");
7561 #if defined(ZLIB_VERSION)
7562 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7563 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7565 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7567 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7568 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7572 if (*version != '\0')
7573 entry->version=ConstantString(version);
7575 #if defined(MAGICKCORE_PNG_DELEGATE)
7576 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7577 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7580 entry->magick=(IsImageFormatHandler *) IsPNG;
7581 entry->adjoin=MagickFalse;
7582 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7583 entry->mime_type=ConstantString("image/png");
7584 entry->module=ConstantString("PNG");
7585 (void) RegisterMagickInfo(entry);
7587 entry=SetMagickInfo("PNG32");
7589 #if defined(MAGICKCORE_PNG_DELEGATE)
7590 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7591 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7594 entry->magick=(IsImageFormatHandler *) IsPNG;
7595 entry->adjoin=MagickFalse;
7596 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7597 entry->mime_type=ConstantString("image/png");
7598 entry->module=ConstantString("PNG");
7599 (void) RegisterMagickInfo(entry);
7601 entry=SetMagickInfo("PNG48");
7603 #if defined(MAGICKCORE_PNG_DELEGATE)
7604 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7605 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7608 entry->magick=(IsImageFormatHandler *) IsPNG;
7609 entry->adjoin=MagickFalse;
7610 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7611 entry->mime_type=ConstantString("image/png");
7612 entry->module=ConstantString("PNG");
7613 (void) RegisterMagickInfo(entry);
7615 entry=SetMagickInfo("PNG64");
7617 #if defined(MAGICKCORE_PNG_DELEGATE)
7618 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7619 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7622 entry->magick=(IsImageFormatHandler *) IsPNG;
7623 entry->adjoin=MagickFalse;
7624 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7625 entry->mime_type=ConstantString("image/png");
7626 entry->module=ConstantString("PNG");
7627 (void) RegisterMagickInfo(entry);
7629 entry=SetMagickInfo("PNG00");
7631 #if defined(MAGICKCORE_PNG_DELEGATE)
7632 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7633 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7636 entry->magick=(IsImageFormatHandler *) IsPNG;
7637 entry->adjoin=MagickFalse;
7638 entry->description=ConstantString(
7639 "PNG inheriting bit-depth and color-type from original");
7640 entry->mime_type=ConstantString("image/png");
7641 entry->module=ConstantString("PNG");
7642 (void) RegisterMagickInfo(entry);
7644 entry=SetMagickInfo("JNG");
7646 #if defined(JNG_SUPPORTED)
7647 #if defined(MAGICKCORE_PNG_DELEGATE)
7648 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7649 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7653 entry->magick=(IsImageFormatHandler *) IsJNG;
7654 entry->adjoin=MagickFalse;
7655 entry->description=ConstantString("JPEG Network Graphics");
7656 entry->mime_type=ConstantString("image/x-jng");
7657 entry->module=ConstantString("PNG");
7658 entry->note=ConstantString(JNGNote);
7659 (void) RegisterMagickInfo(entry);
7661 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7662 ping_semaphore=AcquireSemaphoreInfo();
7665 return(MagickImageCoderSignature);
7669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7673 % U n r e g i s t e r P N G I m a g e %
7677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7679 % UnregisterPNGImage() removes format registrations made by the
7680 % PNG module from the list of supported formats.
7682 % The format of the UnregisterPNGImage method is:
7684 % UnregisterPNGImage(void)
7687 ModuleExport void UnregisterPNGImage(void)
7689 (void) UnregisterMagickInfo("MNG");
7690 (void) UnregisterMagickInfo("PNG");
7691 (void) UnregisterMagickInfo("PNG8");
7692 (void) UnregisterMagickInfo("PNG24");
7693 (void) UnregisterMagickInfo("PNG32");
7694 (void) UnregisterMagickInfo("PNG48");
7695 (void) UnregisterMagickInfo("PNG64");
7696 (void) UnregisterMagickInfo("PNG00");
7697 (void) UnregisterMagickInfo("JNG");
7699 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7700 if (ping_semaphore != (SemaphoreInfo *) NULL)
7701 RelinquishSemaphoreInfo(&ping_semaphore);
7705 #if defined(MAGICKCORE_PNG_DELEGATE)
7706 #if PNG_LIBPNG_VER > 10011
7708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7712 % W r i t e M N G I m a g e %
7716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7718 % WriteMNGImage() writes an image in the Portable Network Graphics
7719 % Group's "Multiple-image Network Graphics" encoded image format.
7721 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7723 % The format of the WriteMNGImage method is:
7725 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7726 % Image *image,ExceptionInfo *exception)
7728 % A description of each parameter follows.
7730 % o image_info: the image info.
7732 % o image: The image.
7734 % o exception: return any errors or warnings in this structure.
7736 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7737 % "To do" under ReadPNGImage):
7739 % Preserve all unknown and not-yet-handled known chunks found in input
7740 % PNG file and copy them into output PNG files according to the PNG
7743 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7745 % Improve selection of color type (use indexed-colour or indexed-colour
7746 % with tRNS when 256 or fewer unique RGBA values are present).
7748 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7749 % This will be complicated if we limit ourselves to generating MNG-LC
7750 % files. For now we ignore disposal method 3 and simply overlay the next
7753 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7754 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7755 % [mostly done 15 June 1999 but still need to take care of tRNS]
7757 % Check for identical sRGB and replace with a global sRGB (and remove
7758 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7759 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7760 % local gAMA/cHRM with local sRGB if appropriate).
7762 % Check for identical sBIT chunks and write global ones.
7764 % Provide option to skip writing the signature tEXt chunks.
7766 % Use signatures to detect identical objects and reuse the first
7767 % instance of such objects instead of writing duplicate objects.
7769 % Use a smaller-than-32k value of compression window size when
7772 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7773 % ancillary text chunks and save profiles.
7775 % Provide an option to force LC files (to ensure exact framing rate)
7778 % Provide an option to force VLC files instead of LC, even when offsets
7779 % are present. This will involve expanding the embedded images with a
7780 % transparent region at the top and/or left.
7784 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7785 png_info *ping_info, unsigned char *profile_type, unsigned char
7786 *profile_description, unsigned char *profile_data, png_uint_32 length)
7805 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7807 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7810 if (image_info->verbose)
7812 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7813 (char *) profile_type, (double) length);
7816 #if PNG_LIBPNG_VER >= 10400
7817 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7819 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7821 description_length=(png_uint_32) strlen((const char *) profile_description);
7822 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7823 + description_length);
7824 #if PNG_LIBPNG_VER >= 10400
7825 text[0].text=(png_charp) png_malloc(ping,
7826 (png_alloc_size_t) allocated_length);
7827 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7829 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7830 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7832 text[0].key[0]='\0';
7833 (void) ConcatenateMagickString(text[0].key,
7834 "Raw profile type ",MaxTextExtent);
7835 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7839 (void) CopyMagickString(dp,(const char *) profile_description,
7841 dp+=description_length;
7843 (void) FormatLocaleString(dp,allocated_length-
7844 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7847 for (i=0; i < (ssize_t) length; i++)
7851 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7852 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7857 text[0].text_length=(png_size_t) (dp-text[0].text);
7858 text[0].compression=image_info->compression == NoCompression ||
7859 (image_info->compression == UndefinedCompression &&
7860 text[0].text_length < 128) ? -1 : 0;
7862 if (text[0].text_length <= allocated_length)
7863 png_set_text(ping,ping_info,text,1);
7865 png_free(ping,text[0].text);
7866 png_free(ping,text[0].key);
7867 png_free(ping,text);
7870 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7871 const char *string, MagickBooleanType logging)
7884 ResetImageProfileIterator(image);
7886 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7888 profile=GetImageProfile(image,name);
7890 if (profile != (const StringInfo *) NULL)
7895 if (LocaleNCompare(name,string,11) == 0)
7897 if (logging != MagickFalse)
7898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7899 " Found %s profile",name);
7901 ping_profile=CloneStringInfo(profile);
7902 data=GetStringInfoDatum(ping_profile),
7903 length=(png_uint_32) GetStringInfoLength(ping_profile);
7908 (void) WriteBlobMSBULong(image,length-5); /* data length */
7909 (void) WriteBlob(image,length-1,data+1);
7910 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7911 ping_profile=DestroyStringInfo(ping_profile);
7915 name=GetNextImageProfile(image);
7922 /* Write one PNG image */
7923 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7924 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7955 ping_trans_alpha[256];
7983 ping_have_cheap_transparency,
7996 /* ping_exclude_EXIF, */
7999 /* ping_exclude_iTXt, */
8004 /* ping_exclude_tRNS, */
8006 ping_exclude_zCCP, /* hex-encoded iCCP */
8009 ping_preserve_colormap,
8011 ping_need_colortype_warning,
8019 *volatile pixel_info;
8038 ping_interlace_method,
8039 ping_compression_method,
8056 number_semitransparent,
8058 ping_pHYs_unit_type;
8061 ping_pHYs_x_resolution,
8062 ping_pHYs_y_resolution;
8064 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8065 " Enter WriteOnePNGImage()");
8067 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8068 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8069 if (image_info == (ImageInfo *) NULL)
8070 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8072 /* Define these outside of the following "if logging()" block so they will
8073 * show in debuggers.
8076 (void) ConcatenateMagickString(im_vers,
8077 MagickLibVersionText,MaxTextExtent);
8078 (void) ConcatenateMagickString(im_vers,
8079 MagickLibAddendum,MaxTextExtent);
8082 (void) ConcatenateMagickString(libpng_vers,
8083 PNG_LIBPNG_VER_STRING,32);
8085 (void) ConcatenateMagickString(libpng_runv,
8086 png_get_libpng_ver(NULL),32);
8089 (void) ConcatenateMagickString(zlib_vers,
8092 (void) ConcatenateMagickString(zlib_runv,
8097 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8099 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8101 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8103 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8106 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8108 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8110 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8115 /* Initialize some stuff */
8118 ping_interlace_method=0,
8119 ping_compression_method=0,
8120 ping_filter_method=0,
8123 ping_background.red = 0;
8124 ping_background.green = 0;
8125 ping_background.blue = 0;
8126 ping_background.gray = 0;
8127 ping_background.index = 0;
8129 ping_trans_color.red=0;
8130 ping_trans_color.green=0;
8131 ping_trans_color.blue=0;
8132 ping_trans_color.gray=0;
8134 ping_pHYs_unit_type = 0;
8135 ping_pHYs_x_resolution = 0;
8136 ping_pHYs_y_resolution = 0;
8138 ping_have_blob=MagickFalse;
8139 ping_have_cheap_transparency=MagickFalse;
8140 ping_have_color=MagickTrue;
8141 ping_have_non_bw=MagickTrue;
8142 ping_have_PLTE=MagickFalse;
8143 ping_have_bKGD=MagickFalse;
8144 ping_have_iCCP=MagickFalse;
8145 ping_have_pHYs=MagickFalse;
8146 ping_have_sRGB=MagickFalse;
8147 ping_have_tRNS=MagickFalse;
8149 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8150 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8151 ping_exclude_date=mng_info->ping_exclude_date;
8152 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8153 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8154 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8155 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8156 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8157 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8158 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8159 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8160 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8161 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8162 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8163 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8165 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8166 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8167 ping_need_colortype_warning = MagickFalse;
8169 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8170 * i.e., eliminate the ICC profile and set image->rendering_intent.
8171 * Note that this will not involve any changes to the actual pixels
8172 * but merely passes information to applications that read the resulting
8175 * To do: recognize other variants of the sRGB profile, using the CRC to
8176 * verify all recognized variants including the 7 already known.
8178 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8180 * Use something other than image->rendering_intent to record the fact
8181 * that the sRGB profile was found.
8183 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8184 * profile. Record the Blackpoint Compensation, if any.
8186 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8194 ResetImageProfileIterator(image);
8195 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8197 profile=GetImageProfile(image,name);
8199 if (profile != (StringInfo *) NULL)
8201 if ((LocaleCompare(name,"ICC") == 0) ||
8202 (LocaleCompare(name,"ICM") == 0))
8217 length=(png_uint_32) GetStringInfoLength(profile);
8219 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8221 if (length == sRGB_info[icheck].len)
8225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226 " Got a %lu-byte ICC profile (potentially sRGB)",
8227 (unsigned long) length);
8229 data=GetStringInfoDatum(profile);
8230 profile_crc=crc32(0,data,length);
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " with crc=%8x",(unsigned int) profile_crc);
8237 if (profile_crc == sRGB_info[icheck].crc)
8239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8240 " It is sRGB with rendering intent = %s",
8241 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8242 sRGB_info[icheck].intent));
8243 if (image->rendering_intent==UndefinedIntent)
8245 image->rendering_intent=
8246 Magick_RenderingIntent_from_PNG_RenderingIntent(
8247 sRGB_info[icheck].intent);
8249 ping_exclude_iCCP = MagickTrue;
8250 ping_exclude_zCCP = MagickTrue;
8251 ping_have_sRGB = MagickTrue;
8256 if (sRGB_info[icheck].len == 0)
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " Got a %lu-byte ICC profile not recognized as sRGB",
8259 (unsigned long) length);
8262 name=GetNextImageProfile(image);
8267 number_semitransparent = 0;
8268 number_transparent = 0;
8270 if (logging != MagickFalse)
8272 if (image->storage_class == UndefinedClass)
8273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274 " storage_class=UndefinedClass");
8275 if (image->storage_class == DirectClass)
8276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277 " storage_class=DirectClass");
8278 if (image->storage_class == PseudoClass)
8279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280 " storage_class=PseudoClass");
8283 if (image->storage_class == PseudoClass &&
8284 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8285 mng_info->write_png48 || mng_info->write_png64 ||
8286 (mng_info->write_png_colortype != 1 &&
8287 mng_info->write_png_colortype != 5)))
8289 (void) SyncImage(image,exception);
8290 image->storage_class = DirectClass;
8293 if (ping_preserve_colormap == MagickFalse)
8295 if (image->storage_class != PseudoClass && image->colormap != NULL)
8297 /* Free the bogus colormap; it can cause trouble later */
8298 if (logging != MagickFalse)
8299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8300 " Freeing bogus colormap");
8301 (void) RelinquishMagickMemory(image->colormap);
8302 image->colormap=NULL;
8306 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8307 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8310 Sometimes we get PseudoClass images whose RGB values don't match
8311 the colors in the colormap. This code syncs the RGB values.
8313 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8314 (void) SyncImage(image,exception);
8316 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8317 if (image->depth > 8)
8319 if (logging != MagickFalse)
8320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8321 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8327 /* Respect the -depth option */
8328 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8333 if (image->depth > 8)
8335 #if MAGICKCORE_QUANTUM_DEPTH > 16
8336 /* Scale to 16-bit */
8337 LBR16PacketRGBO(image->background_color);
8339 for (y=0; y < (ssize_t) image->rows; y++)
8341 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8343 if (r == (Quantum *) NULL)
8346 for (x=0; x < (ssize_t) image->columns; x++)
8349 r+=GetPixelChannels(image);
8352 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8356 if (image->storage_class == PseudoClass && image->colormap != NULL)
8358 for (i=0; i < (ssize_t) image->colors; i++)
8360 LBR16PacketRGBO(image->colormap[i]);
8363 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8366 else if (image->depth > 4)
8368 #if MAGICKCORE_QUANTUM_DEPTH > 8
8369 /* Scale to 8-bit */
8370 LBR08PacketRGBO(image->background_color);
8372 for (y=0; y < (ssize_t) image->rows; y++)
8374 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8376 if (r == (Quantum *) NULL)
8379 for (x=0; x < (ssize_t) image->columns; x++)
8382 r+=GetPixelChannels(image);
8385 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8389 if (image->storage_class == PseudoClass && image->colormap != NULL)
8391 for (i=0; i < (ssize_t) image->colors; i++)
8393 LBR08PacketRGBO(image->colormap[i]);
8396 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8399 if (image->depth > 2)
8401 /* Scale to 4-bit */
8402 LBR04PacketRGBO(image->background_color);
8404 for (y=0; y < (ssize_t) image->rows; y++)
8406 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8408 if (r == (Quantum *) NULL)
8411 for (x=0; x < (ssize_t) image->columns; x++)
8414 r+=GetPixelChannels(image);
8417 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8421 if (image->storage_class == PseudoClass && image->colormap != NULL)
8423 for (i=0; i < (ssize_t) image->colors; i++)
8425 LBR04PacketRGBO(image->colormap[i]);
8430 else if (image->depth > 1)
8432 /* Scale to 2-bit */
8433 LBR02PacketRGBO(image->background_color);
8435 for (y=0; y < (ssize_t) image->rows; y++)
8437 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8439 if (r == (Quantum *) NULL)
8442 for (x=0; x < (ssize_t) image->columns; x++)
8445 r+=GetPixelChannels(image);
8448 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8452 if (image->storage_class == PseudoClass && image->colormap != NULL)
8454 for (i=0; i < (ssize_t) image->colors; i++)
8456 LBR02PacketRGBO(image->colormap[i]);
8462 /* Scale to 1-bit */
8463 LBR01PacketRGBO(image->background_color);
8465 for (y=0; y < (ssize_t) image->rows; y++)
8467 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8469 if (r == (Quantum *) NULL)
8472 for (x=0; x < (ssize_t) image->columns; x++)
8475 r+=GetPixelChannels(image);
8478 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8482 if (image->storage_class == PseudoClass && image->colormap != NULL)
8484 for (i=0; i < (ssize_t) image->colors; i++)
8486 LBR01PacketRGBO(image->colormap[i]);
8492 /* To do: set to next higher multiple of 8 */
8493 if (image->depth < 8)
8496 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8497 /* PNG does not handle depths greater than 16 so reduce it even
8500 if (image->depth > 8)
8504 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8505 if (image->depth > 8)
8507 /* To do: fill low byte properly */
8511 if (image->depth == 16 && mng_info->write_png_depth != 16)
8512 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8516 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8517 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8518 mng_info->write_png_colortype < 4 &&
8519 image->alpha_trait != BlendPixelTrait)))
8521 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8522 * are not going to need the result.
8524 image_colors = (int) image->colors;
8525 number_opaque = (int) image->colors;
8526 if (mng_info->write_png_colortype == 1 ||
8527 mng_info->write_png_colortype == 5)
8528 ping_have_color=MagickFalse;
8530 ping_have_color=MagickTrue;
8531 ping_have_non_bw=MagickFalse;
8533 if (image->alpha_trait == BlendPixelTrait)
8535 number_transparent = 2;
8536 number_semitransparent = 1;
8541 number_transparent = 0;
8542 number_semitransparent = 0;
8550 * Normally we run this just once, but in the case of writing PNG8
8551 * we reduce the transparency to binary and run again, then if there
8552 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8553 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8554 * palette. Then (To do) we take care of a final reduction that is only
8555 * needed if there are still 256 colors present and one of them has both
8556 * transparent and opaque instances.
8559 tried_332 = MagickFalse;
8560 tried_333 = MagickFalse;
8561 tried_444 = MagickFalse;
8566 * Sometimes we get DirectClass images that have 256 colors or fewer.
8567 * This code will build a colormap.
8569 * Also, sometimes we get PseudoClass images with an out-of-date
8570 * colormap. This code will replace the colormap with a new one.
8571 * Sometimes we get PseudoClass images that have more than 256 colors.
8572 * This code will delete the colormap and change the image to
8575 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8576 * even though it sometimes contains left-over non-opaque values.
8578 * Also we gather some information (number of opaque, transparent,
8579 * and semitransparent pixels, and whether the image has any non-gray
8580 * pixels or only black-and-white pixels) that we might need later.
8582 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8583 * we need to check for bogus non-opaque values, at least.
8591 semitransparent[260],
8594 register const Quantum
8601 if (logging != MagickFalse)
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " Enter BUILD_PALETTE:");
8605 if (logging != MagickFalse)
8607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608 " image->columns=%.20g",(double) image->columns);
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " image->rows=%.20g",(double) image->rows);
8611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8612 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8614 " image->depth=%.20g",(double) image->depth);
8616 if (image->storage_class == PseudoClass && image->colormap != NULL)
8618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8619 " Original colormap:");
8620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621 " i (red,green,blue,alpha)");
8623 for (i=0; i < 256; i++)
8625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626 " %d (%d,%d,%d,%d)",
8628 (int) image->colormap[i].red,
8629 (int) image->colormap[i].green,
8630 (int) image->colormap[i].blue,
8631 (int) image->colormap[i].alpha);
8634 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " %d (%d,%d,%d,%d)",
8641 (int) image->colormap[i].red,
8642 (int) image->colormap[i].green,
8643 (int) image->colormap[i].blue,
8644 (int) image->colormap[i].alpha);
8649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8650 " image->colors=%d",(int) image->colors);
8652 if (image->colors == 0)
8653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8654 " (zero means unknown)");
8656 if (ping_preserve_colormap == MagickFalse)
8657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8658 " Regenerate the colormap");
8663 number_semitransparent = 0;
8664 number_transparent = 0;
8666 for (y=0; y < (ssize_t) image->rows; y++)
8668 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8670 if (q == (Quantum *) NULL)
8673 for (x=0; x < (ssize_t) image->columns; x++)
8675 if (image->alpha_trait != BlendPixelTrait ||
8676 GetPixelAlpha(image,q) == OpaqueAlpha)
8678 if (number_opaque < 259)
8680 if (number_opaque == 0)
8682 GetPixelInfoPixel(image, q, opaque);
8683 opaque[0].alpha=OpaqueAlpha;
8687 for (i=0; i< (ssize_t) number_opaque; i++)
8689 if (IsPixelEquivalent(image,q, opaque+i))
8693 if (i == (ssize_t) number_opaque && number_opaque < 259)
8696 GetPixelInfoPixel(image, q, opaque+i);
8697 opaque[i].alpha=OpaqueAlpha;
8701 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8703 if (number_transparent < 259)
8705 if (number_transparent == 0)
8707 GetPixelInfoPixel(image, q, transparent);
8708 ping_trans_color.red=(unsigned short)
8709 GetPixelRed(image,q);
8710 ping_trans_color.green=(unsigned short)
8711 GetPixelGreen(image,q);
8712 ping_trans_color.blue=(unsigned short)
8713 GetPixelBlue(image,q);
8714 ping_trans_color.gray=(unsigned short)
8715 GetPixelGray(image,q);
8716 number_transparent = 1;
8719 for (i=0; i< (ssize_t) number_transparent; i++)
8721 if (IsPixelEquivalent(image,q, transparent+i))
8725 if (i == (ssize_t) number_transparent &&
8726 number_transparent < 259)
8728 number_transparent++;
8729 GetPixelInfoPixel(image,q,transparent+i);
8735 if (number_semitransparent < 259)
8737 if (number_semitransparent == 0)
8739 GetPixelInfoPixel(image,q,semitransparent);
8740 number_semitransparent = 1;
8743 for (i=0; i< (ssize_t) number_semitransparent; i++)
8745 if (IsPixelEquivalent(image,q, semitransparent+i)
8746 && GetPixelAlpha(image,q) ==
8747 semitransparent[i].alpha)
8751 if (i == (ssize_t) number_semitransparent &&
8752 number_semitransparent < 259)
8754 number_semitransparent++;
8755 GetPixelInfoPixel(image, q, semitransparent+i);
8759 q+=GetPixelChannels(image);
8763 if (mng_info->write_png8 == MagickFalse &&
8764 ping_exclude_bKGD == MagickFalse)
8766 /* Add the background color to the palette, if it
8767 * isn't already there.
8769 if (logging != MagickFalse)
8771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8772 " Check colormap for background (%d,%d,%d)",
8773 (int) image->background_color.red,
8774 (int) image->background_color.green,
8775 (int) image->background_color.blue);
8777 for (i=0; i<number_opaque; i++)
8779 if (opaque[i].red == image->background_color.red &&
8780 opaque[i].green == image->background_color.green &&
8781 opaque[i].blue == image->background_color.blue)
8784 if (number_opaque < 259 && i == number_opaque)
8786 opaque[i] = image->background_color;
8787 ping_background.index = i;
8789 if (logging != MagickFalse)
8791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8792 " background_color index is %d",(int) i);
8796 else if (logging != MagickFalse)
8797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798 " No room in the colormap to add background color");
8801 image_colors=number_opaque+number_transparent+number_semitransparent;
8803 if (logging != MagickFalse)
8805 if (image_colors > 256)
8806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807 " image has more than 256 colors");
8810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8811 " image has %d colors",image_colors);
8814 if (ping_preserve_colormap != MagickFalse)
8817 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8819 ping_have_color=MagickFalse;
8820 ping_have_non_bw=MagickFalse;
8822 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8825 "incompatible colorspace");
8826 ping_have_color=MagickTrue;
8827 ping_have_non_bw=MagickTrue;
8830 if(image_colors > 256)
8832 for (y=0; y < (ssize_t) image->rows; y++)
8834 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8836 if (q == (Quantum *) NULL)
8840 for (x=0; x < (ssize_t) image->columns; x++)
8842 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8843 GetPixelRed(image,s) != GetPixelBlue(image,s))
8845 ping_have_color=MagickTrue;
8846 ping_have_non_bw=MagickTrue;
8849 s+=GetPixelChannels(image);
8852 if (ping_have_color != MagickFalse)
8855 /* Worst case is black-and-white; we are looking at every
8859 if (ping_have_non_bw == MagickFalse)
8862 for (x=0; x < (ssize_t) image->columns; x++)
8864 if (GetPixelRed(image,s) != 0 &&
8865 GetPixelRed(image,s) != QuantumRange)
8867 ping_have_non_bw=MagickTrue;
8870 s+=GetPixelChannels(image);
8877 if (image_colors < 257)
8883 * Initialize image colormap.
8886 if (logging != MagickFalse)
8887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888 " Sort the new colormap");
8890 /* Sort palette, transparent first */;
8894 for (i=0; i<number_transparent; i++)
8895 colormap[n++] = transparent[i];
8897 for (i=0; i<number_semitransparent; i++)
8898 colormap[n++] = semitransparent[i];
8900 for (i=0; i<number_opaque; i++)
8901 colormap[n++] = opaque[i];
8903 ping_background.index +=
8904 (number_transparent + number_semitransparent);
8906 /* image_colors < 257; search the colormap instead of the pixels
8907 * to get ping_have_color and ping_have_non_bw
8911 if (ping_have_color == MagickFalse)
8913 if (colormap[i].red != colormap[i].green ||
8914 colormap[i].red != colormap[i].blue)
8916 ping_have_color=MagickTrue;
8917 ping_have_non_bw=MagickTrue;
8922 if (ping_have_non_bw == MagickFalse)
8924 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8925 ping_have_non_bw=MagickTrue;
8929 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8930 (number_transparent == 0 && number_semitransparent == 0)) &&
8931 (((mng_info->write_png_colortype-1) ==
8932 PNG_COLOR_TYPE_PALETTE) ||
8933 (mng_info->write_png_colortype == 0)))
8935 if (logging != MagickFalse)
8937 if (n != (ssize_t) image_colors)
8938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8939 " image_colors (%d) and n (%d) don't match",
8942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8943 " AcquireImageColormap");
8946 image->colors = image_colors;
8948 if (AcquireImageColormap(image,image_colors,exception) ==
8950 ThrowWriterException(ResourceLimitError,
8951 "MemoryAllocationFailed");
8953 for (i=0; i< (ssize_t) image_colors; i++)
8954 image->colormap[i] = colormap[i];
8956 if (logging != MagickFalse)
8958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959 " image->colors=%d (%d)",
8960 (int) image->colors, image_colors);
8962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963 " Update the pixel indexes");
8966 /* Sync the pixel indices with the new colormap */
8968 for (y=0; y < (ssize_t) image->rows; y++)
8970 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8972 if (q == (Quantum *) NULL)
8975 for (x=0; x < (ssize_t) image->columns; x++)
8977 for (i=0; i< (ssize_t) image_colors; i++)
8979 if ((image->alpha_trait != BlendPixelTrait ||
8980 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8981 image->colormap[i].red == GetPixelRed(image,q) &&
8982 image->colormap[i].green == GetPixelGreen(image,q) &&
8983 image->colormap[i].blue == GetPixelBlue(image,q))
8985 SetPixelIndex(image,i,q);
8989 q+=GetPixelChannels(image);
8992 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8998 if (logging != MagickFalse)
9000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001 " image->colors=%d", (int) image->colors);
9003 if (image->colormap != NULL)
9005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006 " i (red,green,blue,alpha)");
9008 for (i=0; i < (ssize_t) image->colors; i++)
9010 if (i < 300 || i >= (ssize_t) image->colors - 10)
9012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013 " %d (%d,%d,%d,%d)",
9015 (int) image->colormap[i].red,
9016 (int) image->colormap[i].green,
9017 (int) image->colormap[i].blue,
9018 (int) image->colormap[i].alpha);
9023 if (number_transparent < 257)
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " number_transparent = %d",
9026 number_transparent);
9029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030 " number_transparent > 256");
9032 if (number_opaque < 257)
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " number_opaque = %d",
9038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9039 " number_opaque > 256");
9041 if (number_semitransparent < 257)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " number_semitransparent = %d",
9044 number_semitransparent);
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048 " number_semitransparent > 256");
9050 if (ping_have_non_bw == MagickFalse)
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " All pixels and the background are black or white");
9054 else if (ping_have_color == MagickFalse)
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " All pixels and the background are gray");
9059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " At least one pixel or the background is non-gray");
9062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063 " Exit BUILD_PALETTE:");
9066 if (mng_info->write_png8 == MagickFalse)
9069 /* Make any reductions necessary for the PNG8 format */
9070 if (image_colors <= 256 &&
9071 image_colors != 0 && image->colormap != NULL &&
9072 number_semitransparent == 0 &&
9073 number_transparent <= 1)
9076 /* PNG8 can't have semitransparent colors so we threshold the
9077 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9078 * transparent color so if more than one is transparent we merge
9079 * them into image->background_color.
9081 if (number_semitransparent != 0 || number_transparent > 1)
9083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084 " Thresholding the alpha channel to binary");
9086 for (y=0; y < (ssize_t) image->rows; y++)
9088 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9090 if (r == (Quantum *) NULL)
9093 for (x=0; x < (ssize_t) image->columns; x++)
9095 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9097 SetPixelInfoPixel(image,&image->background_color,r);
9098 SetPixelAlpha(image,TransparentAlpha,r);
9101 SetPixelAlpha(image,OpaqueAlpha,r);
9102 r+=GetPixelChannels(image);
9105 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9108 if (image_colors != 0 && image_colors <= 256 &&
9109 image->colormap != NULL)
9110 for (i=0; i<image_colors; i++)
9111 image->colormap[i].alpha =
9112 (image->colormap[i].alpha > TransparentAlpha/2 ?
9113 TransparentAlpha : OpaqueAlpha);
9118 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9119 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9120 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9123 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9125 if (logging != MagickFalse)
9126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127 " Quantizing the background color to 4-4-4");
9129 tried_444 = MagickTrue;
9131 LBR04PacketRGB(image->background_color);
9133 if (logging != MagickFalse)
9134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135 " Quantizing the pixel colors to 4-4-4");
9137 if (image->colormap == NULL)
9139 for (y=0; y < (ssize_t) image->rows; y++)
9141 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9143 if (r == (Quantum *) NULL)
9146 for (x=0; x < (ssize_t) image->columns; x++)
9148 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9150 r+=GetPixelChannels(image);
9153 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9158 else /* Should not reach this; colormap already exists and
9161 if (logging != MagickFalse)
9162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9163 " Quantizing the colormap to 4-4-4");
9165 for (i=0; i<image_colors; i++)
9167 LBR04PacketRGB(image->colormap[i]);
9173 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9175 if (logging != MagickFalse)
9176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 " Quantizing the background color to 3-3-3");
9179 tried_333 = MagickTrue;
9181 LBR03PacketRGB(image->background_color);
9183 if (logging != MagickFalse)
9184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9185 " Quantizing the pixel colors to 3-3-3-1");
9187 if (image->colormap == NULL)
9189 for (y=0; y < (ssize_t) image->rows; y++)
9191 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9193 if (r == (Quantum *) NULL)
9196 for (x=0; x < (ssize_t) image->columns; x++)
9198 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9200 r+=GetPixelChannels(image);
9203 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9208 else /* Should not reach this; colormap already exists and
9211 if (logging != MagickFalse)
9212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213 " Quantizing the colormap to 3-3-3-1");
9214 for (i=0; i<image_colors; i++)
9216 LBR03PacketRGB(image->colormap[i]);
9222 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9224 if (logging != MagickFalse)
9225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9226 " Quantizing the background color to 3-3-2");
9228 tried_332 = MagickTrue;
9230 /* Red and green were already done so we only quantize the blue
9234 LBR02PacketBlue(image->background_color);
9236 if (logging != MagickFalse)
9237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238 " Quantizing the pixel colors to 3-3-2-1");
9240 if (image->colormap == NULL)
9242 for (y=0; y < (ssize_t) image->rows; y++)
9244 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9246 if (r == (Quantum *) NULL)
9249 for (x=0; x < (ssize_t) image->columns; x++)
9251 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9253 r+=GetPixelChannels(image);
9256 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9261 else /* Should not reach this; colormap already exists and
9264 if (logging != MagickFalse)
9265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9266 " Quantizing the colormap to 3-3-2-1");
9267 for (i=0; i<image_colors; i++)
9269 LBR02PacketBlue(image->colormap[i]);
9275 if (image_colors == 0 || image_colors > 256)
9277 /* Take care of special case with 256 opaque colors + 1 transparent
9278 * color. We don't need to quantize to 2-3-2-1; we only need to
9279 * eliminate one color, so we'll merge the two darkest red
9280 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9282 if (logging != MagickFalse)
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " Merging two dark red background colors to 3-3-2-1");
9286 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9287 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9288 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9290 image->background_color.red=ScaleCharToQuantum(0x24);
9293 if (logging != MagickFalse)
9294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295 " Merging two dark red pixel colors to 3-3-2-1");
9297 if (image->colormap == NULL)
9299 for (y=0; y < (ssize_t) image->rows; y++)
9301 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9303 if (r == (Quantum *) NULL)
9306 for (x=0; x < (ssize_t) image->columns; x++)
9308 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9309 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9310 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9311 GetPixelAlpha(image,r) == OpaqueAlpha)
9313 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9315 r+=GetPixelChannels(image);
9318 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9326 for (i=0; i<image_colors; i++)
9328 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9329 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9330 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9332 image->colormap[i].red=ScaleCharToQuantum(0x24);
9339 /* END OF BUILD_PALETTE */
9341 /* If we are excluding the tRNS chunk and there is transparency,
9342 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9345 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9346 (number_transparent != 0 || number_semitransparent != 0))
9348 unsigned int colortype=mng_info->write_png_colortype;
9350 if (ping_have_color == MagickFalse)
9351 mng_info->write_png_colortype = 5;
9354 mng_info->write_png_colortype = 7;
9356 if (colortype != 0 &&
9357 mng_info->write_png_colortype != colortype)
9358 ping_need_colortype_warning=MagickTrue;
9362 /* See if cheap transparency is possible. It is only possible
9363 * when there is a single transparent color, no semitransparent
9364 * color, and no opaque color that has the same RGB components
9365 * as the transparent color. We only need this information if
9366 * we are writing a PNG with colortype 0 or 2, and we have not
9367 * excluded the tRNS chunk.
9369 if (number_transparent == 1 &&
9370 mng_info->write_png_colortype < 4)
9372 ping_have_cheap_transparency = MagickTrue;
9374 if (number_semitransparent != 0)
9375 ping_have_cheap_transparency = MagickFalse;
9377 else if (image_colors == 0 || image_colors > 256 ||
9378 image->colormap == NULL)
9380 register const Quantum
9383 for (y=0; y < (ssize_t) image->rows; y++)
9385 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9387 if (q == (Quantum *) NULL)
9390 for (x=0; x < (ssize_t) image->columns; x++)
9392 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9393 (unsigned short) GetPixelRed(image,q) ==
9394 ping_trans_color.red &&
9395 (unsigned short) GetPixelGreen(image,q) ==
9396 ping_trans_color.green &&
9397 (unsigned short) GetPixelBlue(image,q) ==
9398 ping_trans_color.blue)
9400 ping_have_cheap_transparency = MagickFalse;
9404 q+=GetPixelChannels(image);
9407 if (ping_have_cheap_transparency == MagickFalse)
9413 /* Assuming that image->colormap[0] is the one transparent color
9414 * and that all others are opaque.
9416 if (image_colors > 1)
9417 for (i=1; i<image_colors; i++)
9418 if (image->colormap[i].red == image->colormap[0].red &&
9419 image->colormap[i].green == image->colormap[0].green &&
9420 image->colormap[i].blue == image->colormap[0].blue)
9422 ping_have_cheap_transparency = MagickFalse;
9427 if (logging != MagickFalse)
9429 if (ping_have_cheap_transparency == MagickFalse)
9430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9431 " Cheap transparency is not possible.");
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435 " Cheap transparency is possible.");
9439 ping_have_cheap_transparency = MagickFalse;
9441 image_depth=image->depth;
9443 quantum_info = (QuantumInfo *) NULL;
9445 image_colors=(int) image->colors;
9446 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9448 mng_info->IsPalette=image->storage_class == PseudoClass &&
9449 image_colors <= 256 && image->colormap != NULL;
9451 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9452 (image->colors == 0 || image->colormap == NULL))
9454 image_info=DestroyImageInfo(image_info);
9455 image=DestroyImage(image);
9456 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9457 "Cannot write PNG8 or color-type 3; colormap is NULL",
9458 "`%s'",IMimage->filename);
9459 return(MagickFalse);
9463 Allocate the PNG structures
9465 #ifdef PNG_USER_MEM_SUPPORTED
9466 error_info.image=image;
9467 error_info.exception=exception;
9468 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9469 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9470 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9473 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9474 MagickPNGErrorHandler,MagickPNGWarningHandler);
9477 if (ping == (png_struct *) NULL)
9478 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9480 ping_info=png_create_info_struct(ping);
9482 if (ping_info == (png_info *) NULL)
9484 png_destroy_write_struct(&ping,(png_info **) NULL);
9485 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9488 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9489 pixel_info=(MemoryInfo *) NULL;
9491 if (setjmp(png_jmpbuf(ping)))
9497 if (image_info->verbose)
9498 (void) printf("PNG write has failed.\n");
9500 png_destroy_write_struct(&ping,&ping_info);
9501 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9502 UnlockSemaphoreInfo(ping_semaphore);
9505 if (pixel_info != (MemoryInfo *) NULL)
9506 pixel_info=RelinquishVirtualMemory(pixel_info);
9508 if (quantum_info != (QuantumInfo *) NULL)
9509 quantum_info=DestroyQuantumInfo(quantum_info);
9511 if (ping_have_blob != MagickFalse)
9512 (void) CloseBlob(image);
9513 image_info=DestroyImageInfo(image_info);
9514 image=DestroyImage(image);
9515 return(MagickFalse);
9518 /* { For navigation to end of SETJMP-protected block. Within this
9519 * block, use png_error() instead of Throwing an Exception, to ensure
9520 * that libpng is able to clean up, and that the semaphore is unlocked.
9523 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9524 LockSemaphoreInfo(ping_semaphore);
9527 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9528 /* Allow benign errors */
9529 png_set_benign_errors(ping, 1);
9533 Prepare PNG for writing.
9536 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9537 if (mng_info->write_mng)
9539 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9540 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9541 /* Disable new libpng-1.5.10 feature when writing a MNG because
9542 * zero-length PLTE is OK
9544 png_set_check_for_invalid_index (ping, 0);
9549 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9550 if (mng_info->write_mng)
9551 png_permit_empty_plte(ping,MagickTrue);
9558 ping_width=(png_uint_32) image->columns;
9559 ping_height=(png_uint_32) image->rows;
9561 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9564 if (mng_info->write_png48 || mng_info->write_png64)
9567 if (mng_info->write_png_depth != 0)
9568 image_depth=mng_info->write_png_depth;
9570 /* Adjust requested depth to next higher valid depth if necessary */
9571 if (image_depth > 8)
9574 if ((image_depth > 4) && (image_depth < 8))
9577 if (image_depth == 3)
9580 if (logging != MagickFalse)
9582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9583 " width=%.20g",(double) ping_width);
9584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9585 " height=%.20g",(double) ping_height);
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " image_matte=%.20g",(double) image->alpha_trait);
9588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589 " image->depth=%.20g",(double) image->depth);
9590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9591 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9594 save_image_depth=image_depth;
9595 ping_bit_depth=(png_byte) save_image_depth;
9598 #if defined(PNG_pHYs_SUPPORTED)
9599 if (ping_exclude_pHYs == MagickFalse)
9601 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9602 (!mng_info->write_mng || !mng_info->equal_physs))
9604 if (logging != MagickFalse)
9605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606 " Setting up pHYs chunk");
9608 if (image->units == PixelsPerInchResolution)
9610 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9611 ping_pHYs_x_resolution=
9612 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9613 ping_pHYs_y_resolution=
9614 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9617 else if (image->units == PixelsPerCentimeterResolution)
9619 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9620 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9621 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9626 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9627 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9628 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9631 if (logging != MagickFalse)
9632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9633 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9634 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9635 (int) ping_pHYs_unit_type);
9636 ping_have_pHYs = MagickTrue;
9641 if (ping_exclude_bKGD == MagickFalse)
9643 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9649 if (ping_bit_depth == 8)
9652 if (ping_bit_depth == 4)
9655 if (ping_bit_depth == 2)
9658 if (ping_bit_depth == 1)
9661 ping_background.red=(png_uint_16)
9662 (ScaleQuantumToShort(image->background_color.red) & mask);
9664 ping_background.green=(png_uint_16)
9665 (ScaleQuantumToShort(image->background_color.green) & mask);
9667 ping_background.blue=(png_uint_16)
9668 (ScaleQuantumToShort(image->background_color.blue) & mask);
9670 ping_background.gray=(png_uint_16) ping_background.green;
9673 if (logging != MagickFalse)
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676 " Setting up bKGD chunk (1)");
9677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9678 " background_color index is %d",
9679 (int) ping_background.index);
9681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9682 " ping_bit_depth=%d",ping_bit_depth);
9685 ping_have_bKGD = MagickTrue;
9689 Select the color type.
9694 if (mng_info->IsPalette && mng_info->write_png8)
9696 /* To do: make this a function cause it's used twice, except
9697 for reducing the sample depth from 8. */
9699 number_colors=image_colors;
9701 ping_have_tRNS=MagickFalse;
9706 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9708 if (logging != MagickFalse)
9709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9710 " Setting up PLTE chunk with %d colors (%d)",
9711 number_colors, image_colors);
9713 for (i=0; i < (ssize_t) number_colors; i++)
9715 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9716 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9717 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9718 if (logging != MagickFalse)
9719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720 #if MAGICKCORE_QUANTUM_DEPTH == 8
9721 " %3ld (%3d,%3d,%3d)",
9723 " %5ld (%5d,%5d,%5d)",
9725 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9729 ping_have_PLTE=MagickTrue;
9730 image_depth=ping_bit_depth;
9733 if (matte != MagickFalse)
9736 Identify which colormap entry is transparent.
9738 assert(number_colors <= 256);
9739 assert(image->colormap != NULL);
9741 for (i=0; i < (ssize_t) number_transparent; i++)
9742 ping_trans_alpha[i]=0;
9745 ping_num_trans=(unsigned short) (number_transparent +
9746 number_semitransparent);
9748 if (ping_num_trans == 0)
9749 ping_have_tRNS=MagickFalse;
9752 ping_have_tRNS=MagickTrue;
9755 if (ping_exclude_bKGD == MagickFalse)
9758 * Identify which colormap entry is the background color.
9761 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9762 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9765 ping_background.index=(png_byte) i;
9767 if (logging != MagickFalse)
9769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9770 " background_color index is %d",
9771 (int) ping_background.index);
9774 } /* end of write_png8 */
9776 else if (mng_info->write_png_colortype == 1)
9778 image_matte=MagickFalse;
9779 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9782 else if (mng_info->write_png24 || mng_info->write_png48 ||
9783 mng_info->write_png_colortype == 3)
9785 image_matte=MagickFalse;
9786 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9789 else if (mng_info->write_png32 || mng_info->write_png64 ||
9790 mng_info->write_png_colortype == 7)
9792 image_matte=MagickTrue;
9793 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9796 else /* mng_info->write_pngNN not specified */
9798 image_depth=ping_bit_depth;
9800 if (mng_info->write_png_colortype != 0)
9802 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9804 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9805 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9806 image_matte=MagickTrue;
9809 image_matte=MagickFalse;
9811 if (logging != MagickFalse)
9812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813 " PNG colortype %d was specified:",(int) ping_color_type);
9816 else /* write_png_colortype not specified */
9818 if (logging != MagickFalse)
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820 " Selecting PNG colortype:");
9822 ping_color_type=(png_byte) ((matte != MagickFalse)?
9823 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9825 if (image_info->type == TrueColorType)
9827 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9828 image_matte=MagickFalse;
9831 if (image_info->type == TrueColorMatteType)
9833 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9834 image_matte=MagickTrue;
9837 if (image_info->type == PaletteType ||
9838 image_info->type == PaletteMatteType)
9839 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9841 if (mng_info->write_png_colortype == 0 &&
9842 (image_info->type == UndefinedType ||
9843 image_info->type == OptimizeType))
9845 if (ping_have_color == MagickFalse)
9847 if (image_matte == MagickFalse)
9849 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9850 image_matte=MagickFalse;
9855 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9856 image_matte=MagickTrue;
9861 if (image_matte == MagickFalse)
9863 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9864 image_matte=MagickFalse;
9869 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9870 image_matte=MagickTrue;
9877 if (logging != MagickFalse)
9878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9879 " Selected PNG colortype=%d",ping_color_type);
9881 if (ping_bit_depth < 8)
9883 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9884 ping_color_type == PNG_COLOR_TYPE_RGB ||
9885 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9889 old_bit_depth=ping_bit_depth;
9891 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9893 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9897 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9902 if (image->colors == 0)
9905 png_error(ping,"image has 0 colors");
9908 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9909 ping_bit_depth <<= 1;
9912 if (logging != MagickFalse)
9914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9915 " Number of colors: %.20g",(double) image_colors);
9917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9918 " Tentative PNG bit depth: %d",ping_bit_depth);
9921 if (ping_bit_depth < (int) mng_info->write_png_depth)
9922 ping_bit_depth = mng_info->write_png_depth;
9925 image_depth=ping_bit_depth;
9927 if (logging != MagickFalse)
9929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9930 " Tentative PNG color type: %s (%.20g)",
9931 PngColorTypeToString(ping_color_type),
9932 (double) ping_color_type);
9934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9935 " image_info->type: %.20g",(double) image_info->type);
9937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9938 " image_depth: %.20g",(double) image_depth);
9940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " image->depth: %.20g",(double) image->depth);
9944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9945 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9948 if (matte != MagickFalse)
9950 if (mng_info->IsPalette)
9952 if (mng_info->write_png_colortype == 0)
9954 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9956 if (ping_have_color != MagickFalse)
9957 ping_color_type=PNG_COLOR_TYPE_RGBA;
9961 * Determine if there is any transparent color.
9963 if (number_transparent + number_semitransparent == 0)
9966 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9969 image_matte=MagickFalse;
9971 if (mng_info->write_png_colortype == 0)
9972 ping_color_type&=0x03;
9982 if (ping_bit_depth == 8)
9985 if (ping_bit_depth == 4)
9988 if (ping_bit_depth == 2)
9991 if (ping_bit_depth == 1)
9994 ping_trans_color.red=(png_uint_16)
9995 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9997 ping_trans_color.green=(png_uint_16)
9998 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10000 ping_trans_color.blue=(png_uint_16)
10001 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10003 ping_trans_color.gray=(png_uint_16)
10004 (ScaleQuantumToShort(GetPixelInfoIntensity(
10005 image->colormap)) & mask);
10007 ping_trans_color.index=(png_byte) 0;
10009 ping_have_tRNS=MagickTrue;
10012 if (ping_have_tRNS != MagickFalse)
10015 * Determine if there is one and only one transparent color
10016 * and if so if it is fully transparent.
10018 if (ping_have_cheap_transparency == MagickFalse)
10019 ping_have_tRNS=MagickFalse;
10022 if (ping_have_tRNS != MagickFalse)
10024 if (mng_info->write_png_colortype == 0)
10025 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10027 if (image_depth == 8)
10029 ping_trans_color.red&=0xff;
10030 ping_trans_color.green&=0xff;
10031 ping_trans_color.blue&=0xff;
10032 ping_trans_color.gray&=0xff;
10038 if (image_depth == 8)
10040 ping_trans_color.red&=0xff;
10041 ping_trans_color.green&=0xff;
10042 ping_trans_color.blue&=0xff;
10043 ping_trans_color.gray&=0xff;
10050 if (ping_have_tRNS != MagickFalse)
10051 image_matte=MagickFalse;
10053 if ((mng_info->IsPalette) &&
10054 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10055 ping_have_color == MagickFalse &&
10056 (image_matte == MagickFalse || image_depth >= 8))
10060 if (image_matte != MagickFalse)
10061 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10063 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10065 ping_color_type=PNG_COLOR_TYPE_GRAY;
10067 if (save_image_depth == 16 && image_depth == 8)
10069 if (logging != MagickFalse)
10071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10072 " Scaling ping_trans_color (0)");
10074 ping_trans_color.gray*=0x0101;
10078 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10079 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10081 if ((image_colors == 0) ||
10082 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10083 image_colors=(int) (one << image_depth);
10085 if (image_depth > 8)
10091 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10093 if(!mng_info->write_png_depth)
10097 while ((int) (one << ping_bit_depth)
10098 < (ssize_t) image_colors)
10099 ping_bit_depth <<= 1;
10103 else if (ping_color_type ==
10104 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10105 mng_info->IsPalette)
10107 /* Check if grayscale is reducible */
10110 depth_4_ok=MagickTrue,
10111 depth_2_ok=MagickTrue,
10112 depth_1_ok=MagickTrue;
10114 for (i=0; i < (ssize_t) image_colors; i++)
10119 intensity=ScaleQuantumToChar(image->colormap[i].red);
10121 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10122 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10123 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10124 depth_2_ok=depth_1_ok=MagickFalse;
10125 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10126 depth_1_ok=MagickFalse;
10129 if (depth_1_ok && mng_info->write_png_depth <= 1)
10132 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10135 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10140 image_depth=ping_bit_depth;
10145 if (mng_info->IsPalette)
10147 number_colors=image_colors;
10149 if (image_depth <= 8)
10154 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10156 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10158 for (i=0; i < (ssize_t) number_colors; i++)
10160 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10161 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10162 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10165 if (logging != MagickFalse)
10166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10167 " Setting up PLTE chunk with %d colors",
10170 ping_have_PLTE=MagickTrue;
10173 /* color_type is PNG_COLOR_TYPE_PALETTE */
10174 if (mng_info->write_png_depth == 0)
10182 while ((one << ping_bit_depth) < (size_t) number_colors)
10183 ping_bit_depth <<= 1;
10188 if (matte != MagickFalse)
10191 * Set up trans_colors array.
10193 assert(number_colors <= 256);
10195 ping_num_trans=(unsigned short) (number_transparent +
10196 number_semitransparent);
10198 if (ping_num_trans == 0)
10199 ping_have_tRNS=MagickFalse;
10203 if (logging != MagickFalse)
10205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10206 " Scaling ping_trans_color (1)");
10208 ping_have_tRNS=MagickTrue;
10210 for (i=0; i < ping_num_trans; i++)
10212 ping_trans_alpha[i]= (png_byte)
10213 ScaleQuantumToChar(image->colormap[i].alpha);
10223 if (image_depth < 8)
10226 if ((save_image_depth == 16) && (image_depth == 8))
10228 if (logging != MagickFalse)
10230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10231 " Scaling ping_trans_color from (%d,%d,%d)",
10232 (int) ping_trans_color.red,
10233 (int) ping_trans_color.green,
10234 (int) ping_trans_color.blue);
10237 ping_trans_color.red*=0x0101;
10238 ping_trans_color.green*=0x0101;
10239 ping_trans_color.blue*=0x0101;
10240 ping_trans_color.gray*=0x0101;
10242 if (logging != MagickFalse)
10244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10246 (int) ping_trans_color.red,
10247 (int) ping_trans_color.green,
10248 (int) ping_trans_color.blue);
10253 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10254 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10257 Adjust background and transparency samples in sub-8-bit grayscale files.
10259 if (ping_bit_depth < 8 && ping_color_type ==
10260 PNG_COLOR_TYPE_GRAY)
10268 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10270 if (ping_exclude_bKGD == MagickFalse)
10273 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10274 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10275 &image->background_color))) +.5)));
10277 if (logging != MagickFalse)
10278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10279 " Setting up bKGD chunk (2)");
10280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10281 " background_color index is %d",
10282 (int) ping_background.index);
10284 ping_have_bKGD = MagickTrue;
10287 if (logging != MagickFalse)
10288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10289 " Scaling ping_trans_color.gray from %d",
10290 (int)ping_trans_color.gray);
10292 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10293 ping_trans_color.gray)+.5);
10295 if (logging != MagickFalse)
10296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10297 " to %d", (int)ping_trans_color.gray);
10300 if (ping_exclude_bKGD == MagickFalse)
10302 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10305 Identify which colormap entry is the background color.
10308 number_colors=image_colors;
10310 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10311 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10314 ping_background.index=(png_byte) i;
10316 if (logging != MagickFalse)
10318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319 " Setting up bKGD chunk with index=%d",(int) i);
10322 if (i < (ssize_t) number_colors)
10324 ping_have_bKGD = MagickTrue;
10326 if (logging != MagickFalse)
10328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329 " background =(%d,%d,%d)",
10330 (int) ping_background.red,
10331 (int) ping_background.green,
10332 (int) ping_background.blue);
10336 else /* Can't happen */
10338 if (logging != MagickFalse)
10339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340 " No room in PLTE to add bKGD color");
10341 ping_have_bKGD = MagickFalse;
10346 if (logging != MagickFalse)
10347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10351 Initialize compression level and filtering.
10353 if (logging != MagickFalse)
10355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10356 " Setting up deflate compression");
10358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10359 " Compression buffer size: 32768");
10362 png_set_compression_buffer_size(ping,32768L);
10364 if (logging != MagickFalse)
10365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10366 " Compression mem level: 9");
10368 png_set_compression_mem_level(ping, 9);
10370 /* Untangle the "-quality" setting:
10372 Undefined is 0; the default is used.
10377 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10378 zlib default compression level
10380 1-9: the zlib compression level
10384 0-4: the PNG filter method
10386 5: libpng adaptive filtering if compression level > 5
10387 libpng filter type "none" if compression level <= 5
10388 or if image is grayscale or palette
10390 6: libpng adaptive filtering
10392 7: "LOCO" filtering (intrapixel differing) if writing
10393 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10394 and earlier because of a missing "else".
10396 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10397 filtering. Unused prior to IM-6.7.0-10, was same as 6
10399 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10400 Unused prior to IM-6.7.0-10, was same as 6
10402 Note that using the -quality option, not all combinations of
10403 PNG filter type, zlib compression level, and zlib compression
10404 strategy are possible. This will be addressed soon in a
10405 release that accomodates "-define png:compression-strategy", etc.
10409 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10410 image_info->quality;
10414 if (mng_info->write_png_compression_strategy == 0)
10415 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10418 else if (mng_info->write_png_compression_level == 0)
10423 level=(int) MagickMin((ssize_t) quality/10,9);
10425 mng_info->write_png_compression_level = level+1;
10428 if (mng_info->write_png_compression_strategy == 0)
10430 if ((quality %10) == 8 || (quality %10) == 9)
10431 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10432 mng_info->write_png_compression_strategy=Z_RLE+1;
10434 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10438 if (mng_info->write_png_compression_filter == 0)
10439 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10441 if (logging != MagickFalse)
10443 if (mng_info->write_png_compression_level)
10444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10445 " Compression level: %d",
10446 (int) mng_info->write_png_compression_level-1);
10448 if (mng_info->write_png_compression_strategy)
10449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10450 " Compression strategy: %d",
10451 (int) mng_info->write_png_compression_strategy-1);
10453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10454 " Setting up filtering");
10456 if (mng_info->write_png_compression_filter == 6)
10457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10458 " Base filter method: ADAPTIVE");
10459 else if (mng_info->write_png_compression_filter == 0 ||
10460 mng_info->write_png_compression_filter == 1)
10461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462 " Base filter method: NONE");
10464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10465 " Base filter method: %d",
10466 (int) mng_info->write_png_compression_filter-1);
10469 if (mng_info->write_png_compression_level != 0)
10470 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10472 if (mng_info->write_png_compression_filter == 6)
10474 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10475 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10477 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10479 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10481 else if (mng_info->write_png_compression_filter == 7 ||
10482 mng_info->write_png_compression_filter == 10)
10483 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10485 else if (mng_info->write_png_compression_filter == 8)
10487 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10488 if (mng_info->write_mng)
10490 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10491 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10492 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10495 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10498 else if (mng_info->write_png_compression_filter == 9)
10499 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10501 else if (mng_info->write_png_compression_filter != 0)
10502 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10503 mng_info->write_png_compression_filter-1);
10505 if (mng_info->write_png_compression_strategy != 0)
10506 png_set_compression_strategy(ping,
10507 mng_info->write_png_compression_strategy-1);
10509 ping_interlace_method=image_info->interlace != NoInterlace;
10511 if (mng_info->write_mng)
10512 png_set_sig_bytes(ping,8);
10514 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10516 if (mng_info->write_png_colortype != 0)
10518 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10519 if (ping_have_color != MagickFalse)
10521 ping_color_type = PNG_COLOR_TYPE_RGB;
10523 if (ping_bit_depth < 8)
10527 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10528 if (ping_have_color != MagickFalse)
10529 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10532 if (ping_need_colortype_warning != MagickFalse ||
10533 ((mng_info->write_png_depth &&
10534 (int) mng_info->write_png_depth != ping_bit_depth) ||
10535 (mng_info->write_png_colortype &&
10536 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10537 mng_info->write_png_colortype != 7 &&
10538 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10540 if (logging != MagickFalse)
10542 if (ping_need_colortype_warning != MagickFalse)
10544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10545 " Image has transparency but tRNS chunk was excluded");
10548 if (mng_info->write_png_depth)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " Defined png:bit-depth=%u, Computed depth=%u",
10552 mng_info->write_png_depth,
10556 if (mng_info->write_png_colortype)
10558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10559 " Defined png:color-type=%u, Computed color type=%u",
10560 mng_info->write_png_colortype-1,
10566 "Cannot write image with defined png:bit-depth or png:color-type.");
10569 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10571 /* Add an opaque matte channel */
10572 image->alpha_trait = BlendPixelTrait;
10573 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10575 if (logging != MagickFalse)
10576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10577 " Added an opaque matte channel");
10580 if (number_transparent != 0 || number_semitransparent != 0)
10582 if (ping_color_type < 4)
10584 ping_have_tRNS=MagickTrue;
10585 if (logging != MagickFalse)
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " Setting ping_have_tRNS=MagickTrue.");
10591 if (logging != MagickFalse)
10592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593 " Writing PNG header chunks");
10595 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10596 ping_bit_depth,ping_color_type,
10597 ping_interlace_method,ping_compression_method,
10598 ping_filter_method);
10600 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10602 png_set_PLTE(ping,ping_info,palette,number_colors);
10604 if (logging != MagickFalse)
10606 for (i=0; i< (ssize_t) number_colors; i++)
10608 if (i < ping_num_trans)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10612 (int) palette[i].red,
10613 (int) palette[i].green,
10614 (int) palette[i].blue,
10616 (int) ping_trans_alpha[i]);
10618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10619 " PLTE[%d] = (%d,%d,%d)",
10621 (int) palette[i].red,
10622 (int) palette[i].green,
10623 (int) palette[i].blue);
10628 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10629 if (ping_exclude_sRGB != MagickFalse ||
10630 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10632 if ((ping_exclude_tEXt == MagickFalse ||
10633 ping_exclude_zTXt == MagickFalse) &&
10634 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10636 ResetImageProfileIterator(image);
10637 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10639 profile=GetImageProfile(image,name);
10641 if (profile != (StringInfo *) NULL)
10643 #ifdef PNG_WRITE_iCCP_SUPPORTED
10644 if ((LocaleCompare(name,"ICC") == 0) ||
10645 (LocaleCompare(name,"ICM") == 0))
10648 if (ping_exclude_iCCP == MagickFalse)
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " Setting up iCCP chunk");
10653 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10654 #if (PNG_LIBPNG_VER < 10500)
10655 (png_charp) GetStringInfoDatum(profile),
10657 (png_const_bytep) GetStringInfoDatum(profile),
10659 (png_uint_32) GetStringInfoLength(profile));
10660 ping_have_iCCP = MagickTrue;
10666 if (ping_exclude_zCCP == MagickFalse)
10668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669 " Setting up zTXT chunk with uuencoded ICC");
10670 Magick_png_write_raw_profile(image_info,ping,ping_info,
10671 (unsigned char *) name,(unsigned char *) name,
10672 GetStringInfoDatum(profile),
10673 (png_uint_32) GetStringInfoLength(profile));
10674 ping_have_iCCP = MagickTrue;
10678 if (logging != MagickFalse)
10679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10680 " Setting up text chunk with %s profile",name);
10682 name=GetNextImageProfile(image);
10687 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10688 if ((mng_info->have_write_global_srgb == 0) &&
10689 ping_have_iCCP != MagickTrue &&
10690 (ping_have_sRGB != MagickFalse ||
10691 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10693 if (ping_exclude_sRGB == MagickFalse)
10696 Note image rendering intent.
10698 if (logging != MagickFalse)
10699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700 " Setting up sRGB chunk");
10702 (void) png_set_sRGB(ping,ping_info,(
10703 Magick_RenderingIntent_to_PNG_RenderingIntent(
10704 image->rendering_intent)));
10706 ping_have_sRGB = MagickTrue;
10710 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10713 if (ping_exclude_gAMA == MagickFalse &&
10714 ping_have_iCCP == MagickFalse &&
10715 ping_have_sRGB == MagickFalse &&
10716 (ping_exclude_sRGB == MagickFalse ||
10717 (image->gamma < .45 || image->gamma > .46)))
10719 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10723 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10725 if (logging != MagickFalse)
10726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10727 " Setting up gAMA chunk");
10729 png_set_gAMA(ping,ping_info,image->gamma);
10733 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10735 if ((mng_info->have_write_global_chrm == 0) &&
10736 (image->chromaticity.red_primary.x != 0.0))
10739 Note image chromaticity.
10740 Note: if cHRM+gAMA == sRGB write sRGB instead.
10748 wp=image->chromaticity.white_point;
10749 rp=image->chromaticity.red_primary;
10750 gp=image->chromaticity.green_primary;
10751 bp=image->chromaticity.blue_primary;
10753 if (logging != MagickFalse)
10754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10755 " Setting up cHRM chunk");
10757 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10763 if (ping_exclude_bKGD == MagickFalse)
10765 if (ping_have_bKGD != MagickFalse)
10767 png_set_bKGD(ping,ping_info,&ping_background);
10770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771 " Setting up bKGD chunk");
10772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10773 " background color = (%d,%d,%d)",
10774 (int) ping_background.red,
10775 (int) ping_background.green,
10776 (int) ping_background.blue);
10777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778 " index = %d, gray=%d",
10779 (int) ping_background.index,
10780 (int) ping_background.gray);
10785 if (ping_exclude_pHYs == MagickFalse)
10787 if (ping_have_pHYs != MagickFalse)
10789 png_set_pHYs(ping,ping_info,
10790 ping_pHYs_x_resolution,
10791 ping_pHYs_y_resolution,
10792 ping_pHYs_unit_type);
10796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10797 " Setting up pHYs chunk");
10798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10799 " x_resolution=%lu",
10800 (unsigned long) ping_pHYs_x_resolution);
10801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10802 " y_resolution=%lu",
10803 (unsigned long) ping_pHYs_y_resolution);
10804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10806 (unsigned long) ping_pHYs_unit_type);
10811 #if defined(PNG_oFFs_SUPPORTED)
10812 if (ping_exclude_oFFs == MagickFalse)
10814 if (image->page.x || image->page.y)
10816 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10817 (png_int_32) image->page.y, 0);
10819 if (logging != MagickFalse)
10820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10821 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10822 (int) image->page.x, (int) image->page.y);
10827 if (mng_info->need_blob != MagickFalse)
10829 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10831 png_error(ping,"WriteBlob Failed");
10833 ping_have_blob=MagickTrue;
10836 png_write_info_before_PLTE(ping, ping_info);
10838 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10840 if (logging != MagickFalse)
10842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10843 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10846 if (ping_color_type == 3)
10847 (void) png_set_tRNS(ping, ping_info,
10854 (void) png_set_tRNS(ping, ping_info,
10857 &ping_trans_color);
10859 if (logging != MagickFalse)
10861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862 " tRNS color =(%d,%d,%d)",
10863 (int) ping_trans_color.red,
10864 (int) ping_trans_color.green,
10865 (int) ping_trans_color.blue);
10870 /* write any png-chunk-b profiles */
10871 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10873 png_write_info(ping,ping_info);
10875 /* write any PNG-chunk-m profiles */
10876 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10878 if (ping_exclude_vpAg == MagickFalse)
10880 if ((image->page.width != 0 && image->page.width != image->columns) ||
10881 (image->page.height != 0 && image->page.height != image->rows))
10886 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10887 PNGType(chunk,mng_vpAg);
10888 LogPNGChunk(logging,mng_vpAg,9L);
10889 PNGLong(chunk+4,(png_uint_32) image->page.width);
10890 PNGLong(chunk+8,(png_uint_32) image->page.height);
10891 chunk[12]=0; /* unit = pixels */
10892 (void) WriteBlob(image,13,chunk);
10893 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10897 #if (PNG_LIBPNG_VER == 10206)
10898 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10899 #define PNG_HAVE_IDAT 0x04
10900 ping->mode |= PNG_HAVE_IDAT;
10901 #undef PNG_HAVE_IDAT
10904 png_set_packing(ping);
10908 rowbytes=image->columns;
10909 if (image_depth > 8)
10911 switch (ping_color_type)
10913 case PNG_COLOR_TYPE_RGB:
10917 case PNG_COLOR_TYPE_GRAY_ALPHA:
10921 case PNG_COLOR_TYPE_RGBA:
10929 if (logging != MagickFalse)
10931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932 " Writing PNG image data");
10934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10935 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10937 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10938 if (pixel_info == (MemoryInfo *) NULL)
10939 png_error(ping,"Allocation of memory for pixels failed");
10940 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10943 Initialize image scanlines.
10945 quantum_info=AcquireQuantumInfo(image_info,image);
10946 if (quantum_info == (QuantumInfo *) NULL)
10947 png_error(ping,"Memory allocation for quantum_info failed");
10948 quantum_info->format=UndefinedQuantumFormat;
10949 quantum_info->depth=image_depth;
10950 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10951 num_passes=png_set_interlace_handling(ping);
10953 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10954 !mng_info->write_png48 && !mng_info->write_png64 &&
10955 !mng_info->write_png32) &&
10956 (mng_info->IsPalette ||
10957 (image_info->type == BilevelType)) &&
10958 image_matte == MagickFalse &&
10959 ping_have_non_bw == MagickFalse)
10961 /* Palette, Bilevel, or Opaque Monochrome */
10962 register const Quantum
10965 quantum_info->depth=8;
10966 for (pass=0; pass < num_passes; pass++)
10969 Convert PseudoClass image to a PNG monochrome image.
10971 for (y=0; y < (ssize_t) image->rows; y++)
10973 if (logging != MagickFalse && y == 0)
10974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10975 " Writing row of pixels (0)");
10977 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10979 if (p == (const Quantum *) NULL)
10982 if (mng_info->IsPalette)
10984 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10985 quantum_info,GrayQuantum,ping_pixels,exception);
10986 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10987 mng_info->write_png_depth &&
10988 mng_info->write_png_depth != old_bit_depth)
10990 /* Undo pixel scaling */
10991 for (i=0; i < (ssize_t) image->columns; i++)
10992 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10993 >> (8-old_bit_depth));
10999 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11000 quantum_info,RedQuantum,ping_pixels,exception);
11003 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11004 for (i=0; i < (ssize_t) image->columns; i++)
11005 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11008 if (logging != MagickFalse && y == 0)
11009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11010 " Writing row of pixels (1)");
11012 png_write_row(ping,ping_pixels);
11014 if (image->previous == (Image *) NULL)
11016 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11017 if (status == MagickFalse)
11023 else /* Not Palette, Bilevel, or Opaque Monochrome */
11025 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11026 !mng_info->write_png48 && !mng_info->write_png64 &&
11027 !mng_info->write_png32) && (image_matte != MagickFalse ||
11028 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11029 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11031 register const Quantum
11034 for (pass=0; pass < num_passes; pass++)
11037 for (y=0; y < (ssize_t) image->rows; y++)
11039 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11041 if (p == (const Quantum *) NULL)
11044 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11046 if (mng_info->IsPalette)
11047 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11048 quantum_info,GrayQuantum,ping_pixels,exception);
11051 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11052 quantum_info,RedQuantum,ping_pixels,exception);
11054 if (logging != MagickFalse && y == 0)
11055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11056 " Writing GRAY PNG pixels (2)");
11059 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11061 if (logging != MagickFalse && y == 0)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " Writing GRAY_ALPHA PNG pixels (2)");
11065 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11066 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11069 if (logging != MagickFalse && y == 0)
11070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11071 " Writing row of pixels (2)");
11073 png_write_row(ping,ping_pixels);
11076 if (image->previous == (Image *) NULL)
11078 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11079 if (status == MagickFalse)
11087 register const Quantum
11090 for (pass=0; pass < num_passes; pass++)
11092 if ((image_depth > 8) ||
11093 mng_info->write_png24 ||
11094 mng_info->write_png32 ||
11095 mng_info->write_png48 ||
11096 mng_info->write_png64 ||
11097 (!mng_info->write_png8 && !mng_info->IsPalette))
11099 for (y=0; y < (ssize_t) image->rows; y++)
11101 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11103 if (p == (const Quantum *) NULL)
11106 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11108 if (image->storage_class == DirectClass)
11109 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11110 quantum_info,RedQuantum,ping_pixels,exception);
11113 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11114 quantum_info,GrayQuantum,ping_pixels,exception);
11117 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11119 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11120 quantum_info,GrayAlphaQuantum,ping_pixels,
11123 if (logging != MagickFalse && y == 0)
11124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11125 " Writing GRAY_ALPHA PNG pixels (3)");
11128 else if (image_matte != MagickFalse)
11129 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11130 quantum_info,RGBAQuantum,ping_pixels,exception);
11133 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11134 quantum_info,RGBQuantum,ping_pixels,exception);
11136 if (logging != MagickFalse && y == 0)
11137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11138 " Writing row of pixels (3)");
11140 png_write_row(ping,ping_pixels);
11145 /* not ((image_depth > 8) ||
11146 mng_info->write_png24 || mng_info->write_png32 ||
11147 mng_info->write_png48 || mng_info->write_png64 ||
11148 (!mng_info->write_png8 && !mng_info->IsPalette))
11151 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11152 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11154 if (logging != MagickFalse)
11155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11158 quantum_info->depth=8;
11162 for (y=0; y < (ssize_t) image->rows; y++)
11164 if (logging != MagickFalse && y == 0)
11165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11166 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11168 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11170 if (p == (const Quantum *) NULL)
11173 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11175 quantum_info->depth=image->depth;
11177 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11178 quantum_info,GrayQuantum,ping_pixels,exception);
11181 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11183 if (logging != MagickFalse && y == 0)
11184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11185 " Writing GRAY_ALPHA PNG pixels (4)");
11187 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11188 quantum_info,GrayAlphaQuantum,ping_pixels,
11194 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11195 quantum_info,IndexQuantum,ping_pixels,exception);
11197 if (logging != MagickFalse && y <= 2)
11199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11200 " Writing row of non-gray pixels (4)");
11202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11204 (int)ping_pixels[0],(int)ping_pixels[1]);
11207 png_write_row(ping,ping_pixels);
11211 if (image->previous == (Image *) NULL)
11213 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11214 if (status == MagickFalse)
11221 if (quantum_info != (QuantumInfo *) NULL)
11222 quantum_info=DestroyQuantumInfo(quantum_info);
11224 if (logging != MagickFalse)
11226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11227 " Wrote PNG image data");
11229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11230 " Width: %.20g",(double) ping_width);
11232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11233 " Height: %.20g",(double) ping_height);
11235 if (mng_info->write_png_depth)
11237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11238 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11242 " PNG bit-depth written: %d",ping_bit_depth);
11244 if (mng_info->write_png_colortype)
11246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11247 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251 " PNG color-type written: %d",ping_color_type);
11253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11254 " PNG Interlace method: %d",ping_interlace_method);
11257 Generate text chunks after IDAT.
11259 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11261 ResetImagePropertyIterator(image);
11262 property=GetNextImageProperty(image);
11263 while (property != (const char *) NULL)
11268 value=GetImageProperty(image,property,exception);
11270 /* Don't write any "png:" or "jpeg:" properties; those are just for
11271 * "identify" or for passing through to another JPEG
11273 if ((LocaleNCompare(property,"png:",4) != 0 &&
11274 LocaleNCompare(property,"jpeg:",5) != 0) &&
11277 /* Suppress density and units if we wrote a pHYs chunk */
11278 (ping_exclude_pHYs != MagickFalse ||
11279 LocaleCompare(property,"density") != 0 ||
11280 LocaleCompare(property,"units") != 0) &&
11282 /* Suppress the IM-generated Date:create and Date:modify */
11283 (ping_exclude_date == MagickFalse ||
11284 LocaleNCompare(property, "Date:",5) != 0))
11286 if (value != (const char *) NULL)
11289 #if PNG_LIBPNG_VER >= 10400
11290 text=(png_textp) png_malloc(ping,
11291 (png_alloc_size_t) sizeof(png_text));
11293 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11295 text[0].key=(char *) property;
11296 text[0].text=(char *) value;
11297 text[0].text_length=strlen(value);
11299 if (ping_exclude_tEXt != MagickFalse)
11300 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11302 else if (ping_exclude_zTXt != MagickFalse)
11303 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11307 text[0].compression=image_info->compression == NoCompression ||
11308 (image_info->compression == UndefinedCompression &&
11309 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11310 PNG_TEXT_COMPRESSION_zTXt ;
11313 if (logging != MagickFalse)
11315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11316 " Setting up text chunk");
11318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319 " keyword: '%s'",text[0].key);
11322 png_set_text(ping,ping_info,text,1);
11323 png_free(ping,text);
11326 property=GetNextImageProperty(image);
11330 /* write any PNG-chunk-e profiles */
11331 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11333 if (logging != MagickFalse)
11334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335 " Writing PNG end info");
11337 png_write_end(ping,ping_info);
11339 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11341 if (mng_info->page.x || mng_info->page.y ||
11342 (ping_width != mng_info->page.width) ||
11343 (ping_height != mng_info->page.height))
11349 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11351 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11352 PNGType(chunk,mng_FRAM);
11353 LogPNGChunk(logging,mng_FRAM,27L);
11355 chunk[5]=0; /* frame name separator (no name) */
11356 chunk[6]=1; /* flag for changing delay, for next frame only */
11357 chunk[7]=0; /* flag for changing frame timeout */
11358 chunk[8]=1; /* flag for changing frame clipping for next frame */
11359 chunk[9]=0; /* flag for changing frame sync_id */
11360 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11361 chunk[14]=0; /* clipping boundaries delta type */
11362 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11364 (png_uint_32) (mng_info->page.x + ping_width));
11365 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11367 (png_uint_32) (mng_info->page.y + ping_height));
11368 (void) WriteBlob(image,31,chunk);
11369 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11370 mng_info->old_framing_mode=4;
11371 mng_info->framing_mode=1;
11375 mng_info->framing_mode=3;
11377 if (mng_info->write_mng && !mng_info->need_fram &&
11378 ((int) image->dispose == 3))
11379 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11382 Free PNG resources.
11385 png_destroy_write_struct(&ping,&ping_info);
11387 pixel_info=RelinquishVirtualMemory(pixel_info);
11389 if (ping_have_blob != MagickFalse)
11390 (void) CloseBlob(image);
11392 image_info=DestroyImageInfo(image_info);
11393 image=DestroyImage(image);
11395 /* Store bit depth actually written */
11396 s[0]=(char) ping_bit_depth;
11399 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11401 if (logging != MagickFalse)
11402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11403 " exit WriteOnePNGImage()");
11405 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11406 UnlockSemaphoreInfo(ping_semaphore);
11409 /* } for navigation to beginning of SETJMP-protected block. Revert to
11410 * Throwing an Exception when an error occurs.
11413 return(MagickTrue);
11414 /* End write one PNG image */
11419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11423 % W r i t e P N G I m a g e %
11427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11429 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11430 % Multiple-image Network Graphics (MNG) image file.
11432 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11434 % The format of the WritePNGImage method is:
11436 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11437 % Image *image,ExceptionInfo *exception)
11439 % A description of each parameter follows:
11441 % o image_info: the image info.
11443 % o image: The image.
11445 % o exception: return any errors or warnings in this structure.
11447 % Returns MagickTrue on success, MagickFalse on failure.
11449 % Communicating with the PNG encoder:
11451 % While the datastream written is always in PNG format and normally would
11452 % be given the "png" file extension, this method also writes the following
11453 % pseudo-formats which are subsets of png:
11455 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11456 % a depth greater than 8, the depth is reduced. If transparency
11457 % is present, the tRNS chunk must only have values 0 and 255
11458 % (i.e., transparency is binary: fully opaque or fully
11459 % transparent). If other values are present they will be
11460 % 50%-thresholded to binary transparency. If more than 256
11461 % colors are present, they will be quantized to the 4-4-4-1,
11462 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11463 % of any resulting fully-transparent pixels is changed to
11464 % the image's background color.
11466 % If you want better quantization or dithering of the colors
11467 % or alpha than that, you need to do it before calling the
11468 % PNG encoder. The pixels contain 8-bit indices even if
11469 % they could be represented with 1, 2, or 4 bits. Grayscale
11470 % images will be written as indexed PNG files even though the
11471 % PNG grayscale type might be slightly more efficient. Please
11472 % note that writing to the PNG8 format may result in loss
11473 % of color and alpha data.
11475 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11476 % chunk can be present to convey binary transparency by naming
11477 % one of the colors as transparent. The only loss incurred
11478 % is reduction of sample depth to 8. If the image has more
11479 % than one transparent color, has semitransparent pixels, or
11480 % has an opaque pixel with the same RGB components as the
11481 % transparent color, an image is not written.
11483 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11484 % transparency is permitted, i.e., the alpha sample for
11485 % each pixel can have any value from 0 to 255. The alpha
11486 % channel is present even if the image is fully opaque.
11487 % The only loss in data is the reduction of the sample depth
11490 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11491 % chunk can be present to convey binary transparency by naming
11492 % one of the colors as transparent. If the image has more
11493 % than one transparent color, has semitransparent pixels, or
11494 % has an opaque pixel with the same RGB components as the
11495 % transparent color, an image is not written.
11497 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11498 % transparency is permitted, i.e., the alpha sample for
11499 % each pixel can have any value from 0 to 65535. The alpha
11500 % channel is present even if the image is fully opaque.
11502 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11503 % image, if the input was a PNG, is written. If these values
11504 % cannot be found, then "PNG00" falls back to the regular "PNG"
11507 % o -define: For more precise control of the PNG output, you can use the
11508 % Image options "png:bit-depth" and "png:color-type". These
11509 % can be set from the commandline with "-define" and also
11510 % from the application programming interfaces. The options
11511 % are case-independent and are converted to lowercase before
11512 % being passed to this encoder.
11514 % png:color-type can be 0, 2, 3, 4, or 6.
11516 % When png:color-type is 0 (Grayscale), png:bit-depth can
11517 % be 1, 2, 4, 8, or 16.
11519 % When png:color-type is 2 (RGB), png:bit-depth can
11522 % When png:color-type is 3 (Indexed), png:bit-depth can
11523 % be 1, 2, 4, or 8. This refers to the number of bits
11524 % used to store the index. The color samples always have
11525 % bit-depth 8 in indexed PNG files.
11527 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11528 % png:bit-depth can be 8 or 16.
11530 % If the image cannot be written without loss with the
11531 % requested bit-depth and color-type, a PNG file will not
11532 % be written, a warning will be issued, and the encoder will
11533 % return MagickFalse.
11535 % Since image encoders should not be responsible for the "heavy lifting",
11536 % the user should make sure that ImageMagick has already reduced the
11537 % image depth and number of colors and limit transparency to binary
11538 % transparency prior to attempting to write the image with depth, color,
11539 % or transparency limitations.
11541 % Note that another definition, "png:bit-depth-written" exists, but it
11542 % is not intended for external use. It is only used internally by the
11543 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11545 % It is possible to request that the PNG encoder write previously-formatted
11546 % ancillary chunks in the output PNG file, using the "-profile" commandline
11547 % option as shown below or by setting the profile via a programming
11550 % -profile PNG-chunk-x:<file>
11552 % where x is a location flag and <file> is a file containing the chunk
11553 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11554 % This encoder will compute the chunk length and CRC, so those must not
11555 % be included in the file.
11557 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11558 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11559 % of the same type, then add a short unique string after the "x" to prevent
11560 % subsequent profiles from overwriting the preceding ones, e.g.,
11562 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11564 % As of version 6.6.6 the following optimizations are always done:
11566 % o 32-bit depth is reduced to 16.
11567 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11568 % high byte and low byte are identical.
11569 % o Palette is sorted to remove unused entries and to put a
11570 % transparent color first, if BUILD_PNG_PALETTE is defined.
11571 % o Opaque matte channel is removed when writing an indexed PNG.
11572 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11573 % this can be done without loss and a larger bit depth N was not
11574 % requested via the "-define png:bit-depth=N" option.
11575 % o If matte channel is present but only one transparent color is
11576 % present, RGB+tRNS is written instead of RGBA
11577 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11578 % was requested when converting an opaque image).
11580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11582 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11583 Image *image,ExceptionInfo *exception)
11588 have_mng_structure,
11604 assert(image_info != (const ImageInfo *) NULL);
11605 assert(image_info->signature == MagickSignature);
11606 assert(image != (Image *) NULL);
11607 assert(image->signature == MagickSignature);
11608 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11609 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11611 Allocate a MngInfo structure.
11613 have_mng_structure=MagickFalse;
11614 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11616 if (mng_info == (MngInfo *) NULL)
11617 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11620 Initialize members of the MngInfo structure.
11622 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11623 mng_info->image=image;
11624 mng_info->equal_backgrounds=MagickTrue;
11625 have_mng_structure=MagickTrue;
11627 /* See if user has requested a specific PNG subformat */
11629 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11630 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11631 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11632 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11633 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11635 value=GetImageOption(image_info,"png:format");
11637 if (value != (char *) NULL)
11639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640 " Format=%s",value);
11642 mng_info->write_png8 = MagickFalse;
11643 mng_info->write_png24 = MagickFalse;
11644 mng_info->write_png32 = MagickFalse;
11645 mng_info->write_png48 = MagickFalse;
11646 mng_info->write_png64 = MagickFalse;
11648 if (LocaleCompare(value,"png8") == 0)
11649 mng_info->write_png8 = MagickTrue;
11651 else if (LocaleCompare(value,"png24") == 0)
11652 mng_info->write_png24 = MagickTrue;
11654 else if (LocaleCompare(value,"png32") == 0)
11655 mng_info->write_png32 = MagickTrue;
11657 else if (LocaleCompare(value,"png48") == 0)
11658 mng_info->write_png48 = MagickTrue;
11660 else if (LocaleCompare(value,"png64") == 0)
11661 mng_info->write_png64 = MagickTrue;
11663 else if (LocaleCompare(value,"png00") == 0)
11665 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11666 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11668 if (value != (char *) NULL)
11670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11671 " png00 inherited bit depth=%s",value);
11673 if (LocaleCompare(value,"1") == 0)
11674 mng_info->write_png_depth = 1;
11676 else if (LocaleCompare(value,"1") == 0)
11677 mng_info->write_png_depth = 2;
11679 else if (LocaleCompare(value,"2") == 0)
11680 mng_info->write_png_depth = 4;
11682 else if (LocaleCompare(value,"8") == 0)
11683 mng_info->write_png_depth = 8;
11685 else if (LocaleCompare(value,"16") == 0)
11686 mng_info->write_png_depth = 16;
11689 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11691 if (value != (char *) NULL)
11693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11694 " png00 inherited color type=%s",value);
11696 if (LocaleCompare(value,"0") == 0)
11697 mng_info->write_png_colortype = 1;
11699 else if (LocaleCompare(value,"2") == 0)
11700 mng_info->write_png_colortype = 3;
11702 else if (LocaleCompare(value,"3") == 0)
11703 mng_info->write_png_colortype = 4;
11705 else if (LocaleCompare(value,"4") == 0)
11706 mng_info->write_png_colortype = 5;
11708 else if (LocaleCompare(value,"6") == 0)
11709 mng_info->write_png_colortype = 7;
11714 if (mng_info->write_png8)
11716 mng_info->write_png_colortype = /* 3 */ 4;
11717 mng_info->write_png_depth = 8;
11721 if (mng_info->write_png24)
11723 mng_info->write_png_colortype = /* 2 */ 3;
11724 mng_info->write_png_depth = 8;
11727 if (image->alpha_trait == BlendPixelTrait)
11728 (void) SetImageType(image,TrueColorMatteType,exception);
11731 (void) SetImageType(image,TrueColorType,exception);
11733 (void) SyncImage(image,exception);
11736 if (mng_info->write_png32)
11738 mng_info->write_png_colortype = /* 6 */ 7;
11739 mng_info->write_png_depth = 8;
11742 if (image->alpha_trait == BlendPixelTrait)
11743 (void) SetImageType(image,TrueColorMatteType,exception);
11746 (void) SetImageType(image,TrueColorType,exception);
11748 (void) SyncImage(image,exception);
11751 if (mng_info->write_png48)
11753 mng_info->write_png_colortype = /* 2 */ 3;
11754 mng_info->write_png_depth = 16;
11757 if (image->alpha_trait == BlendPixelTrait)
11758 (void) SetImageType(image,TrueColorMatteType,exception);
11761 (void) SetImageType(image,TrueColorType,exception);
11763 (void) SyncImage(image,exception);
11766 if (mng_info->write_png64)
11768 mng_info->write_png_colortype = /* 6 */ 7;
11769 mng_info->write_png_depth = 16;
11772 if (image->alpha_trait == BlendPixelTrait)
11773 (void) SetImageType(image,TrueColorMatteType,exception);
11776 (void) SetImageType(image,TrueColorType,exception);
11778 (void) SyncImage(image,exception);
11781 value=GetImageOption(image_info,"png:bit-depth");
11783 if (value != (char *) NULL)
11785 if (LocaleCompare(value,"1") == 0)
11786 mng_info->write_png_depth = 1;
11788 else if (LocaleCompare(value,"2") == 0)
11789 mng_info->write_png_depth = 2;
11791 else if (LocaleCompare(value,"4") == 0)
11792 mng_info->write_png_depth = 4;
11794 else if (LocaleCompare(value,"8") == 0)
11795 mng_info->write_png_depth = 8;
11797 else if (LocaleCompare(value,"16") == 0)
11798 mng_info->write_png_depth = 16;
11801 (void) ThrowMagickException(exception,
11802 GetMagickModule(),CoderWarning,
11803 "ignoring invalid defined png:bit-depth",
11806 if (logging != MagickFalse)
11807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11808 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11811 value=GetImageOption(image_info,"png:color-type");
11813 if (value != (char *) NULL)
11815 /* We must store colortype+1 because 0 is a valid colortype */
11816 if (LocaleCompare(value,"0") == 0)
11817 mng_info->write_png_colortype = 1;
11819 else if (LocaleCompare(value,"1") == 0)
11820 mng_info->write_png_colortype = 2;
11822 else if (LocaleCompare(value,"2") == 0)
11823 mng_info->write_png_colortype = 3;
11825 else if (LocaleCompare(value,"3") == 0)
11826 mng_info->write_png_colortype = 4;
11828 else if (LocaleCompare(value,"4") == 0)
11829 mng_info->write_png_colortype = 5;
11831 else if (LocaleCompare(value,"6") == 0)
11832 mng_info->write_png_colortype = 7;
11835 (void) ThrowMagickException(exception,
11836 GetMagickModule(),CoderWarning,
11837 "ignoring invalid defined png:color-type",
11840 if (logging != MagickFalse)
11841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11845 /* Check for chunks to be excluded:
11847 * The default is to not exclude any known chunks except for any
11848 * listed in the "unused_chunks" array, above.
11850 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11851 * define (in the image properties or in the image artifacts)
11852 * or via a mng_info member. For convenience, in addition
11853 * to or instead of a comma-separated list of chunks, the
11854 * "exclude-chunk" string can be simply "all" or "none".
11856 * The exclude-chunk define takes priority over the mng_info.
11858 * A "png:include-chunk" define takes priority over both the
11859 * mng_info and the "png:exclude-chunk" define. Like the
11860 * "exclude-chunk" string, it can define "all" or "none" as
11861 * well as a comma-separated list. Chunks that are unknown to
11862 * ImageMagick are always excluded, regardless of their "copy-safe"
11863 * status according to the PNG specification, and even if they
11864 * appear in the "include-chunk" list. Such defines appearing among
11865 * the image options take priority over those found among the image
11868 * Finally, all chunks listed in the "unused_chunks" array are
11869 * automatically excluded, regardless of the other instructions
11872 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11873 * will not be written and the gAMA chunk will only be written if it
11874 * is not between .45 and .46, or approximately (1.0/2.2).
11876 * If you exclude tRNS and the image has transparency, the colortype
11877 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11879 * The -strip option causes StripImage() to set the png:include-chunk
11880 * artifact to "none,trns,gama".
11883 mng_info->ping_exclude_bKGD=MagickFalse;
11884 mng_info->ping_exclude_cHRM=MagickFalse;
11885 mng_info->ping_exclude_date=MagickFalse;
11886 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11887 mng_info->ping_exclude_gAMA=MagickFalse;
11888 mng_info->ping_exclude_iCCP=MagickFalse;
11889 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11890 mng_info->ping_exclude_oFFs=MagickFalse;
11891 mng_info->ping_exclude_pHYs=MagickFalse;
11892 mng_info->ping_exclude_sRGB=MagickFalse;
11893 mng_info->ping_exclude_tEXt=MagickFalse;
11894 mng_info->ping_exclude_tRNS=MagickFalse;
11895 mng_info->ping_exclude_vpAg=MagickFalse;
11896 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11897 mng_info->ping_exclude_zTXt=MagickFalse;
11899 mng_info->ping_preserve_colormap=MagickFalse;
11901 value=GetImageOption(image_info,"png:preserve-colormap");
11903 value=GetImageArtifact(image,"png:preserve-colormap");
11905 mng_info->ping_preserve_colormap=MagickTrue;
11907 mng_info->ping_preserve_iCCP=MagickFalse;
11909 value=GetImageOption(image_info,"png:preserve-iCCP");
11911 value=GetImageArtifact(image,"png:preserve-iCCP");
11913 mng_info->ping_preserve_iCCP=MagickTrue;
11915 /* These compression-level, compression-strategy, and compression-filter
11916 * defines take precedence over values from the -quality option.
11918 value=GetImageOption(image_info,"png:compression-level");
11920 value=GetImageArtifact(image,"png:compression-level");
11923 /* We have to add 1 to everything because 0 is a valid input,
11924 * and we want to use 0 (the default) to mean undefined.
11926 if (LocaleCompare(value,"0") == 0)
11927 mng_info->write_png_compression_level = 1;
11929 else if (LocaleCompare(value,"1") == 0)
11930 mng_info->write_png_compression_level = 2;
11932 else if (LocaleCompare(value,"2") == 0)
11933 mng_info->write_png_compression_level = 3;
11935 else if (LocaleCompare(value,"3") == 0)
11936 mng_info->write_png_compression_level = 4;
11938 else if (LocaleCompare(value,"4") == 0)
11939 mng_info->write_png_compression_level = 5;
11941 else if (LocaleCompare(value,"5") == 0)
11942 mng_info->write_png_compression_level = 6;
11944 else if (LocaleCompare(value,"6") == 0)
11945 mng_info->write_png_compression_level = 7;
11947 else if (LocaleCompare(value,"7") == 0)
11948 mng_info->write_png_compression_level = 8;
11950 else if (LocaleCompare(value,"8") == 0)
11951 mng_info->write_png_compression_level = 9;
11953 else if (LocaleCompare(value,"9") == 0)
11954 mng_info->write_png_compression_level = 10;
11957 (void) ThrowMagickException(exception,
11958 GetMagickModule(),CoderWarning,
11959 "ignoring invalid defined png:compression-level",
11963 value=GetImageOption(image_info,"png:compression-strategy");
11965 value=GetImageArtifact(image,"png:compression-strategy");
11969 if (LocaleCompare(value,"0") == 0)
11970 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11972 else if (LocaleCompare(value,"1") == 0)
11973 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11975 else if (LocaleCompare(value,"2") == 0)
11976 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11978 else if (LocaleCompare(value,"3") == 0)
11979 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11980 mng_info->write_png_compression_strategy = Z_RLE+1;
11982 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11985 else if (LocaleCompare(value,"4") == 0)
11986 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11987 mng_info->write_png_compression_strategy = Z_FIXED+1;
11989 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11993 (void) ThrowMagickException(exception,
11994 GetMagickModule(),CoderWarning,
11995 "ignoring invalid defined png:compression-strategy",
11999 value=GetImageOption(image_info,"png:compression-filter");
12001 value=GetImageArtifact(image,"png:compression-filter");
12005 /* To do: combinations of filters allowed by libpng
12006 * masks 0x08 through 0xf8
12008 * Implement this as a comma-separated list of 0,1,2,3,4,5
12009 * where 5 is a special case meaning PNG_ALL_FILTERS.
12012 if (LocaleCompare(value,"0") == 0)
12013 mng_info->write_png_compression_filter = 1;
12015 else if (LocaleCompare(value,"1") == 0)
12016 mng_info->write_png_compression_filter = 2;
12018 else if (LocaleCompare(value,"2") == 0)
12019 mng_info->write_png_compression_filter = 3;
12021 else if (LocaleCompare(value,"3") == 0)
12022 mng_info->write_png_compression_filter = 4;
12024 else if (LocaleCompare(value,"4") == 0)
12025 mng_info->write_png_compression_filter = 5;
12027 else if (LocaleCompare(value,"5") == 0)
12028 mng_info->write_png_compression_filter = 6;
12031 (void) ThrowMagickException(exception,
12032 GetMagickModule(),CoderWarning,
12033 "ignoring invalid defined png:compression-filter",
12037 excluding=MagickFalse;
12039 for (source=0; source<1; source++)
12043 value=GetImageOption(image_info,"png:exclude-chunk");
12046 value=GetImageArtifact(image,"png:exclude-chunks");
12050 value=GetImageOption(image_info,"png:exclude-chunk");
12053 value=GetImageArtifact(image,"png:exclude-chunks");
12062 excluding=MagickTrue;
12064 if (logging != MagickFalse)
12067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12068 " png:exclude-chunk=%s found in image artifacts.\n", value);
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071 " png:exclude-chunk=%s found in image properties.\n", value);
12074 last=strlen(value);
12076 for (i=0; i<(int) last; i+=5)
12079 if (LocaleNCompare(value+i,"none",4) == 0)
12081 mng_info->ping_exclude_bKGD=MagickFalse;
12082 mng_info->ping_exclude_cHRM=MagickFalse;
12083 mng_info->ping_exclude_date=MagickFalse;
12084 mng_info->ping_exclude_EXIF=MagickFalse;
12085 mng_info->ping_exclude_gAMA=MagickFalse;
12086 mng_info->ping_exclude_iCCP=MagickFalse;
12087 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12088 mng_info->ping_exclude_oFFs=MagickFalse;
12089 mng_info->ping_exclude_pHYs=MagickFalse;
12090 mng_info->ping_exclude_sRGB=MagickFalse;
12091 mng_info->ping_exclude_tEXt=MagickFalse;
12092 mng_info->ping_exclude_tRNS=MagickFalse;
12093 mng_info->ping_exclude_vpAg=MagickFalse;
12094 mng_info->ping_exclude_zCCP=MagickFalse;
12095 mng_info->ping_exclude_zTXt=MagickFalse;
12098 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12099 mng_info->ping_exclude_bKGD=MagickTrue;
12101 if (LocaleNCompare(value+i,"chrm",4) == 0)
12102 mng_info->ping_exclude_cHRM=MagickTrue;
12104 if (LocaleNCompare(value+i,"date",4) == 0)
12105 mng_info->ping_exclude_date=MagickTrue;
12107 if (LocaleNCompare(value+i,"exif",4) == 0)
12108 mng_info->ping_exclude_EXIF=MagickTrue;
12110 if (LocaleNCompare(value+i,"gama",4) == 0)
12111 mng_info->ping_exclude_gAMA=MagickTrue;
12113 if (LocaleNCompare(value+i,"iccp",4) == 0)
12114 mng_info->ping_exclude_iCCP=MagickTrue;
12117 if (LocaleNCompare(value+i,"itxt",4) == 0)
12118 mng_info->ping_exclude_iTXt=MagickTrue;
12121 if (LocaleNCompare(value+i,"gama",4) == 0)
12122 mng_info->ping_exclude_gAMA=MagickTrue;
12124 if (LocaleNCompare(value+i,"offs",4) == 0)
12125 mng_info->ping_exclude_oFFs=MagickTrue;
12127 if (LocaleNCompare(value+i,"phys",4) == 0)
12128 mng_info->ping_exclude_pHYs=MagickTrue;
12130 if (LocaleNCompare(value+i,"srgb",4) == 0)
12131 mng_info->ping_exclude_sRGB=MagickTrue;
12133 if (LocaleNCompare(value+i,"text",4) == 0)
12134 mng_info->ping_exclude_tEXt=MagickTrue;
12136 if (LocaleNCompare(value+i,"trns",4) == 0)
12137 mng_info->ping_exclude_tRNS=MagickTrue;
12139 if (LocaleNCompare(value+i,"vpag",4) == 0)
12140 mng_info->ping_exclude_vpAg=MagickTrue;
12142 if (LocaleNCompare(value+i,"zccp",4) == 0)
12143 mng_info->ping_exclude_zCCP=MagickTrue;
12145 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12146 mng_info->ping_exclude_zTXt=MagickTrue;
12148 if (LocaleNCompare(value+i,"all",3) == 0)
12150 mng_info->ping_exclude_bKGD=MagickTrue;
12151 mng_info->ping_exclude_cHRM=MagickTrue;
12152 mng_info->ping_exclude_date=MagickTrue;
12153 mng_info->ping_exclude_EXIF=MagickTrue;
12154 mng_info->ping_exclude_gAMA=MagickTrue;
12155 mng_info->ping_exclude_iCCP=MagickTrue;
12156 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12157 mng_info->ping_exclude_oFFs=MagickTrue;
12158 mng_info->ping_exclude_pHYs=MagickTrue;
12159 mng_info->ping_exclude_sRGB=MagickTrue;
12160 mng_info->ping_exclude_tEXt=MagickTrue;
12161 mng_info->ping_exclude_tRNS=MagickTrue;
12162 mng_info->ping_exclude_vpAg=MagickTrue;
12163 mng_info->ping_exclude_zCCP=MagickTrue;
12164 mng_info->ping_exclude_zTXt=MagickTrue;
12171 for (source=0; source<1; source++)
12175 value=GetImageOption(image_info,"png:include-chunk");
12178 value=GetImageArtifact(image,"png:include-chunks");
12182 value=GetImageOption(image_info,"png:include-chunk");
12185 value=GetImageArtifact(image,"png:include-chunks");
12193 excluding=MagickTrue;
12195 if (logging != MagickFalse)
12198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12199 " png:include-chunk=%s found in image artifacts.\n", value);
12201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202 " png:include-chunk=%s found in image properties.\n", value);
12205 last=strlen(value);
12207 for (i=0; i<(int) last; i+=5)
12209 if (LocaleNCompare(value+i,"none",4) == 0)
12211 mng_info->ping_exclude_bKGD=MagickTrue;
12212 mng_info->ping_exclude_cHRM=MagickTrue;
12213 mng_info->ping_exclude_date=MagickTrue;
12214 mng_info->ping_exclude_EXIF=MagickTrue;
12215 mng_info->ping_exclude_gAMA=MagickTrue;
12216 mng_info->ping_exclude_iCCP=MagickTrue;
12217 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12218 mng_info->ping_exclude_oFFs=MagickTrue;
12219 mng_info->ping_exclude_pHYs=MagickTrue;
12220 mng_info->ping_exclude_sRGB=MagickTrue;
12221 mng_info->ping_exclude_tEXt=MagickTrue;
12222 mng_info->ping_exclude_tRNS=MagickTrue;
12223 mng_info->ping_exclude_vpAg=MagickTrue;
12224 mng_info->ping_exclude_zCCP=MagickTrue;
12225 mng_info->ping_exclude_zTXt=MagickTrue;
12228 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12229 mng_info->ping_exclude_bKGD=MagickFalse;
12231 if (LocaleNCompare(value+i,"chrm",4) == 0)
12232 mng_info->ping_exclude_cHRM=MagickFalse;
12234 if (LocaleNCompare(value+i,"date",4) == 0)
12235 mng_info->ping_exclude_date=MagickFalse;
12237 if (LocaleNCompare(value+i,"exif",4) == 0)
12238 mng_info->ping_exclude_EXIF=MagickFalse;
12240 if (LocaleNCompare(value+i,"gama",4) == 0)
12241 mng_info->ping_exclude_gAMA=MagickFalse;
12243 if (LocaleNCompare(value+i,"iccp",4) == 0)
12244 mng_info->ping_exclude_iCCP=MagickFalse;
12247 if (LocaleNCompare(value+i,"itxt",4) == 0)
12248 mng_info->ping_exclude_iTXt=MagickFalse;
12251 if (LocaleNCompare(value+i,"gama",4) == 0)
12252 mng_info->ping_exclude_gAMA=MagickFalse;
12254 if (LocaleNCompare(value+i,"offs",4) == 0)
12255 mng_info->ping_exclude_oFFs=MagickFalse;
12257 if (LocaleNCompare(value+i,"phys",4) == 0)
12258 mng_info->ping_exclude_pHYs=MagickFalse;
12260 if (LocaleNCompare(value+i,"srgb",4) == 0)
12261 mng_info->ping_exclude_sRGB=MagickFalse;
12263 if (LocaleNCompare(value+i,"text",4) == 0)
12264 mng_info->ping_exclude_tEXt=MagickFalse;
12266 if (LocaleNCompare(value+i,"trns",4) == 0)
12267 mng_info->ping_exclude_tRNS=MagickFalse;
12269 if (LocaleNCompare(value+i,"vpag",4) == 0)
12270 mng_info->ping_exclude_vpAg=MagickFalse;
12272 if (LocaleNCompare(value+i,"zccp",4) == 0)
12273 mng_info->ping_exclude_zCCP=MagickFalse;
12275 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12276 mng_info->ping_exclude_zTXt=MagickFalse;
12278 if (LocaleNCompare(value+i,"all",3) == 0)
12280 mng_info->ping_exclude_bKGD=MagickFalse;
12281 mng_info->ping_exclude_cHRM=MagickFalse;
12282 mng_info->ping_exclude_date=MagickFalse;
12283 mng_info->ping_exclude_EXIF=MagickFalse;
12284 mng_info->ping_exclude_gAMA=MagickFalse;
12285 mng_info->ping_exclude_iCCP=MagickFalse;
12286 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12287 mng_info->ping_exclude_oFFs=MagickFalse;
12288 mng_info->ping_exclude_pHYs=MagickFalse;
12289 mng_info->ping_exclude_sRGB=MagickFalse;
12290 mng_info->ping_exclude_tEXt=MagickFalse;
12291 mng_info->ping_exclude_tRNS=MagickFalse;
12292 mng_info->ping_exclude_vpAg=MagickFalse;
12293 mng_info->ping_exclude_zCCP=MagickFalse;
12294 mng_info->ping_exclude_zTXt=MagickFalse;
12301 if (excluding != MagickFalse && logging != MagickFalse)
12303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12304 " Chunks to be excluded from the output png:");
12305 if (mng_info->ping_exclude_bKGD != MagickFalse)
12306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12308 if (mng_info->ping_exclude_cHRM != MagickFalse)
12309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12311 if (mng_info->ping_exclude_date != MagickFalse)
12312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12314 if (mng_info->ping_exclude_EXIF != MagickFalse)
12315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12317 if (mng_info->ping_exclude_gAMA != MagickFalse)
12318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320 if (mng_info->ping_exclude_iCCP != MagickFalse)
12321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 if (mng_info->ping_exclude_iTXt != MagickFalse)
12325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12328 if (mng_info->ping_exclude_oFFs != MagickFalse)
12329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12331 if (mng_info->ping_exclude_pHYs != MagickFalse)
12332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12334 if (mng_info->ping_exclude_sRGB != MagickFalse)
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12337 if (mng_info->ping_exclude_tEXt != MagickFalse)
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12340 if (mng_info->ping_exclude_tRNS != MagickFalse)
12341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12343 if (mng_info->ping_exclude_vpAg != MagickFalse)
12344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12346 if (mng_info->ping_exclude_zCCP != MagickFalse)
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12349 if (mng_info->ping_exclude_zTXt != MagickFalse)
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354 mng_info->need_blob = MagickTrue;
12356 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12358 MngInfoFreeStruct(mng_info,&have_mng_structure);
12360 if (logging != MagickFalse)
12361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12366 #if defined(JNG_SUPPORTED)
12368 /* Write one JNG image */
12369 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12370 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12391 jng_alpha_compression_method,
12392 jng_alpha_sample_depth,
12400 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12401 " Enter WriteOneJNGImage()");
12403 blob=(unsigned char *) NULL;
12404 jpeg_image=(Image *) NULL;
12405 jpeg_image_info=(ImageInfo *) NULL;
12408 transparent=image_info->type==GrayscaleMatteType ||
12409 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12411 jng_alpha_sample_depth = 0;
12413 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12415 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12417 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12418 image_info->quality;
12420 if (jng_alpha_quality >= 1000)
12421 jng_alpha_quality /= 1000;
12427 /* Create JPEG blob, image, and image_info */
12428 if (logging != MagickFalse)
12429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12430 " Creating jpeg_image_info for alpha.");
12432 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12434 if (jpeg_image_info == (ImageInfo *) NULL)
12435 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12437 if (logging != MagickFalse)
12438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12439 " Creating jpeg_image.");
12441 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12442 if (jpeg_image == (Image *) NULL)
12443 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12444 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12445 jpeg_image->alpha_trait=UndefinedPixelTrait;
12446 jpeg_image->quality=jng_alpha_quality;
12447 jpeg_image_info->type=GrayscaleType;
12448 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12449 (void) AcquireUniqueFilename(jpeg_image->filename);
12450 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12451 "%s",jpeg_image->filename);
12455 jng_alpha_compression_method=0;
12457 jng_alpha_sample_depth=0;
12460 /* To do: check bit depth of PNG alpha channel */
12462 /* Check if image is grayscale. */
12463 if (image_info->type != TrueColorMatteType && image_info->type !=
12464 TrueColorType && IsImageGray(image,exception))
12467 if (logging != MagickFalse)
12469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12470 " JNG Quality = %d",(int) jng_quality);
12471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12472 " JNG Color Type = %d",jng_color_type);
12475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12476 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12478 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12480 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12486 if (jng_alpha_compression_method==0)
12491 /* Encode alpha as a grayscale PNG blob */
12492 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12494 if (logging != MagickFalse)
12495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12496 " Creating PNG blob.");
12499 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12500 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12501 jpeg_image_info->interlace=NoInterlace;
12503 /* Exclude all ancillary chunks */
12504 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12506 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12509 /* Retrieve sample depth used */
12510 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12511 if (value != (char *) NULL)
12512 jng_alpha_sample_depth= (unsigned int) value[0];
12516 /* Encode alpha as a grayscale JPEG blob */
12518 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12521 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12522 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12523 jpeg_image_info->interlace=NoInterlace;
12524 if (logging != MagickFalse)
12525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12526 " Creating blob.");
12527 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12529 jng_alpha_sample_depth=8;
12531 if (logging != MagickFalse)
12532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12533 " Successfully read jpeg_image into a blob, length=%.20g.",
12537 /* Destroy JPEG image and image_info */
12538 jpeg_image=DestroyImage(jpeg_image);
12539 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12540 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12543 /* Write JHDR chunk */
12544 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12545 PNGType(chunk,mng_JHDR);
12546 LogPNGChunk(logging,mng_JHDR,16L);
12547 PNGLong(chunk+4,(png_uint_32) image->columns);
12548 PNGLong(chunk+8,(png_uint_32) image->rows);
12549 chunk[12]=jng_color_type;
12550 chunk[13]=8; /* sample depth */
12551 chunk[14]=8; /*jng_image_compression_method */
12552 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12553 chunk[16]=jng_alpha_sample_depth;
12554 chunk[17]=jng_alpha_compression_method;
12555 chunk[18]=0; /*jng_alpha_filter_method */
12556 chunk[19]=0; /*jng_alpha_interlace_method */
12557 (void) WriteBlob(image,20,chunk);
12558 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12559 if (logging != MagickFalse)
12561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562 " JNG width:%15lu",(unsigned long) image->columns);
12564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12565 " JNG height:%14lu",(unsigned long) image->rows);
12567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12568 " JNG color type:%10d",jng_color_type);
12570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12571 " JNG sample depth:%8d",8);
12573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12574 " JNG compression:%9d",8);
12576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12577 " JNG interlace:%11d",0);
12579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12580 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12583 " JNG alpha compression:%3d",jng_alpha_compression_method);
12585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12586 " JNG alpha filter:%8d",0);
12588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12589 " JNG alpha interlace:%5d",0);
12592 /* Write any JNG-chunk-b profiles */
12593 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12596 Write leading ancillary chunks
12602 Write JNG bKGD chunk
12613 if (jng_color_type == 8 || jng_color_type == 12)
12617 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12618 PNGType(chunk,mng_bKGD);
12619 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12620 red=ScaleQuantumToChar(image->background_color.red);
12621 green=ScaleQuantumToChar(image->background_color.green);
12622 blue=ScaleQuantumToChar(image->background_color.blue);
12629 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12630 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12633 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12636 Write JNG sRGB chunk
12638 (void) WriteBlobMSBULong(image,1L);
12639 PNGType(chunk,mng_sRGB);
12640 LogPNGChunk(logging,mng_sRGB,1L);
12642 if (image->rendering_intent != UndefinedIntent)
12643 chunk[4]=(unsigned char)
12644 Magick_RenderingIntent_to_PNG_RenderingIntent(
12645 (image->rendering_intent));
12648 chunk[4]=(unsigned char)
12649 Magick_RenderingIntent_to_PNG_RenderingIntent(
12650 (PerceptualIntent));
12652 (void) WriteBlob(image,5,chunk);
12653 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12657 if (image->gamma != 0.0)
12660 Write JNG gAMA chunk
12662 (void) WriteBlobMSBULong(image,4L);
12663 PNGType(chunk,mng_gAMA);
12664 LogPNGChunk(logging,mng_gAMA,4L);
12665 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12666 (void) WriteBlob(image,8,chunk);
12667 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12670 if ((mng_info->equal_chrms == MagickFalse) &&
12671 (image->chromaticity.red_primary.x != 0.0))
12677 Write JNG cHRM chunk
12679 (void) WriteBlobMSBULong(image,32L);
12680 PNGType(chunk,mng_cHRM);
12681 LogPNGChunk(logging,mng_cHRM,32L);
12682 primary=image->chromaticity.white_point;
12683 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12684 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12685 primary=image->chromaticity.red_primary;
12686 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12687 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12688 primary=image->chromaticity.green_primary;
12689 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12690 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12691 primary=image->chromaticity.blue_primary;
12692 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12693 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12694 (void) WriteBlob(image,36,chunk);
12695 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12699 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12702 Write JNG pHYs chunk
12704 (void) WriteBlobMSBULong(image,9L);
12705 PNGType(chunk,mng_pHYs);
12706 LogPNGChunk(logging,mng_pHYs,9L);
12707 if (image->units == PixelsPerInchResolution)
12709 PNGLong(chunk+4,(png_uint_32)
12710 (image->resolution.x*100.0/2.54+0.5));
12712 PNGLong(chunk+8,(png_uint_32)
12713 (image->resolution.y*100.0/2.54+0.5));
12720 if (image->units == PixelsPerCentimeterResolution)
12722 PNGLong(chunk+4,(png_uint_32)
12723 (image->resolution.x*100.0+0.5));
12725 PNGLong(chunk+8,(png_uint_32)
12726 (image->resolution.y*100.0+0.5));
12733 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12734 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12738 (void) WriteBlob(image,13,chunk);
12739 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12742 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12745 Write JNG oFFs chunk
12747 (void) WriteBlobMSBULong(image,9L);
12748 PNGType(chunk,mng_oFFs);
12749 LogPNGChunk(logging,mng_oFFs,9L);
12750 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12751 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12753 (void) WriteBlob(image,13,chunk);
12754 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12756 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12758 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12759 PNGType(chunk,mng_vpAg);
12760 LogPNGChunk(logging,mng_vpAg,9L);
12761 PNGLong(chunk+4,(png_uint_32) image->page.width);
12762 PNGLong(chunk+8,(png_uint_32) image->page.height);
12763 chunk[12]=0; /* unit = pixels */
12764 (void) WriteBlob(image,13,chunk);
12765 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12771 if (jng_alpha_compression_method==0)
12779 /* Write IDAT chunk header */
12780 if (logging != MagickFalse)
12781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12782 " Write IDAT chunks from blob, length=%.20g.",(double)
12785 /* Copy IDAT chunks */
12788 for (i=8; i<(ssize_t) length; i+=len+12)
12790 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12793 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12795 /* Found an IDAT chunk. */
12796 (void) WriteBlobMSBULong(image,(size_t) len);
12797 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12798 (void) WriteBlob(image,(size_t) len+4,p);
12799 (void) WriteBlobMSBULong(image,
12800 crc32(0,p,(uInt) len+4));
12805 if (logging != MagickFalse)
12806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12807 " Skipping %c%c%c%c chunk, length=%.20g.",
12808 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12815 /* Write JDAA chunk header */
12816 if (logging != MagickFalse)
12817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12818 " Write JDAA chunk, length=%.20g.",(double) length);
12819 (void) WriteBlobMSBULong(image,(size_t) length);
12820 PNGType(chunk,mng_JDAA);
12821 LogPNGChunk(logging,mng_JDAA,length);
12822 /* Write JDAT chunk(s) data */
12823 (void) WriteBlob(image,4,chunk);
12824 (void) WriteBlob(image,length,blob);
12825 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12828 blob=(unsigned char *) RelinquishMagickMemory(blob);
12831 /* Encode image as a JPEG blob */
12832 if (logging != MagickFalse)
12833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12834 " Creating jpeg_image_info.");
12835 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12836 if (jpeg_image_info == (ImageInfo *) NULL)
12837 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12839 if (logging != MagickFalse)
12840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12841 " Creating jpeg_image.");
12843 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12844 if (jpeg_image == (Image *) NULL)
12845 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12846 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12848 (void) AcquireUniqueFilename(jpeg_image->filename);
12849 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12850 jpeg_image->filename);
12852 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12855 if (logging != MagickFalse)
12856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12857 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12858 (double) jpeg_image->rows);
12860 if (jng_color_type == 8 || jng_color_type == 12)
12861 jpeg_image_info->type=GrayscaleType;
12863 jpeg_image_info->quality=jng_quality;
12864 jpeg_image->quality=jng_quality;
12865 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12866 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12868 if (logging != MagickFalse)
12869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870 " Creating blob.");
12872 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12874 if (logging != MagickFalse)
12876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12877 " Successfully read jpeg_image into a blob, length=%.20g.",
12880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12881 " Write JDAT chunk, length=%.20g.",(double) length);
12884 /* Write JDAT chunk(s) */
12885 (void) WriteBlobMSBULong(image,(size_t) length);
12886 PNGType(chunk,mng_JDAT);
12887 LogPNGChunk(logging,mng_JDAT,length);
12888 (void) WriteBlob(image,4,chunk);
12889 (void) WriteBlob(image,length,blob);
12890 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12892 jpeg_image=DestroyImage(jpeg_image);
12893 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12894 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12895 blob=(unsigned char *) RelinquishMagickMemory(blob);
12897 /* Write any JNG-chunk-e profiles */
12898 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12900 /* Write IEND chunk */
12901 (void) WriteBlobMSBULong(image,0L);
12902 PNGType(chunk,mng_IEND);
12903 LogPNGChunk(logging,mng_IEND,0);
12904 (void) WriteBlob(image,4,chunk);
12905 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12907 if (logging != MagickFalse)
12908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12909 " exit WriteOneJNGImage()");
12916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12920 % W r i t e J N G I m a g e %
12924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12926 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12928 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12930 % The format of the WriteJNGImage method is:
12932 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12933 % Image *image,ExceptionInfo *exception)
12935 % A description of each parameter follows:
12937 % o image_info: the image info.
12939 % o image: The image.
12941 % o exception: return any errors or warnings in this structure.
12943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12945 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12946 ExceptionInfo *exception)
12949 have_mng_structure,
12959 assert(image_info != (const ImageInfo *) NULL);
12960 assert(image_info->signature == MagickSignature);
12961 assert(image != (Image *) NULL);
12962 assert(image->signature == MagickSignature);
12963 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12964 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12965 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12966 if (status == MagickFalse)
12970 Allocate a MngInfo structure.
12972 have_mng_structure=MagickFalse;
12973 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12974 if (mng_info == (MngInfo *) NULL)
12975 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12977 Initialize members of the MngInfo structure.
12979 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12980 mng_info->image=image;
12981 have_mng_structure=MagickTrue;
12983 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12985 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12986 (void) CloseBlob(image);
12988 (void) CatchImageException(image);
12989 MngInfoFreeStruct(mng_info,&have_mng_structure);
12990 if (logging != MagickFalse)
12991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12996 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12997 ExceptionInfo *exception)
13006 have_mng_structure,
13009 volatile MagickBooleanType
13021 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13022 defined(PNG_MNG_FEATURES_SUPPORTED)
13025 all_images_are_gray,
13035 volatile unsigned int
13046 #if (PNG_LIBPNG_VER < 10200)
13047 if (image_info->verbose)
13048 printf("Your PNG library (libpng-%s) is rather old.\n",
13049 PNG_LIBPNG_VER_STRING);
13055 assert(image_info != (const ImageInfo *) NULL);
13056 assert(image_info->signature == MagickSignature);
13057 assert(image != (Image *) NULL);
13058 assert(image->signature == MagickSignature);
13059 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13060 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13061 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13062 if (status == MagickFalse)
13066 Allocate a MngInfo structure.
13068 have_mng_structure=MagickFalse;
13069 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13070 if (mng_info == (MngInfo *) NULL)
13071 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13073 Initialize members of the MngInfo structure.
13075 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13076 mng_info->image=image;
13077 have_mng_structure=MagickTrue;
13078 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13081 * See if user has requested a specific PNG subformat to be used
13082 * for all of the PNGs in the MNG being written, e.g.,
13084 * convert *.png png8:animation.mng
13086 * To do: check -define png:bit_depth and png:color_type as well,
13087 * or perhaps use mng:bit_depth and mng:color_type instead for
13091 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13092 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13093 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13095 write_jng=MagickFalse;
13096 if (image_info->compression == JPEGCompression)
13097 write_jng=MagickTrue;
13099 mng_info->adjoin=image_info->adjoin &&
13100 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13102 if (logging != MagickFalse)
13104 /* Log some info about the input */
13108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13109 " Checking input image(s)");
13111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13112 " Image_info depth: %.20g",(double) image_info->depth);
13114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13115 " Type: %d",image_info->type);
13118 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13121 " Scene: %.20g",(double) scene++);
13123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13124 " Image depth: %.20g",(double) p->depth);
13126 if (p->alpha_trait == BlendPixelTrait)
13127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13134 if (p->storage_class == PseudoClass)
13135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13136 " Storage class: PseudoClass");
13139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13140 " Storage class: DirectClass");
13143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13144 " Number of colors: %.20g",(double) p->colors);
13147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13148 " Number of colors: unspecified");
13150 if (mng_info->adjoin == MagickFalse)
13155 use_global_plte=MagickFalse;
13156 all_images_are_gray=MagickFalse;
13157 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13158 need_local_plte=MagickTrue;
13160 need_defi=MagickFalse;
13161 need_matte=MagickFalse;
13162 mng_info->framing_mode=1;
13163 mng_info->old_framing_mode=1;
13166 if (image_info->page != (char *) NULL)
13169 Determine image bounding box.
13171 SetGeometry(image,&mng_info->page);
13172 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13173 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13185 mng_info->page=image->page;
13186 need_geom=MagickTrue;
13187 if (mng_info->page.width || mng_info->page.height)
13188 need_geom=MagickFalse;
13190 Check all the scenes.
13192 initial_delay=image->delay;
13193 need_iterations=MagickFalse;
13194 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13195 mng_info->equal_physs=MagickTrue,
13196 mng_info->equal_gammas=MagickTrue;
13197 mng_info->equal_srgbs=MagickTrue;
13198 mng_info->equal_backgrounds=MagickTrue;
13200 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13201 defined(PNG_MNG_FEATURES_SUPPORTED)
13202 all_images_are_gray=MagickTrue;
13203 mng_info->equal_palettes=MagickFalse;
13204 need_local_plte=MagickFalse;
13206 for (next_image=image; next_image != (Image *) NULL; )
13210 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13211 mng_info->page.width=next_image->columns+next_image->page.x;
13213 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13214 mng_info->page.height=next_image->rows+next_image->page.y;
13217 if (next_image->page.x || next_image->page.y)
13218 need_defi=MagickTrue;
13220 if (next_image->alpha_trait == BlendPixelTrait)
13221 need_matte=MagickTrue;
13223 if ((int) next_image->dispose >= BackgroundDispose)
13224 if ((next_image->alpha_trait == BlendPixelTrait) ||
13225 next_image->page.x || next_image->page.y ||
13226 ((next_image->columns < mng_info->page.width) &&
13227 (next_image->rows < mng_info->page.height)))
13228 mng_info->need_fram=MagickTrue;
13230 if (next_image->iterations)
13231 need_iterations=MagickTrue;
13233 final_delay=next_image->delay;
13235 if (final_delay != initial_delay || final_delay > 1UL*
13236 next_image->ticks_per_second)
13237 mng_info->need_fram=1;
13239 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13240 defined(PNG_MNG_FEATURES_SUPPORTED)
13242 check for global palette possibility.
13244 if (image->alpha_trait == BlendPixelTrait)
13245 need_local_plte=MagickTrue;
13247 if (need_local_plte == 0)
13249 if (IsImageGray(image,exception) == MagickFalse)
13250 all_images_are_gray=MagickFalse;
13251 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13252 if (use_global_plte == 0)
13253 use_global_plte=mng_info->equal_palettes;
13254 need_local_plte=!mng_info->equal_palettes;
13257 if (GetNextImageInList(next_image) != (Image *) NULL)
13259 if (next_image->background_color.red !=
13260 next_image->next->background_color.red ||
13261 next_image->background_color.green !=
13262 next_image->next->background_color.green ||
13263 next_image->background_color.blue !=
13264 next_image->next->background_color.blue)
13265 mng_info->equal_backgrounds=MagickFalse;
13267 if (next_image->gamma != next_image->next->gamma)
13268 mng_info->equal_gammas=MagickFalse;
13270 if (next_image->rendering_intent !=
13271 next_image->next->rendering_intent)
13272 mng_info->equal_srgbs=MagickFalse;
13274 if ((next_image->units != next_image->next->units) ||
13275 (next_image->resolution.x != next_image->next->resolution.x) ||
13276 (next_image->resolution.y != next_image->next->resolution.y))
13277 mng_info->equal_physs=MagickFalse;
13279 if (mng_info->equal_chrms)
13281 if (next_image->chromaticity.red_primary.x !=
13282 next_image->next->chromaticity.red_primary.x ||
13283 next_image->chromaticity.red_primary.y !=
13284 next_image->next->chromaticity.red_primary.y ||
13285 next_image->chromaticity.green_primary.x !=
13286 next_image->next->chromaticity.green_primary.x ||
13287 next_image->chromaticity.green_primary.y !=
13288 next_image->next->chromaticity.green_primary.y ||
13289 next_image->chromaticity.blue_primary.x !=
13290 next_image->next->chromaticity.blue_primary.x ||
13291 next_image->chromaticity.blue_primary.y !=
13292 next_image->next->chromaticity.blue_primary.y ||
13293 next_image->chromaticity.white_point.x !=
13294 next_image->next->chromaticity.white_point.x ||
13295 next_image->chromaticity.white_point.y !=
13296 next_image->next->chromaticity.white_point.y)
13297 mng_info->equal_chrms=MagickFalse;
13301 next_image=GetNextImageInList(next_image);
13303 if (image_count < 2)
13305 mng_info->equal_backgrounds=MagickFalse;
13306 mng_info->equal_chrms=MagickFalse;
13307 mng_info->equal_gammas=MagickFalse;
13308 mng_info->equal_srgbs=MagickFalse;
13309 mng_info->equal_physs=MagickFalse;
13310 use_global_plte=MagickFalse;
13311 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13312 need_local_plte=MagickTrue;
13314 need_iterations=MagickFalse;
13317 if (mng_info->need_fram == MagickFalse)
13320 Only certain framing rates 100/n are exactly representable without
13321 the FRAM chunk but we'll allow some slop in VLC files
13323 if (final_delay == 0)
13325 if (need_iterations != MagickFalse)
13328 It's probably a GIF with loop; don't run it *too* fast.
13330 if (mng_info->adjoin)
13333 (void) ThrowMagickException(exception,GetMagickModule(),
13335 "input has zero delay between all frames; assuming",
13340 mng_info->ticks_per_second=0;
13342 if (final_delay != 0)
13343 mng_info->ticks_per_second=(png_uint_32)
13344 (image->ticks_per_second/final_delay);
13345 if (final_delay > 50)
13346 mng_info->ticks_per_second=2;
13348 if (final_delay > 75)
13349 mng_info->ticks_per_second=1;
13351 if (final_delay > 125)
13352 mng_info->need_fram=MagickTrue;
13354 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13355 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13356 (final_delay != 25) && (final_delay != 50) && (final_delay !=
13357 1UL*image->ticks_per_second))
13358 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13361 if (mng_info->need_fram != MagickFalse)
13362 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13364 If pseudocolor, we should also check to see if all the
13365 palettes are identical and write a global PLTE if they are.
13369 Write the MNG version 1.0 signature and MHDR chunk.
13371 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13372 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13373 PNGType(chunk,mng_MHDR);
13374 LogPNGChunk(logging,mng_MHDR,28L);
13375 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13376 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13377 PNGLong(chunk+12,mng_info->ticks_per_second);
13378 PNGLong(chunk+16,0L); /* layer count=unknown */
13379 PNGLong(chunk+20,0L); /* frame count=unknown */
13380 PNGLong(chunk+24,0L); /* play time=unknown */
13385 if (need_defi || mng_info->need_fram || use_global_plte)
13386 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13389 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13394 if (need_defi || mng_info->need_fram || use_global_plte)
13395 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13398 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13406 if (need_defi || mng_info->need_fram || use_global_plte)
13407 PNGLong(chunk+28,11L); /* simplicity=LC */
13410 PNGLong(chunk+28,9L); /* simplicity=VLC */
13415 if (need_defi || mng_info->need_fram || use_global_plte)
13416 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13419 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13422 (void) WriteBlob(image,32,chunk);
13423 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13424 option=GetImageOption(image_info,"mng:need-cacheoff");
13425 if (option != (const char *) NULL)
13431 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13433 PNGType(chunk,mng_nEED);
13434 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13435 (void) WriteBlobMSBULong(image,(size_t) length);
13436 LogPNGChunk(logging,mng_nEED,(size_t) length);
13438 (void) WriteBlob(image,length,chunk);
13439 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13441 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13442 (GetNextImageInList(image) != (Image *) NULL) &&
13443 (image->iterations != 1))
13446 Write MNG TERM chunk
13448 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13449 PNGType(chunk,mng_TERM);
13450 LogPNGChunk(logging,mng_TERM,10L);
13451 chunk[4]=3; /* repeat animation */
13452 chunk[5]=0; /* show last frame when done */
13453 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13454 final_delay/MagickMax(image->ticks_per_second,1)));
13456 if (image->iterations == 0)
13457 PNGLong(chunk+10,PNG_UINT_31_MAX);
13460 PNGLong(chunk+10,(png_uint_32) image->iterations);
13462 if (logging != MagickFalse)
13464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13465 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13466 final_delay/MagickMax(image->ticks_per_second,1)));
13468 if (image->iterations == 0)
13469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13470 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13474 " Image iterations: %.20g",(double) image->iterations);
13476 (void) WriteBlob(image,14,chunk);
13477 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13480 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13482 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13483 mng_info->equal_srgbs)
13486 Write MNG sRGB chunk
13488 (void) WriteBlobMSBULong(image,1L);
13489 PNGType(chunk,mng_sRGB);
13490 LogPNGChunk(logging,mng_sRGB,1L);
13492 if (image->rendering_intent != UndefinedIntent)
13493 chunk[4]=(unsigned char)
13494 Magick_RenderingIntent_to_PNG_RenderingIntent(
13495 (image->rendering_intent));
13498 chunk[4]=(unsigned char)
13499 Magick_RenderingIntent_to_PNG_RenderingIntent(
13500 (PerceptualIntent));
13502 (void) WriteBlob(image,5,chunk);
13503 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13504 mng_info->have_write_global_srgb=MagickTrue;
13509 if (image->gamma && mng_info->equal_gammas)
13512 Write MNG gAMA chunk
13514 (void) WriteBlobMSBULong(image,4L);
13515 PNGType(chunk,mng_gAMA);
13516 LogPNGChunk(logging,mng_gAMA,4L);
13517 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13518 (void) WriteBlob(image,8,chunk);
13519 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13520 mng_info->have_write_global_gama=MagickTrue;
13522 if (mng_info->equal_chrms)
13528 Write MNG cHRM chunk
13530 (void) WriteBlobMSBULong(image,32L);
13531 PNGType(chunk,mng_cHRM);
13532 LogPNGChunk(logging,mng_cHRM,32L);
13533 primary=image->chromaticity.white_point;
13534 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13535 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13536 primary=image->chromaticity.red_primary;
13537 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13538 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13539 primary=image->chromaticity.green_primary;
13540 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13541 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13542 primary=image->chromaticity.blue_primary;
13543 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13544 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13545 (void) WriteBlob(image,36,chunk);
13546 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13547 mng_info->have_write_global_chrm=MagickTrue;
13550 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13553 Write MNG pHYs chunk
13555 (void) WriteBlobMSBULong(image,9L);
13556 PNGType(chunk,mng_pHYs);
13557 LogPNGChunk(logging,mng_pHYs,9L);
13559 if (image->units == PixelsPerInchResolution)
13561 PNGLong(chunk+4,(png_uint_32)
13562 (image->resolution.x*100.0/2.54+0.5));
13564 PNGLong(chunk+8,(png_uint_32)
13565 (image->resolution.y*100.0/2.54+0.5));
13572 if (image->units == PixelsPerCentimeterResolution)
13574 PNGLong(chunk+4,(png_uint_32)
13575 (image->resolution.x*100.0+0.5));
13577 PNGLong(chunk+8,(png_uint_32)
13578 (image->resolution.y*100.0+0.5));
13585 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13586 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13590 (void) WriteBlob(image,13,chunk);
13591 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13594 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13595 or does not cover the entire frame.
13597 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13598 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13599 (image->page.width+image->page.x < mng_info->page.width))
13600 || (image->page.height && (image->page.height+image->page.y
13601 < mng_info->page.height))))
13603 (void) WriteBlobMSBULong(image,6L);
13604 PNGType(chunk,mng_BACK);
13605 LogPNGChunk(logging,mng_BACK,6L);
13606 red=ScaleQuantumToShort(image->background_color.red);
13607 green=ScaleQuantumToShort(image->background_color.green);
13608 blue=ScaleQuantumToShort(image->background_color.blue);
13609 PNGShort(chunk+4,red);
13610 PNGShort(chunk+6,green);
13611 PNGShort(chunk+8,blue);
13612 (void) WriteBlob(image,10,chunk);
13613 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13614 if (mng_info->equal_backgrounds)
13616 (void) WriteBlobMSBULong(image,6L);
13617 PNGType(chunk,mng_bKGD);
13618 LogPNGChunk(logging,mng_bKGD,6L);
13619 (void) WriteBlob(image,10,chunk);
13620 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13624 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13625 if ((need_local_plte == MagickFalse) &&
13626 (image->storage_class == PseudoClass) &&
13627 (all_images_are_gray == MagickFalse))
13633 Write MNG PLTE chunk
13635 data_length=3*image->colors;
13636 (void) WriteBlobMSBULong(image,data_length);
13637 PNGType(chunk,mng_PLTE);
13638 LogPNGChunk(logging,mng_PLTE,data_length);
13640 for (i=0; i < (ssize_t) image->colors; i++)
13642 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13643 image->colormap[i].red) & 0xff);
13644 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13645 image->colormap[i].green) & 0xff);
13646 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13647 image->colormap[i].blue) & 0xff);
13650 (void) WriteBlob(image,data_length+4,chunk);
13651 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13652 mng_info->have_write_global_plte=MagickTrue;
13658 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13659 defined(PNG_MNG_FEATURES_SUPPORTED)
13660 mng_info->equal_palettes=MagickFalse;
13664 if (mng_info->adjoin)
13666 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13667 defined(PNG_MNG_FEATURES_SUPPORTED)
13669 If we aren't using a global palette for the entire MNG, check to
13670 see if we can use one for two or more consecutive images.
13672 if (need_local_plte && use_global_plte && !all_images_are_gray)
13674 if (mng_info->IsPalette)
13677 When equal_palettes is true, this image has the same palette
13678 as the previous PseudoClass image
13680 mng_info->have_write_global_plte=mng_info->equal_palettes;
13681 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13682 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13685 Write MNG PLTE chunk
13690 data_length=3*image->colors;
13691 (void) WriteBlobMSBULong(image,data_length);
13692 PNGType(chunk,mng_PLTE);
13693 LogPNGChunk(logging,mng_PLTE,data_length);
13695 for (i=0; i < (ssize_t) image->colors; i++)
13697 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13698 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13699 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13702 (void) WriteBlob(image,data_length+4,chunk);
13703 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13704 (uInt) (data_length+4)));
13705 mng_info->have_write_global_plte=MagickTrue;
13709 mng_info->have_write_global_plte=MagickFalse;
13720 previous_x=mng_info->page.x;
13721 previous_y=mng_info->page.y;
13728 mng_info->page=image->page;
13729 if ((mng_info->page.x != previous_x) ||
13730 (mng_info->page.y != previous_y))
13732 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13733 PNGType(chunk,mng_DEFI);
13734 LogPNGChunk(logging,mng_DEFI,12L);
13735 chunk[4]=0; /* object 0 MSB */
13736 chunk[5]=0; /* object 0 LSB */
13737 chunk[6]=0; /* visible */
13738 chunk[7]=0; /* abstract */
13739 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13740 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13741 (void) WriteBlob(image,16,chunk);
13742 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13747 mng_info->write_mng=write_mng;
13749 if ((int) image->dispose >= 3)
13750 mng_info->framing_mode=3;
13752 if (mng_info->need_fram && mng_info->adjoin &&
13753 ((image->delay != mng_info->delay) ||
13754 (mng_info->framing_mode != mng_info->old_framing_mode)))
13756 if (image->delay == mng_info->delay)
13759 Write a MNG FRAM chunk with the new framing mode.
13761 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13762 PNGType(chunk,mng_FRAM);
13763 LogPNGChunk(logging,mng_FRAM,1L);
13764 chunk[4]=(unsigned char) mng_info->framing_mode;
13765 (void) WriteBlob(image,5,chunk);
13766 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13771 Write a MNG FRAM chunk with the delay.
13773 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13774 PNGType(chunk,mng_FRAM);
13775 LogPNGChunk(logging,mng_FRAM,10L);
13776 chunk[4]=(unsigned char) mng_info->framing_mode;
13777 chunk[5]=0; /* frame name separator (no name) */
13778 chunk[6]=2; /* flag for changing default delay */
13779 chunk[7]=0; /* flag for changing frame timeout */
13780 chunk[8]=0; /* flag for changing frame clipping */
13781 chunk[9]=0; /* flag for changing frame sync_id */
13782 PNGLong(chunk+10,(png_uint_32)
13783 ((mng_info->ticks_per_second*
13784 image->delay)/MagickMax(image->ticks_per_second,1)));
13785 (void) WriteBlob(image,14,chunk);
13786 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13787 mng_info->delay=(png_uint_32) image->delay;
13789 mng_info->old_framing_mode=mng_info->framing_mode;
13792 #if defined(JNG_SUPPORTED)
13793 if (image_info->compression == JPEGCompression)
13798 if (logging != MagickFalse)
13799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13800 " Writing JNG object.");
13801 /* To do: specify the desired alpha compression method. */
13802 write_info=CloneImageInfo(image_info);
13803 write_info->compression=UndefinedCompression;
13804 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13805 write_info=DestroyImageInfo(write_info);
13810 if (logging != MagickFalse)
13811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13812 " Writing PNG object.");
13814 mng_info->need_blob = MagickFalse;
13815 mng_info->ping_preserve_colormap = MagickFalse;
13817 /* We don't want any ancillary chunks written */
13818 mng_info->ping_exclude_bKGD=MagickTrue;
13819 mng_info->ping_exclude_cHRM=MagickTrue;
13820 mng_info->ping_exclude_date=MagickTrue;
13821 mng_info->ping_exclude_EXIF=MagickTrue;
13822 mng_info->ping_exclude_gAMA=MagickTrue;
13823 mng_info->ping_exclude_iCCP=MagickTrue;
13824 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13825 mng_info->ping_exclude_oFFs=MagickTrue;
13826 mng_info->ping_exclude_pHYs=MagickTrue;
13827 mng_info->ping_exclude_sRGB=MagickTrue;
13828 mng_info->ping_exclude_tEXt=MagickTrue;
13829 mng_info->ping_exclude_tRNS=MagickTrue;
13830 mng_info->ping_exclude_vpAg=MagickTrue;
13831 mng_info->ping_exclude_zCCP=MagickTrue;
13832 mng_info->ping_exclude_zTXt=MagickTrue;
13834 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13837 if (status == MagickFalse)
13839 MngInfoFreeStruct(mng_info,&have_mng_structure);
13840 (void) CloseBlob(image);
13841 return(MagickFalse);
13843 (void) CatchImageException(image);
13844 if (GetNextImageInList(image) == (Image *) NULL)
13846 image=SyncNextImageInList(image);
13847 status=SetImageProgress(image,SaveImagesTag,scene++,
13848 GetImageListLength(image));
13850 if (status == MagickFalse)
13853 } while (mng_info->adjoin);
13857 while (GetPreviousImageInList(image) != (Image *) NULL)
13858 image=GetPreviousImageInList(image);
13860 Write the MEND chunk.
13862 (void) WriteBlobMSBULong(image,0x00000000L);
13863 PNGType(chunk,mng_MEND);
13864 LogPNGChunk(logging,mng_MEND,0L);
13865 (void) WriteBlob(image,4,chunk);
13866 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13869 Relinquish resources.
13871 (void) CloseBlob(image);
13872 MngInfoFreeStruct(mng_info,&have_mng_structure);
13874 if (logging != MagickFalse)
13875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13877 return(MagickTrue);
13879 #else /* PNG_LIBPNG_VER > 10011 */
13881 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13884 printf("Your PNG library is too old: You have libpng-%s\n",
13885 PNG_LIBPNG_VER_STRING);
13887 ThrowBinaryException(CoderError,"PNG library is too old",
13888 image_info->filename);
13891 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13893 return(WritePNGImage(image_info,image));
13895 #endif /* PNG_LIBPNG_VER > 10011 */