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 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2469 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2470 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2472 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2473 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2476 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2479 (void) png_get_bKGD(ping, ping_info, &ping_background);
2481 if (ping_bit_depth < 8)
2483 png_set_packing(ping);
2487 image->depth=ping_bit_depth;
2488 image->depth=GetImageQuantumDepth(image,MagickFalse);
2489 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2491 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2492 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2494 image->rendering_intent=UndefinedIntent;
2495 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2497 (void) ResetMagickMemory(&image->chromaticity,0,
2498 sizeof(image->chromaticity));
2501 if (logging != MagickFalse)
2503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2504 " PNG width: %.20g, height: %.20g",
2505 (double) ping_width, (double) ping_height);
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " PNG color_type: %d, bit_depth: %d",
2509 ping_color_type, ping_bit_depth);
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 " PNG compression_method: %d",
2513 ping_compression_method);
2515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2516 " PNG interlace_method: %d, filter_method: %d",
2517 ping_interlace_method,ping_filter_method);
2520 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2522 ping_found_iCCP=MagickTrue;
2523 if (logging != MagickFalse)
2524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525 " Found PNG iCCP chunk.");
2528 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2530 ping_found_gAMA=MagickTrue;
2531 if (logging != MagickFalse)
2532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2533 " Found PNG gAMA chunk.");
2536 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2538 ping_found_cHRM=MagickTrue;
2539 if (logging != MagickFalse)
2540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2541 " Found PNG cHRM chunk.");
2544 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2547 ping_found_sRGB=MagickTrue;
2548 if (logging != MagickFalse)
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550 " Found PNG sRGB chunk.");
2553 #ifdef PNG_READ_iCCP_SUPPORTED
2554 if (ping_found_iCCP !=MagickTrue &&
2555 ping_found_sRGB != MagickTrue &&
2556 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2558 ping_found_iCCP=MagickTrue;
2559 if (logging != MagickFalse)
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2561 " Found PNG iCCP chunk.");
2564 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2569 #if (PNG_LIBPNG_VER < 10500)
2583 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2586 if (profile_length != 0)
2591 if (logging != MagickFalse)
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 " Reading PNG iCCP chunk.");
2595 profile=BlobToStringInfo(info,profile_length);
2597 if (profile == (StringInfo *) NULL)
2599 png_warning(ping, "ICC profile is NULL");
2600 profile=DestroyStringInfo(profile);
2604 if (ping_preserve_iCCP == MagickFalse)
2618 length=(png_uint_32) GetStringInfoLength(profile);
2620 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2622 if (length == sRGB_info[icheck].len)
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Got a %lu-byte ICC profile (potentially sRGB)",
2628 (unsigned long) length);
2630 data=GetStringInfoDatum(profile);
2631 profile_crc=crc32(0,data,length);
2633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2634 " with crc=%8x",(unsigned int) profile_crc);
2638 if (profile_crc == sRGB_info[icheck].crc)
2640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2641 " It is sRGB with rendering intent = %s",
2642 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2643 sRGB_info[icheck].intent));
2644 if (image->rendering_intent==UndefinedIntent)
2646 image->rendering_intent=
2647 Magick_RenderingIntent_from_PNG_RenderingIntent(
2648 sRGB_info[icheck].intent);
2654 if (sRGB_info[icheck].len == 0)
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " Got a %lu-byte ICC profile not recognized as sRGB",
2658 (unsigned long) length);
2659 (void) SetImageProfile(image,"icc",profile,exception);
2662 else /* Preserve-iCCP */
2664 (void) SetImageProfile(image,"icc",profile,exception);
2667 profile=DestroyStringInfo(profile);
2673 #if defined(PNG_READ_sRGB_SUPPORTED)
2675 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2678 if (png_get_sRGB(ping,ping_info,&intent))
2680 if (image->rendering_intent == UndefinedIntent)
2681 image->rendering_intent=
2682 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2684 if (logging != MagickFalse)
2685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2686 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2690 else if (mng_info->have_global_srgb)
2692 if (image->rendering_intent == UndefinedIntent)
2693 image->rendering_intent=
2694 Magick_RenderingIntent_from_PNG_RenderingIntent
2695 (mng_info->global_srgb_intent);
2702 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2703 if (mng_info->have_global_gama)
2704 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2706 if (png_get_gAMA(ping,ping_info,&file_gamma))
2708 image->gamma=(float) file_gamma;
2709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2715 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2717 if (mng_info->have_global_chrm != MagickFalse)
2719 (void) png_set_cHRM(ping,ping_info,
2720 mng_info->global_chrm.white_point.x,
2721 mng_info->global_chrm.white_point.y,
2722 mng_info->global_chrm.red_primary.x,
2723 mng_info->global_chrm.red_primary.y,
2724 mng_info->global_chrm.green_primary.x,
2725 mng_info->global_chrm.green_primary.y,
2726 mng_info->global_chrm.blue_primary.x,
2727 mng_info->global_chrm.blue_primary.y);
2731 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2733 (void) png_get_cHRM(ping,ping_info,
2734 &image->chromaticity.white_point.x,
2735 &image->chromaticity.white_point.y,
2736 &image->chromaticity.red_primary.x,
2737 &image->chromaticity.red_primary.y,
2738 &image->chromaticity.green_primary.x,
2739 &image->chromaticity.green_primary.y,
2740 &image->chromaticity.blue_primary.x,
2741 &image->chromaticity.blue_primary.y);
2743 ping_found_cHRM=MagickTrue;
2745 if (image->chromaticity.red_primary.x>0.6399f &&
2746 image->chromaticity.red_primary.x<0.6401f &&
2747 image->chromaticity.red_primary.y>0.3299f &&
2748 image->chromaticity.red_primary.y<0.3301f &&
2749 image->chromaticity.green_primary.x>0.2999f &&
2750 image->chromaticity.green_primary.x<0.3001f &&
2751 image->chromaticity.green_primary.y>0.5999f &&
2752 image->chromaticity.green_primary.y<0.6001f &&
2753 image->chromaticity.blue_primary.x>0.1499f &&
2754 image->chromaticity.blue_primary.x<0.1501f &&
2755 image->chromaticity.blue_primary.y>0.0599f &&
2756 image->chromaticity.blue_primary.y<0.0601f &&
2757 image->chromaticity.white_point.x>0.3126f &&
2758 image->chromaticity.white_point.x<0.3128f &&
2759 image->chromaticity.white_point.y>0.3289f &&
2760 image->chromaticity.white_point.y<0.3291f)
2761 ping_found_sRGB_cHRM=MagickTrue;
2764 if (image->rendering_intent != UndefinedIntent)
2766 if (ping_found_sRGB != MagickTrue &&
2767 (ping_found_gAMA != MagickTrue ||
2768 (image->gamma > .45 && image->gamma < .46)) &&
2769 (ping_found_cHRM != MagickTrue ||
2770 ping_found_sRGB_cHRM != MagickFalse) &&
2771 ping_found_iCCP != MagickTrue)
2773 png_set_sRGB(ping,ping_info,
2774 Magick_RenderingIntent_to_PNG_RenderingIntent
2775 (image->rendering_intent));
2776 file_gamma=1.000f/2.200f;
2777 ping_found_sRGB=MagickTrue;
2778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2779 " Setting sRGB as if in input");
2783 #if defined(PNG_oFFs_SUPPORTED)
2784 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2786 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2787 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2789 if (logging != MagickFalse)
2790 if (image->page.x || image->page.y)
2791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2792 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2793 image->page.x,(double) image->page.y);
2796 #if defined(PNG_pHYs_SUPPORTED)
2797 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2799 if (mng_info->have_global_phys)
2801 png_set_pHYs(ping,ping_info,
2802 mng_info->global_x_pixels_per_unit,
2803 mng_info->global_y_pixels_per_unit,
2804 mng_info->global_phys_unit_type);
2808 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2811 Set image resolution.
2813 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2815 image->resolution.x=(double) x_resolution;
2816 image->resolution.y=(double) y_resolution;
2818 if (unit_type == PNG_RESOLUTION_METER)
2820 image->units=PixelsPerCentimeterResolution;
2821 image->resolution.x=(double) x_resolution/100.0;
2822 image->resolution.y=(double) y_resolution/100.0;
2825 if (logging != MagickFalse)
2826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2827 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2828 (double) x_resolution,(double) y_resolution,unit_type);
2832 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2837 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2839 if ((number_colors == 0) &&
2840 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2842 if (mng_info->global_plte_length)
2844 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2845 (int) mng_info->global_plte_length);
2847 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2849 if (mng_info->global_trns_length)
2852 "global tRNS has more entries than global PLTE");
2856 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2857 (int) mng_info->global_trns_length,NULL);
2860 #ifdef PNG_READ_bKGD_SUPPORTED
2862 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2863 mng_info->have_saved_bkgd_index ||
2865 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2870 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2871 if (mng_info->have_saved_bkgd_index)
2872 background.index=mng_info->saved_bkgd_index;
2874 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2875 background.index=ping_background->index;
2877 background.red=(png_uint_16)
2878 mng_info->global_plte[background.index].red;
2880 background.green=(png_uint_16)
2881 mng_info->global_plte[background.index].green;
2883 background.blue=(png_uint_16)
2884 mng_info->global_plte[background.index].blue;
2886 background.gray=(png_uint_16)
2887 mng_info->global_plte[background.index].green;
2889 png_set_bKGD(ping,ping_info,&background);
2894 png_error(ping,"No global PLTE in file");
2898 #ifdef PNG_READ_bKGD_SUPPORTED
2899 if (mng_info->have_global_bkgd &&
2900 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2901 image->background_color=mng_info->mng_global_bkgd;
2903 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2909 Set image background color.
2911 if (logging != MagickFalse)
2912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2913 " Reading PNG bKGD chunk.");
2915 /* Scale background components to 16-bit, then scale
2918 if (logging != MagickFalse)
2919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2920 " raw ping_background=(%d,%d,%d).",ping_background->red,
2921 ping_background->green,ping_background->blue);
2925 if (ping_file_depth == 1)
2928 else if (ping_file_depth == 2)
2931 else if (ping_file_depth == 4)
2934 if (ping_file_depth <= 8)
2937 ping_background->red *= bkgd_scale;
2938 ping_background->green *= bkgd_scale;
2939 ping_background->blue *= bkgd_scale;
2941 if (logging != MagickFalse)
2943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2944 " bkgd_scale=%d.",bkgd_scale);
2946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2947 " ping_background=(%d,%d,%d).",ping_background->red,
2948 ping_background->green,ping_background->blue);
2951 image->background_color.red=
2952 ScaleShortToQuantum(ping_background->red);
2954 image->background_color.green=
2955 ScaleShortToQuantum(ping_background->green);
2957 image->background_color.blue=
2958 ScaleShortToQuantum(ping_background->blue);
2960 image->background_color.alpha=OpaqueAlpha;
2962 if (logging != MagickFalse)
2963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2964 " image->background_color=(%.20g,%.20g,%.20g).",
2965 (double) image->background_color.red,
2966 (double) image->background_color.green,
2967 (double) image->background_color.blue);
2969 #endif /* PNG_READ_bKGD_SUPPORTED */
2971 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2974 Image has a tRNS chunk.
2982 if (logging != MagickFalse)
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984 " Reading PNG tRNS chunk.");
2986 max_sample = (int) ((one << ping_file_depth) - 1);
2988 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2989 (int)ping_trans_color->gray > max_sample) ||
2990 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2991 ((int)ping_trans_color->red > max_sample ||
2992 (int)ping_trans_color->green > max_sample ||
2993 (int)ping_trans_color->blue > max_sample)))
2995 if (logging != MagickFalse)
2996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2997 " Ignoring PNG tRNS chunk with out-of-range sample.");
2998 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2999 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3000 image->alpha_trait=UndefinedPixelTrait;
3007 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3009 /* Scale transparent_color to short */
3010 transparent_color.red= scale_to_short*ping_trans_color->red;
3011 transparent_color.green= scale_to_short*ping_trans_color->green;
3012 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3013 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3015 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3017 if (logging != MagickFalse)
3019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3020 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " scaled graylevel is %.20g.",transparent_color.alpha);
3025 transparent_color.red=transparent_color.alpha;
3026 transparent_color.green=transparent_color.alpha;
3027 transparent_color.blue=transparent_color.alpha;
3031 #if defined(PNG_READ_sBIT_SUPPORTED)
3032 if (mng_info->have_global_sbit)
3034 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3035 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3038 num_passes=png_set_interlace_handling(ping);
3040 png_read_update_info(ping,ping_info);
3042 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3045 Initialize image structure.
3047 mng_info->image_box.left=0;
3048 mng_info->image_box.right=(ssize_t) ping_width;
3049 mng_info->image_box.top=0;
3050 mng_info->image_box.bottom=(ssize_t) ping_height;
3051 if (mng_info->mng_type == 0)
3053 mng_info->mng_width=ping_width;
3054 mng_info->mng_height=ping_height;
3055 mng_info->frame=mng_info->image_box;
3056 mng_info->clip=mng_info->image_box;
3061 image->page.y=mng_info->y_off[mng_info->object_id];
3064 image->compression=ZipCompression;
3065 image->columns=ping_width;
3066 image->rows=ping_height;
3068 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3069 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3071 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
3072 image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
3074 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
3075 * image->colorspace to GRAY, and reset image->chromaticity.
3077 image->intensity = Rec709LuminancePixelIntensityMethod;
3078 SetImageColorspace(image,GRAYColorspace,exception);
3082 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3083 " image->colorspace=%d",(int) image->colorspace);
3085 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3086 ((int) ping_bit_depth < 16 &&
3087 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3092 image->storage_class=PseudoClass;
3094 image->colors=one << ping_file_depth;
3095 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3096 if (image->colors > 256)
3099 if (image->colors > 65536L)
3100 image->colors=65536L;
3102 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3107 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3108 image->colors=(size_t) number_colors;
3110 if (logging != MagickFalse)
3111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3112 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3116 if (image->storage_class == PseudoClass)
3119 Initialize image colormap.
3121 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3122 png_error(ping,"Memory allocation failed");
3124 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3129 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3131 for (i=0; i < (ssize_t) number_colors; i++)
3133 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3134 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3135 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3138 for ( ; i < (ssize_t) image->colors; i++)
3140 image->colormap[i].red=0;
3141 image->colormap[i].green=0;
3142 image->colormap[i].blue=0;
3151 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3156 for (i=0; i < (ssize_t) image->colors; i++)
3158 image->colormap[i].red=(Quantum) (i*scale);
3159 image->colormap[i].green=(Quantum) (i*scale);
3160 image->colormap[i].blue=(Quantum) (i*scale);
3165 /* Set some properties for reporting by "identify" */
3170 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3171 ping_interlace_method in value */
3173 (void) FormatLocaleString(msg,MaxTextExtent,
3174 "%d, %d",(int) ping_width, (int) ping_height);
3175 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3177 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3178 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3180 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3181 (int) ping_color_type,
3182 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3183 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3185 if (ping_interlace_method == 0)
3187 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3188 (int) ping_interlace_method);
3190 else if (ping_interlace_method == 1)
3192 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3193 (int) ping_interlace_method);
3197 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3198 (int) ping_interlace_method);
3200 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3202 if (number_colors != 0)
3204 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3205 (int) number_colors);
3206 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3212 Read image scanlines.
3214 if (image->delay != 0)
3215 mng_info->scenes_found++;
3217 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3218 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3219 (image_info->first_scene+image_info->number_scenes))))
3221 /* This happens later in non-ping decodes */
3222 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3223 image->storage_class=DirectClass;
3225 if (logging != MagickFalse)
3226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3227 " Skipping PNG image data for scene %.20g",(double)
3228 mng_info->scenes_found-1);
3229 png_destroy_read_struct(&ping,&ping_info,&end_info);
3231 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3232 UnlockSemaphoreInfo(ping_semaphore);
3235 if (logging != MagickFalse)
3236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3237 " exit ReadOnePNGImage().");
3242 if (logging != MagickFalse)
3243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3244 " Reading PNG IDAT chunk(s)");
3247 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3248 sizeof(*ping_pixels));
3250 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3252 if (pixel_info == (MemoryInfo *) NULL)
3253 png_error(ping,"Memory allocation failed");
3254 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3256 if (logging != MagickFalse)
3257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3258 " Converting PNG pixels to pixel packets");
3260 Convert PNG pixels to pixel packets.
3262 quantum_info=AcquireQuantumInfo(image_info,image);
3264 if (quantum_info == (QuantumInfo *) NULL)
3265 png_error(ping,"Failed to allocate quantum_info");
3267 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3272 found_transparent_pixel;
3274 found_transparent_pixel=MagickFalse;
3276 if (image->storage_class == DirectClass)
3278 for (pass=0; pass < num_passes; pass++)
3281 Convert image to DirectClass pixel packets.
3283 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3284 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3285 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3286 BlendPixelTrait : UndefinedPixelTrait;
3288 for (y=0; y < (ssize_t) image->rows; y++)
3291 row_offset=ping_rowbytes*y;
3296 png_read_row(ping,ping_pixels+row_offset,NULL);
3298 if (pass < num_passes-1)
3301 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3303 if (q == (Quantum *) NULL)
3306 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3307 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3308 GrayQuantum,ping_pixels+row_offset,exception);
3310 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3311 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3312 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3314 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3315 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3316 RGBAQuantum,ping_pixels+row_offset,exception);
3318 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3319 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3320 IndexQuantum,ping_pixels+row_offset,exception);
3322 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3323 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3324 RGBQuantum,ping_pixels+row_offset,exception);
3326 if (found_transparent_pixel == MagickFalse)
3328 /* Is there a transparent pixel in the row? */
3329 if (y== 0 && logging != MagickFalse)
3330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3331 " Looking for cheap transparent pixel");
3333 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3335 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3336 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3337 (GetPixelAlpha(image,q) != OpaqueAlpha))
3339 if (logging != MagickFalse)
3340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3343 found_transparent_pixel = MagickTrue;
3346 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3347 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3348 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3349 transparent_color.red &&
3350 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3351 transparent_color.green &&
3352 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3353 transparent_color.blue))
3355 if (logging != MagickFalse)
3356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3358 found_transparent_pixel = MagickTrue;
3361 q+=GetPixelChannels(image);
3365 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3367 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3370 if (status == MagickFalse)
3373 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3377 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3379 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3380 if (status == MagickFalse)
3386 else /* image->storage_class != DirectClass */
3388 for (pass=0; pass < num_passes; pass++)
3397 Convert grayscale image to PseudoClass pixel packets.
3399 if (logging != MagickFalse)
3400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3401 " Converting grayscale pixels to pixel packets");
3403 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3404 BlendPixelTrait : UndefinedPixelTrait;
3406 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3407 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3408 sizeof(*quantum_scanline));
3410 if (quantum_scanline == (Quantum *) NULL)
3411 png_error(ping,"Memory allocation failed");
3413 for (y=0; y < (ssize_t) image->rows; y++)
3419 row_offset=ping_rowbytes*y;
3424 png_read_row(ping,ping_pixels+row_offset,NULL);
3426 if (pass < num_passes-1)
3429 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3431 if (q == (Quantum *) NULL)
3434 p=ping_pixels+row_offset;
3437 switch (ping_bit_depth)
3442 if (ping_color_type == 4)
3443 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3447 alpha=ScaleCharToQuantum((unsigned char)*p++);
3449 SetPixelAlpha(image,alpha,q);
3451 if (alpha != OpaqueAlpha)
3452 found_transparent_pixel = MagickTrue;
3454 q+=GetPixelChannels(image);
3458 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3466 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3468 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3472 if (image->colors > 256)
3473 quantum=((*p++) << 8);
3479 *r=ScaleShortToQuantum(quantum);
3482 if (ping_color_type == 4)
3484 if (image->colors > 256)
3485 quantum=((*p++) << 8);
3491 alpha=ScaleShortToQuantum(quantum);
3492 SetPixelAlpha(image,alpha,q);
3494 if (alpha != OpaqueAlpha)
3495 found_transparent_pixel = MagickTrue;
3497 q+=GetPixelChannels(image);
3500 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3502 p++; /* strip low byte */
3504 if (ping_color_type == 4)
3506 SetPixelAlpha(image,*p++,q);
3508 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3509 found_transparent_pixel = MagickTrue;
3512 q+=GetPixelChannels(image);
3525 Transfer image scanline.
3529 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3531 if (q == (Quantum *) NULL)
3533 for (x=0; x < (ssize_t) image->columns; x++)
3535 SetPixelIndex(image,*r++,q);
3536 q+=GetPixelChannels(image);
3539 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3542 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3544 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3547 if (status == MagickFalse)
3552 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3554 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3556 if (status == MagickFalse)
3560 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3563 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3564 UndefinedPixelTrait;
3566 if (logging != MagickFalse)
3568 if (found_transparent_pixel != MagickFalse)
3569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3570 " Found transparent pixel");
3573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3574 " No transparent pixel was found");
3576 ping_color_type&=0x03;
3581 if (quantum_info != (QuantumInfo *) NULL)
3582 quantum_info=DestroyQuantumInfo(quantum_info);
3584 if (image->storage_class == PseudoClass)
3589 alpha_trait=image->alpha_trait;
3590 image->alpha_trait=UndefinedPixelTrait;
3591 (void) SyncImage(image,exception);
3592 image->alpha_trait=alpha_trait;
3595 png_read_end(ping,end_info);
3597 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3598 (ssize_t) image_info->first_scene && image->delay != 0)
3600 png_destroy_read_struct(&ping,&ping_info,&end_info);
3601 pixel_info=RelinquishVirtualMemory(pixel_info);
3603 (void) SetImageBackgroundColor(image,exception);
3604 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3605 UnlockSemaphoreInfo(ping_semaphore);
3607 if (logging != MagickFalse)
3608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3609 " exit ReadOnePNGImage() early.");
3613 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3619 Image has a transparent background.
3621 storage_class=image->storage_class;
3622 image->alpha_trait=BlendPixelTrait;
3624 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3626 if (storage_class == PseudoClass)
3628 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3630 for (x=0; x < ping_num_trans; x++)
3632 image->colormap[x].alpha_trait=BlendPixelTrait;
3633 image->colormap[x].alpha =
3634 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3638 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3640 for (x=0; x < (int) image->colors; x++)
3642 if (ScaleQuantumToShort(image->colormap[x].red) ==
3643 transparent_color.alpha)
3645 image->colormap[x].alpha_trait=BlendPixelTrait;
3646 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3650 (void) SyncImage(image,exception);
3653 #if 1 /* Should have already been done above, but glennrp problem P10
3658 for (y=0; y < (ssize_t) image->rows; y++)
3660 image->storage_class=storage_class;
3661 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3663 if (q == (Quantum *) NULL)
3667 /* Caution: on a Q8 build, this does not distinguish between
3668 * 16-bit colors that differ only in the low byte
3670 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3672 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3673 transparent_color.red &&
3674 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3675 transparent_color.green &&
3676 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3677 transparent_color.blue)
3679 SetPixelAlpha(image,TransparentAlpha,q);
3682 #if 0 /* I have not found a case where this is needed. */
3685 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3689 q+=GetPixelChannels(image);
3692 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3698 image->storage_class=DirectClass;
3701 for (j = 0; j < 2; j++)
3704 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3705 MagickTrue : MagickFalse;
3707 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3708 MagickTrue : MagickFalse;
3710 if (status != MagickFalse)
3711 for (i=0; i < (ssize_t) num_text; i++)
3713 /* Check for a profile */
3715 if (logging != MagickFalse)
3716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3717 " Reading PNG text chunk");
3719 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3721 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3731 length=text[i].text_length;
3732 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3734 if (value == (char *) NULL)
3736 png_error(ping,"Memory allocation failed");
3740 (void) ConcatenateMagickString(value,text[i].text,length+2);
3742 /* Don't save "density" or "units" property if we have a pHYs
3745 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3746 (LocaleCompare(text[i].key,"density") != 0 &&
3747 LocaleCompare(text[i].key,"units") != 0))
3748 (void) SetImageProperty(image,text[i].key,value,exception);
3750 if (logging != MagickFalse)
3752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3753 " length: %lu",(unsigned long) length);
3754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3755 " Keyword: %s",text[i].key);
3758 value=DestroyString(value);
3761 num_text_total += num_text;
3764 #ifdef MNG_OBJECT_BUFFERS
3766 Store the object if necessary.
3768 if (object_id && !mng_info->frozen[object_id])
3770 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3773 create a new object buffer.
3775 mng_info->ob[object_id]=(MngBuffer *)
3776 AcquireMagickMemory(sizeof(MngBuffer));
3778 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3780 mng_info->ob[object_id]->image=(Image *) NULL;
3781 mng_info->ob[object_id]->reference_count=1;
3785 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3786 mng_info->ob[object_id]->frozen)
3788 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3789 png_error(ping,"Memory allocation failed");
3791 if (mng_info->ob[object_id]->frozen)
3792 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3798 if (mng_info->ob[object_id]->image != (Image *) NULL)
3799 mng_info->ob[object_id]->image=DestroyImage
3800 (mng_info->ob[object_id]->image);
3802 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3805 if (mng_info->ob[object_id]->image != (Image *) NULL)
3806 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3809 png_error(ping, "Cloning image for object buffer failed");
3811 if (ping_width > 250000L || ping_height > 250000L)
3812 png_error(ping,"PNG Image dimensions are too large.");
3814 mng_info->ob[object_id]->width=ping_width;
3815 mng_info->ob[object_id]->height=ping_height;
3816 mng_info->ob[object_id]->color_type=ping_color_type;
3817 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3818 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3819 mng_info->ob[object_id]->compression_method=
3820 ping_compression_method;
3821 mng_info->ob[object_id]->filter_method=ping_filter_method;
3823 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3829 Copy the PLTE to the object buffer.
3831 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3832 mng_info->ob[object_id]->plte_length=number_colors;
3834 for (i=0; i < number_colors; i++)
3836 mng_info->ob[object_id]->plte[i]=plte[i];
3841 mng_info->ob[object_id]->plte_length=0;
3846 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3847 * alpha or if a valid tRNS chunk is present, no matter whether there
3848 * is actual transparency present.
3850 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3851 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3852 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3853 BlendPixelTrait : UndefinedPixelTrait;
3855 #if 0 /* I'm not sure what's wrong here but it does not work. */
3856 if (image->alpha_trait == BlendPixelTrait)
3858 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3859 (void) SetImageType(image,GrayscaleMatteType,exception);
3861 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3862 (void) SetImageType(image,PaletteMatteType,exception);
3865 (void) SetImageType(image,TrueColorMatteType,exception);
3870 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3871 (void) SetImageType(image,GrayscaleType,exception);
3873 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3874 (void) SetImageType(image,PaletteType,exception);
3877 (void) SetImageType(image,TrueColorType,exception);
3881 /* Set more properties for identify to retrieve */
3886 if (num_text_total != 0)
3888 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3889 (void) FormatLocaleString(msg,MaxTextExtent,
3890 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3891 (void) SetImageProperty(image,"png:text",msg,
3895 if (num_raw_profiles != 0)
3897 (void) FormatLocaleString(msg,MaxTextExtent,
3898 "%d were found", num_raw_profiles);
3899 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3903 if (ping_found_cHRM != MagickFalse)
3905 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3906 "chunk was found (see Chromaticity, above)");
3907 (void) SetImageProperty(image,"png:cHRM",msg,
3911 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3913 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3914 "chunk was found (see Background color, above)");
3915 (void) SetImageProperty(image,"png:bKGD",msg,
3919 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3922 #if defined(PNG_iCCP_SUPPORTED)
3923 if (ping_found_iCCP != MagickFalse)
3924 (void) SetImageProperty(image,"png:iCCP",msg,
3928 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3929 (void) SetImageProperty(image,"png:tRNS",msg,
3932 #if defined(PNG_sRGB_SUPPORTED)
3933 if (ping_found_sRGB != MagickFalse)
3935 (void) FormatLocaleString(msg,MaxTextExtent,
3938 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3939 (void) SetImageProperty(image,"png:sRGB",msg,
3944 if (ping_found_gAMA != MagickFalse)
3946 (void) FormatLocaleString(msg,MaxTextExtent,
3947 "gamma=%.8g (See Gamma, above)",
3949 (void) SetImageProperty(image,"png:gAMA",msg,
3953 #if defined(PNG_pHYs_SUPPORTED)
3954 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3956 (void) FormatLocaleString(msg,MaxTextExtent,
3957 "x_res=%.10g, y_res=%.10g, units=%d",
3958 (double) x_resolution,(double) y_resolution, unit_type);
3959 (void) SetImageProperty(image,"png:pHYs",msg,
3964 #if defined(PNG_oFFs_SUPPORTED)
3965 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3967 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3968 (double) image->page.x,(double) image->page.y);
3969 (void) SetImageProperty(image,"png:oFFs",msg,
3974 if ((image->page.width != 0 && image->page.width != image->columns) ||
3975 (image->page.height != 0 && image->page.height != image->rows))
3977 (void) FormatLocaleString(msg,MaxTextExtent,
3978 "width=%.20g, height=%.20g",
3979 (double) image->page.width,(double) image->page.height);
3980 (void) SetImageProperty(image,"png:vpAg",msg,
3986 Relinquish resources.
3988 png_destroy_read_struct(&ping,&ping_info,&end_info);
3990 pixel_info=RelinquishVirtualMemory(pixel_info);
3992 if (logging != MagickFalse)
3993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " exit ReadOnePNGImage()");
3996 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3997 UnlockSemaphoreInfo(ping_semaphore);
4000 /* } for navigation to beginning of SETJMP-protected block, revert to
4001 * Throwing an Exception when an error occurs.
4006 /* end of reading one PNG image */
4009 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4024 magic_number[MaxTextExtent];
4032 assert(image_info != (const ImageInfo *) NULL);
4033 assert(image_info->signature == MagickSignature);
4035 if (image_info->debug != MagickFalse)
4036 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4037 image_info->filename);
4039 assert(exception != (ExceptionInfo *) NULL);
4040 assert(exception->signature == MagickSignature);
4041 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4042 image=AcquireImage(image_info,exception);
4043 mng_info=(MngInfo *) NULL;
4044 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4046 if (status == MagickFalse)
4047 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4050 Verify PNG signature.
4052 count=ReadBlob(image,8,(unsigned char *) magic_number);
4054 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4055 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4058 Allocate a MngInfo structure.
4060 have_mng_structure=MagickFalse;
4061 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4063 if (mng_info == (MngInfo *) NULL)
4064 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4067 Initialize members of the MngInfo structure.
4069 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4070 mng_info->image=image;
4071 have_mng_structure=MagickTrue;
4074 image=ReadOnePNGImage(mng_info,image_info,exception);
4075 MngInfoFreeStruct(mng_info,&have_mng_structure);
4077 if (image == (Image *) NULL)
4079 if (previous != (Image *) NULL)
4081 if (previous->signature != MagickSignature)
4082 ThrowReaderException(CorruptImageError,"CorruptImage");
4084 (void) CloseBlob(previous);
4085 (void) DestroyImageList(previous);
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 "exit ReadPNGImage() with error");
4092 return((Image *) NULL);
4095 (void) CloseBlob(image);
4097 if ((image->columns == 0) || (image->rows == 0))
4099 if (logging != MagickFalse)
4100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4101 "exit ReadPNGImage() with error.");
4103 ThrowReaderException(CorruptImageError,"CorruptImage");
4106 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4107 ((image->gamma < .45) || (image->gamma > .46)) &&
4108 !(image->chromaticity.red_primary.x>0.6399f &&
4109 image->chromaticity.red_primary.x<0.6401f &&
4110 image->chromaticity.red_primary.y>0.3299f &&
4111 image->chromaticity.red_primary.y<0.3301f &&
4112 image->chromaticity.green_primary.x>0.2999f &&
4113 image->chromaticity.green_primary.x<0.3001f &&
4114 image->chromaticity.green_primary.y>0.5999f &&
4115 image->chromaticity.green_primary.y<0.6001f &&
4116 image->chromaticity.blue_primary.x>0.1499f &&
4117 image->chromaticity.blue_primary.x<0.1501f &&
4118 image->chromaticity.blue_primary.y>0.0599f &&
4119 image->chromaticity.blue_primary.y<0.0601f &&
4120 image->chromaticity.white_point.x>0.3126f &&
4121 image->chromaticity.white_point.x<0.3128f &&
4122 image->chromaticity.white_point.y>0.3289f &&
4123 image->chromaticity.white_point.y<0.3291f))
4124 SetImageColorspace(image,RGBColorspace,exception);
4126 if (logging != MagickFalse)
4127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4128 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4129 (double) image->page.width,(double) image->page.height,
4130 (double) image->page.x,(double) image->page.y);
4132 if (logging != MagickFalse)
4133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4140 #if defined(JNG_SUPPORTED)
4142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4146 % R e a d O n e J N G I m a g e %
4150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4152 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4153 % (minus the 8-byte signature) and returns it. It allocates the memory
4154 % necessary for the new Image structure and returns a pointer to the new
4157 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4159 % The format of the ReadOneJNGImage method is:
4161 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4162 % ExceptionInfo *exception)
4164 % A description of each parameter follows:
4166 % o mng_info: Specifies a pointer to a MngInfo structure.
4168 % o image_info: the image info.
4170 % o exception: return any errors or warnings in this structure.
4173 static Image *ReadOneJNGImage(MngInfo *mng_info,
4174 const ImageInfo *image_info, ExceptionInfo *exception)
4201 jng_image_sample_depth,
4202 jng_image_compression_method,
4203 jng_image_interlace_method,
4204 jng_alpha_sample_depth,
4205 jng_alpha_compression_method,
4206 jng_alpha_filter_method,
4207 jng_alpha_interlace_method;
4209 register const Quantum
4219 register unsigned char
4230 jng_alpha_compression_method=0;
4231 jng_alpha_sample_depth=8;
4235 alpha_image=(Image *) NULL;
4236 color_image=(Image *) NULL;
4237 alpha_image_info=(ImageInfo *) NULL;
4238 color_image_info=(ImageInfo *) NULL;
4240 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4241 " Enter ReadOneJNGImage()");
4243 image=mng_info->image;
4245 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4248 Allocate next image structure.
4250 if (logging != MagickFalse)
4251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4252 " AcquireNextImage()");
4254 AcquireNextImage(image_info,image,exception);
4256 if (GetNextImageInList(image) == (Image *) NULL)
4257 return((Image *) NULL);
4259 image=SyncNextImageInList(image);
4261 mng_info->image=image;
4264 Signature bytes have already been read.
4267 read_JSEP=MagickFalse;
4268 reading_idat=MagickFalse;
4269 skip_to_iend=MagickFalse;
4273 type[MaxTextExtent];
4282 Read a new JNG chunk.
4284 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4285 2*GetBlobSize(image));
4287 if (status == MagickFalse)
4291 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4292 length=ReadBlobMSBLong(image);
4293 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4295 if (logging != MagickFalse)
4296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4297 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4298 type[0],type[1],type[2],type[3],(double) length);
4300 if (length > PNG_UINT_31_MAX || count == 0)
4301 ThrowReaderException(CorruptImageError,"CorruptImage");
4304 chunk=(unsigned char *) NULL;
4308 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4310 if (chunk == (unsigned char *) NULL)
4311 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4313 for (i=0; i < (ssize_t) length; i++)
4314 chunk[i]=(unsigned char) ReadBlobByte(image);
4319 (void) ReadBlobMSBLong(image); /* read crc word */
4324 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4329 if (memcmp(type,mng_JHDR,4) == 0)
4333 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4334 (p[2] << 8) | p[3]);
4335 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4336 (p[6] << 8) | p[7]);
4337 jng_color_type=p[8];
4338 jng_image_sample_depth=p[9];
4339 jng_image_compression_method=p[10];
4340 jng_image_interlace_method=p[11];
4342 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4345 jng_alpha_sample_depth=p[12];
4346 jng_alpha_compression_method=p[13];
4347 jng_alpha_filter_method=p[14];
4348 jng_alpha_interlace_method=p[15];
4350 if (logging != MagickFalse)
4352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4353 " jng_width: %16lu",(unsigned long) jng_width);
4355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4356 " jng_width: %16lu",(unsigned long) jng_height);
4358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4359 " jng_color_type: %16d",jng_color_type);
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4362 " jng_image_sample_depth: %3d",
4363 jng_image_sample_depth);
4365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4366 " jng_image_compression_method:%3d",
4367 jng_image_compression_method);
4369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4370 " jng_image_interlace_method: %3d",
4371 jng_image_interlace_method);
4373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4374 " jng_alpha_sample_depth: %3d",
4375 jng_alpha_sample_depth);
4377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4378 " jng_alpha_compression_method:%3d",
4379 jng_alpha_compression_method);
4381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4382 " jng_alpha_filter_method: %3d",
4383 jng_alpha_filter_method);
4385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4386 " jng_alpha_interlace_method: %3d",
4387 jng_alpha_interlace_method);
4392 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4398 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4399 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4400 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4403 o create color_image
4404 o open color_blob, attached to color_image
4405 o if (color type has alpha)
4406 open alpha_blob, attached to alpha_image
4409 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4411 if (color_image_info == (ImageInfo *) NULL)
4412 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4414 GetImageInfo(color_image_info);
4415 color_image=AcquireImage(color_image_info,exception);
4417 if (color_image == (Image *) NULL)
4418 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4420 if (logging != MagickFalse)
4421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4422 " Creating color_blob.");
4424 (void) AcquireUniqueFilename(color_image->filename);
4425 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4428 if (status == MagickFalse)
4429 return((Image *) NULL);
4431 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4433 alpha_image_info=(ImageInfo *)
4434 AcquireMagickMemory(sizeof(ImageInfo));
4436 if (alpha_image_info == (ImageInfo *) NULL)
4437 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4439 GetImageInfo(alpha_image_info);
4440 alpha_image=AcquireImage(alpha_image_info,exception);
4442 if (alpha_image == (Image *) NULL)
4444 alpha_image=DestroyImage(alpha_image);
4445 ThrowReaderException(ResourceLimitError,
4446 "MemoryAllocationFailed");
4449 if (logging != MagickFalse)
4450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4451 " Creating alpha_blob.");
4453 (void) AcquireUniqueFilename(alpha_image->filename);
4454 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4457 if (status == MagickFalse)
4458 return((Image *) NULL);
4460 if (jng_alpha_compression_method == 0)
4465 if (logging != MagickFalse)
4466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4467 " Writing IHDR chunk to alpha_blob.");
4469 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4470 "\211PNG\r\n\032\n");
4472 (void) WriteBlobMSBULong(alpha_image,13L);
4473 PNGType(data,mng_IHDR);
4474 LogPNGChunk(logging,mng_IHDR,13L);
4475 PNGLong(data+4,jng_width);
4476 PNGLong(data+8,jng_height);
4477 data[12]=jng_alpha_sample_depth;
4478 data[13]=0; /* color_type gray */
4479 data[14]=0; /* compression method 0 */
4480 data[15]=0; /* filter_method 0 */
4481 data[16]=0; /* interlace_method 0 */
4482 (void) WriteBlob(alpha_image,17,data);
4483 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4486 reading_idat=MagickTrue;
4489 if (memcmp(type,mng_JDAT,4) == 0)
4491 /* Copy chunk to color_image->blob */
4493 if (logging != MagickFalse)
4494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4495 " Copying JDAT chunk data to color_blob.");
4497 (void) WriteBlob(color_image,length,chunk);
4500 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4505 if (memcmp(type,mng_IDAT,4) == 0)
4510 /* Copy IDAT header and chunk data to alpha_image->blob */
4512 if (image_info->ping == MagickFalse)
4514 if (logging != MagickFalse)
4515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4516 " Copying IDAT chunk data to alpha_blob.");
4518 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4519 PNGType(data,mng_IDAT);
4520 LogPNGChunk(logging,mng_IDAT,length);
4521 (void) WriteBlob(alpha_image,4,data);
4522 (void) WriteBlob(alpha_image,length,chunk);
4523 (void) WriteBlobMSBULong(alpha_image,
4524 crc32(crc32(0,data,4),chunk,(uInt) length));
4528 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4533 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4535 /* Copy chunk data to alpha_image->blob */
4537 if (image_info->ping == MagickFalse)
4539 if (logging != MagickFalse)
4540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4541 " Copying JDAA chunk data to alpha_blob.");
4543 (void) WriteBlob(alpha_image,length,chunk);
4547 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4552 if (memcmp(type,mng_JSEP,4) == 0)
4554 read_JSEP=MagickTrue;
4557 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4562 if (memcmp(type,mng_bKGD,4) == 0)
4566 image->background_color.red=ScaleCharToQuantum(p[1]);
4567 image->background_color.green=image->background_color.red;
4568 image->background_color.blue=image->background_color.red;
4573 image->background_color.red=ScaleCharToQuantum(p[1]);
4574 image->background_color.green=ScaleCharToQuantum(p[3]);
4575 image->background_color.blue=ScaleCharToQuantum(p[5]);
4578 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4582 if (memcmp(type,mng_gAMA,4) == 0)
4585 image->gamma=((float) mng_get_long(p))*0.00001;
4587 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4591 if (memcmp(type,mng_cHRM,4) == 0)
4595 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4596 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4597 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4598 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4599 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4600 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4601 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4602 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4609 if (memcmp(type,mng_sRGB,4) == 0)
4613 image->rendering_intent=
4614 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4615 image->gamma=1.000f/2.200f;
4616 image->chromaticity.red_primary.x=0.6400f;
4617 image->chromaticity.red_primary.y=0.3300f;
4618 image->chromaticity.green_primary.x=0.3000f;
4619 image->chromaticity.green_primary.y=0.6000f;
4620 image->chromaticity.blue_primary.x=0.1500f;
4621 image->chromaticity.blue_primary.y=0.0600f;
4622 image->chromaticity.white_point.x=0.3127f;
4623 image->chromaticity.white_point.y=0.3290f;
4626 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4630 if (memcmp(type,mng_oFFs,4) == 0)
4634 image->page.x=(ssize_t) mng_get_long(p);
4635 image->page.y=(ssize_t) mng_get_long(&p[4]);
4637 if ((int) p[8] != 0)
4639 image->page.x/=10000;
4640 image->page.y/=10000;
4645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4650 if (memcmp(type,mng_pHYs,4) == 0)
4654 image->resolution.x=(double) mng_get_long(p);
4655 image->resolution.y=(double) mng_get_long(&p[4]);
4656 if ((int) p[8] == PNG_RESOLUTION_METER)
4658 image->units=PixelsPerCentimeterResolution;
4659 image->resolution.x=image->resolution.x/100.0f;
4660 image->resolution.y=image->resolution.y/100.0f;
4664 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4669 if (memcmp(type,mng_iCCP,4) == 0)
4673 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4680 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4682 if (memcmp(type,mng_IEND,4))
4692 Finish up reading image data:
4694 o read main image from color_blob.
4698 o if (color_type has alpha)
4699 if alpha_encoding is PNG
4700 read secondary image from alpha_blob via ReadPNG
4701 if alpha_encoding is JPEG
4702 read secondary image from alpha_blob via ReadJPEG
4706 o copy intensity of secondary image into
4707 alpha samples of main image.
4709 o destroy the secondary image.
4712 (void) CloseBlob(color_image);
4714 if (logging != MagickFalse)
4715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4716 " Reading jng_image from color_blob.");
4718 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4719 color_image->filename);
4721 color_image_info->ping=MagickFalse; /* To do: avoid this */
4722 jng_image=ReadImage(color_image_info,exception);
4724 if (jng_image == (Image *) NULL)
4725 return((Image *) NULL);
4727 (void) RelinquishUniqueFileResource(color_image->filename);
4728 color_image=DestroyImage(color_image);
4729 color_image_info=DestroyImageInfo(color_image_info);
4731 if (jng_image == (Image *) NULL)
4732 return((Image *) NULL);
4734 if (logging != MagickFalse)
4735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4736 " Copying jng_image pixels to main image.");
4738 image->rows=jng_height;
4739 image->columns=jng_width;
4741 for (y=0; y < (ssize_t) image->rows; y++)
4743 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4744 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4745 for (x=(ssize_t) image->columns; x != 0; x--)
4747 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4748 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4749 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4750 q+=GetPixelChannels(image);
4751 s+=GetPixelChannels(jng_image);
4754 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4758 jng_image=DestroyImage(jng_image);
4760 if (image_info->ping == MagickFalse)
4762 if (jng_color_type >= 12)
4764 if (jng_alpha_compression_method == 0)
4768 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4769 PNGType(data,mng_IEND);
4770 LogPNGChunk(logging,mng_IEND,0L);
4771 (void) WriteBlob(alpha_image,4,data);
4772 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4775 (void) CloseBlob(alpha_image);
4777 if (logging != MagickFalse)
4778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4779 " Reading alpha from alpha_blob.");
4781 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4782 "%s",alpha_image->filename);
4784 jng_image=ReadImage(alpha_image_info,exception);
4786 if (jng_image != (Image *) NULL)
4787 for (y=0; y < (ssize_t) image->rows; y++)
4789 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4791 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4793 if (image->alpha_trait == BlendPixelTrait)
4794 for (x=(ssize_t) image->columns; x != 0; x--)
4796 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4797 q+=GetPixelChannels(image);
4798 s+=GetPixelChannels(jng_image);
4802 for (x=(ssize_t) image->columns; x != 0; x--)
4804 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4805 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4806 image->alpha_trait=BlendPixelTrait;
4807 q+=GetPixelChannels(image);
4808 s+=GetPixelChannels(jng_image);
4811 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4814 (void) RelinquishUniqueFileResource(alpha_image->filename);
4815 alpha_image=DestroyImage(alpha_image);
4816 alpha_image_info=DestroyImageInfo(alpha_image_info);
4817 if (jng_image != (Image *) NULL)
4818 jng_image=DestroyImage(jng_image);
4822 /* Read the JNG image. */
4824 if (mng_info->mng_type == 0)
4826 mng_info->mng_width=jng_width;
4827 mng_info->mng_height=jng_height;
4830 if (image->page.width == 0 && image->page.height == 0)
4832 image->page.width=jng_width;
4833 image->page.height=jng_height;
4836 if (image->page.x == 0 && image->page.y == 0)
4838 image->page.x=mng_info->x_off[mng_info->object_id];
4839 image->page.y=mng_info->y_off[mng_info->object_id];
4844 image->page.y=mng_info->y_off[mng_info->object_id];
4847 mng_info->image_found++;
4848 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4849 2*GetBlobSize(image));
4851 if (logging != MagickFalse)
4852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4853 " exit ReadOneJNGImage()");
4859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4863 % R e a d J N G I m a g e %
4867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4869 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4870 % (including the 8-byte signature) and returns it. It allocates the memory
4871 % necessary for the new Image structure and returns a pointer to the new
4874 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4876 % The format of the ReadJNGImage method is:
4878 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4881 % A description of each parameter follows:
4883 % o image_info: the image info.
4885 % o exception: return any errors or warnings in this structure.
4889 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4904 magic_number[MaxTextExtent];
4912 assert(image_info != (const ImageInfo *) NULL);
4913 assert(image_info->signature == MagickSignature);
4914 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4915 assert(exception != (ExceptionInfo *) NULL);
4916 assert(exception->signature == MagickSignature);
4917 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4918 image=AcquireImage(image_info,exception);
4919 mng_info=(MngInfo *) NULL;
4920 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4922 if (status == MagickFalse)
4923 return((Image *) NULL);
4925 if (LocaleCompare(image_info->magick,"JNG") != 0)
4926 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4928 /* Verify JNG signature. */
4930 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4932 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4933 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4935 /* Allocate a MngInfo structure. */
4937 have_mng_structure=MagickFalse;
4938 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4940 if (mng_info == (MngInfo *) NULL)
4941 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4943 /* Initialize members of the MngInfo structure. */
4945 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4946 have_mng_structure=MagickTrue;
4948 mng_info->image=image;
4950 image=ReadOneJNGImage(mng_info,image_info,exception);
4951 MngInfoFreeStruct(mng_info,&have_mng_structure);
4953 if (image == (Image *) NULL)
4955 if (IsImageObject(previous) != MagickFalse)
4957 (void) CloseBlob(previous);
4958 (void) DestroyImageList(previous);
4961 if (logging != MagickFalse)
4962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4963 "exit ReadJNGImage() with error");
4965 return((Image *) NULL);
4967 (void) CloseBlob(image);
4969 if (image->columns == 0 || image->rows == 0)
4971 if (logging != MagickFalse)
4972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4973 "exit ReadJNGImage() with error");
4975 ThrowReaderException(CorruptImageError,"CorruptImage");
4978 if (logging != MagickFalse)
4979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4985 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4988 page_geometry[MaxTextExtent];
5021 #if defined(MNG_INSERT_LAYERS)
5023 mng_background_color;
5026 register unsigned char
5041 #if defined(MNG_INSERT_LAYERS)
5046 volatile unsigned int
5047 #ifdef MNG_OBJECT_BUFFERS
5048 mng_background_object=0,
5050 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5053 default_frame_timeout,
5055 #if defined(MNG_INSERT_LAYERS)
5061 /* These delays are all measured in image ticks_per_second,
5062 * not in MNG ticks_per_second
5065 default_frame_delay,
5069 #if defined(MNG_INSERT_LAYERS)
5078 previous_fb.bottom=0;
5080 previous_fb.right=0;
5082 default_fb.bottom=0;
5086 /* Open image file. */
5088 assert(image_info != (const ImageInfo *) NULL);
5089 assert(image_info->signature == MagickSignature);
5090 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5091 assert(exception != (ExceptionInfo *) NULL);
5092 assert(exception->signature == MagickSignature);
5093 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5094 image=AcquireImage(image_info,exception);
5095 mng_info=(MngInfo *) NULL;
5096 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5098 if (status == MagickFalse)
5099 return((Image *) NULL);
5101 first_mng_object=MagickFalse;
5103 have_mng_structure=MagickFalse;
5105 /* Allocate a MngInfo structure. */
5107 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5109 if (mng_info == (MngInfo *) NULL)
5110 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5112 /* Initialize members of the MngInfo structure. */
5114 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5115 mng_info->image=image;
5116 have_mng_structure=MagickTrue;
5118 if (LocaleCompare(image_info->magick,"MNG") == 0)
5121 magic_number[MaxTextExtent];
5123 /* Verify MNG signature. */
5124 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5125 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5126 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5128 /* Initialize some nonzero members of the MngInfo structure. */
5129 for (i=0; i < MNG_MAX_OBJECTS; i++)
5131 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5132 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5134 mng_info->exists[0]=MagickTrue;
5137 first_mng_object=MagickTrue;
5139 #if defined(MNG_INSERT_LAYERS)
5140 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5142 default_frame_delay=0;
5143 default_frame_timeout=0;
5146 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5148 skip_to_iend=MagickFalse;
5149 term_chunk_found=MagickFalse;
5150 mng_info->framing_mode=1;
5151 #if defined(MNG_INSERT_LAYERS)
5152 mandatory_back=MagickFalse;
5154 #if defined(MNG_INSERT_LAYERS)
5155 mng_background_color=image->background_color;
5157 default_fb=mng_info->frame;
5158 previous_fb=mng_info->frame;
5162 type[MaxTextExtent];
5164 if (LocaleCompare(image_info->magick,"MNG") == 0)
5173 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5174 length=ReadBlobMSBLong(image);
5175 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5177 if (logging != MagickFalse)
5178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5179 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5180 type[0],type[1],type[2],type[3],(double) length);
5182 if (length > PNG_UINT_31_MAX)
5186 ThrowReaderException(CorruptImageError,"CorruptImage");
5189 chunk=(unsigned char *) NULL;
5193 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5195 if (chunk == (unsigned char *) NULL)
5196 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5198 for (i=0; i < (ssize_t) length; i++)
5199 chunk[i]=(unsigned char) ReadBlobByte(image);
5204 (void) ReadBlobMSBLong(image); /* read crc word */
5206 #if !defined(JNG_SUPPORTED)
5207 if (memcmp(type,mng_JHDR,4) == 0)
5209 skip_to_iend=MagickTrue;
5211 if (mng_info->jhdr_warning == 0)
5212 (void) ThrowMagickException(exception,GetMagickModule(),
5213 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5215 mng_info->jhdr_warning++;
5218 if (memcmp(type,mng_DHDR,4) == 0)
5220 skip_to_iend=MagickTrue;
5222 if (mng_info->dhdr_warning == 0)
5223 (void) ThrowMagickException(exception,GetMagickModule(),
5224 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5226 mng_info->dhdr_warning++;
5228 if (memcmp(type,mng_MEND,4) == 0)
5233 if (memcmp(type,mng_IEND,4) == 0)
5234 skip_to_iend=MagickFalse;
5237 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5239 if (logging != MagickFalse)
5240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5246 if (memcmp(type,mng_MHDR,4) == 0)
5248 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5249 (p[2] << 8) | p[3]);
5251 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5252 (p[6] << 8) | p[7]);
5254 if (logging != MagickFalse)
5256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5257 " MNG width: %.20g",(double) mng_info->mng_width);
5258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5259 " MNG height: %.20g",(double) mng_info->mng_height);
5263 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5265 if (mng_info->ticks_per_second == 0)
5266 default_frame_delay=0;
5269 default_frame_delay=1UL*image->ticks_per_second/
5270 mng_info->ticks_per_second;
5272 frame_delay=default_frame_delay;
5278 simplicity=(size_t) mng_get_long(p);
5281 mng_type=1; /* Full MNG */
5283 if ((simplicity != 0) && ((simplicity | 11) == 11))
5284 mng_type=2; /* LC */
5286 if ((simplicity != 0) && ((simplicity | 9) == 9))
5287 mng_type=3; /* VLC */
5289 #if defined(MNG_INSERT_LAYERS)
5291 insert_layers=MagickTrue;
5293 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5295 /* Allocate next image structure. */
5296 AcquireNextImage(image_info,image,exception);
5298 if (GetNextImageInList(image) == (Image *) NULL)
5299 return((Image *) NULL);
5301 image=SyncNextImageInList(image);
5302 mng_info->image=image;
5305 if ((mng_info->mng_width > 65535L) ||
5306 (mng_info->mng_height > 65535L))
5307 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5309 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5310 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5311 mng_info->mng_height);
5313 mng_info->frame.left=0;
5314 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5315 mng_info->frame.top=0;
5316 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5317 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5319 for (i=0; i < MNG_MAX_OBJECTS; i++)
5320 mng_info->object_clip[i]=mng_info->frame;
5322 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5326 if (memcmp(type,mng_TERM,4) == 0)
5337 final_delay=(png_uint_32) mng_get_long(&p[2]);
5338 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5340 if (mng_iterations == PNG_UINT_31_MAX)
5343 image->iterations=mng_iterations;
5344 term_chunk_found=MagickTrue;
5347 if (logging != MagickFalse)
5349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5350 " repeat=%d",repeat);
5352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5353 " final_delay=%.20g",(double) final_delay);
5355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5356 " image->iterations=%.20g",(double) image->iterations);
5359 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5362 if (memcmp(type,mng_DEFI,4) == 0)
5365 (void) ThrowMagickException(exception,GetMagickModule(),
5366 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5369 object_id=(p[0] << 8) | p[1];
5371 if (mng_type == 2 && object_id != 0)
5372 (void) ThrowMagickException(exception,GetMagickModule(),
5373 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5376 if (object_id > MNG_MAX_OBJECTS)
5379 Instead of using a warning we should allocate a larger
5380 MngInfo structure and continue.
5382 (void) ThrowMagickException(exception,GetMagickModule(),
5383 CoderError,"object id too large","`%s'",image->filename);
5384 object_id=MNG_MAX_OBJECTS;
5387 if (mng_info->exists[object_id])
5388 if (mng_info->frozen[object_id])
5390 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5391 (void) ThrowMagickException(exception,
5392 GetMagickModule(),CoderError,
5393 "DEFI cannot redefine a frozen MNG object","`%s'",
5398 mng_info->exists[object_id]=MagickTrue;
5401 mng_info->invisible[object_id]=p[2];
5404 Extract object offset info.
5408 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5409 (p[5] << 16) | (p[6] << 8) | p[7]);
5411 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5412 (p[9] << 16) | (p[10] << 8) | p[11]);
5414 if (logging != MagickFalse)
5416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5417 " x_off[%d]: %.20g",object_id,(double)
5418 mng_info->x_off[object_id]);
5420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5421 " y_off[%d]: %.20g",object_id,(double)
5422 mng_info->y_off[object_id]);
5427 Extract object clipping info.
5430 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5433 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5436 if (memcmp(type,mng_bKGD,4) == 0)
5438 mng_info->have_global_bkgd=MagickFalse;
5442 mng_info->mng_global_bkgd.red=
5443 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5445 mng_info->mng_global_bkgd.green=
5446 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5448 mng_info->mng_global_bkgd.blue=
5449 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5451 mng_info->have_global_bkgd=MagickTrue;
5454 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5457 if (memcmp(type,mng_BACK,4) == 0)
5459 #if defined(MNG_INSERT_LAYERS)
5461 mandatory_back=p[6];
5466 if (mandatory_back && length > 5)
5468 mng_background_color.red=
5469 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5471 mng_background_color.green=
5472 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5474 mng_background_color.blue=
5475 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5477 mng_background_color.alpha=OpaqueAlpha;
5480 #ifdef MNG_OBJECT_BUFFERS
5482 mng_background_object=(p[7] << 8) | p[8];
5485 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5489 if (memcmp(type,mng_PLTE,4) == 0)
5491 /* Read global PLTE. */
5493 if (length && (length < 769))
5495 if (mng_info->global_plte == (png_colorp) NULL)
5496 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5497 sizeof(*mng_info->global_plte));
5499 for (i=0; i < (ssize_t) (length/3); i++)
5501 mng_info->global_plte[i].red=p[3*i];
5502 mng_info->global_plte[i].green=p[3*i+1];
5503 mng_info->global_plte[i].blue=p[3*i+2];
5506 mng_info->global_plte_length=(unsigned int) (length/3);
5509 for ( ; i < 256; i++)
5511 mng_info->global_plte[i].red=i;
5512 mng_info->global_plte[i].green=i;
5513 mng_info->global_plte[i].blue=i;
5517 mng_info->global_plte_length=256;
5520 mng_info->global_plte_length=0;
5522 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5526 if (memcmp(type,mng_tRNS,4) == 0)
5528 /* read global tRNS */
5531 for (i=0; i < (ssize_t) length; i++)
5532 mng_info->global_trns[i]=p[i];
5535 for ( ; i < 256; i++)
5536 mng_info->global_trns[i]=255;
5538 mng_info->global_trns_length=(unsigned int) length;
5539 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5542 if (memcmp(type,mng_gAMA,4) == 0)
5549 igamma=mng_get_long(p);
5550 mng_info->global_gamma=((float) igamma)*0.00001;
5551 mng_info->have_global_gama=MagickTrue;
5555 mng_info->have_global_gama=MagickFalse;
5557 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5561 if (memcmp(type,mng_cHRM,4) == 0)
5563 /* Read global cHRM */
5567 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5568 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5569 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5570 mng_info->global_chrm.red_primary.y=0.00001*
5571 mng_get_long(&p[12]);
5572 mng_info->global_chrm.green_primary.x=0.00001*
5573 mng_get_long(&p[16]);
5574 mng_info->global_chrm.green_primary.y=0.00001*
5575 mng_get_long(&p[20]);
5576 mng_info->global_chrm.blue_primary.x=0.00001*
5577 mng_get_long(&p[24]);
5578 mng_info->global_chrm.blue_primary.y=0.00001*
5579 mng_get_long(&p[28]);
5580 mng_info->have_global_chrm=MagickTrue;
5583 mng_info->have_global_chrm=MagickFalse;
5585 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5589 if (memcmp(type,mng_sRGB,4) == 0)
5596 mng_info->global_srgb_intent=
5597 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5598 mng_info->have_global_srgb=MagickTrue;
5601 mng_info->have_global_srgb=MagickFalse;
5603 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5607 if (memcmp(type,mng_iCCP,4) == 0)
5615 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5620 if (memcmp(type,mng_FRAM,4) == 0)
5623 (void) ThrowMagickException(exception,GetMagickModule(),
5624 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5627 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5628 image->delay=frame_delay;
5630 frame_delay=default_frame_delay;
5631 frame_timeout=default_frame_timeout;
5636 mng_info->framing_mode=p[0];
5638 if (logging != MagickFalse)
5639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5640 " Framing_mode=%d",mng_info->framing_mode);
5644 /* Note the delay and frame clipping boundaries. */
5646 p++; /* framing mode */
5648 while (*p && ((p-chunk) < (ssize_t) length))
5649 p++; /* frame name */
5651 p++; /* frame name terminator */
5653 if ((p-chunk) < (ssize_t) (length-4))
5660 change_delay=(*p++);
5661 change_timeout=(*p++);
5662 change_clipping=(*p++);
5663 p++; /* change_sync */
5667 frame_delay=1UL*image->ticks_per_second*
5670 if (mng_info->ticks_per_second != 0)
5671 frame_delay/=mng_info->ticks_per_second;
5674 frame_delay=PNG_UINT_31_MAX;
5676 if (change_delay == 2)
5677 default_frame_delay=frame_delay;
5681 if (logging != MagickFalse)
5682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5683 " Framing_delay=%.20g",(double) frame_delay);
5688 frame_timeout=1UL*image->ticks_per_second*
5691 if (mng_info->ticks_per_second != 0)
5692 frame_timeout/=mng_info->ticks_per_second;
5695 frame_timeout=PNG_UINT_31_MAX;
5697 if (change_delay == 2)
5698 default_frame_timeout=frame_timeout;
5702 if (logging != MagickFalse)
5703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5704 " Framing_timeout=%.20g",(double) frame_timeout);
5707 if (change_clipping)
5709 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5713 if (logging != MagickFalse)
5714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5715 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5716 (double) fb.left,(double) fb.right,(double) fb.top,
5717 (double) fb.bottom);
5719 if (change_clipping == 2)
5725 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5727 subframe_width=(size_t) (mng_info->clip.right
5728 -mng_info->clip.left);
5730 subframe_height=(size_t) (mng_info->clip.bottom
5731 -mng_info->clip.top);
5733 Insert a background layer behind the frame if framing_mode is 4.
5735 #if defined(MNG_INSERT_LAYERS)
5736 if (logging != MagickFalse)
5737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5738 " subframe_width=%.20g, subframe_height=%.20g",(double)
5739 subframe_width,(double) subframe_height);
5741 if (insert_layers && (mng_info->framing_mode == 4) &&
5742 (subframe_width) && (subframe_height))
5744 /* Allocate next image structure. */
5745 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5747 AcquireNextImage(image_info,image,exception);
5749 if (GetNextImageInList(image) == (Image *) NULL)
5751 image=DestroyImageList(image);
5752 MngInfoFreeStruct(mng_info,&have_mng_structure);
5753 return((Image *) NULL);
5756 image=SyncNextImageInList(image);
5759 mng_info->image=image;
5761 if (term_chunk_found)
5763 image->start_loop=MagickTrue;
5764 image->iterations=mng_iterations;
5765 term_chunk_found=MagickFalse;
5769 image->start_loop=MagickFalse;
5771 image->columns=subframe_width;
5772 image->rows=subframe_height;
5773 image->page.width=subframe_width;
5774 image->page.height=subframe_height;
5775 image->page.x=mng_info->clip.left;
5776 image->page.y=mng_info->clip.top;
5777 image->background_color=mng_background_color;
5778 image->alpha_trait=UndefinedPixelTrait;
5780 (void) SetImageBackgroundColor(image,exception);
5782 if (logging != MagickFalse)
5783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5784 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5785 (double) mng_info->clip.left,(double) mng_info->clip.right,
5786 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5789 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5792 if (memcmp(type,mng_CLIP,4) == 0)
5801 first_object=(p[0] << 8) | p[1];
5802 last_object=(p[2] << 8) | p[3];
5804 for (i=(int) first_object; i <= (int) last_object; i++)
5806 if (mng_info->exists[i] && !mng_info->frozen[i])
5811 box=mng_info->object_clip[i];
5812 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5816 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5819 if (memcmp(type,mng_SAVE,4) == 0)
5821 for (i=1; i < MNG_MAX_OBJECTS; i++)
5822 if (mng_info->exists[i])
5824 mng_info->frozen[i]=MagickTrue;
5825 #ifdef MNG_OBJECT_BUFFERS
5826 if (mng_info->ob[i] != (MngBuffer *) NULL)
5827 mng_info->ob[i]->frozen=MagickTrue;
5832 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5837 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5839 /* Read DISC or SEEK. */
5841 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5843 for (i=1; i < MNG_MAX_OBJECTS; i++)
5844 MngInfoDiscardObject(mng_info,i);
5852 for (j=0; j < (ssize_t) length; j+=2)
5854 i=p[j] << 8 | p[j+1];
5855 MngInfoDiscardObject(mng_info,i);
5860 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5865 if (memcmp(type,mng_MOVE,4) == 0)
5873 first_object=(p[0] << 8) | p[1];
5874 last_object=(p[2] << 8) | p[3];
5875 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5877 if (mng_info->exists[i] && !mng_info->frozen[i])
5885 old_pair.a=mng_info->x_off[i];
5886 old_pair.b=mng_info->y_off[i];
5887 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5888 mng_info->x_off[i]=new_pair.a;
5889 mng_info->y_off[i]=new_pair.b;
5893 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5897 if (memcmp(type,mng_LOOP,4) == 0)
5899 ssize_t loop_iters=1;
5900 loop_level=chunk[0];
5901 mng_info->loop_active[loop_level]=1; /* mark loop active */
5903 /* Record starting point. */
5904 loop_iters=mng_get_long(&chunk[1]);
5906 if (logging != MagickFalse)
5907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5908 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5909 (double) loop_iters);
5911 if (loop_iters == 0)
5912 skipping_loop=loop_level;
5916 mng_info->loop_jump[loop_level]=TellBlob(image);
5917 mng_info->loop_count[loop_level]=loop_iters;
5920 mng_info->loop_iteration[loop_level]=0;
5921 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5925 if (memcmp(type,mng_ENDL,4) == 0)
5927 loop_level=chunk[0];
5929 if (skipping_loop > 0)
5931 if (skipping_loop == loop_level)
5934 Found end of zero-iteration loop.
5937 mng_info->loop_active[loop_level]=0;
5943 if (mng_info->loop_active[loop_level] == 1)
5945 mng_info->loop_count[loop_level]--;
5946 mng_info->loop_iteration[loop_level]++;
5948 if (logging != MagickFalse)
5949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5950 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5951 (double) loop_level,(double)
5952 mng_info->loop_count[loop_level]);
5954 if (mng_info->loop_count[loop_level] != 0)
5956 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5960 ThrowReaderException(CorruptImageError,
5961 "ImproperImageHeader");
5972 mng_info->loop_active[loop_level]=0;
5974 for (i=0; i < loop_level; i++)
5975 if (mng_info->loop_active[i] == 1)
5976 last_level=(short) i;
5977 loop_level=last_level;
5982 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5986 if (memcmp(type,mng_CLON,4) == 0)
5988 if (mng_info->clon_warning == 0)
5989 (void) ThrowMagickException(exception,GetMagickModule(),
5990 CoderError,"CLON is not implemented yet","`%s'",
5993 mng_info->clon_warning++;
5996 if (memcmp(type,mng_MAGN,4) == 0)
6011 magn_first=(p[0] << 8) | p[1];
6017 magn_last=(p[2] << 8) | p[3];
6020 magn_last=magn_first;
6021 #ifndef MNG_OBJECT_BUFFERS
6022 if (magn_first || magn_last)
6023 if (mng_info->magn_warning == 0)
6025 (void) ThrowMagickException(exception,
6026 GetMagickModule(),CoderError,
6027 "MAGN is not implemented yet for nonzero objects",
6028 "`%s'",image->filename);
6030 mng_info->magn_warning++;
6040 magn_mx=(p[5] << 8) | p[6];
6049 magn_my=(p[7] << 8) | p[8];
6058 magn_ml=(p[9] << 8) | p[10];
6067 magn_mr=(p[11] << 8) | p[12];
6076 magn_mt=(p[13] << 8) | p[14];
6085 magn_mb=(p[15] << 8) | p[16];
6097 magn_methy=magn_methx;
6100 if (magn_methx > 5 || magn_methy > 5)
6101 if (mng_info->magn_warning == 0)
6103 (void) ThrowMagickException(exception,
6104 GetMagickModule(),CoderError,
6105 "Unknown MAGN method in MNG datastream","`%s'",
6108 mng_info->magn_warning++;
6110 #ifdef MNG_OBJECT_BUFFERS
6111 /* Magnify existing objects in the range magn_first to magn_last */
6113 if (magn_first == 0 || magn_last == 0)
6115 /* Save the magnification factors for object 0 */
6116 mng_info->magn_mb=magn_mb;
6117 mng_info->magn_ml=magn_ml;
6118 mng_info->magn_mr=magn_mr;
6119 mng_info->magn_mt=magn_mt;
6120 mng_info->magn_mx=magn_mx;
6121 mng_info->magn_my=magn_my;
6122 mng_info->magn_methx=magn_methx;
6123 mng_info->magn_methy=magn_methy;
6127 if (memcmp(type,mng_PAST,4) == 0)
6129 if (mng_info->past_warning == 0)
6130 (void) ThrowMagickException(exception,GetMagickModule(),
6131 CoderError,"PAST is not implemented yet","`%s'",
6134 mng_info->past_warning++;
6137 if (memcmp(type,mng_SHOW,4) == 0)
6139 if (mng_info->show_warning == 0)
6140 (void) ThrowMagickException(exception,GetMagickModule(),
6141 CoderError,"SHOW is not implemented yet","`%s'",
6144 mng_info->show_warning++;
6147 if (memcmp(type,mng_sBIT,4) == 0)
6150 mng_info->have_global_sbit=MagickFalse;
6154 mng_info->global_sbit.gray=p[0];
6155 mng_info->global_sbit.red=p[0];
6156 mng_info->global_sbit.green=p[1];
6157 mng_info->global_sbit.blue=p[2];
6158 mng_info->global_sbit.alpha=p[3];
6159 mng_info->have_global_sbit=MagickTrue;
6162 if (memcmp(type,mng_pHYs,4) == 0)
6166 mng_info->global_x_pixels_per_unit=
6167 (size_t) mng_get_long(p);
6168 mng_info->global_y_pixels_per_unit=
6169 (size_t) mng_get_long(&p[4]);
6170 mng_info->global_phys_unit_type=p[8];
6171 mng_info->have_global_phys=MagickTrue;
6175 mng_info->have_global_phys=MagickFalse;
6177 if (memcmp(type,mng_pHYg,4) == 0)
6179 if (mng_info->phyg_warning == 0)
6180 (void) ThrowMagickException(exception,GetMagickModule(),
6181 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6183 mng_info->phyg_warning++;
6185 if (memcmp(type,mng_BASI,4) == 0)
6187 skip_to_iend=MagickTrue;
6189 if (mng_info->basi_warning == 0)
6190 (void) ThrowMagickException(exception,GetMagickModule(),
6191 CoderError,"BASI is not implemented yet","`%s'",
6194 mng_info->basi_warning++;
6195 #ifdef MNG_BASI_SUPPORTED
6196 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6197 (p[2] << 8) | p[3]);
6198 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6199 (p[6] << 8) | p[7]);
6200 basi_color_type=p[8];
6201 basi_compression_method=p[9];
6202 basi_filter_type=p[10];
6203 basi_interlace_method=p[11];
6205 basi_red=(p[12] << 8) & p[13];
6211 basi_green=(p[14] << 8) & p[15];
6217 basi_blue=(p[16] << 8) & p[17];
6223 basi_alpha=(p[18] << 8) & p[19];
6227 if (basi_sample_depth == 16)
6234 basi_viewable=p[20];
6240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6244 if (memcmp(type,mng_IHDR,4)
6245 #if defined(JNG_SUPPORTED)
6246 && memcmp(type,mng_JHDR,4)
6250 /* Not an IHDR or JHDR chunk */
6252 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6257 if (logging != MagickFalse)
6258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6259 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6261 mng_info->exists[object_id]=MagickTrue;
6262 mng_info->viewable[object_id]=MagickTrue;
6264 if (mng_info->invisible[object_id])
6266 if (logging != MagickFalse)
6267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6268 " Skipping invisible object");
6270 skip_to_iend=MagickTrue;
6271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6274 #if defined(MNG_INSERT_LAYERS)
6276 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6278 image_width=(size_t) mng_get_long(p);
6279 image_height=(size_t) mng_get_long(&p[4]);
6281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6284 Insert a transparent background layer behind the entire animation
6285 if it is not full screen.
6287 #if defined(MNG_INSERT_LAYERS)
6288 if (insert_layers && mng_type && first_mng_object)
6290 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6291 (image_width < mng_info->mng_width) ||
6292 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6293 (image_height < mng_info->mng_height) ||
6294 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6296 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6299 Allocate next image structure.
6301 AcquireNextImage(image_info,image,exception);
6303 if (GetNextImageInList(image) == (Image *) NULL)
6305 image=DestroyImageList(image);
6306 MngInfoFreeStruct(mng_info,&have_mng_structure);
6307 return((Image *) NULL);
6310 image=SyncNextImageInList(image);
6312 mng_info->image=image;
6314 if (term_chunk_found)
6316 image->start_loop=MagickTrue;
6317 image->iterations=mng_iterations;
6318 term_chunk_found=MagickFalse;
6322 image->start_loop=MagickFalse;
6324 /* Make a background rectangle. */
6327 image->columns=mng_info->mng_width;
6328 image->rows=mng_info->mng_height;
6329 image->page.width=mng_info->mng_width;
6330 image->page.height=mng_info->mng_height;
6333 image->background_color=mng_background_color;
6334 (void) SetImageBackgroundColor(image,exception);
6335 if (logging != MagickFalse)
6336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6337 " Inserted transparent background layer, W=%.20g, H=%.20g",
6338 (double) mng_info->mng_width,(double) mng_info->mng_height);
6342 Insert a background layer behind the upcoming image if
6343 framing_mode is 3, and we haven't already inserted one.
6345 if (insert_layers && (mng_info->framing_mode == 3) &&
6346 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6347 (simplicity & 0x08)))
6349 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6352 Allocate next image structure.
6354 AcquireNextImage(image_info,image,exception);
6356 if (GetNextImageInList(image) == (Image *) NULL)
6358 image=DestroyImageList(image);
6359 MngInfoFreeStruct(mng_info,&have_mng_structure);
6360 return((Image *) NULL);
6363 image=SyncNextImageInList(image);
6366 mng_info->image=image;
6368 if (term_chunk_found)
6370 image->start_loop=MagickTrue;
6371 image->iterations=mng_iterations;
6372 term_chunk_found=MagickFalse;
6376 image->start_loop=MagickFalse;
6379 image->columns=subframe_width;
6380 image->rows=subframe_height;
6381 image->page.width=subframe_width;
6382 image->page.height=subframe_height;
6383 image->page.x=mng_info->clip.left;
6384 image->page.y=mng_info->clip.top;
6385 image->background_color=mng_background_color;
6386 image->alpha_trait=UndefinedPixelTrait;
6387 (void) SetImageBackgroundColor(image,exception);
6389 if (logging != MagickFalse)
6390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6391 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6392 (double) mng_info->clip.left,(double) mng_info->clip.right,
6393 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6395 #endif /* MNG_INSERT_LAYERS */
6396 first_mng_object=MagickFalse;
6398 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6401 Allocate next image structure.
6403 AcquireNextImage(image_info,image,exception);
6405 if (GetNextImageInList(image) == (Image *) NULL)
6407 image=DestroyImageList(image);
6408 MngInfoFreeStruct(mng_info,&have_mng_structure);
6409 return((Image *) NULL);
6412 image=SyncNextImageInList(image);
6414 mng_info->image=image;
6415 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6416 GetBlobSize(image));
6418 if (status == MagickFalse)
6421 if (term_chunk_found)
6423 image->start_loop=MagickTrue;
6424 term_chunk_found=MagickFalse;
6428 image->start_loop=MagickFalse;
6430 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6432 image->delay=frame_delay;
6433 frame_delay=default_frame_delay;
6439 image->page.width=mng_info->mng_width;
6440 image->page.height=mng_info->mng_height;
6441 image->page.x=mng_info->x_off[object_id];
6442 image->page.y=mng_info->y_off[object_id];
6443 image->iterations=mng_iterations;
6446 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6449 if (logging != MagickFalse)
6450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6451 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6454 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6457 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6461 mng_info->image=image;
6462 mng_info->mng_type=mng_type;
6463 mng_info->object_id=object_id;
6465 if (memcmp(type,mng_IHDR,4) == 0)
6466 image=ReadOnePNGImage(mng_info,image_info,exception);
6468 #if defined(JNG_SUPPORTED)
6470 image=ReadOneJNGImage(mng_info,image_info,exception);
6473 if (image == (Image *) NULL)
6475 if (IsImageObject(previous) != MagickFalse)
6477 (void) DestroyImageList(previous);
6478 (void) CloseBlob(previous);
6481 MngInfoFreeStruct(mng_info,&have_mng_structure);
6482 return((Image *) NULL);
6485 if (image->columns == 0 || image->rows == 0)
6487 (void) CloseBlob(image);
6488 image=DestroyImageList(image);
6489 MngInfoFreeStruct(mng_info,&have_mng_structure);
6490 return((Image *) NULL);
6493 mng_info->image=image;
6500 if (mng_info->magn_methx || mng_info->magn_methy)
6506 if (logging != MagickFalse)
6507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6508 " Processing MNG MAGN chunk");
6510 if (mng_info->magn_methx == 1)
6512 magnified_width=mng_info->magn_ml;
6514 if (image->columns > 1)
6515 magnified_width += mng_info->magn_mr;
6517 if (image->columns > 2)
6518 magnified_width += (png_uint_32)
6519 ((image->columns-2)*(mng_info->magn_mx));
6524 magnified_width=(png_uint_32) image->columns;
6526 if (image->columns > 1)
6527 magnified_width += mng_info->magn_ml-1;
6529 if (image->columns > 2)
6530 magnified_width += mng_info->magn_mr-1;
6532 if (image->columns > 3)
6533 magnified_width += (png_uint_32)
6534 ((image->columns-3)*(mng_info->magn_mx-1));
6537 if (mng_info->magn_methy == 1)
6539 magnified_height=mng_info->magn_mt;
6541 if (image->rows > 1)
6542 magnified_height += mng_info->magn_mb;
6544 if (image->rows > 2)
6545 magnified_height += (png_uint_32)
6546 ((image->rows-2)*(mng_info->magn_my));
6551 magnified_height=(png_uint_32) image->rows;
6553 if (image->rows > 1)
6554 magnified_height += mng_info->magn_mt-1;
6556 if (image->rows > 2)
6557 magnified_height += mng_info->magn_mb-1;
6559 if (image->rows > 3)
6560 magnified_height += (png_uint_32)
6561 ((image->rows-3)*(mng_info->magn_my-1));
6564 if (magnified_height > image->rows ||
6565 magnified_width > image->columns)
6592 /* Allocate next image structure. */
6594 if (logging != MagickFalse)
6595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6596 " Allocate magnified image");
6598 AcquireNextImage(image_info,image,exception);
6600 if (GetNextImageInList(image) == (Image *) NULL)
6602 image=DestroyImageList(image);
6603 MngInfoFreeStruct(mng_info,&have_mng_structure);
6604 return((Image *) NULL);
6607 large_image=SyncNextImageInList(image);
6609 large_image->columns=magnified_width;
6610 large_image->rows=magnified_height;
6612 magn_methx=mng_info->magn_methx;
6613 magn_methy=mng_info->magn_methy;
6615 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6616 #define QM unsigned short
6617 if (magn_methx != 1 || magn_methy != 1)
6620 Scale pixels to unsigned shorts to prevent
6621 overflow of intermediate values of interpolations
6623 for (y=0; y < (ssize_t) image->rows; y++)
6625 q=GetAuthenticPixels(image,0,y,image->columns,1,
6628 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6630 SetPixelRed(image,ScaleQuantumToShort(
6631 GetPixelRed(image,q)),q);
6632 SetPixelGreen(image,ScaleQuantumToShort(
6633 GetPixelGreen(image,q)),q);
6634 SetPixelBlue(image,ScaleQuantumToShort(
6635 GetPixelBlue(image,q)),q);
6636 SetPixelAlpha(image,ScaleQuantumToShort(
6637 GetPixelAlpha(image,q)),q);
6638 q+=GetPixelChannels(image);
6641 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6649 if (image->alpha_trait == BlendPixelTrait)
6650 (void) SetImageBackgroundColor(large_image,exception);
6654 large_image->background_color.alpha=OpaqueAlpha;
6655 (void) SetImageBackgroundColor(large_image,exception);
6657 if (magn_methx == 4)
6660 if (magn_methx == 5)
6663 if (magn_methy == 4)
6666 if (magn_methy == 5)
6670 /* magnify the rows into the right side of the large image */
6672 if (logging != MagickFalse)
6673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6674 " Magnify the rows to %.20g",(double) large_image->rows);
6675 m=(ssize_t) mng_info->magn_mt;
6677 length=(size_t) image->columns*GetPixelChannels(image);
6678 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6679 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6681 if ((prev == (Quantum *) NULL) ||
6682 (next == (Quantum *) NULL))
6684 image=DestroyImageList(image);
6685 MngInfoFreeStruct(mng_info,&have_mng_structure);
6686 ThrowReaderException(ResourceLimitError,
6687 "MemoryAllocationFailed");
6690 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6691 (void) CopyMagickMemory(next,n,length);
6693 for (y=0; y < (ssize_t) image->rows; y++)
6696 m=(ssize_t) mng_info->magn_mt;
6698 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6699 m=(ssize_t) mng_info->magn_mb;
6701 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6702 m=(ssize_t) mng_info->magn_mb;
6704 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6708 m=(ssize_t) mng_info->magn_my;
6714 if (y < (ssize_t) image->rows-1)
6716 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6718 (void) CopyMagickMemory(next,n,length);
6721 for (i=0; i < m; i++, yy++)
6726 assert(yy < (ssize_t) large_image->rows);
6729 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6731 q+=(large_image->columns-image->columns)*
6732 GetPixelChannels(large_image);
6734 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6736 /* To do: get color as function of indexes[x] */
6738 if (image->storage_class == PseudoClass)
6743 if (magn_methy <= 1)
6745 /* replicate previous */
6746 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6747 SetPixelGreen(large_image,GetPixelGreen(image,
6749 SetPixelBlue(large_image,GetPixelBlue(image,
6751 SetPixelAlpha(large_image,GetPixelAlpha(image,
6755 else if (magn_methy == 2 || magn_methy == 4)
6759 SetPixelRed(large_image,GetPixelRed(image,
6761 SetPixelGreen(large_image,GetPixelGreen(image,
6763 SetPixelBlue(large_image,GetPixelBlue(image,
6765 SetPixelAlpha(large_image,GetPixelAlpha(image,
6772 SetPixelRed(large_image,((QM) (((ssize_t)
6773 (2*i*(GetPixelRed(image,n)
6774 -GetPixelRed(image,pixels)+m))/
6776 +GetPixelRed(image,pixels)))),q);
6777 SetPixelGreen(large_image,((QM) (((ssize_t)
6778 (2*i*(GetPixelGreen(image,n)
6779 -GetPixelGreen(image,pixels)+m))/
6781 +GetPixelGreen(image,pixels)))),q);
6782 SetPixelBlue(large_image,((QM) (((ssize_t)
6783 (2*i*(GetPixelBlue(image,n)
6784 -GetPixelBlue(image,pixels)+m))/
6786 +GetPixelBlue(image,pixels)))),q);
6788 if (image->alpha_trait == BlendPixelTrait)
6789 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6790 (2*i*(GetPixelAlpha(image,n)
6791 -GetPixelAlpha(image,pixels)+m))
6793 GetPixelAlpha(image,pixels)))),q);
6796 if (magn_methy == 4)
6798 /* Replicate nearest */
6799 if (i <= ((m+1) << 1))
6800 SetPixelAlpha(large_image,GetPixelAlpha(image,
6803 SetPixelAlpha(large_image,GetPixelAlpha(image,
6808 else /* if (magn_methy == 3 || magn_methy == 5) */
6810 /* Replicate nearest */
6811 if (i <= ((m+1) << 1))
6813 SetPixelRed(large_image,GetPixelRed(image,
6815 SetPixelGreen(large_image,GetPixelGreen(image,
6817 SetPixelBlue(large_image,GetPixelBlue(image,
6819 SetPixelAlpha(large_image,GetPixelAlpha(image,
6825 SetPixelRed(large_image,GetPixelRed(image,n),q);
6826 SetPixelGreen(large_image,GetPixelGreen(image,n),
6828 SetPixelBlue(large_image,GetPixelBlue(image,n),
6830 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6834 if (magn_methy == 5)
6836 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6837 (GetPixelAlpha(image,n)
6838 -GetPixelAlpha(image,pixels))
6839 +m))/((ssize_t) (m*2))
6840 +GetPixelAlpha(image,pixels)),q);
6843 n+=GetPixelChannels(image);
6844 q+=GetPixelChannels(large_image);
6845 pixels+=GetPixelChannels(image);
6848 if (SyncAuthenticPixels(large_image,exception) == 0)
6854 prev=(Quantum *) RelinquishMagickMemory(prev);
6855 next=(Quantum *) RelinquishMagickMemory(next);
6857 length=image->columns;
6859 if (logging != MagickFalse)
6860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6861 " Delete original image");
6863 DeleteImageFromList(&image);
6867 mng_info->image=image;
6869 /* magnify the columns */
6870 if (logging != MagickFalse)
6871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6872 " Magnify the columns to %.20g",(double) image->columns);
6874 for (y=0; y < (ssize_t) image->rows; y++)
6879 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6880 pixels=q+(image->columns-length)*GetPixelChannels(image);
6881 n=pixels+GetPixelChannels(image);
6883 for (x=(ssize_t) (image->columns-length);
6884 x < (ssize_t) image->columns; x++)
6886 /* To do: Rewrite using Get/Set***PixelChannel() */
6888 if (x == (ssize_t) (image->columns-length))
6889 m=(ssize_t) mng_info->magn_ml;
6891 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6892 m=(ssize_t) mng_info->magn_mr;
6894 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6895 m=(ssize_t) mng_info->magn_mr;
6897 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6901 m=(ssize_t) mng_info->magn_mx;
6903 for (i=0; i < m; i++)
6905 if (magn_methx <= 1)
6907 /* replicate previous */
6908 SetPixelRed(image,GetPixelRed(image,pixels),q);
6909 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6910 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6911 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6914 else if (magn_methx == 2 || magn_methx == 4)
6918 SetPixelRed(image,GetPixelRed(image,pixels),q);
6919 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6920 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6921 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6924 /* To do: Rewrite using Get/Set***PixelChannel() */
6928 SetPixelRed(image,(QM) ((2*i*(
6929 GetPixelRed(image,n)
6930 -GetPixelRed(image,pixels))+m)
6932 GetPixelRed(image,pixels)),q);
6934 SetPixelGreen(image,(QM) ((2*i*(
6935 GetPixelGreen(image,n)
6936 -GetPixelGreen(image,pixels))+m)
6938 GetPixelGreen(image,pixels)),q);
6940 SetPixelBlue(image,(QM) ((2*i*(
6941 GetPixelBlue(image,n)
6942 -GetPixelBlue(image,pixels))+m)
6944 GetPixelBlue(image,pixels)),q);
6945 if (image->alpha_trait == BlendPixelTrait)
6946 SetPixelAlpha(image,(QM) ((2*i*(
6947 GetPixelAlpha(image,n)
6948 -GetPixelAlpha(image,pixels))+m)
6950 GetPixelAlpha(image,pixels)),q);
6953 if (magn_methx == 4)
6955 /* Replicate nearest */
6956 if (i <= ((m+1) << 1))
6958 SetPixelAlpha(image,
6959 GetPixelAlpha(image,pixels)+0,q);
6963 SetPixelAlpha(image,
6964 GetPixelAlpha(image,n)+0,q);
6969 else /* if (magn_methx == 3 || magn_methx == 5) */
6971 /* Replicate nearest */
6972 if (i <= ((m+1) << 1))
6974 SetPixelRed(image,GetPixelRed(image,pixels),q);
6975 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6976 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6977 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6982 SetPixelRed(image,GetPixelRed(image,n),q);
6983 SetPixelGreen(image,GetPixelGreen(image,n),q);
6984 SetPixelBlue(image,GetPixelBlue(image,n),q);
6985 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6988 if (magn_methx == 5)
6991 SetPixelAlpha(image,
6992 (QM) ((2*i*( GetPixelAlpha(image,n)
6993 -GetPixelAlpha(image,pixels))+m)/
6995 +GetPixelAlpha(image,pixels)),q);
6998 q+=GetPixelChannels(image);
7000 n+=GetPixelChannels(image);
7001 p+=GetPixelChannels(image);
7004 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7007 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7008 if (magn_methx != 1 || magn_methy != 1)
7011 Rescale pixels to Quantum
7013 for (y=0; y < (ssize_t) image->rows; y++)
7015 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7017 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7019 SetPixelRed(image,ScaleShortToQuantum(
7020 GetPixelRed(image,q)),q);
7021 SetPixelGreen(image,ScaleShortToQuantum(
7022 GetPixelGreen(image,q)),q);
7023 SetPixelBlue(image,ScaleShortToQuantum(
7024 GetPixelBlue(image,q)),q);
7025 SetPixelAlpha(image,ScaleShortToQuantum(
7026 GetPixelAlpha(image,q)),q);
7027 q+=GetPixelChannels(image);
7030 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7035 if (logging != MagickFalse)
7036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7037 " Finished MAGN processing");
7042 Crop_box is with respect to the upper left corner of the MNG.
7044 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7045 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7046 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7047 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7048 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7049 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7050 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7051 if ((crop_box.left != (mng_info->image_box.left
7052 +mng_info->x_off[object_id])) ||
7053 (crop_box.right != (mng_info->image_box.right
7054 +mng_info->x_off[object_id])) ||
7055 (crop_box.top != (mng_info->image_box.top
7056 +mng_info->y_off[object_id])) ||
7057 (crop_box.bottom != (mng_info->image_box.bottom
7058 +mng_info->y_off[object_id])))
7060 if (logging != MagickFalse)
7061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7062 " Crop the PNG image");
7064 if ((crop_box.left < crop_box.right) &&
7065 (crop_box.top < crop_box.bottom))
7074 Crop_info is with respect to the upper left corner of
7077 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7078 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7079 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7080 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7081 image->page.width=image->columns;
7082 image->page.height=image->rows;
7085 im=CropImage(image,&crop_info,exception);
7087 if (im != (Image *) NULL)
7089 image->columns=im->columns;
7090 image->rows=im->rows;
7091 im=DestroyImage(im);
7092 image->page.width=image->columns;
7093 image->page.height=image->rows;
7094 image->page.x=crop_box.left;
7095 image->page.y=crop_box.top;
7102 No pixels in crop area. The MNG spec still requires
7103 a layer, though, so make a single transparent pixel in
7104 the top left corner.
7109 (void) SetImageBackgroundColor(image,exception);
7110 image->page.width=1;
7111 image->page.height=1;
7116 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7117 image=mng_info->image;
7121 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7122 /* PNG does not handle depths greater than 16 so reduce it even
7125 if (image->depth > 16)
7129 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7130 if (image->depth > 8)
7132 /* To do: fill low byte properly */
7136 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7140 if (image_info->number_scenes != 0)
7142 if (mng_info->scenes_found >
7143 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7147 if (logging != MagickFalse)
7148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7149 " Finished reading image datastream.");
7151 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7153 (void) CloseBlob(image);
7155 if (logging != MagickFalse)
7156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7157 " Finished reading all image datastreams.");
7159 #if defined(MNG_INSERT_LAYERS)
7160 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7161 (mng_info->mng_height))
7164 Insert a background layer if nothing else was found.
7166 if (logging != MagickFalse)
7167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7168 " No images found. Inserting a background layer.");
7170 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7173 Allocate next image structure.
7175 AcquireNextImage(image_info,image,exception);
7176 if (GetNextImageInList(image) == (Image *) NULL)
7178 image=DestroyImageList(image);
7179 MngInfoFreeStruct(mng_info,&have_mng_structure);
7181 if (logging != MagickFalse)
7182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7183 " Allocation failed, returning NULL.");
7185 return((Image *) NULL);
7187 image=SyncNextImageInList(image);
7189 image->columns=mng_info->mng_width;
7190 image->rows=mng_info->mng_height;
7191 image->page.width=mng_info->mng_width;
7192 image->page.height=mng_info->mng_height;
7195 image->background_color=mng_background_color;
7196 image->alpha_trait=UndefinedPixelTrait;
7198 if (image_info->ping == MagickFalse)
7199 (void) SetImageBackgroundColor(image,exception);
7201 mng_info->image_found++;
7204 image->iterations=mng_iterations;
7206 if (mng_iterations == 1)
7207 image->start_loop=MagickTrue;
7209 while (GetPreviousImageInList(image) != (Image *) NULL)
7212 if (image_count > 10*mng_info->image_found)
7214 if (logging != MagickFalse)
7215 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7217 (void) ThrowMagickException(exception,GetMagickModule(),
7218 CoderError,"Linked list is corrupted, beginning of list not found",
7219 "`%s'",image_info->filename);
7221 return((Image *) NULL);
7224 image=GetPreviousImageInList(image);
7226 if (GetNextImageInList(image) == (Image *) NULL)
7228 if (logging != MagickFalse)
7229 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7231 (void) ThrowMagickException(exception,GetMagickModule(),
7232 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7233 image_info->filename);
7237 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7238 GetNextImageInList(image) ==
7241 if (logging != MagickFalse)
7242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7243 " First image null");
7245 (void) ThrowMagickException(exception,GetMagickModule(),
7246 CoderError,"image->next for first image is NULL but shouldn't be.",
7247 "`%s'",image_info->filename);
7250 if (mng_info->image_found == 0)
7252 if (logging != MagickFalse)
7253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254 " No visible images found.");
7256 (void) ThrowMagickException(exception,GetMagickModule(),
7257 CoderError,"No visible images in file","`%s'",image_info->filename);
7259 if (image != (Image *) NULL)
7260 image=DestroyImageList(image);
7262 MngInfoFreeStruct(mng_info,&have_mng_structure);
7263 return((Image *) NULL);
7266 if (mng_info->ticks_per_second)
7267 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7268 final_delay/mng_info->ticks_per_second;
7271 image->start_loop=MagickTrue;
7273 /* Find final nonzero image delay */
7274 final_image_delay=0;
7276 while (GetNextImageInList(image) != (Image *) NULL)
7279 final_image_delay=image->delay;
7281 image=GetNextImageInList(image);
7284 if (final_delay < final_image_delay)
7285 final_delay=final_image_delay;
7287 image->delay=final_delay;
7289 if (logging != MagickFalse)
7290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7291 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7292 (double) final_delay);
7294 if (logging != MagickFalse)
7300 image=GetFirstImageInList(image);
7302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7303 " Before coalesce:");
7305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7306 " scene 0 delay=%.20g",(double) image->delay);
7308 while (GetNextImageInList(image) != (Image *) NULL)
7310 image=GetNextImageInList(image);
7311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7312 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7316 image=GetFirstImageInList(image);
7317 #ifdef MNG_COALESCE_LAYERS
7327 if (logging != MagickFalse)
7328 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7331 next_image=CoalesceImages(image,exception);
7333 if (next_image == (Image *) NULL)
7334 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7336 image=DestroyImageList(image);
7339 for (next=image; next != (Image *) NULL; next=next_image)
7341 next->page.width=mng_info->mng_width;
7342 next->page.height=mng_info->mng_height;
7345 next->scene=scene++;
7346 next_image=GetNextImageInList(next);
7348 if (next_image == (Image *) NULL)
7351 if (next->delay == 0)
7354 next_image->previous=GetPreviousImageInList(next);
7355 if (GetPreviousImageInList(next) == (Image *) NULL)
7358 next->previous->next=next_image;
7359 next=DestroyImage(next);
7365 while (GetNextImageInList(image) != (Image *) NULL)
7366 image=GetNextImageInList(image);
7368 image->dispose=BackgroundDispose;
7370 if (logging != MagickFalse)
7376 image=GetFirstImageInList(image);
7378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7379 " After coalesce:");
7381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7382 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7383 (double) image->dispose);
7385 while (GetNextImageInList(image) != (Image *) NULL)
7387 image=GetNextImageInList(image);
7389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7390 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7391 (double) image->delay,(double) image->dispose);
7395 image=GetFirstImageInList(image);
7396 MngInfoFreeStruct(mng_info,&have_mng_structure);
7397 have_mng_structure=MagickFalse;
7399 if (logging != MagickFalse)
7400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7402 return(GetFirstImageInList(image));
7404 #else /* PNG_LIBPNG_VER > 10011 */
7405 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7407 printf("Your PNG library is too old: You have libpng-%s\n",
7408 PNG_LIBPNG_VER_STRING);
7410 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7411 "PNG library is too old","`%s'",image_info->filename);
7413 return(Image *) NULL;
7416 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7418 return(ReadPNGImage(image_info,exception));
7420 #endif /* PNG_LIBPNG_VER > 10011 */
7424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7428 % R e g i s t e r P N G I m a g e %
7432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7434 % RegisterPNGImage() adds properties for the PNG image format to
7435 % the list of supported formats. The properties include the image format
7436 % tag, a method to read and/or write the format, whether the format
7437 % supports the saving of more than one frame to the same file or blob,
7438 % whether the format supports native in-memory I/O, and a brief
7439 % description of the format.
7441 % The format of the RegisterPNGImage method is:
7443 % size_t RegisterPNGImage(void)
7446 ModuleExport size_t RegisterPNGImage(void)
7449 version[MaxTextExtent];
7457 "See http://www.libpng.org/ for details about the PNG format."
7462 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7468 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7474 #if defined(PNG_LIBPNG_VER_STRING)
7475 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7476 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7478 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7480 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7481 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7486 entry=SetMagickInfo("MNG");
7487 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7489 #if defined(MAGICKCORE_PNG_DELEGATE)
7490 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7491 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7494 entry->magick=(IsImageFormatHandler *) IsMNG;
7495 entry->description=ConstantString("Multiple-image Network Graphics");
7497 if (*version != '\0')
7498 entry->version=ConstantString(version);
7500 entry->mime_type=ConstantString("video/x-mng");
7501 entry->module=ConstantString("PNG");
7502 entry->note=ConstantString(MNGNote);
7503 (void) RegisterMagickInfo(entry);
7505 entry=SetMagickInfo("PNG");
7507 #if defined(MAGICKCORE_PNG_DELEGATE)
7508 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7509 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7512 entry->magick=(IsImageFormatHandler *) IsPNG;
7513 entry->adjoin=MagickFalse;
7514 entry->description=ConstantString("Portable Network Graphics");
7515 entry->mime_type=ConstantString("image/png");
7516 entry->module=ConstantString("PNG");
7518 if (*version != '\0')
7519 entry->version=ConstantString(version);
7521 entry->note=ConstantString(PNGNote);
7522 (void) RegisterMagickInfo(entry);
7524 entry=SetMagickInfo("PNG8");
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(
7534 "8-bit indexed with optional binary transparency");
7535 entry->mime_type=ConstantString("image/png");
7536 entry->module=ConstantString("PNG");
7537 (void) RegisterMagickInfo(entry);
7539 entry=SetMagickInfo("PNG24");
7542 #if defined(ZLIB_VERSION)
7543 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7544 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7546 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7548 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7549 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7553 if (*version != '\0')
7554 entry->version=ConstantString(version);
7556 #if defined(MAGICKCORE_PNG_DELEGATE)
7557 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7558 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7561 entry->magick=(IsImageFormatHandler *) IsPNG;
7562 entry->adjoin=MagickFalse;
7563 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7564 entry->mime_type=ConstantString("image/png");
7565 entry->module=ConstantString("PNG");
7566 (void) RegisterMagickInfo(entry);
7568 entry=SetMagickInfo("PNG32");
7570 #if defined(MAGICKCORE_PNG_DELEGATE)
7571 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7572 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7575 entry->magick=(IsImageFormatHandler *) IsPNG;
7576 entry->adjoin=MagickFalse;
7577 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7578 entry->mime_type=ConstantString("image/png");
7579 entry->module=ConstantString("PNG");
7580 (void) RegisterMagickInfo(entry);
7582 entry=SetMagickInfo("PNG48");
7584 #if defined(MAGICKCORE_PNG_DELEGATE)
7585 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7586 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7589 entry->magick=(IsImageFormatHandler *) IsPNG;
7590 entry->adjoin=MagickFalse;
7591 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7592 entry->mime_type=ConstantString("image/png");
7593 entry->module=ConstantString("PNG");
7594 (void) RegisterMagickInfo(entry);
7596 entry=SetMagickInfo("PNG64");
7598 #if defined(MAGICKCORE_PNG_DELEGATE)
7599 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7600 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7603 entry->magick=(IsImageFormatHandler *) IsPNG;
7604 entry->adjoin=MagickFalse;
7605 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7606 entry->mime_type=ConstantString("image/png");
7607 entry->module=ConstantString("PNG");
7608 (void) RegisterMagickInfo(entry);
7610 entry=SetMagickInfo("PNG00");
7612 #if defined(MAGICKCORE_PNG_DELEGATE)
7613 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7614 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7617 entry->magick=(IsImageFormatHandler *) IsPNG;
7618 entry->adjoin=MagickFalse;
7619 entry->description=ConstantString(
7620 "PNG inheriting bit-depth and color-type from original");
7621 entry->mime_type=ConstantString("image/png");
7622 entry->module=ConstantString("PNG");
7623 (void) RegisterMagickInfo(entry);
7625 entry=SetMagickInfo("JNG");
7627 #if defined(JNG_SUPPORTED)
7628 #if defined(MAGICKCORE_PNG_DELEGATE)
7629 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7630 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7634 entry->magick=(IsImageFormatHandler *) IsJNG;
7635 entry->adjoin=MagickFalse;
7636 entry->description=ConstantString("JPEG Network Graphics");
7637 entry->mime_type=ConstantString("image/x-jng");
7638 entry->module=ConstantString("PNG");
7639 entry->note=ConstantString(JNGNote);
7640 (void) RegisterMagickInfo(entry);
7642 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7643 ping_semaphore=AllocateSemaphoreInfo();
7646 return(MagickImageCoderSignature);
7650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7654 % U n r e g i s t e r P N G I m a g e %
7658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7660 % UnregisterPNGImage() removes format registrations made by the
7661 % PNG module from the list of supported formats.
7663 % The format of the UnregisterPNGImage method is:
7665 % UnregisterPNGImage(void)
7668 ModuleExport void UnregisterPNGImage(void)
7670 (void) UnregisterMagickInfo("MNG");
7671 (void) UnregisterMagickInfo("PNG");
7672 (void) UnregisterMagickInfo("PNG8");
7673 (void) UnregisterMagickInfo("PNG24");
7674 (void) UnregisterMagickInfo("PNG32");
7675 (void) UnregisterMagickInfo("PNG48");
7676 (void) UnregisterMagickInfo("PNG64");
7677 (void) UnregisterMagickInfo("PNG00");
7678 (void) UnregisterMagickInfo("JNG");
7680 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7681 if (ping_semaphore != (SemaphoreInfo *) NULL)
7682 DestroySemaphoreInfo(&ping_semaphore);
7686 #if defined(MAGICKCORE_PNG_DELEGATE)
7687 #if PNG_LIBPNG_VER > 10011
7689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7693 % W r i t e M N G I m a g e %
7697 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7699 % WriteMNGImage() writes an image in the Portable Network Graphics
7700 % Group's "Multiple-image Network Graphics" encoded image format.
7702 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7704 % The format of the WriteMNGImage method is:
7706 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7707 % Image *image,ExceptionInfo *exception)
7709 % A description of each parameter follows.
7711 % o image_info: the image info.
7713 % o image: The image.
7715 % o exception: return any errors or warnings in this structure.
7717 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7718 % "To do" under ReadPNGImage):
7720 % Preserve all unknown and not-yet-handled known chunks found in input
7721 % PNG file and copy them into output PNG files according to the PNG
7724 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7726 % Improve selection of color type (use indexed-colour or indexed-colour
7727 % with tRNS when 256 or fewer unique RGBA values are present).
7729 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7730 % This will be complicated if we limit ourselves to generating MNG-LC
7731 % files. For now we ignore disposal method 3 and simply overlay the next
7734 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7735 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7736 % [mostly done 15 June 1999 but still need to take care of tRNS]
7738 % Check for identical sRGB and replace with a global sRGB (and remove
7739 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7740 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7741 % local gAMA/cHRM with local sRGB if appropriate).
7743 % Check for identical sBIT chunks and write global ones.
7745 % Provide option to skip writing the signature tEXt chunks.
7747 % Use signatures to detect identical objects and reuse the first
7748 % instance of such objects instead of writing duplicate objects.
7750 % Use a smaller-than-32k value of compression window size when
7753 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7754 % ancillary text chunks and save profiles.
7756 % Provide an option to force LC files (to ensure exact framing rate)
7759 % Provide an option to force VLC files instead of LC, even when offsets
7760 % are present. This will involve expanding the embedded images with a
7761 % transparent region at the top and/or left.
7765 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7766 png_info *ping_info, unsigned char *profile_type, unsigned char
7767 *profile_description, unsigned char *profile_data, png_uint_32 length)
7786 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7788 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7791 if (image_info->verbose)
7793 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7794 (char *) profile_type, (double) length);
7797 #if PNG_LIBPNG_VER >= 10400
7798 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7800 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7802 description_length=(png_uint_32) strlen((const char *) profile_description);
7803 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7804 + description_length);
7805 #if PNG_LIBPNG_VER >= 10400
7806 text[0].text=(png_charp) png_malloc(ping,
7807 (png_alloc_size_t) allocated_length);
7808 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7810 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7811 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7813 text[0].key[0]='\0';
7814 (void) ConcatenateMagickString(text[0].key,
7815 "Raw profile type ",MaxTextExtent);
7816 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7820 (void) CopyMagickString(dp,(const char *) profile_description,
7822 dp+=description_length;
7824 (void) FormatLocaleString(dp,allocated_length-
7825 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7828 for (i=0; i < (ssize_t) length; i++)
7832 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7833 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7838 text[0].text_length=(png_size_t) (dp-text[0].text);
7839 text[0].compression=image_info->compression == NoCompression ||
7840 (image_info->compression == UndefinedCompression &&
7841 text[0].text_length < 128) ? -1 : 0;
7843 if (text[0].text_length <= allocated_length)
7844 png_set_text(ping,ping_info,text,1);
7846 png_free(ping,text[0].text);
7847 png_free(ping,text[0].key);
7848 png_free(ping,text);
7851 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7852 const char *string, MagickBooleanType logging)
7865 ResetImageProfileIterator(image);
7867 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7869 profile=GetImageProfile(image,name);
7871 if (profile != (const StringInfo *) NULL)
7876 if (LocaleNCompare(name,string,11) == 0)
7878 if (logging != MagickFalse)
7879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7880 " Found %s profile",name);
7882 ping_profile=CloneStringInfo(profile);
7883 data=GetStringInfoDatum(ping_profile),
7884 length=(png_uint_32) GetStringInfoLength(ping_profile);
7889 (void) WriteBlobMSBULong(image,length-5); /* data length */
7890 (void) WriteBlob(image,length-1,data+1);
7891 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7892 ping_profile=DestroyStringInfo(ping_profile);
7896 name=GetNextImageProfile(image);
7903 /* Write one PNG image */
7904 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7905 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7936 ping_trans_alpha[256];
7964 ping_have_cheap_transparency,
7977 /* ping_exclude_EXIF, */
7980 /* ping_exclude_iTXt, */
7985 /* ping_exclude_tRNS, */
7987 ping_exclude_zCCP, /* hex-encoded iCCP */
7990 ping_preserve_colormap,
7992 ping_need_colortype_warning,
8000 *volatile pixel_info;
8019 ping_interlace_method,
8020 ping_compression_method,
8037 number_semitransparent,
8039 ping_pHYs_unit_type;
8042 ping_pHYs_x_resolution,
8043 ping_pHYs_y_resolution;
8045 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8046 " Enter WriteOnePNGImage()");
8048 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8049 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8050 if (image_info == (ImageInfo *) NULL)
8051 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8053 /* Define these outside of the following "if logging()" block so they will
8054 * show in debuggers.
8057 (void) ConcatenateMagickString(im_vers,
8058 MagickLibVersionText,MaxTextExtent);
8059 (void) ConcatenateMagickString(im_vers,
8060 MagickLibAddendum,MaxTextExtent);
8063 (void) ConcatenateMagickString(libpng_vers,
8064 PNG_LIBPNG_VER_STRING,32);
8066 (void) ConcatenateMagickString(libpng_runv,
8067 png_get_libpng_ver(NULL),32);
8070 (void) ConcatenateMagickString(zlib_vers,
8073 (void) ConcatenateMagickString(zlib_runv,
8078 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8080 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8082 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8084 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8087 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8089 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8091 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8096 /* Initialize some stuff */
8099 ping_interlace_method=0,
8100 ping_compression_method=0,
8101 ping_filter_method=0,
8104 ping_background.red = 0;
8105 ping_background.green = 0;
8106 ping_background.blue = 0;
8107 ping_background.gray = 0;
8108 ping_background.index = 0;
8110 ping_trans_color.red=0;
8111 ping_trans_color.green=0;
8112 ping_trans_color.blue=0;
8113 ping_trans_color.gray=0;
8115 ping_pHYs_unit_type = 0;
8116 ping_pHYs_x_resolution = 0;
8117 ping_pHYs_y_resolution = 0;
8119 ping_have_blob=MagickFalse;
8120 ping_have_cheap_transparency=MagickFalse;
8121 ping_have_color=MagickTrue;
8122 ping_have_non_bw=MagickTrue;
8123 ping_have_PLTE=MagickFalse;
8124 ping_have_bKGD=MagickFalse;
8125 ping_have_iCCP=MagickFalse;
8126 ping_have_pHYs=MagickFalse;
8127 ping_have_sRGB=MagickFalse;
8128 ping_have_tRNS=MagickFalse;
8130 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8131 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8132 ping_exclude_date=mng_info->ping_exclude_date;
8133 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8134 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8135 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8136 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8137 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8138 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8139 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8140 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8141 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8142 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8143 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8144 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8146 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8147 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8148 ping_need_colortype_warning = MagickFalse;
8150 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8151 * i.e., eliminate the ICC profile and set image->rendering_intent.
8152 * Note that this will not involve any changes to the actual pixels
8153 * but merely passes information to applications that read the resulting
8156 * To do: recognize other variants of the sRGB profile, using the CRC to
8157 * verify all recognized variants including the 7 already known.
8159 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8161 * Use something other than image->rendering_intent to record the fact
8162 * that the sRGB profile was found.
8164 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8165 * profile. Record the Blackpoint Compensation, if any.
8167 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8175 ResetImageProfileIterator(image);
8176 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8178 profile=GetImageProfile(image,name);
8180 if (profile != (StringInfo *) NULL)
8182 if ((LocaleCompare(name,"ICC") == 0) ||
8183 (LocaleCompare(name,"ICM") == 0))
8198 length=(png_uint_32) GetStringInfoLength(profile);
8200 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8202 if (length == sRGB_info[icheck].len)
8206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 " Got a %lu-byte ICC profile (potentially sRGB)",
8208 (unsigned long) length);
8210 data=GetStringInfoDatum(profile);
8211 profile_crc=crc32(0,data,length);
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " with crc=%8x",(unsigned int) profile_crc);
8218 if (profile_crc == sRGB_info[icheck].crc)
8220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8221 " It is sRGB with rendering intent = %s",
8222 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8223 sRGB_info[icheck].intent));
8224 if (image->rendering_intent==UndefinedIntent)
8226 image->rendering_intent=
8227 Magick_RenderingIntent_from_PNG_RenderingIntent(
8228 sRGB_info[icheck].intent);
8230 ping_exclude_iCCP = MagickTrue;
8231 ping_exclude_zCCP = MagickTrue;
8232 ping_have_sRGB = MagickTrue;
8237 if (sRGB_info[icheck].len == 0)
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " Got a %lu-byte ICC profile not recognized as sRGB",
8240 (unsigned long) length);
8243 name=GetNextImageProfile(image);
8248 number_semitransparent = 0;
8249 number_transparent = 0;
8251 if (logging != MagickFalse)
8253 if (image->storage_class == UndefinedClass)
8254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8255 " storage_class=UndefinedClass");
8256 if (image->storage_class == DirectClass)
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " storage_class=DirectClass");
8259 if (image->storage_class == PseudoClass)
8260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8261 " storage_class=PseudoClass");
8264 if (image->storage_class == PseudoClass &&
8265 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8266 mng_info->write_png48 || mng_info->write_png64 ||
8267 (mng_info->write_png_colortype != 1 &&
8268 mng_info->write_png_colortype != 5)))
8270 (void) SyncImage(image,exception);
8271 image->storage_class = DirectClass;
8274 if (ping_preserve_colormap == MagickFalse)
8276 if (image->storage_class != PseudoClass && image->colormap != NULL)
8278 /* Free the bogus colormap; it can cause trouble later */
8279 if (logging != MagickFalse)
8280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8281 " Freeing bogus colormap");
8282 (void) RelinquishMagickMemory(image->colormap);
8283 image->colormap=NULL;
8287 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8288 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8291 Sometimes we get PseudoClass images whose RGB values don't match
8292 the colors in the colormap. This code syncs the RGB values.
8294 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8295 (void) SyncImage(image,exception);
8297 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8298 if (image->depth > 8)
8300 if (logging != MagickFalse)
8301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8302 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8308 /* Respect the -depth option */
8309 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8314 if (image->depth > 8)
8316 #if MAGICKCORE_QUANTUM_DEPTH > 16
8317 /* Scale to 16-bit */
8318 LBR16PacketRGBO(image->background_color);
8320 for (y=0; y < (ssize_t) image->rows; y++)
8322 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8324 if (r == (Quantum *) NULL)
8327 for (x=0; x < (ssize_t) image->columns; x++)
8330 r+=GetPixelChannels(image);
8333 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8337 if (image->storage_class == PseudoClass && image->colormap != NULL)
8339 for (i=0; i < (ssize_t) image->colors; i++)
8341 LBR16PacketRGBO(image->colormap[i]);
8344 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8347 else if (image->depth > 4)
8349 #if MAGICKCORE_QUANTUM_DEPTH > 8
8350 /* Scale to 8-bit */
8351 LBR08PacketRGBO(image->background_color);
8353 for (y=0; y < (ssize_t) image->rows; y++)
8355 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8357 if (r == (Quantum *) NULL)
8360 for (x=0; x < (ssize_t) image->columns; x++)
8363 r+=GetPixelChannels(image);
8366 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8370 if (image->storage_class == PseudoClass && image->colormap != NULL)
8372 for (i=0; i < (ssize_t) image->colors; i++)
8374 LBR08PacketRGBO(image->colormap[i]);
8377 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8380 if (image->depth > 2)
8382 /* Scale to 4-bit */
8383 LBR04PacketRGBO(image->background_color);
8385 for (y=0; y < (ssize_t) image->rows; y++)
8387 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8389 if (r == (Quantum *) NULL)
8392 for (x=0; x < (ssize_t) image->columns; x++)
8395 r+=GetPixelChannels(image);
8398 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8402 if (image->storage_class == PseudoClass && image->colormap != NULL)
8404 for (i=0; i < (ssize_t) image->colors; i++)
8406 LBR04PacketRGBO(image->colormap[i]);
8411 else if (image->depth > 1)
8413 /* Scale to 2-bit */
8414 LBR02PacketRGBO(image->background_color);
8416 for (y=0; y < (ssize_t) image->rows; y++)
8418 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8420 if (r == (Quantum *) NULL)
8423 for (x=0; x < (ssize_t) image->columns; x++)
8426 r+=GetPixelChannels(image);
8429 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8433 if (image->storage_class == PseudoClass && image->colormap != NULL)
8435 for (i=0; i < (ssize_t) image->colors; i++)
8437 LBR02PacketRGBO(image->colormap[i]);
8443 /* Scale to 1-bit */
8444 LBR01PacketRGBO(image->background_color);
8446 for (y=0; y < (ssize_t) image->rows; y++)
8448 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8450 if (r == (Quantum *) NULL)
8453 for (x=0; x < (ssize_t) image->columns; x++)
8456 r+=GetPixelChannels(image);
8459 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8463 if (image->storage_class == PseudoClass && image->colormap != NULL)
8465 for (i=0; i < (ssize_t) image->colors; i++)
8467 LBR01PacketRGBO(image->colormap[i]);
8473 /* To do: set to next higher multiple of 8 */
8474 if (image->depth < 8)
8477 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8478 /* PNG does not handle depths greater than 16 so reduce it even
8481 if (image->depth > 8)
8485 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8486 if (image->depth > 8)
8488 /* To do: fill low byte properly */
8492 if (image->depth == 16 && mng_info->write_png_depth != 16)
8493 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8497 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8498 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8499 mng_info->write_png_colortype < 4 &&
8500 image->alpha_trait != BlendPixelTrait)))
8502 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8503 * are not going to need the result.
8505 image_colors = (int) image->colors;
8506 number_opaque = (int) image->colors;
8507 if (mng_info->write_png_colortype == 1 ||
8508 mng_info->write_png_colortype == 5)
8509 ping_have_color=MagickFalse;
8511 ping_have_color=MagickTrue;
8512 ping_have_non_bw=MagickFalse;
8514 if (image->alpha_trait == BlendPixelTrait)
8516 number_transparent = 2;
8517 number_semitransparent = 1;
8522 number_transparent = 0;
8523 number_semitransparent = 0;
8531 * Normally we run this just once, but in the case of writing PNG8
8532 * we reduce the transparency to binary and run again, then if there
8533 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8534 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8535 * palette. Then (To do) we take care of a final reduction that is only
8536 * needed if there are still 256 colors present and one of them has both
8537 * transparent and opaque instances.
8540 tried_332 = MagickFalse;
8541 tried_333 = MagickFalse;
8542 tried_444 = MagickFalse;
8547 * Sometimes we get DirectClass images that have 256 colors or fewer.
8548 * This code will build a colormap.
8550 * Also, sometimes we get PseudoClass images with an out-of-date
8551 * colormap. This code will replace the colormap with a new one.
8552 * Sometimes we get PseudoClass images that have more than 256 colors.
8553 * This code will delete the colormap and change the image to
8556 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8557 * even though it sometimes contains left-over non-opaque values.
8559 * Also we gather some information (number of opaque, transparent,
8560 * and semitransparent pixels, and whether the image has any non-gray
8561 * pixels or only black-and-white pixels) that we might need later.
8563 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8564 * we need to check for bogus non-opaque values, at least.
8572 semitransparent[260],
8575 register const Quantum
8582 if (logging != MagickFalse)
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584 " Enter BUILD_PALETTE:");
8586 if (logging != MagickFalse)
8588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8589 " image->columns=%.20g",(double) image->columns);
8590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8591 " image->rows=%.20g",(double) image->rows);
8592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8593 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595 " image->depth=%.20g",(double) image->depth);
8597 if (image->storage_class == PseudoClass && image->colormap != NULL)
8599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600 " Original colormap:");
8601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8602 " i (red,green,blue,alpha)");
8604 for (i=0; i < 256; i++)
8606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8607 " %d (%d,%d,%d,%d)",
8609 (int) image->colormap[i].red,
8610 (int) image->colormap[i].green,
8611 (int) image->colormap[i].blue,
8612 (int) image->colormap[i].alpha);
8615 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8620 " %d (%d,%d,%d,%d)",
8622 (int) image->colormap[i].red,
8623 (int) image->colormap[i].green,
8624 (int) image->colormap[i].blue,
8625 (int) image->colormap[i].alpha);
8630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8631 " image->colors=%d",(int) image->colors);
8633 if (image->colors == 0)
8634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8635 " (zero means unknown)");
8637 if (ping_preserve_colormap == MagickFalse)
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " Regenerate the colormap");
8644 number_semitransparent = 0;
8645 number_transparent = 0;
8647 for (y=0; y < (ssize_t) image->rows; y++)
8649 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8651 if (q == (Quantum *) NULL)
8654 for (x=0; x < (ssize_t) image->columns; x++)
8656 if (image->alpha_trait != BlendPixelTrait ||
8657 GetPixelAlpha(image,q) == OpaqueAlpha)
8659 if (number_opaque < 259)
8661 if (number_opaque == 0)
8663 GetPixelInfoPixel(image, q, opaque);
8664 opaque[0].alpha=OpaqueAlpha;
8668 for (i=0; i< (ssize_t) number_opaque; i++)
8670 if (IsPixelEquivalent(image,q, opaque+i))
8674 if (i == (ssize_t) number_opaque && number_opaque < 259)
8677 GetPixelInfoPixel(image, q, opaque+i);
8678 opaque[i].alpha=OpaqueAlpha;
8682 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8684 if (number_transparent < 259)
8686 if (number_transparent == 0)
8688 GetPixelInfoPixel(image, q, transparent);
8689 ping_trans_color.red=(unsigned short)
8690 GetPixelRed(image,q);
8691 ping_trans_color.green=(unsigned short)
8692 GetPixelGreen(image,q);
8693 ping_trans_color.blue=(unsigned short)
8694 GetPixelBlue(image,q);
8695 ping_trans_color.gray=(unsigned short)
8696 GetPixelGray(image,q);
8697 number_transparent = 1;
8700 for (i=0; i< (ssize_t) number_transparent; i++)
8702 if (IsPixelEquivalent(image,q, transparent+i))
8706 if (i == (ssize_t) number_transparent &&
8707 number_transparent < 259)
8709 number_transparent++;
8710 GetPixelInfoPixel(image,q,transparent+i);
8716 if (number_semitransparent < 259)
8718 if (number_semitransparent == 0)
8720 GetPixelInfoPixel(image,q,semitransparent);
8721 number_semitransparent = 1;
8724 for (i=0; i< (ssize_t) number_semitransparent; i++)
8726 if (IsPixelEquivalent(image,q, semitransparent+i)
8727 && GetPixelAlpha(image,q) ==
8728 semitransparent[i].alpha)
8732 if (i == (ssize_t) number_semitransparent &&
8733 number_semitransparent < 259)
8735 number_semitransparent++;
8736 GetPixelInfoPixel(image, q, semitransparent+i);
8740 q+=GetPixelChannels(image);
8744 if (mng_info->write_png8 == MagickFalse &&
8745 ping_exclude_bKGD == MagickFalse)
8747 /* Add the background color to the palette, if it
8748 * isn't already there.
8750 if (logging != MagickFalse)
8752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753 " Check colormap for background (%d,%d,%d)",
8754 (int) image->background_color.red,
8755 (int) image->background_color.green,
8756 (int) image->background_color.blue);
8758 for (i=0; i<number_opaque; i++)
8760 if (opaque[i].red == image->background_color.red &&
8761 opaque[i].green == image->background_color.green &&
8762 opaque[i].blue == image->background_color.blue)
8765 if (number_opaque < 259 && i == number_opaque)
8767 opaque[i] = image->background_color;
8768 ping_background.index = i;
8770 if (logging != MagickFalse)
8772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8773 " background_color index is %d",(int) i);
8777 else if (logging != MagickFalse)
8778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8779 " No room in the colormap to add background color");
8782 image_colors=number_opaque+number_transparent+number_semitransparent;
8784 if (logging != MagickFalse)
8786 if (image_colors > 256)
8787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8788 " image has more than 256 colors");
8791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8792 " image has %d colors",image_colors);
8795 if (ping_preserve_colormap != MagickFalse)
8798 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8800 ping_have_color=MagickFalse;
8801 ping_have_non_bw=MagickFalse;
8803 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8806 "incompatible colorspace");
8807 ping_have_color=MagickTrue;
8808 ping_have_non_bw=MagickTrue;
8811 if(image_colors > 256)
8813 for (y=0; y < (ssize_t) image->rows; y++)
8815 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8817 if (q == (Quantum *) NULL)
8821 for (x=0; x < (ssize_t) image->columns; x++)
8823 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8824 GetPixelRed(image,s) != GetPixelBlue(image,s))
8826 ping_have_color=MagickTrue;
8827 ping_have_non_bw=MagickTrue;
8830 s+=GetPixelChannels(image);
8833 if (ping_have_color != MagickFalse)
8836 /* Worst case is black-and-white; we are looking at every
8840 if (ping_have_non_bw == MagickFalse)
8843 for (x=0; x < (ssize_t) image->columns; x++)
8845 if (GetPixelRed(image,s) != 0 &&
8846 GetPixelRed(image,s) != QuantumRange)
8848 ping_have_non_bw=MagickTrue;
8851 s+=GetPixelChannels(image);
8858 if (image_colors < 257)
8864 * Initialize image colormap.
8867 if (logging != MagickFalse)
8868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8869 " Sort the new colormap");
8871 /* Sort palette, transparent first */;
8875 for (i=0; i<number_transparent; i++)
8876 colormap[n++] = transparent[i];
8878 for (i=0; i<number_semitransparent; i++)
8879 colormap[n++] = semitransparent[i];
8881 for (i=0; i<number_opaque; i++)
8882 colormap[n++] = opaque[i];
8884 ping_background.index +=
8885 (number_transparent + number_semitransparent);
8887 /* image_colors < 257; search the colormap instead of the pixels
8888 * to get ping_have_color and ping_have_non_bw
8892 if (ping_have_color == MagickFalse)
8894 if (colormap[i].red != colormap[i].green ||
8895 colormap[i].red != colormap[i].blue)
8897 ping_have_color=MagickTrue;
8898 ping_have_non_bw=MagickTrue;
8903 if (ping_have_non_bw == MagickFalse)
8905 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8906 ping_have_non_bw=MagickTrue;
8910 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8911 (number_transparent == 0 && number_semitransparent == 0)) &&
8912 (((mng_info->write_png_colortype-1) ==
8913 PNG_COLOR_TYPE_PALETTE) ||
8914 (mng_info->write_png_colortype == 0)))
8916 if (logging != MagickFalse)
8918 if (n != (ssize_t) image_colors)
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " image_colors (%d) and n (%d) don't match",
8923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8924 " AcquireImageColormap");
8927 image->colors = image_colors;
8929 if (AcquireImageColormap(image,image_colors,exception) ==
8931 ThrowWriterException(ResourceLimitError,
8932 "MemoryAllocationFailed");
8934 for (i=0; i< (ssize_t) image_colors; i++)
8935 image->colormap[i] = colormap[i];
8937 if (logging != MagickFalse)
8939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8940 " image->colors=%d (%d)",
8941 (int) image->colors, image_colors);
8943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944 " Update the pixel indexes");
8947 /* Sync the pixel indices with the new colormap */
8949 for (y=0; y < (ssize_t) image->rows; y++)
8951 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8953 if (q == (Quantum *) NULL)
8956 for (x=0; x < (ssize_t) image->columns; x++)
8958 for (i=0; i< (ssize_t) image_colors; i++)
8960 if ((image->alpha_trait != BlendPixelTrait ||
8961 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8962 image->colormap[i].red == GetPixelRed(image,q) &&
8963 image->colormap[i].green == GetPixelGreen(image,q) &&
8964 image->colormap[i].blue == GetPixelBlue(image,q))
8966 SetPixelIndex(image,i,q);
8970 q+=GetPixelChannels(image);
8973 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8979 if (logging != MagickFalse)
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " image->colors=%d", (int) image->colors);
8984 if (image->colormap != NULL)
8986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8987 " i (red,green,blue,alpha)");
8989 for (i=0; i < (ssize_t) image->colors; i++)
8991 if (i < 300 || i >= (ssize_t) image->colors - 10)
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994 " %d (%d,%d,%d,%d)",
8996 (int) image->colormap[i].red,
8997 (int) image->colormap[i].green,
8998 (int) image->colormap[i].blue,
8999 (int) image->colormap[i].alpha);
9004 if (number_transparent < 257)
9005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006 " number_transparent = %d",
9007 number_transparent);
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " number_transparent > 256");
9013 if (number_opaque < 257)
9014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9015 " number_opaque = %d",
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " number_opaque > 256");
9022 if (number_semitransparent < 257)
9023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9024 " number_semitransparent = %d",
9025 number_semitransparent);
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " number_semitransparent > 256");
9031 if (ping_have_non_bw == MagickFalse)
9032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9033 " All pixels and the background are black or white");
9035 else if (ping_have_color == MagickFalse)
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " All pixels and the background are gray");
9040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9041 " At least one pixel or the background is non-gray");
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " Exit BUILD_PALETTE:");
9047 if (mng_info->write_png8 == MagickFalse)
9050 /* Make any reductions necessary for the PNG8 format */
9051 if (image_colors <= 256 &&
9052 image_colors != 0 && image->colormap != NULL &&
9053 number_semitransparent == 0 &&
9054 number_transparent <= 1)
9057 /* PNG8 can't have semitransparent colors so we threshold the
9058 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9059 * transparent color so if more than one is transparent we merge
9060 * them into image->background_color.
9062 if (number_semitransparent != 0 || number_transparent > 1)
9064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9065 " Thresholding the alpha channel to binary");
9067 for (y=0; y < (ssize_t) image->rows; y++)
9069 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9071 if (r == (Quantum *) NULL)
9074 for (x=0; x < (ssize_t) image->columns; x++)
9076 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9078 SetPixelInfoPixel(image,&image->background_color,r);
9079 SetPixelAlpha(image,TransparentAlpha,r);
9082 SetPixelAlpha(image,OpaqueAlpha,r);
9083 r+=GetPixelChannels(image);
9086 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9089 if (image_colors != 0 && image_colors <= 256 &&
9090 image->colormap != NULL)
9091 for (i=0; i<image_colors; i++)
9092 image->colormap[i].alpha =
9093 (image->colormap[i].alpha > TransparentAlpha/2 ?
9094 TransparentAlpha : OpaqueAlpha);
9099 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9100 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9101 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9104 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9106 if (logging != MagickFalse)
9107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9108 " Quantizing the background color to 4-4-4");
9110 tried_444 = MagickTrue;
9112 LBR04PacketRGB(image->background_color);
9114 if (logging != MagickFalse)
9115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9116 " Quantizing the pixel colors to 4-4-4");
9118 if (image->colormap == NULL)
9120 for (y=0; y < (ssize_t) image->rows; y++)
9122 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9124 if (r == (Quantum *) NULL)
9127 for (x=0; x < (ssize_t) image->columns; x++)
9129 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9131 r+=GetPixelChannels(image);
9134 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9139 else /* Should not reach this; colormap already exists and
9142 if (logging != MagickFalse)
9143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9144 " Quantizing the colormap to 4-4-4");
9146 for (i=0; i<image_colors; i++)
9148 LBR04PacketRGB(image->colormap[i]);
9154 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9156 if (logging != MagickFalse)
9157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9158 " Quantizing the background color to 3-3-3");
9160 tried_333 = MagickTrue;
9162 LBR03PacketRGB(image->background_color);
9164 if (logging != MagickFalse)
9165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9166 " Quantizing the pixel colors to 3-3-3-1");
9168 if (image->colormap == NULL)
9170 for (y=0; y < (ssize_t) image->rows; y++)
9172 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9174 if (r == (Quantum *) NULL)
9177 for (x=0; x < (ssize_t) image->columns; x++)
9179 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9181 r+=GetPixelChannels(image);
9184 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9189 else /* Should not reach this; colormap already exists and
9192 if (logging != MagickFalse)
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194 " Quantizing the colormap to 3-3-3-1");
9195 for (i=0; i<image_colors; i++)
9197 LBR03PacketRGB(image->colormap[i]);
9203 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9205 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " Quantizing the background color to 3-3-2");
9209 tried_332 = MagickTrue;
9211 /* Red and green were already done so we only quantize the blue
9215 LBR02PacketBlue(image->background_color);
9217 if (logging != MagickFalse)
9218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9219 " Quantizing the pixel colors to 3-3-2-1");
9221 if (image->colormap == NULL)
9223 for (y=0; y < (ssize_t) image->rows; y++)
9225 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9227 if (r == (Quantum *) NULL)
9230 for (x=0; x < (ssize_t) image->columns; x++)
9232 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9234 r+=GetPixelChannels(image);
9237 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9242 else /* Should not reach this; colormap already exists and
9245 if (logging != MagickFalse)
9246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9247 " Quantizing the colormap to 3-3-2-1");
9248 for (i=0; i<image_colors; i++)
9250 LBR02PacketBlue(image->colormap[i]);
9256 if (image_colors == 0 || image_colors > 256)
9258 /* Take care of special case with 256 opaque colors + 1 transparent
9259 * color. We don't need to quantize to 2-3-2-1; we only need to
9260 * eliminate one color, so we'll merge the two darkest red
9261 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9263 if (logging != MagickFalse)
9264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265 " Merging two dark red background colors to 3-3-2-1");
9267 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9268 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9269 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9271 image->background_color.red=ScaleCharToQuantum(0x24);
9274 if (logging != MagickFalse)
9275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9276 " Merging two dark red pixel colors to 3-3-2-1");
9278 if (image->colormap == NULL)
9280 for (y=0; y < (ssize_t) image->rows; y++)
9282 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9284 if (r == (Quantum *) NULL)
9287 for (x=0; x < (ssize_t) image->columns; x++)
9289 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9290 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9291 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9292 GetPixelAlpha(image,r) == OpaqueAlpha)
9294 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9296 r+=GetPixelChannels(image);
9299 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9307 for (i=0; i<image_colors; i++)
9309 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9310 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9311 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9313 image->colormap[i].red=ScaleCharToQuantum(0x24);
9320 /* END OF BUILD_PALETTE */
9322 /* If we are excluding the tRNS chunk and there is transparency,
9323 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9326 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9327 (number_transparent != 0 || number_semitransparent != 0))
9329 unsigned int colortype=mng_info->write_png_colortype;
9331 if (ping_have_color == MagickFalse)
9332 mng_info->write_png_colortype = 5;
9335 mng_info->write_png_colortype = 7;
9337 if (colortype != 0 &&
9338 mng_info->write_png_colortype != colortype)
9339 ping_need_colortype_warning=MagickTrue;
9343 /* See if cheap transparency is possible. It is only possible
9344 * when there is a single transparent color, no semitransparent
9345 * color, and no opaque color that has the same RGB components
9346 * as the transparent color. We only need this information if
9347 * we are writing a PNG with colortype 0 or 2, and we have not
9348 * excluded the tRNS chunk.
9350 if (number_transparent == 1 &&
9351 mng_info->write_png_colortype < 4)
9353 ping_have_cheap_transparency = MagickTrue;
9355 if (number_semitransparent != 0)
9356 ping_have_cheap_transparency = MagickFalse;
9358 else if (image_colors == 0 || image_colors > 256 ||
9359 image->colormap == NULL)
9361 register const Quantum
9364 for (y=0; y < (ssize_t) image->rows; y++)
9366 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9368 if (q == (Quantum *) NULL)
9371 for (x=0; x < (ssize_t) image->columns; x++)
9373 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9374 (unsigned short) GetPixelRed(image,q) ==
9375 ping_trans_color.red &&
9376 (unsigned short) GetPixelGreen(image,q) ==
9377 ping_trans_color.green &&
9378 (unsigned short) GetPixelBlue(image,q) ==
9379 ping_trans_color.blue)
9381 ping_have_cheap_transparency = MagickFalse;
9385 q+=GetPixelChannels(image);
9388 if (ping_have_cheap_transparency == MagickFalse)
9394 /* Assuming that image->colormap[0] is the one transparent color
9395 * and that all others are opaque.
9397 if (image_colors > 1)
9398 for (i=1; i<image_colors; i++)
9399 if (image->colormap[i].red == image->colormap[0].red &&
9400 image->colormap[i].green == image->colormap[0].green &&
9401 image->colormap[i].blue == image->colormap[0].blue)
9403 ping_have_cheap_transparency = MagickFalse;
9408 if (logging != MagickFalse)
9410 if (ping_have_cheap_transparency == MagickFalse)
9411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9412 " Cheap transparency is not possible.");
9415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9416 " Cheap transparency is possible.");
9420 ping_have_cheap_transparency = MagickFalse;
9422 image_depth=image->depth;
9424 quantum_info = (QuantumInfo *) NULL;
9426 image_colors=(int) image->colors;
9427 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9429 mng_info->IsPalette=image->storage_class == PseudoClass &&
9430 image_colors <= 256 && image->colormap != NULL;
9432 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9433 (image->colors == 0 || image->colormap == NULL))
9435 image_info=DestroyImageInfo(image_info);
9436 image=DestroyImage(image);
9437 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9438 "Cannot write PNG8 or color-type 3; colormap is NULL",
9439 "`%s'",IMimage->filename);
9440 return(MagickFalse);
9444 Allocate the PNG structures
9446 #ifdef PNG_USER_MEM_SUPPORTED
9447 error_info.image=image;
9448 error_info.exception=exception;
9449 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9450 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9451 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9454 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9455 MagickPNGErrorHandler,MagickPNGWarningHandler);
9458 if (ping == (png_struct *) NULL)
9459 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9461 ping_info=png_create_info_struct(ping);
9463 if (ping_info == (png_info *) NULL)
9465 png_destroy_write_struct(&ping,(png_info **) NULL);
9466 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9469 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9470 pixel_info=(MemoryInfo *) NULL;
9472 if (setjmp(png_jmpbuf(ping)))
9478 if (image_info->verbose)
9479 (void) printf("PNG write has failed.\n");
9481 png_destroy_write_struct(&ping,&ping_info);
9482 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9483 UnlockSemaphoreInfo(ping_semaphore);
9486 if (pixel_info != (MemoryInfo *) NULL)
9487 pixel_info=RelinquishVirtualMemory(pixel_info);
9489 if (quantum_info != (QuantumInfo *) NULL)
9490 quantum_info=DestroyQuantumInfo(quantum_info);
9492 if (ping_have_blob != MagickFalse)
9493 (void) CloseBlob(image);
9494 image_info=DestroyImageInfo(image_info);
9495 image=DestroyImage(image);
9496 return(MagickFalse);
9499 /* { For navigation to end of SETJMP-protected block. Within this
9500 * block, use png_error() instead of Throwing an Exception, to ensure
9501 * that libpng is able to clean up, and that the semaphore is unlocked.
9504 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9505 LockSemaphoreInfo(ping_semaphore);
9508 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9509 /* Allow benign errors */
9510 png_set_benign_errors(ping, 1);
9514 Prepare PNG for writing.
9517 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9518 if (mng_info->write_mng)
9520 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9521 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9522 /* Disable new libpng-1.5.10 feature when writing a MNG because
9523 * zero-length PLTE is OK
9525 png_set_check_for_invalid_index (ping, 0);
9530 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9531 if (mng_info->write_mng)
9532 png_permit_empty_plte(ping,MagickTrue);
9539 ping_width=(png_uint_32) image->columns;
9540 ping_height=(png_uint_32) image->rows;
9542 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9545 if (mng_info->write_png48 || mng_info->write_png64)
9548 if (mng_info->write_png_depth != 0)
9549 image_depth=mng_info->write_png_depth;
9551 /* Adjust requested depth to next higher valid depth if necessary */
9552 if (image_depth > 8)
9555 if ((image_depth > 4) && (image_depth < 8))
9558 if (image_depth == 3)
9561 if (logging != MagickFalse)
9563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9564 " width=%.20g",(double) ping_width);
9565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9566 " height=%.20g",(double) ping_height);
9567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9568 " image_matte=%.20g",(double) image->alpha_trait);
9569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9570 " image->depth=%.20g",(double) image->depth);
9571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9575 save_image_depth=image_depth;
9576 ping_bit_depth=(png_byte) save_image_depth;
9579 #if defined(PNG_pHYs_SUPPORTED)
9580 if (ping_exclude_pHYs == MagickFalse)
9582 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9583 (!mng_info->write_mng || !mng_info->equal_physs))
9585 if (logging != MagickFalse)
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " Setting up pHYs chunk");
9589 if (image->units == PixelsPerInchResolution)
9591 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9592 ping_pHYs_x_resolution=
9593 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9594 ping_pHYs_y_resolution=
9595 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9598 else if (image->units == PixelsPerCentimeterResolution)
9600 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9601 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9602 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9607 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9608 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9609 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9612 if (logging != MagickFalse)
9613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9614 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9615 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9616 (int) ping_pHYs_unit_type);
9617 ping_have_pHYs = MagickTrue;
9622 if (ping_exclude_bKGD == MagickFalse)
9624 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9630 if (ping_bit_depth == 8)
9633 if (ping_bit_depth == 4)
9636 if (ping_bit_depth == 2)
9639 if (ping_bit_depth == 1)
9642 ping_background.red=(png_uint_16)
9643 (ScaleQuantumToShort(image->background_color.red) & mask);
9645 ping_background.green=(png_uint_16)
9646 (ScaleQuantumToShort(image->background_color.green) & mask);
9648 ping_background.blue=(png_uint_16)
9649 (ScaleQuantumToShort(image->background_color.blue) & mask);
9651 ping_background.gray=(png_uint_16) ping_background.green;
9654 if (logging != MagickFalse)
9656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9657 " Setting up bKGD chunk (1)");
9658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9659 " background_color index is %d",
9660 (int) ping_background.index);
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9663 " ping_bit_depth=%d",ping_bit_depth);
9666 ping_have_bKGD = MagickTrue;
9670 Select the color type.
9675 if (mng_info->IsPalette && mng_info->write_png8)
9677 /* To do: make this a function cause it's used twice, except
9678 for reducing the sample depth from 8. */
9680 number_colors=image_colors;
9682 ping_have_tRNS=MagickFalse;
9687 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9689 if (logging != MagickFalse)
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 " Setting up PLTE chunk with %d colors (%d)",
9692 number_colors, image_colors);
9694 for (i=0; i < (ssize_t) number_colors; i++)
9696 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9697 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9698 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9699 if (logging != MagickFalse)
9700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9701 #if MAGICKCORE_QUANTUM_DEPTH == 8
9702 " %3ld (%3d,%3d,%3d)",
9704 " %5ld (%5d,%5d,%5d)",
9706 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9710 ping_have_PLTE=MagickTrue;
9711 image_depth=ping_bit_depth;
9714 if (matte != MagickFalse)
9717 Identify which colormap entry is transparent.
9719 assert(number_colors <= 256);
9720 assert(image->colormap != NULL);
9722 for (i=0; i < (ssize_t) number_transparent; i++)
9723 ping_trans_alpha[i]=0;
9726 ping_num_trans=(unsigned short) (number_transparent +
9727 number_semitransparent);
9729 if (ping_num_trans == 0)
9730 ping_have_tRNS=MagickFalse;
9733 ping_have_tRNS=MagickTrue;
9736 if (ping_exclude_bKGD == MagickFalse)
9739 * Identify which colormap entry is the background color.
9742 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9743 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9746 ping_background.index=(png_byte) i;
9748 if (logging != MagickFalse)
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " background_color index is %d",
9752 (int) ping_background.index);
9755 } /* end of write_png8 */
9757 else if (mng_info->write_png_colortype == 1)
9759 image_matte=MagickFalse;
9760 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9763 else if (mng_info->write_png24 || mng_info->write_png48 ||
9764 mng_info->write_png_colortype == 3)
9766 image_matte=MagickFalse;
9767 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9770 else if (mng_info->write_png32 || mng_info->write_png64 ||
9771 mng_info->write_png_colortype == 7)
9773 image_matte=MagickTrue;
9774 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9777 else /* mng_info->write_pngNN not specified */
9779 image_depth=ping_bit_depth;
9781 if (mng_info->write_png_colortype != 0)
9783 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9785 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9786 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9787 image_matte=MagickTrue;
9790 image_matte=MagickFalse;
9792 if (logging != MagickFalse)
9793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794 " PNG colortype %d was specified:",(int) ping_color_type);
9797 else /* write_png_colortype not specified */
9799 if (logging != MagickFalse)
9800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9801 " Selecting PNG colortype:");
9803 ping_color_type=(png_byte) ((matte != MagickFalse)?
9804 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9806 if (image_info->type == TrueColorType)
9808 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9809 image_matte=MagickFalse;
9812 if (image_info->type == TrueColorMatteType)
9814 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9815 image_matte=MagickTrue;
9818 if (image_info->type == PaletteType ||
9819 image_info->type == PaletteMatteType)
9820 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9822 if (mng_info->write_png_colortype == 0 &&
9823 (image_info->type == UndefinedType ||
9824 image_info->type == OptimizeType))
9826 if (ping_have_color == MagickFalse)
9828 if (image_matte == MagickFalse)
9830 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9831 image_matte=MagickFalse;
9836 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9837 image_matte=MagickTrue;
9842 if (image_matte == MagickFalse)
9844 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9845 image_matte=MagickFalse;
9850 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9851 image_matte=MagickTrue;
9858 if (logging != MagickFalse)
9859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9860 " Selected PNG colortype=%d",ping_color_type);
9862 if (ping_bit_depth < 8)
9864 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9865 ping_color_type == PNG_COLOR_TYPE_RGB ||
9866 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9870 old_bit_depth=ping_bit_depth;
9872 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9874 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9878 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9883 if (image->colors == 0)
9886 png_error(ping,"image has 0 colors");
9889 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9890 ping_bit_depth <<= 1;
9893 if (logging != MagickFalse)
9895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9896 " Number of colors: %.20g",(double) image_colors);
9898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899 " Tentative PNG bit depth: %d",ping_bit_depth);
9902 if (ping_bit_depth < (int) mng_info->write_png_depth)
9903 ping_bit_depth = mng_info->write_png_depth;
9906 image_depth=ping_bit_depth;
9908 if (logging != MagickFalse)
9910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9911 " Tentative PNG color type: %s (%.20g)",
9912 PngColorTypeToString(ping_color_type),
9913 (double) ping_color_type);
9915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916 " image_info->type: %.20g",(double) image_info->type);
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919 " image_depth: %.20g",(double) image_depth);
9921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " image->depth: %.20g",(double) image->depth);
9925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9926 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9929 if (matte != MagickFalse)
9931 if (mng_info->IsPalette)
9933 if (mng_info->write_png_colortype == 0)
9935 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9937 if (ping_have_color != MagickFalse)
9938 ping_color_type=PNG_COLOR_TYPE_RGBA;
9942 * Determine if there is any transparent color.
9944 if (number_transparent + number_semitransparent == 0)
9947 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9950 image_matte=MagickFalse;
9952 if (mng_info->write_png_colortype == 0)
9953 ping_color_type&=0x03;
9963 if (ping_bit_depth == 8)
9966 if (ping_bit_depth == 4)
9969 if (ping_bit_depth == 2)
9972 if (ping_bit_depth == 1)
9975 ping_trans_color.red=(png_uint_16)
9976 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9978 ping_trans_color.green=(png_uint_16)
9979 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9981 ping_trans_color.blue=(png_uint_16)
9982 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9984 ping_trans_color.gray=(png_uint_16)
9985 (ScaleQuantumToShort(GetPixelInfoIntensity(
9986 image->colormap)) & mask);
9988 ping_trans_color.index=(png_byte) 0;
9990 ping_have_tRNS=MagickTrue;
9993 if (ping_have_tRNS != MagickFalse)
9996 * Determine if there is one and only one transparent color
9997 * and if so if it is fully transparent.
9999 if (ping_have_cheap_transparency == MagickFalse)
10000 ping_have_tRNS=MagickFalse;
10003 if (ping_have_tRNS != MagickFalse)
10005 if (mng_info->write_png_colortype == 0)
10006 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10008 if (image_depth == 8)
10010 ping_trans_color.red&=0xff;
10011 ping_trans_color.green&=0xff;
10012 ping_trans_color.blue&=0xff;
10013 ping_trans_color.gray&=0xff;
10019 if (image_depth == 8)
10021 ping_trans_color.red&=0xff;
10022 ping_trans_color.green&=0xff;
10023 ping_trans_color.blue&=0xff;
10024 ping_trans_color.gray&=0xff;
10031 if (ping_have_tRNS != MagickFalse)
10032 image_matte=MagickFalse;
10034 if ((mng_info->IsPalette) &&
10035 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10036 ping_have_color == MagickFalse &&
10037 (image_matte == MagickFalse || image_depth >= 8))
10041 if (image_matte != MagickFalse)
10042 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10044 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10046 ping_color_type=PNG_COLOR_TYPE_GRAY;
10048 if (save_image_depth == 16 && image_depth == 8)
10050 if (logging != MagickFalse)
10052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10053 " Scaling ping_trans_color (0)");
10055 ping_trans_color.gray*=0x0101;
10059 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10060 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10062 if ((image_colors == 0) ||
10063 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10064 image_colors=(int) (one << image_depth);
10066 if (image_depth > 8)
10072 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10074 if(!mng_info->write_png_depth)
10078 while ((int) (one << ping_bit_depth)
10079 < (ssize_t) image_colors)
10080 ping_bit_depth <<= 1;
10084 else if (ping_color_type ==
10085 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10086 mng_info->IsPalette)
10088 /* Check if grayscale is reducible */
10091 depth_4_ok=MagickTrue,
10092 depth_2_ok=MagickTrue,
10093 depth_1_ok=MagickTrue;
10095 for (i=0; i < (ssize_t) image_colors; i++)
10100 intensity=ScaleQuantumToChar(image->colormap[i].red);
10102 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10103 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10104 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10105 depth_2_ok=depth_1_ok=MagickFalse;
10106 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10107 depth_1_ok=MagickFalse;
10110 if (depth_1_ok && mng_info->write_png_depth <= 1)
10113 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10116 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10121 image_depth=ping_bit_depth;
10126 if (mng_info->IsPalette)
10128 number_colors=image_colors;
10130 if (image_depth <= 8)
10135 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10137 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10139 for (i=0; i < (ssize_t) number_colors; i++)
10141 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10142 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10143 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10146 if (logging != MagickFalse)
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " Setting up PLTE chunk with %d colors",
10151 ping_have_PLTE=MagickTrue;
10154 /* color_type is PNG_COLOR_TYPE_PALETTE */
10155 if (mng_info->write_png_depth == 0)
10163 while ((one << ping_bit_depth) < (size_t) number_colors)
10164 ping_bit_depth <<= 1;
10169 if (matte != MagickFalse)
10172 * Set up trans_colors array.
10174 assert(number_colors <= 256);
10176 ping_num_trans=(unsigned short) (number_transparent +
10177 number_semitransparent);
10179 if (ping_num_trans == 0)
10180 ping_have_tRNS=MagickFalse;
10184 if (logging != MagickFalse)
10186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10187 " Scaling ping_trans_color (1)");
10189 ping_have_tRNS=MagickTrue;
10191 for (i=0; i < ping_num_trans; i++)
10193 ping_trans_alpha[i]= (png_byte)
10194 ScaleQuantumToChar(image->colormap[i].alpha);
10204 if (image_depth < 8)
10207 if ((save_image_depth == 16) && (image_depth == 8))
10209 if (logging != MagickFalse)
10211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10212 " Scaling ping_trans_color from (%d,%d,%d)",
10213 (int) ping_trans_color.red,
10214 (int) ping_trans_color.green,
10215 (int) ping_trans_color.blue);
10218 ping_trans_color.red*=0x0101;
10219 ping_trans_color.green*=0x0101;
10220 ping_trans_color.blue*=0x0101;
10221 ping_trans_color.gray*=0x0101;
10223 if (logging != MagickFalse)
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10227 (int) ping_trans_color.red,
10228 (int) ping_trans_color.green,
10229 (int) ping_trans_color.blue);
10234 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10235 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10238 Adjust background and transparency samples in sub-8-bit grayscale files.
10240 if (ping_bit_depth < 8 && ping_color_type ==
10241 PNG_COLOR_TYPE_GRAY)
10249 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10251 if (ping_exclude_bKGD == MagickFalse)
10254 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10255 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10256 &image->background_color))) +.5)));
10258 if (logging != MagickFalse)
10259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10260 " Setting up bKGD chunk (2)");
10261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10262 " background_color index is %d",
10263 (int) ping_background.index);
10265 ping_have_bKGD = MagickTrue;
10268 if (logging != MagickFalse)
10269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10270 " Scaling ping_trans_color.gray from %d",
10271 (int)ping_trans_color.gray);
10273 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10274 ping_trans_color.gray)+.5);
10276 if (logging != MagickFalse)
10277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10278 " to %d", (int)ping_trans_color.gray);
10281 if (ping_exclude_bKGD == MagickFalse)
10283 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10286 Identify which colormap entry is the background color.
10289 number_colors=image_colors;
10291 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10292 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10295 ping_background.index=(png_byte) i;
10297 if (logging != MagickFalse)
10299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10300 " Setting up bKGD chunk with index=%d",(int) i);
10303 if (i < (ssize_t) number_colors)
10305 ping_have_bKGD = MagickTrue;
10307 if (logging != MagickFalse)
10309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10310 " background =(%d,%d,%d)",
10311 (int) ping_background.red,
10312 (int) ping_background.green,
10313 (int) ping_background.blue);
10317 else /* Can't happen */
10319 if (logging != MagickFalse)
10320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10321 " No room in PLTE to add bKGD color");
10322 ping_have_bKGD = MagickFalse;
10327 if (logging != MagickFalse)
10328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10332 Initialize compression level and filtering.
10334 if (logging != MagickFalse)
10336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10337 " Setting up deflate compression");
10339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340 " Compression buffer size: 32768");
10343 png_set_compression_buffer_size(ping,32768L);
10345 if (logging != MagickFalse)
10346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10347 " Compression mem level: 9");
10349 png_set_compression_mem_level(ping, 9);
10351 /* Untangle the "-quality" setting:
10353 Undefined is 0; the default is used.
10358 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10359 zlib default compression level
10361 1-9: the zlib compression level
10365 0-4: the PNG filter method
10367 5: libpng adaptive filtering if compression level > 5
10368 libpng filter type "none" if compression level <= 5
10369 or if image is grayscale or palette
10371 6: libpng adaptive filtering
10373 7: "LOCO" filtering (intrapixel differing) if writing
10374 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10375 and earlier because of a missing "else".
10377 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10378 filtering. Unused prior to IM-6.7.0-10, was same as 6
10380 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10381 Unused prior to IM-6.7.0-10, was same as 6
10383 Note that using the -quality option, not all combinations of
10384 PNG filter type, zlib compression level, and zlib compression
10385 strategy are possible. This will be addressed soon in a
10386 release that accomodates "-define png:compression-strategy", etc.
10390 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10391 image_info->quality;
10395 if (mng_info->write_png_compression_strategy == 0)
10396 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10399 else if (mng_info->write_png_compression_level == 0)
10404 level=(int) MagickMin((ssize_t) quality/10,9);
10406 mng_info->write_png_compression_level = level+1;
10409 if (mng_info->write_png_compression_strategy == 0)
10411 if ((quality %10) == 8 || (quality %10) == 9)
10412 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10413 mng_info->write_png_compression_strategy=Z_RLE+1;
10415 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10419 if (mng_info->write_png_compression_filter == 0)
10420 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10422 if (logging != MagickFalse)
10424 if (mng_info->write_png_compression_level)
10425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426 " Compression level: %d",
10427 (int) mng_info->write_png_compression_level-1);
10429 if (mng_info->write_png_compression_strategy)
10430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10431 " Compression strategy: %d",
10432 (int) mng_info->write_png_compression_strategy-1);
10434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10435 " Setting up filtering");
10437 if (mng_info->write_png_compression_filter == 6)
10438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10439 " Base filter method: ADAPTIVE");
10440 else if (mng_info->write_png_compression_filter == 0 ||
10441 mng_info->write_png_compression_filter == 1)
10442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10443 " Base filter method: NONE");
10445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10446 " Base filter method: %d",
10447 (int) mng_info->write_png_compression_filter-1);
10450 if (mng_info->write_png_compression_level != 0)
10451 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10453 if (mng_info->write_png_compression_filter == 6)
10455 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10456 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10458 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10460 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10462 else if (mng_info->write_png_compression_filter == 7 ||
10463 mng_info->write_png_compression_filter == 10)
10464 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10466 else if (mng_info->write_png_compression_filter == 8)
10468 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10469 if (mng_info->write_mng)
10471 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10472 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10473 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10476 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10479 else if (mng_info->write_png_compression_filter == 9)
10480 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10482 else if (mng_info->write_png_compression_filter != 0)
10483 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10484 mng_info->write_png_compression_filter-1);
10486 if (mng_info->write_png_compression_strategy != 0)
10487 png_set_compression_strategy(ping,
10488 mng_info->write_png_compression_strategy-1);
10490 ping_interlace_method=image_info->interlace != NoInterlace;
10492 if (mng_info->write_mng)
10493 png_set_sig_bytes(ping,8);
10495 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10497 if (mng_info->write_png_colortype != 0)
10499 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10500 if (ping_have_color != MagickFalse)
10502 ping_color_type = PNG_COLOR_TYPE_RGB;
10504 if (ping_bit_depth < 8)
10508 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10509 if (ping_have_color != MagickFalse)
10510 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10513 if (ping_need_colortype_warning != MagickFalse ||
10514 ((mng_info->write_png_depth &&
10515 (int) mng_info->write_png_depth != ping_bit_depth) ||
10516 (mng_info->write_png_colortype &&
10517 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10518 mng_info->write_png_colortype != 7 &&
10519 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10521 if (logging != MagickFalse)
10523 if (ping_need_colortype_warning != MagickFalse)
10525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10526 " Image has transparency but tRNS chunk was excluded");
10529 if (mng_info->write_png_depth)
10531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10532 " Defined png:bit-depth=%u, Computed depth=%u",
10533 mng_info->write_png_depth,
10537 if (mng_info->write_png_colortype)
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " Defined png:color-type=%u, Computed color type=%u",
10541 mng_info->write_png_colortype-1,
10547 "Cannot write image with defined png:bit-depth or png:color-type.");
10550 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10552 /* Add an opaque matte channel */
10553 image->alpha_trait = BlendPixelTrait;
10554 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10556 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " Added an opaque matte channel");
10561 if (number_transparent != 0 || number_semitransparent != 0)
10563 if (ping_color_type < 4)
10565 ping_have_tRNS=MagickTrue;
10566 if (logging != MagickFalse)
10567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10568 " Setting ping_have_tRNS=MagickTrue.");
10572 if (logging != MagickFalse)
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10574 " Writing PNG header chunks");
10576 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10577 ping_bit_depth,ping_color_type,
10578 ping_interlace_method,ping_compression_method,
10579 ping_filter_method);
10581 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10583 png_set_PLTE(ping,ping_info,palette,number_colors);
10585 if (logging != MagickFalse)
10587 for (i=0; i< (ssize_t) number_colors; i++)
10589 if (i < ping_num_trans)
10590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10591 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10593 (int) palette[i].red,
10594 (int) palette[i].green,
10595 (int) palette[i].blue,
10597 (int) ping_trans_alpha[i]);
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600 " PLTE[%d] = (%d,%d,%d)",
10602 (int) palette[i].red,
10603 (int) palette[i].green,
10604 (int) palette[i].blue);
10609 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10610 if (ping_exclude_sRGB != MagickFalse ||
10611 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10613 if ((ping_exclude_tEXt == MagickFalse ||
10614 ping_exclude_zTXt == MagickFalse) &&
10615 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10617 ResetImageProfileIterator(image);
10618 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10620 profile=GetImageProfile(image,name);
10622 if (profile != (StringInfo *) NULL)
10624 #ifdef PNG_WRITE_iCCP_SUPPORTED
10625 if ((LocaleCompare(name,"ICC") == 0) ||
10626 (LocaleCompare(name,"ICM") == 0))
10629 if (ping_exclude_iCCP == MagickFalse)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " Setting up iCCP chunk");
10634 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10635 #if (PNG_LIBPNG_VER < 10500)
10636 (png_charp) GetStringInfoDatum(profile),
10638 (png_const_bytep) GetStringInfoDatum(profile),
10640 (png_uint_32) GetStringInfoLength(profile));
10641 ping_have_iCCP = MagickTrue;
10647 if (ping_exclude_zCCP == MagickFalse)
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " Setting up zTXT chunk with uuencoded ICC");
10651 Magick_png_write_raw_profile(image_info,ping,ping_info,
10652 (unsigned char *) name,(unsigned char *) name,
10653 GetStringInfoDatum(profile),
10654 (png_uint_32) GetStringInfoLength(profile));
10655 ping_have_iCCP = MagickTrue;
10659 if (logging != MagickFalse)
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " Setting up text chunk with %s profile",name);
10663 name=GetNextImageProfile(image);
10668 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10669 if ((mng_info->have_write_global_srgb == 0) &&
10670 ping_have_iCCP != MagickTrue &&
10671 (ping_have_sRGB != MagickFalse ||
10672 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10674 if (ping_exclude_sRGB == MagickFalse)
10677 Note image rendering intent.
10679 if (logging != MagickFalse)
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " Setting up sRGB chunk");
10683 (void) png_set_sRGB(ping,ping_info,(
10684 Magick_RenderingIntent_to_PNG_RenderingIntent(
10685 image->rendering_intent)));
10687 ping_have_sRGB = MagickTrue;
10691 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10694 if (ping_exclude_gAMA == MagickFalse &&
10695 ping_have_iCCP == MagickFalse &&
10696 ping_have_sRGB == MagickFalse &&
10697 (ping_exclude_sRGB == MagickFalse ||
10698 (image->gamma < .45 || image->gamma > .46)))
10700 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10704 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10706 if (logging != MagickFalse)
10707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10708 " Setting up gAMA chunk");
10710 png_set_gAMA(ping,ping_info,image->gamma);
10714 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10716 if ((mng_info->have_write_global_chrm == 0) &&
10717 (image->chromaticity.red_primary.x != 0.0))
10720 Note image chromaticity.
10721 Note: if cHRM+gAMA == sRGB write sRGB instead.
10729 wp=image->chromaticity.white_point;
10730 rp=image->chromaticity.red_primary;
10731 gp=image->chromaticity.green_primary;
10732 bp=image->chromaticity.blue_primary;
10734 if (logging != MagickFalse)
10735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10736 " Setting up cHRM chunk");
10738 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10744 if (ping_exclude_bKGD == MagickFalse)
10746 if (ping_have_bKGD != MagickFalse)
10748 png_set_bKGD(ping,ping_info,&ping_background);
10751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10752 " Setting up bKGD chunk");
10753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10754 " background color = (%d,%d,%d)",
10755 (int) ping_background.red,
10756 (int) ping_background.green,
10757 (int) ping_background.blue);
10758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10759 " index = %d, gray=%d",
10760 (int) ping_background.index,
10761 (int) ping_background.gray);
10766 if (ping_exclude_pHYs == MagickFalse)
10768 if (ping_have_pHYs != MagickFalse)
10770 png_set_pHYs(ping,ping_info,
10771 ping_pHYs_x_resolution,
10772 ping_pHYs_y_resolution,
10773 ping_pHYs_unit_type);
10777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778 " Setting up pHYs chunk");
10779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10780 " x_resolution=%lu",
10781 (unsigned long) ping_pHYs_x_resolution);
10782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10783 " y_resolution=%lu",
10784 (unsigned long) ping_pHYs_y_resolution);
10785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10787 (unsigned long) ping_pHYs_unit_type);
10792 #if defined(PNG_oFFs_SUPPORTED)
10793 if (ping_exclude_oFFs == MagickFalse)
10795 if (image->page.x || image->page.y)
10797 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10798 (png_int_32) image->page.y, 0);
10800 if (logging != MagickFalse)
10801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10802 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10803 (int) image->page.x, (int) image->page.y);
10808 if (mng_info->need_blob != MagickFalse)
10810 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10812 png_error(ping,"WriteBlob Failed");
10814 ping_have_blob=MagickTrue;
10817 png_write_info_before_PLTE(ping, ping_info);
10819 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10821 if (logging != MagickFalse)
10823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10824 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10827 if (ping_color_type == 3)
10828 (void) png_set_tRNS(ping, ping_info,
10835 (void) png_set_tRNS(ping, ping_info,
10838 &ping_trans_color);
10840 if (logging != MagickFalse)
10842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10843 " tRNS color =(%d,%d,%d)",
10844 (int) ping_trans_color.red,
10845 (int) ping_trans_color.green,
10846 (int) ping_trans_color.blue);
10851 /* write any png-chunk-b profiles */
10852 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10854 png_write_info(ping,ping_info);
10856 /* write any PNG-chunk-m profiles */
10857 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10859 if (ping_exclude_vpAg == MagickFalse)
10861 if ((image->page.width != 0 && image->page.width != image->columns) ||
10862 (image->page.height != 0 && image->page.height != image->rows))
10867 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10868 PNGType(chunk,mng_vpAg);
10869 LogPNGChunk(logging,mng_vpAg,9L);
10870 PNGLong(chunk+4,(png_uint_32) image->page.width);
10871 PNGLong(chunk+8,(png_uint_32) image->page.height);
10872 chunk[12]=0; /* unit = pixels */
10873 (void) WriteBlob(image,13,chunk);
10874 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10878 #if (PNG_LIBPNG_VER == 10206)
10879 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10880 #define PNG_HAVE_IDAT 0x04
10881 ping->mode |= PNG_HAVE_IDAT;
10882 #undef PNG_HAVE_IDAT
10885 png_set_packing(ping);
10889 rowbytes=image->columns;
10890 if (image_depth > 8)
10892 switch (ping_color_type)
10894 case PNG_COLOR_TYPE_RGB:
10898 case PNG_COLOR_TYPE_GRAY_ALPHA:
10902 case PNG_COLOR_TYPE_RGBA:
10910 if (logging != MagickFalse)
10912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913 " Writing PNG image data");
10915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10916 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10918 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10919 if (pixel_info == (MemoryInfo *) NULL)
10920 png_error(ping,"Allocation of memory for pixels failed");
10921 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10924 Initialize image scanlines.
10926 quantum_info=AcquireQuantumInfo(image_info,image);
10927 if (quantum_info == (QuantumInfo *) NULL)
10928 png_error(ping,"Memory allocation for quantum_info failed");
10929 quantum_info->format=UndefinedQuantumFormat;
10930 quantum_info->depth=image_depth;
10931 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10932 num_passes=png_set_interlace_handling(ping);
10934 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10935 !mng_info->write_png48 && !mng_info->write_png64 &&
10936 !mng_info->write_png32) &&
10937 (mng_info->IsPalette ||
10938 (image_info->type == BilevelType)) &&
10939 image_matte == MagickFalse &&
10940 ping_have_non_bw == MagickFalse)
10942 /* Palette, Bilevel, or Opaque Monochrome */
10943 register const Quantum
10946 quantum_info->depth=8;
10947 for (pass=0; pass < num_passes; pass++)
10950 Convert PseudoClass image to a PNG monochrome image.
10952 for (y=0; y < (ssize_t) image->rows; y++)
10954 if (logging != MagickFalse && y == 0)
10955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10956 " Writing row of pixels (0)");
10958 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10960 if (p == (const Quantum *) NULL)
10963 if (mng_info->IsPalette)
10965 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10966 quantum_info,GrayQuantum,ping_pixels,exception);
10967 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10968 mng_info->write_png_depth &&
10969 mng_info->write_png_depth != old_bit_depth)
10971 /* Undo pixel scaling */
10972 for (i=0; i < (ssize_t) image->columns; i++)
10973 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10974 >> (8-old_bit_depth));
10980 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10981 quantum_info,RedQuantum,ping_pixels,exception);
10984 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10985 for (i=0; i < (ssize_t) image->columns; i++)
10986 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10989 if (logging != MagickFalse && y == 0)
10990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10991 " Writing row of pixels (1)");
10993 png_write_row(ping,ping_pixels);
10995 if (image->previous == (Image *) NULL)
10997 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10998 if (status == MagickFalse)
11004 else /* Not Palette, Bilevel, or Opaque Monochrome */
11006 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11007 !mng_info->write_png48 && !mng_info->write_png64 &&
11008 !mng_info->write_png32) && (image_matte != MagickFalse ||
11009 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11010 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11012 register const Quantum
11015 for (pass=0; pass < num_passes; pass++)
11018 for (y=0; y < (ssize_t) image->rows; y++)
11020 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11022 if (p == (const Quantum *) NULL)
11025 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11027 if (mng_info->IsPalette)
11028 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11029 quantum_info,GrayQuantum,ping_pixels,exception);
11032 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11033 quantum_info,RedQuantum,ping_pixels,exception);
11035 if (logging != MagickFalse && y == 0)
11036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11037 " Writing GRAY PNG pixels (2)");
11040 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11042 if (logging != MagickFalse && y == 0)
11043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11044 " Writing GRAY_ALPHA PNG pixels (2)");
11046 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11047 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11050 if (logging != MagickFalse && y == 0)
11051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11052 " Writing row of pixels (2)");
11054 png_write_row(ping,ping_pixels);
11057 if (image->previous == (Image *) NULL)
11059 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11060 if (status == MagickFalse)
11068 register const Quantum
11071 for (pass=0; pass < num_passes; pass++)
11073 if ((image_depth > 8) ||
11074 mng_info->write_png24 ||
11075 mng_info->write_png32 ||
11076 mng_info->write_png48 ||
11077 mng_info->write_png64 ||
11078 (!mng_info->write_png8 && !mng_info->IsPalette))
11080 for (y=0; y < (ssize_t) image->rows; y++)
11082 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11084 if (p == (const Quantum *) NULL)
11087 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11089 if (image->storage_class == DirectClass)
11090 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11091 quantum_info,RedQuantum,ping_pixels,exception);
11094 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11095 quantum_info,GrayQuantum,ping_pixels,exception);
11098 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11100 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11101 quantum_info,GrayAlphaQuantum,ping_pixels,
11104 if (logging != MagickFalse && y == 0)
11105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11106 " Writing GRAY_ALPHA PNG pixels (3)");
11109 else if (image_matte != MagickFalse)
11110 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11111 quantum_info,RGBAQuantum,ping_pixels,exception);
11114 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11115 quantum_info,RGBQuantum,ping_pixels,exception);
11117 if (logging != MagickFalse && y == 0)
11118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11119 " Writing row of pixels (3)");
11121 png_write_row(ping,ping_pixels);
11126 /* not ((image_depth > 8) ||
11127 mng_info->write_png24 || mng_info->write_png32 ||
11128 mng_info->write_png48 || mng_info->write_png64 ||
11129 (!mng_info->write_png8 && !mng_info->IsPalette))
11132 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11133 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11135 if (logging != MagickFalse)
11136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11137 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11139 quantum_info->depth=8;
11143 for (y=0; y < (ssize_t) image->rows; y++)
11145 if (logging != MagickFalse && y == 0)
11146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11147 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11149 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11151 if (p == (const Quantum *) NULL)
11154 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11156 quantum_info->depth=image->depth;
11158 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11159 quantum_info,GrayQuantum,ping_pixels,exception);
11162 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11164 if (logging != MagickFalse && y == 0)
11165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11166 " Writing GRAY_ALPHA PNG pixels (4)");
11168 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11169 quantum_info,GrayAlphaQuantum,ping_pixels,
11175 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11176 quantum_info,IndexQuantum,ping_pixels,exception);
11178 if (logging != MagickFalse && y <= 2)
11180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11181 " Writing row of non-gray pixels (4)");
11183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11184 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11185 (int)ping_pixels[0],(int)ping_pixels[1]);
11188 png_write_row(ping,ping_pixels);
11192 if (image->previous == (Image *) NULL)
11194 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11195 if (status == MagickFalse)
11202 if (quantum_info != (QuantumInfo *) NULL)
11203 quantum_info=DestroyQuantumInfo(quantum_info);
11205 if (logging != MagickFalse)
11207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11208 " Wrote PNG image data");
11210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11211 " Width: %.20g",(double) ping_width);
11213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11214 " Height: %.20g",(double) ping_height);
11216 if (mng_info->write_png_depth)
11218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11219 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11223 " PNG bit-depth written: %d",ping_bit_depth);
11225 if (mng_info->write_png_colortype)
11227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11228 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11232 " PNG color-type written: %d",ping_color_type);
11234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11235 " PNG Interlace method: %d",ping_interlace_method);
11238 Generate text chunks after IDAT.
11240 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11242 ResetImagePropertyIterator(image);
11243 property=GetNextImageProperty(image);
11244 while (property != (const char *) NULL)
11249 value=GetImageProperty(image,property,exception);
11251 /* Don't write any "png:" or "jpeg:" properties; those are just for
11252 * "identify" or for passing through to another JPEG
11254 if ((LocaleNCompare(property,"png:",4) != 0 &&
11255 LocaleNCompare(property,"jpeg:",5)) &&
11258 /* Suppress density and units if we wrote a pHYs chunk */
11259 (ping_exclude_pHYs != MagickFalse ||
11260 LocaleCompare(property,"density") != 0 ||
11261 LocaleCompare(property,"units") != 0) &&
11263 /* Suppress the IM-generated Date:create and Date:modify */
11264 (ping_exclude_date == MagickFalse ||
11265 LocaleNCompare(property, "Date:",5) != 0))
11267 if (value != (const char *) NULL)
11270 #if PNG_LIBPNG_VER >= 10400
11271 text=(png_textp) png_malloc(ping,
11272 (png_alloc_size_t) sizeof(png_text));
11274 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11276 text[0].key=(char *) property;
11277 text[0].text=(char *) value;
11278 text[0].text_length=strlen(value);
11280 if (ping_exclude_tEXt != MagickFalse)
11281 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11283 else if (ping_exclude_zTXt != MagickFalse)
11284 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11288 text[0].compression=image_info->compression == NoCompression ||
11289 (image_info->compression == UndefinedCompression &&
11290 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11291 PNG_TEXT_COMPRESSION_zTXt ;
11294 if (logging != MagickFalse)
11296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11297 " Setting up text chunk");
11299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11300 " keyword: '%s'",text[0].key);
11303 png_set_text(ping,ping_info,text,1);
11304 png_free(ping,text);
11307 property=GetNextImageProperty(image);
11311 /* write any PNG-chunk-e profiles */
11312 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11314 if (logging != MagickFalse)
11315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11316 " Writing PNG end info");
11318 png_write_end(ping,ping_info);
11320 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11322 if (mng_info->page.x || mng_info->page.y ||
11323 (ping_width != mng_info->page.width) ||
11324 (ping_height != mng_info->page.height))
11330 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11332 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11333 PNGType(chunk,mng_FRAM);
11334 LogPNGChunk(logging,mng_FRAM,27L);
11336 chunk[5]=0; /* frame name separator (no name) */
11337 chunk[6]=1; /* flag for changing delay, for next frame only */
11338 chunk[7]=0; /* flag for changing frame timeout */
11339 chunk[8]=1; /* flag for changing frame clipping for next frame */
11340 chunk[9]=0; /* flag for changing frame sync_id */
11341 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11342 chunk[14]=0; /* clipping boundaries delta type */
11343 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11345 (png_uint_32) (mng_info->page.x + ping_width));
11346 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11348 (png_uint_32) (mng_info->page.y + ping_height));
11349 (void) WriteBlob(image,31,chunk);
11350 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11351 mng_info->old_framing_mode=4;
11352 mng_info->framing_mode=1;
11356 mng_info->framing_mode=3;
11358 if (mng_info->write_mng && !mng_info->need_fram &&
11359 ((int) image->dispose == 3))
11360 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11363 Free PNG resources.
11366 png_destroy_write_struct(&ping,&ping_info);
11368 pixel_info=RelinquishVirtualMemory(pixel_info);
11370 if (ping_have_blob != MagickFalse)
11371 (void) CloseBlob(image);
11373 image_info=DestroyImageInfo(image_info);
11374 image=DestroyImage(image);
11376 /* Store bit depth actually written */
11377 s[0]=(char) ping_bit_depth;
11380 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11382 if (logging != MagickFalse)
11383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11384 " exit WriteOnePNGImage()");
11386 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11387 UnlockSemaphoreInfo(ping_semaphore);
11390 /* } for navigation to beginning of SETJMP-protected block. Revert to
11391 * Throwing an Exception when an error occurs.
11394 return(MagickTrue);
11395 /* End write one PNG image */
11400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11404 % W r i t e P N G I m a g e %
11408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11410 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11411 % Multiple-image Network Graphics (MNG) image file.
11413 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11415 % The format of the WritePNGImage method is:
11417 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11418 % Image *image,ExceptionInfo *exception)
11420 % A description of each parameter follows:
11422 % o image_info: the image info.
11424 % o image: The image.
11426 % o exception: return any errors or warnings in this structure.
11428 % Returns MagickTrue on success, MagickFalse on failure.
11430 % Communicating with the PNG encoder:
11432 % While the datastream written is always in PNG format and normally would
11433 % be given the "png" file extension, this method also writes the following
11434 % pseudo-formats which are subsets of png:
11436 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11437 % a depth greater than 8, the depth is reduced. If transparency
11438 % is present, the tRNS chunk must only have values 0 and 255
11439 % (i.e., transparency is binary: fully opaque or fully
11440 % transparent). If other values are present they will be
11441 % 50%-thresholded to binary transparency. If more than 256
11442 % colors are present, they will be quantized to the 4-4-4-1,
11443 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11444 % of any resulting fully-transparent pixels is changed to
11445 % the image's background color.
11447 % If you want better quantization or dithering of the colors
11448 % or alpha than that, you need to do it before calling the
11449 % PNG encoder. The pixels contain 8-bit indices even if
11450 % they could be represented with 1, 2, or 4 bits. Grayscale
11451 % images will be written as indexed PNG files even though the
11452 % PNG grayscale type might be slightly more efficient. Please
11453 % note that writing to the PNG8 format may result in loss
11454 % of color and alpha data.
11456 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11457 % chunk can be present to convey binary transparency by naming
11458 % one of the colors as transparent. The only loss incurred
11459 % is reduction of sample depth to 8. If the image has more
11460 % than one transparent color, has semitransparent pixels, or
11461 % has an opaque pixel with the same RGB components as the
11462 % transparent color, an image is not written.
11464 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11465 % transparency is permitted, i.e., the alpha sample for
11466 % each pixel can have any value from 0 to 255. The alpha
11467 % channel is present even if the image is fully opaque.
11468 % The only loss in data is the reduction of the sample depth
11471 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11472 % chunk can be present to convey binary transparency by naming
11473 % one of the colors as transparent. If the image has more
11474 % than one transparent color, has semitransparent pixels, or
11475 % has an opaque pixel with the same RGB components as the
11476 % transparent color, an image is not written.
11478 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11479 % transparency is permitted, i.e., the alpha sample for
11480 % each pixel can have any value from 0 to 65535. The alpha
11481 % channel is present even if the image is fully opaque.
11483 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11484 % image, if the input was a PNG, is written. If these values
11485 % cannot be found, then "PNG00" falls back to the regular "PNG"
11488 % o -define: For more precise control of the PNG output, you can use the
11489 % Image options "png:bit-depth" and "png:color-type". These
11490 % can be set from the commandline with "-define" and also
11491 % from the application programming interfaces. The options
11492 % are case-independent and are converted to lowercase before
11493 % being passed to this encoder.
11495 % png:color-type can be 0, 2, 3, 4, or 6.
11497 % When png:color-type is 0 (Grayscale), png:bit-depth can
11498 % be 1, 2, 4, 8, or 16.
11500 % When png:color-type is 2 (RGB), png:bit-depth can
11503 % When png:color-type is 3 (Indexed), png:bit-depth can
11504 % be 1, 2, 4, or 8. This refers to the number of bits
11505 % used to store the index. The color samples always have
11506 % bit-depth 8 in indexed PNG files.
11508 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11509 % png:bit-depth can be 8 or 16.
11511 % If the image cannot be written without loss with the
11512 % requested bit-depth and color-type, a PNG file will not
11513 % be written, a warning will be issued, and the encoder will
11514 % return MagickFalse.
11516 % Since image encoders should not be responsible for the "heavy lifting",
11517 % the user should make sure that ImageMagick has already reduced the
11518 % image depth and number of colors and limit transparency to binary
11519 % transparency prior to attempting to write the image with depth, color,
11520 % or transparency limitations.
11522 % Note that another definition, "png:bit-depth-written" exists, but it
11523 % is not intended for external use. It is only used internally by the
11524 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11526 % It is possible to request that the PNG encoder write previously-formatted
11527 % ancillary chunks in the output PNG file, using the "-profile" commandline
11528 % option as shown below or by setting the profile via a programming
11531 % -profile PNG-chunk-x:<file>
11533 % where x is a location flag and <file> is a file containing the chunk
11534 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11535 % This encoder will compute the chunk length and CRC, so those must not
11536 % be included in the file.
11538 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11539 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11540 % of the same type, then add a short unique string after the "x" to prevent
11541 % subsequent profiles from overwriting the preceding ones, e.g.,
11543 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11545 % As of version 6.6.6 the following optimizations are always done:
11547 % o 32-bit depth is reduced to 16.
11548 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11549 % high byte and low byte are identical.
11550 % o Palette is sorted to remove unused entries and to put a
11551 % transparent color first, if BUILD_PNG_PALETTE is defined.
11552 % o Opaque matte channel is removed when writing an indexed PNG.
11553 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11554 % this can be done without loss and a larger bit depth N was not
11555 % requested via the "-define png:bit-depth=N" option.
11556 % o If matte channel is present but only one transparent color is
11557 % present, RGB+tRNS is written instead of RGBA
11558 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11559 % was requested when converting an opaque image).
11561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11563 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11564 Image *image,ExceptionInfo *exception)
11569 have_mng_structure,
11585 assert(image_info != (const ImageInfo *) NULL);
11586 assert(image_info->signature == MagickSignature);
11587 assert(image != (Image *) NULL);
11588 assert(image->signature == MagickSignature);
11589 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11590 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11592 Allocate a MngInfo structure.
11594 have_mng_structure=MagickFalse;
11595 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11597 if (mng_info == (MngInfo *) NULL)
11598 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11601 Initialize members of the MngInfo structure.
11603 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11604 mng_info->image=image;
11605 mng_info->equal_backgrounds=MagickTrue;
11606 have_mng_structure=MagickTrue;
11608 /* See if user has requested a specific PNG subformat */
11610 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11611 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11612 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11613 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11614 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11616 value=GetImageOption(image_info,"png:format");
11618 if (value != (char *) NULL)
11620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11621 " Format=%s",value);
11623 mng_info->write_png8 = MagickFalse;
11624 mng_info->write_png24 = MagickFalse;
11625 mng_info->write_png32 = MagickFalse;
11626 mng_info->write_png48 = MagickFalse;
11627 mng_info->write_png64 = MagickFalse;
11629 if (LocaleCompare(value,"png8") == 0)
11630 mng_info->write_png8 = MagickTrue;
11632 else if (LocaleCompare(value,"png24") == 0)
11633 mng_info->write_png24 = MagickTrue;
11635 else if (LocaleCompare(value,"png32") == 0)
11636 mng_info->write_png32 = MagickTrue;
11638 else if (LocaleCompare(value,"png48") == 0)
11639 mng_info->write_png48 = MagickTrue;
11641 else if (LocaleCompare(value,"png64") == 0)
11642 mng_info->write_png64 = MagickTrue;
11644 else if (LocaleCompare(value,"png00") == 0)
11646 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11647 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11649 if (value != (char *) NULL)
11651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11652 " png00 inherited bit depth=%s",value);
11654 if (LocaleCompare(value,"1") == 0)
11655 mng_info->write_png_depth = 1;
11657 else if (LocaleCompare(value,"1") == 0)
11658 mng_info->write_png_depth = 2;
11660 else if (LocaleCompare(value,"2") == 0)
11661 mng_info->write_png_depth = 4;
11663 else if (LocaleCompare(value,"8") == 0)
11664 mng_info->write_png_depth = 8;
11666 else if (LocaleCompare(value,"16") == 0)
11667 mng_info->write_png_depth = 16;
11670 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11672 if (value != (char *) NULL)
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675 " png00 inherited color type=%s",value);
11677 if (LocaleCompare(value,"0") == 0)
11678 mng_info->write_png_colortype = 1;
11680 else if (LocaleCompare(value,"2") == 0)
11681 mng_info->write_png_colortype = 3;
11683 else if (LocaleCompare(value,"3") == 0)
11684 mng_info->write_png_colortype = 4;
11686 else if (LocaleCompare(value,"4") == 0)
11687 mng_info->write_png_colortype = 5;
11689 else if (LocaleCompare(value,"6") == 0)
11690 mng_info->write_png_colortype = 7;
11695 if (mng_info->write_png8)
11697 mng_info->write_png_colortype = /* 3 */ 4;
11698 mng_info->write_png_depth = 8;
11702 if (mng_info->write_png24)
11704 mng_info->write_png_colortype = /* 2 */ 3;
11705 mng_info->write_png_depth = 8;
11708 if (image->alpha_trait == BlendPixelTrait)
11709 (void) SetImageType(image,TrueColorMatteType,exception);
11712 (void) SetImageType(image,TrueColorType,exception);
11714 (void) SyncImage(image,exception);
11717 if (mng_info->write_png32)
11719 mng_info->write_png_colortype = /* 6 */ 7;
11720 mng_info->write_png_depth = 8;
11723 if (image->alpha_trait == BlendPixelTrait)
11724 (void) SetImageType(image,TrueColorMatteType,exception);
11727 (void) SetImageType(image,TrueColorType,exception);
11729 (void) SyncImage(image,exception);
11732 if (mng_info->write_png48)
11734 mng_info->write_png_colortype = /* 2 */ 3;
11735 mng_info->write_png_depth = 16;
11738 if (image->alpha_trait == BlendPixelTrait)
11739 (void) SetImageType(image,TrueColorMatteType,exception);
11742 (void) SetImageType(image,TrueColorType,exception);
11744 (void) SyncImage(image,exception);
11747 if (mng_info->write_png64)
11749 mng_info->write_png_colortype = /* 6 */ 7;
11750 mng_info->write_png_depth = 16;
11753 if (image->alpha_trait == BlendPixelTrait)
11754 (void) SetImageType(image,TrueColorMatteType,exception);
11757 (void) SetImageType(image,TrueColorType,exception);
11759 (void) SyncImage(image,exception);
11762 value=GetImageOption(image_info,"png:bit-depth");
11764 if (value != (char *) NULL)
11766 if (LocaleCompare(value,"1") == 0)
11767 mng_info->write_png_depth = 1;
11769 else if (LocaleCompare(value,"2") == 0)
11770 mng_info->write_png_depth = 2;
11772 else if (LocaleCompare(value,"4") == 0)
11773 mng_info->write_png_depth = 4;
11775 else if (LocaleCompare(value,"8") == 0)
11776 mng_info->write_png_depth = 8;
11778 else if (LocaleCompare(value,"16") == 0)
11779 mng_info->write_png_depth = 16;
11782 (void) ThrowMagickException(exception,
11783 GetMagickModule(),CoderWarning,
11784 "ignoring invalid defined png:bit-depth",
11787 if (logging != MagickFalse)
11788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11789 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11792 value=GetImageOption(image_info,"png:color-type");
11794 if (value != (char *) NULL)
11796 /* We must store colortype+1 because 0 is a valid colortype */
11797 if (LocaleCompare(value,"0") == 0)
11798 mng_info->write_png_colortype = 1;
11800 else if (LocaleCompare(value,"1") == 0)
11801 mng_info->write_png_colortype = 2;
11803 else if (LocaleCompare(value,"2") == 0)
11804 mng_info->write_png_colortype = 3;
11806 else if (LocaleCompare(value,"3") == 0)
11807 mng_info->write_png_colortype = 4;
11809 else if (LocaleCompare(value,"4") == 0)
11810 mng_info->write_png_colortype = 5;
11812 else if (LocaleCompare(value,"6") == 0)
11813 mng_info->write_png_colortype = 7;
11816 (void) ThrowMagickException(exception,
11817 GetMagickModule(),CoderWarning,
11818 "ignoring invalid defined png:color-type",
11821 if (logging != MagickFalse)
11822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11823 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11826 /* Check for chunks to be excluded:
11828 * The default is to not exclude any known chunks except for any
11829 * listed in the "unused_chunks" array, above.
11831 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11832 * define (in the image properties or in the image artifacts)
11833 * or via a mng_info member. For convenience, in addition
11834 * to or instead of a comma-separated list of chunks, the
11835 * "exclude-chunk" string can be simply "all" or "none".
11837 * The exclude-chunk define takes priority over the mng_info.
11839 * A "png:include-chunk" define takes priority over both the
11840 * mng_info and the "png:exclude-chunk" define. Like the
11841 * "exclude-chunk" string, it can define "all" or "none" as
11842 * well as a comma-separated list. Chunks that are unknown to
11843 * ImageMagick are always excluded, regardless of their "copy-safe"
11844 * status according to the PNG specification, and even if they
11845 * appear in the "include-chunk" list. Such defines appearing among
11846 * the image options take priority over those found among the image
11849 * Finally, all chunks listed in the "unused_chunks" array are
11850 * automatically excluded, regardless of the other instructions
11853 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11854 * will not be written and the gAMA chunk will only be written if it
11855 * is not between .45 and .46, or approximately (1.0/2.2).
11857 * If you exclude tRNS and the image has transparency, the colortype
11858 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11860 * The -strip option causes StripImage() to set the png:include-chunk
11861 * artifact to "none,trns,gama".
11864 mng_info->ping_exclude_bKGD=MagickFalse;
11865 mng_info->ping_exclude_cHRM=MagickFalse;
11866 mng_info->ping_exclude_date=MagickFalse;
11867 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11868 mng_info->ping_exclude_gAMA=MagickFalse;
11869 mng_info->ping_exclude_iCCP=MagickFalse;
11870 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11871 mng_info->ping_exclude_oFFs=MagickFalse;
11872 mng_info->ping_exclude_pHYs=MagickFalse;
11873 mng_info->ping_exclude_sRGB=MagickFalse;
11874 mng_info->ping_exclude_tEXt=MagickFalse;
11875 mng_info->ping_exclude_tRNS=MagickFalse;
11876 mng_info->ping_exclude_vpAg=MagickFalse;
11877 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11878 mng_info->ping_exclude_zTXt=MagickFalse;
11880 mng_info->ping_preserve_colormap=MagickFalse;
11882 value=GetImageOption(image_info,"png:preserve-colormap");
11884 value=GetImageArtifact(image,"png:preserve-colormap");
11886 mng_info->ping_preserve_colormap=MagickTrue;
11888 mng_info->ping_preserve_iCCP=MagickFalse;
11890 value=GetImageOption(image_info,"png:preserve-iCCP");
11892 value=GetImageArtifact(image,"png:preserve-iCCP");
11894 mng_info->ping_preserve_iCCP=MagickTrue;
11896 /* These compression-level, compression-strategy, and compression-filter
11897 * defines take precedence over values from the -quality option.
11899 value=GetImageOption(image_info,"png:compression-level");
11901 value=GetImageArtifact(image,"png:compression-level");
11904 /* We have to add 1 to everything because 0 is a valid input,
11905 * and we want to use 0 (the default) to mean undefined.
11907 if (LocaleCompare(value,"0") == 0)
11908 mng_info->write_png_compression_level = 1;
11910 else if (LocaleCompare(value,"1") == 0)
11911 mng_info->write_png_compression_level = 2;
11913 else if (LocaleCompare(value,"2") == 0)
11914 mng_info->write_png_compression_level = 3;
11916 else if (LocaleCompare(value,"3") == 0)
11917 mng_info->write_png_compression_level = 4;
11919 else if (LocaleCompare(value,"4") == 0)
11920 mng_info->write_png_compression_level = 5;
11922 else if (LocaleCompare(value,"5") == 0)
11923 mng_info->write_png_compression_level = 6;
11925 else if (LocaleCompare(value,"6") == 0)
11926 mng_info->write_png_compression_level = 7;
11928 else if (LocaleCompare(value,"7") == 0)
11929 mng_info->write_png_compression_level = 8;
11931 else if (LocaleCompare(value,"8") == 0)
11932 mng_info->write_png_compression_level = 9;
11934 else if (LocaleCompare(value,"9") == 0)
11935 mng_info->write_png_compression_level = 10;
11938 (void) ThrowMagickException(exception,
11939 GetMagickModule(),CoderWarning,
11940 "ignoring invalid defined png:compression-level",
11944 value=GetImageOption(image_info,"png:compression-strategy");
11946 value=GetImageArtifact(image,"png:compression-strategy");
11950 if (LocaleCompare(value,"0") == 0)
11951 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11953 else if (LocaleCompare(value,"1") == 0)
11954 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11956 else if (LocaleCompare(value,"2") == 0)
11957 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11959 else if (LocaleCompare(value,"3") == 0)
11960 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11961 mng_info->write_png_compression_strategy = Z_RLE+1;
11963 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11966 else if (LocaleCompare(value,"4") == 0)
11967 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11968 mng_info->write_png_compression_strategy = Z_FIXED+1;
11970 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11974 (void) ThrowMagickException(exception,
11975 GetMagickModule(),CoderWarning,
11976 "ignoring invalid defined png:compression-strategy",
11980 value=GetImageOption(image_info,"png:compression-filter");
11982 value=GetImageArtifact(image,"png:compression-filter");
11986 /* To do: combinations of filters allowed by libpng
11987 * masks 0x08 through 0xf8
11989 * Implement this as a comma-separated list of 0,1,2,3,4,5
11990 * where 5 is a special case meaning PNG_ALL_FILTERS.
11993 if (LocaleCompare(value,"0") == 0)
11994 mng_info->write_png_compression_filter = 1;
11996 else if (LocaleCompare(value,"1") == 0)
11997 mng_info->write_png_compression_filter = 2;
11999 else if (LocaleCompare(value,"2") == 0)
12000 mng_info->write_png_compression_filter = 3;
12002 else if (LocaleCompare(value,"3") == 0)
12003 mng_info->write_png_compression_filter = 4;
12005 else if (LocaleCompare(value,"4") == 0)
12006 mng_info->write_png_compression_filter = 5;
12008 else if (LocaleCompare(value,"5") == 0)
12009 mng_info->write_png_compression_filter = 6;
12012 (void) ThrowMagickException(exception,
12013 GetMagickModule(),CoderWarning,
12014 "ignoring invalid defined png:compression-filter",
12018 excluding=MagickFalse;
12020 for (source=0; source<1; source++)
12024 value=GetImageOption(image_info,"png:exclude-chunk");
12027 value=GetImageArtifact(image,"png:exclude-chunks");
12031 value=GetImageOption(image_info,"png:exclude-chunk");
12034 value=GetImageArtifact(image,"png:exclude-chunks");
12043 excluding=MagickTrue;
12045 if (logging != MagickFalse)
12048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12049 " png:exclude-chunk=%s found in image artifacts.\n", value);
12051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12052 " png:exclude-chunk=%s found in image properties.\n", value);
12055 last=strlen(value);
12057 for (i=0; i<(int) last; i+=5)
12060 if (LocaleNCompare(value+i,"none",4) == 0)
12062 mng_info->ping_exclude_bKGD=MagickFalse;
12063 mng_info->ping_exclude_cHRM=MagickFalse;
12064 mng_info->ping_exclude_date=MagickFalse;
12065 mng_info->ping_exclude_EXIF=MagickFalse;
12066 mng_info->ping_exclude_gAMA=MagickFalse;
12067 mng_info->ping_exclude_iCCP=MagickFalse;
12068 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12069 mng_info->ping_exclude_oFFs=MagickFalse;
12070 mng_info->ping_exclude_pHYs=MagickFalse;
12071 mng_info->ping_exclude_sRGB=MagickFalse;
12072 mng_info->ping_exclude_tEXt=MagickFalse;
12073 mng_info->ping_exclude_tRNS=MagickFalse;
12074 mng_info->ping_exclude_vpAg=MagickFalse;
12075 mng_info->ping_exclude_zCCP=MagickFalse;
12076 mng_info->ping_exclude_zTXt=MagickFalse;
12079 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12080 mng_info->ping_exclude_bKGD=MagickTrue;
12082 if (LocaleNCompare(value+i,"chrm",4) == 0)
12083 mng_info->ping_exclude_cHRM=MagickTrue;
12085 if (LocaleNCompare(value+i,"date",4) == 0)
12086 mng_info->ping_exclude_date=MagickTrue;
12088 if (LocaleNCompare(value+i,"exif",4) == 0)
12089 mng_info->ping_exclude_EXIF=MagickTrue;
12091 if (LocaleNCompare(value+i,"gama",4) == 0)
12092 mng_info->ping_exclude_gAMA=MagickTrue;
12094 if (LocaleNCompare(value+i,"iccp",4) == 0)
12095 mng_info->ping_exclude_iCCP=MagickTrue;
12098 if (LocaleNCompare(value+i,"itxt",4) == 0)
12099 mng_info->ping_exclude_iTXt=MagickTrue;
12102 if (LocaleNCompare(value+i,"gama",4) == 0)
12103 mng_info->ping_exclude_gAMA=MagickTrue;
12105 if (LocaleNCompare(value+i,"offs",4) == 0)
12106 mng_info->ping_exclude_oFFs=MagickTrue;
12108 if (LocaleNCompare(value+i,"phys",4) == 0)
12109 mng_info->ping_exclude_pHYs=MagickTrue;
12111 if (LocaleNCompare(value+i,"srgb",4) == 0)
12112 mng_info->ping_exclude_sRGB=MagickTrue;
12114 if (LocaleNCompare(value+i,"text",4) == 0)
12115 mng_info->ping_exclude_tEXt=MagickTrue;
12117 if (LocaleNCompare(value+i,"trns",4) == 0)
12118 mng_info->ping_exclude_tRNS=MagickTrue;
12120 if (LocaleNCompare(value+i,"vpag",4) == 0)
12121 mng_info->ping_exclude_vpAg=MagickTrue;
12123 if (LocaleNCompare(value+i,"zccp",4) == 0)
12124 mng_info->ping_exclude_zCCP=MagickTrue;
12126 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12127 mng_info->ping_exclude_zTXt=MagickTrue;
12129 if (LocaleNCompare(value+i,"all",3) == 0)
12131 mng_info->ping_exclude_bKGD=MagickTrue;
12132 mng_info->ping_exclude_cHRM=MagickTrue;
12133 mng_info->ping_exclude_date=MagickTrue;
12134 mng_info->ping_exclude_EXIF=MagickTrue;
12135 mng_info->ping_exclude_gAMA=MagickTrue;
12136 mng_info->ping_exclude_iCCP=MagickTrue;
12137 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12138 mng_info->ping_exclude_oFFs=MagickTrue;
12139 mng_info->ping_exclude_pHYs=MagickTrue;
12140 mng_info->ping_exclude_sRGB=MagickTrue;
12141 mng_info->ping_exclude_tEXt=MagickTrue;
12142 mng_info->ping_exclude_tRNS=MagickTrue;
12143 mng_info->ping_exclude_vpAg=MagickTrue;
12144 mng_info->ping_exclude_zCCP=MagickTrue;
12145 mng_info->ping_exclude_zTXt=MagickTrue;
12152 for (source=0; source<1; source++)
12156 value=GetImageOption(image_info,"png:include-chunk");
12159 value=GetImageArtifact(image,"png:include-chunks");
12163 value=GetImageOption(image_info,"png:include-chunk");
12166 value=GetImageArtifact(image,"png:include-chunks");
12174 excluding=MagickTrue;
12176 if (logging != MagickFalse)
12179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180 " png:include-chunk=%s found in image artifacts.\n", value);
12182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12183 " png:include-chunk=%s found in image properties.\n", value);
12186 last=strlen(value);
12188 for (i=0; i<(int) last; i+=5)
12190 if (LocaleNCompare(value+i,"none",4) == 0)
12192 mng_info->ping_exclude_bKGD=MagickTrue;
12193 mng_info->ping_exclude_cHRM=MagickTrue;
12194 mng_info->ping_exclude_date=MagickTrue;
12195 mng_info->ping_exclude_EXIF=MagickTrue;
12196 mng_info->ping_exclude_gAMA=MagickTrue;
12197 mng_info->ping_exclude_iCCP=MagickTrue;
12198 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12199 mng_info->ping_exclude_oFFs=MagickTrue;
12200 mng_info->ping_exclude_pHYs=MagickTrue;
12201 mng_info->ping_exclude_sRGB=MagickTrue;
12202 mng_info->ping_exclude_tEXt=MagickTrue;
12203 mng_info->ping_exclude_tRNS=MagickTrue;
12204 mng_info->ping_exclude_vpAg=MagickTrue;
12205 mng_info->ping_exclude_zCCP=MagickTrue;
12206 mng_info->ping_exclude_zTXt=MagickTrue;
12209 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12210 mng_info->ping_exclude_bKGD=MagickFalse;
12212 if (LocaleNCompare(value+i,"chrm",4) == 0)
12213 mng_info->ping_exclude_cHRM=MagickFalse;
12215 if (LocaleNCompare(value+i,"date",4) == 0)
12216 mng_info->ping_exclude_date=MagickFalse;
12218 if (LocaleNCompare(value+i,"exif",4) == 0)
12219 mng_info->ping_exclude_EXIF=MagickFalse;
12221 if (LocaleNCompare(value+i,"gama",4) == 0)
12222 mng_info->ping_exclude_gAMA=MagickFalse;
12224 if (LocaleNCompare(value+i,"iccp",4) == 0)
12225 mng_info->ping_exclude_iCCP=MagickFalse;
12228 if (LocaleNCompare(value+i,"itxt",4) == 0)
12229 mng_info->ping_exclude_iTXt=MagickFalse;
12232 if (LocaleNCompare(value+i,"gama",4) == 0)
12233 mng_info->ping_exclude_gAMA=MagickFalse;
12235 if (LocaleNCompare(value+i,"offs",4) == 0)
12236 mng_info->ping_exclude_oFFs=MagickFalse;
12238 if (LocaleNCompare(value+i,"phys",4) == 0)
12239 mng_info->ping_exclude_pHYs=MagickFalse;
12241 if (LocaleNCompare(value+i,"srgb",4) == 0)
12242 mng_info->ping_exclude_sRGB=MagickFalse;
12244 if (LocaleNCompare(value+i,"text",4) == 0)
12245 mng_info->ping_exclude_tEXt=MagickFalse;
12247 if (LocaleNCompare(value+i,"trns",4) == 0)
12248 mng_info->ping_exclude_tRNS=MagickFalse;
12250 if (LocaleNCompare(value+i,"vpag",4) == 0)
12251 mng_info->ping_exclude_vpAg=MagickFalse;
12253 if (LocaleNCompare(value+i,"zccp",4) == 0)
12254 mng_info->ping_exclude_zCCP=MagickFalse;
12256 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12257 mng_info->ping_exclude_zTXt=MagickFalse;
12259 if (LocaleNCompare(value+i,"all",3) == 0)
12261 mng_info->ping_exclude_bKGD=MagickFalse;
12262 mng_info->ping_exclude_cHRM=MagickFalse;
12263 mng_info->ping_exclude_date=MagickFalse;
12264 mng_info->ping_exclude_EXIF=MagickFalse;
12265 mng_info->ping_exclude_gAMA=MagickFalse;
12266 mng_info->ping_exclude_iCCP=MagickFalse;
12267 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12268 mng_info->ping_exclude_oFFs=MagickFalse;
12269 mng_info->ping_exclude_pHYs=MagickFalse;
12270 mng_info->ping_exclude_sRGB=MagickFalse;
12271 mng_info->ping_exclude_tEXt=MagickFalse;
12272 mng_info->ping_exclude_tRNS=MagickFalse;
12273 mng_info->ping_exclude_vpAg=MagickFalse;
12274 mng_info->ping_exclude_zCCP=MagickFalse;
12275 mng_info->ping_exclude_zTXt=MagickFalse;
12282 if (excluding != MagickFalse && logging != MagickFalse)
12284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12285 " Chunks to be excluded from the output png:");
12286 if (mng_info->ping_exclude_bKGD != MagickFalse)
12287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12289 if (mng_info->ping_exclude_cHRM != MagickFalse)
12290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12292 if (mng_info->ping_exclude_date != MagickFalse)
12293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12295 if (mng_info->ping_exclude_EXIF != MagickFalse)
12296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12298 if (mng_info->ping_exclude_gAMA != MagickFalse)
12299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301 if (mng_info->ping_exclude_iCCP != MagickFalse)
12302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12305 if (mng_info->ping_exclude_iTXt != MagickFalse)
12306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12309 if (mng_info->ping_exclude_oFFs != MagickFalse)
12310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12312 if (mng_info->ping_exclude_pHYs != MagickFalse)
12313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12315 if (mng_info->ping_exclude_sRGB != MagickFalse)
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12318 if (mng_info->ping_exclude_tEXt != MagickFalse)
12319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12321 if (mng_info->ping_exclude_tRNS != MagickFalse)
12322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 if (mng_info->ping_exclude_vpAg != MagickFalse)
12325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12327 if (mng_info->ping_exclude_zCCP != MagickFalse)
12328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12330 if (mng_info->ping_exclude_zTXt != MagickFalse)
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12335 mng_info->need_blob = MagickTrue;
12337 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12339 MngInfoFreeStruct(mng_info,&have_mng_structure);
12341 if (logging != MagickFalse)
12342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12347 #if defined(JNG_SUPPORTED)
12349 /* Write one JNG image */
12350 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12351 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12372 jng_alpha_compression_method,
12373 jng_alpha_sample_depth,
12381 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12382 " Enter WriteOneJNGImage()");
12384 blob=(unsigned char *) NULL;
12385 jpeg_image=(Image *) NULL;
12386 jpeg_image_info=(ImageInfo *) NULL;
12389 transparent=image_info->type==GrayscaleMatteType ||
12390 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12392 jng_alpha_sample_depth = 0;
12394 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12396 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12398 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12399 image_info->quality;
12401 if (jng_alpha_quality >= 1000)
12402 jng_alpha_quality /= 1000;
12408 /* Create JPEG blob, image, and image_info */
12409 if (logging != MagickFalse)
12410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12411 " Creating jpeg_image_info for alpha.");
12413 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12415 if (jpeg_image_info == (ImageInfo *) NULL)
12416 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12418 if (logging != MagickFalse)
12419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420 " Creating jpeg_image.");
12422 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12423 if (jpeg_image == (Image *) NULL)
12424 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12425 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12426 jpeg_image->alpha_trait=UndefinedPixelTrait;
12427 jpeg_image->quality=jng_alpha_quality;
12428 jpeg_image_info->type=GrayscaleType;
12429 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12430 (void) AcquireUniqueFilename(jpeg_image->filename);
12431 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12432 "%s",jpeg_image->filename);
12436 jng_alpha_compression_method=0;
12438 jng_alpha_sample_depth=0;
12441 /* To do: check bit depth of PNG alpha channel */
12443 /* Check if image is grayscale. */
12444 if (image_info->type != TrueColorMatteType && image_info->type !=
12445 TrueColorType && IsImageGray(image,exception))
12448 if (logging != MagickFalse)
12450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12451 " JNG Quality = %d",(int) jng_quality);
12452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12453 " JNG Color Type = %d",jng_color_type);
12456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12457 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12459 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12461 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12467 if (jng_alpha_compression_method==0)
12472 /* Encode alpha as a grayscale PNG blob */
12473 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12475 if (logging != MagickFalse)
12476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12477 " Creating PNG blob.");
12480 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12481 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12482 jpeg_image_info->interlace=NoInterlace;
12484 /* Exclude all ancillary chunks */
12485 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12487 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12490 /* Retrieve sample depth used */
12491 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12492 if (value != (char *) NULL)
12493 jng_alpha_sample_depth= (unsigned int) value[0];
12497 /* Encode alpha as a grayscale JPEG blob */
12499 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12502 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12503 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12504 jpeg_image_info->interlace=NoInterlace;
12505 if (logging != MagickFalse)
12506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12507 " Creating blob.");
12508 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12510 jng_alpha_sample_depth=8;
12512 if (logging != MagickFalse)
12513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12514 " Successfully read jpeg_image into a blob, length=%.20g.",
12518 /* Destroy JPEG image and image_info */
12519 jpeg_image=DestroyImage(jpeg_image);
12520 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12521 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12524 /* Write JHDR chunk */
12525 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12526 PNGType(chunk,mng_JHDR);
12527 LogPNGChunk(logging,mng_JHDR,16L);
12528 PNGLong(chunk+4,(png_uint_32) image->columns);
12529 PNGLong(chunk+8,(png_uint_32) image->rows);
12530 chunk[12]=jng_color_type;
12531 chunk[13]=8; /* sample depth */
12532 chunk[14]=8; /*jng_image_compression_method */
12533 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12534 chunk[16]=jng_alpha_sample_depth;
12535 chunk[17]=jng_alpha_compression_method;
12536 chunk[18]=0; /*jng_alpha_filter_method */
12537 chunk[19]=0; /*jng_alpha_interlace_method */
12538 (void) WriteBlob(image,20,chunk);
12539 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12540 if (logging != MagickFalse)
12542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12543 " JNG width:%15lu",(unsigned long) image->columns);
12545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546 " JNG height:%14lu",(unsigned long) image->rows);
12548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12549 " JNG color type:%10d",jng_color_type);
12551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12552 " JNG sample depth:%8d",8);
12554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12555 " JNG compression:%9d",8);
12557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12558 " JNG interlace:%11d",0);
12560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12561 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12564 " JNG alpha compression:%3d",jng_alpha_compression_method);
12566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12567 " JNG alpha filter:%8d",0);
12569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12570 " JNG alpha interlace:%5d",0);
12573 /* Write any JNG-chunk-b profiles */
12574 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12577 Write leading ancillary chunks
12583 Write JNG bKGD chunk
12594 if (jng_color_type == 8 || jng_color_type == 12)
12598 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12599 PNGType(chunk,mng_bKGD);
12600 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12601 red=ScaleQuantumToChar(image->background_color.red);
12602 green=ScaleQuantumToChar(image->background_color.green);
12603 blue=ScaleQuantumToChar(image->background_color.blue);
12610 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12611 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12614 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12617 Write JNG sRGB chunk
12619 (void) WriteBlobMSBULong(image,1L);
12620 PNGType(chunk,mng_sRGB);
12621 LogPNGChunk(logging,mng_sRGB,1L);
12623 if (image->rendering_intent != UndefinedIntent)
12624 chunk[4]=(unsigned char)
12625 Magick_RenderingIntent_to_PNG_RenderingIntent(
12626 (image->rendering_intent));
12629 chunk[4]=(unsigned char)
12630 Magick_RenderingIntent_to_PNG_RenderingIntent(
12631 (PerceptualIntent));
12633 (void) WriteBlob(image,5,chunk);
12634 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12638 if (image->gamma != 0.0)
12641 Write JNG gAMA chunk
12643 (void) WriteBlobMSBULong(image,4L);
12644 PNGType(chunk,mng_gAMA);
12645 LogPNGChunk(logging,mng_gAMA,4L);
12646 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12647 (void) WriteBlob(image,8,chunk);
12648 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12651 if ((mng_info->equal_chrms == MagickFalse) &&
12652 (image->chromaticity.red_primary.x != 0.0))
12658 Write JNG cHRM chunk
12660 (void) WriteBlobMSBULong(image,32L);
12661 PNGType(chunk,mng_cHRM);
12662 LogPNGChunk(logging,mng_cHRM,32L);
12663 primary=image->chromaticity.white_point;
12664 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12665 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12666 primary=image->chromaticity.red_primary;
12667 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12668 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12669 primary=image->chromaticity.green_primary;
12670 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12671 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12672 primary=image->chromaticity.blue_primary;
12673 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12674 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12675 (void) WriteBlob(image,36,chunk);
12676 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12680 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12683 Write JNG pHYs chunk
12685 (void) WriteBlobMSBULong(image,9L);
12686 PNGType(chunk,mng_pHYs);
12687 LogPNGChunk(logging,mng_pHYs,9L);
12688 if (image->units == PixelsPerInchResolution)
12690 PNGLong(chunk+4,(png_uint_32)
12691 (image->resolution.x*100.0/2.54+0.5));
12693 PNGLong(chunk+8,(png_uint_32)
12694 (image->resolution.y*100.0/2.54+0.5));
12701 if (image->units == PixelsPerCentimeterResolution)
12703 PNGLong(chunk+4,(png_uint_32)
12704 (image->resolution.x*100.0+0.5));
12706 PNGLong(chunk+8,(png_uint_32)
12707 (image->resolution.y*100.0+0.5));
12714 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12715 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12719 (void) WriteBlob(image,13,chunk);
12720 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12723 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12726 Write JNG oFFs chunk
12728 (void) WriteBlobMSBULong(image,9L);
12729 PNGType(chunk,mng_oFFs);
12730 LogPNGChunk(logging,mng_oFFs,9L);
12731 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12732 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12734 (void) WriteBlob(image,13,chunk);
12735 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12737 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12739 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12740 PNGType(chunk,mng_vpAg);
12741 LogPNGChunk(logging,mng_vpAg,9L);
12742 PNGLong(chunk+4,(png_uint_32) image->page.width);
12743 PNGLong(chunk+8,(png_uint_32) image->page.height);
12744 chunk[12]=0; /* unit = pixels */
12745 (void) WriteBlob(image,13,chunk);
12746 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12752 if (jng_alpha_compression_method==0)
12760 /* Write IDAT chunk header */
12761 if (logging != MagickFalse)
12762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12763 " Write IDAT chunks from blob, length=%.20g.",(double)
12766 /* Copy IDAT chunks */
12769 for (i=8; i<(ssize_t) length; i+=len+12)
12771 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12774 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12776 /* Found an IDAT chunk. */
12777 (void) WriteBlobMSBULong(image,(size_t) len);
12778 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12779 (void) WriteBlob(image,(size_t) len+4,p);
12780 (void) WriteBlobMSBULong(image,
12781 crc32(0,p,(uInt) len+4));
12786 if (logging != MagickFalse)
12787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12788 " Skipping %c%c%c%c chunk, length=%.20g.",
12789 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12796 /* Write JDAA chunk header */
12797 if (logging != MagickFalse)
12798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12799 " Write JDAA chunk, length=%.20g.",(double) length);
12800 (void) WriteBlobMSBULong(image,(size_t) length);
12801 PNGType(chunk,mng_JDAA);
12802 LogPNGChunk(logging,mng_JDAA,length);
12803 /* Write JDAT chunk(s) data */
12804 (void) WriteBlob(image,4,chunk);
12805 (void) WriteBlob(image,length,blob);
12806 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12809 blob=(unsigned char *) RelinquishMagickMemory(blob);
12812 /* Encode image as a JPEG blob */
12813 if (logging != MagickFalse)
12814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12815 " Creating jpeg_image_info.");
12816 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12817 if (jpeg_image_info == (ImageInfo *) NULL)
12818 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12820 if (logging != MagickFalse)
12821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12822 " Creating jpeg_image.");
12824 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12825 if (jpeg_image == (Image *) NULL)
12826 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12827 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12829 (void) AcquireUniqueFilename(jpeg_image->filename);
12830 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12831 jpeg_image->filename);
12833 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12836 if (logging != MagickFalse)
12837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12838 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12839 (double) jpeg_image->rows);
12841 if (jng_color_type == 8 || jng_color_type == 12)
12842 jpeg_image_info->type=GrayscaleType;
12844 jpeg_image_info->quality=jng_quality;
12845 jpeg_image->quality=jng_quality;
12846 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12847 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12849 if (logging != MagickFalse)
12850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12851 " Creating blob.");
12853 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12855 if (logging != MagickFalse)
12857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12858 " Successfully read jpeg_image into a blob, length=%.20g.",
12861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12862 " Write JDAT chunk, length=%.20g.",(double) length);
12865 /* Write JDAT chunk(s) */
12866 (void) WriteBlobMSBULong(image,(size_t) length);
12867 PNGType(chunk,mng_JDAT);
12868 LogPNGChunk(logging,mng_JDAT,length);
12869 (void) WriteBlob(image,4,chunk);
12870 (void) WriteBlob(image,length,blob);
12871 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12873 jpeg_image=DestroyImage(jpeg_image);
12874 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12875 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12876 blob=(unsigned char *) RelinquishMagickMemory(blob);
12878 /* Write any JNG-chunk-e profiles */
12879 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12881 /* Write IEND chunk */
12882 (void) WriteBlobMSBULong(image,0L);
12883 PNGType(chunk,mng_IEND);
12884 LogPNGChunk(logging,mng_IEND,0);
12885 (void) WriteBlob(image,4,chunk);
12886 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12888 if (logging != MagickFalse)
12889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12890 " exit WriteOneJNGImage()");
12897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12901 % W r i t e J N G I m a g e %
12905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12907 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12909 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12911 % The format of the WriteJNGImage method is:
12913 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12914 % Image *image,ExceptionInfo *exception)
12916 % A description of each parameter follows:
12918 % o image_info: the image info.
12920 % o image: The image.
12922 % o exception: return any errors or warnings in this structure.
12924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12926 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12927 ExceptionInfo *exception)
12930 have_mng_structure,
12940 assert(image_info != (const ImageInfo *) NULL);
12941 assert(image_info->signature == MagickSignature);
12942 assert(image != (Image *) NULL);
12943 assert(image->signature == MagickSignature);
12944 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12945 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12946 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12947 if (status == MagickFalse)
12951 Allocate a MngInfo structure.
12953 have_mng_structure=MagickFalse;
12954 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12955 if (mng_info == (MngInfo *) NULL)
12956 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12958 Initialize members of the MngInfo structure.
12960 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12961 mng_info->image=image;
12962 have_mng_structure=MagickTrue;
12964 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12966 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12967 (void) CloseBlob(image);
12969 (void) CatchImageException(image);
12970 MngInfoFreeStruct(mng_info,&have_mng_structure);
12971 if (logging != MagickFalse)
12972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12977 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12978 ExceptionInfo *exception)
12987 have_mng_structure,
12990 volatile MagickBooleanType
13002 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13003 defined(PNG_MNG_FEATURES_SUPPORTED)
13006 all_images_are_gray,
13016 volatile unsigned int
13027 #if (PNG_LIBPNG_VER < 10200)
13028 if (image_info->verbose)
13029 printf("Your PNG library (libpng-%s) is rather old.\n",
13030 PNG_LIBPNG_VER_STRING);
13036 assert(image_info != (const ImageInfo *) NULL);
13037 assert(image_info->signature == MagickSignature);
13038 assert(image != (Image *) NULL);
13039 assert(image->signature == MagickSignature);
13040 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13041 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13042 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13043 if (status == MagickFalse)
13047 Allocate a MngInfo structure.
13049 have_mng_structure=MagickFalse;
13050 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13051 if (mng_info == (MngInfo *) NULL)
13052 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13054 Initialize members of the MngInfo structure.
13056 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13057 mng_info->image=image;
13058 have_mng_structure=MagickTrue;
13059 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13062 * See if user has requested a specific PNG subformat to be used
13063 * for all of the PNGs in the MNG being written, e.g.,
13065 * convert *.png png8:animation.mng
13067 * To do: check -define png:bit_depth and png:color_type as well,
13068 * or perhaps use mng:bit_depth and mng:color_type instead for
13072 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13073 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13074 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13076 write_jng=MagickFalse;
13077 if (image_info->compression == JPEGCompression)
13078 write_jng=MagickTrue;
13080 mng_info->adjoin=image_info->adjoin &&
13081 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13083 if (logging != MagickFalse)
13085 /* Log some info about the input */
13089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13090 " Checking input image(s)");
13092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13093 " Image_info depth: %.20g",(double) image_info->depth);
13095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13096 " Type: %d",image_info->type);
13099 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13102 " Scene: %.20g",(double) scene++);
13104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13105 " Image depth: %.20g",(double) p->depth);
13107 if (p->alpha_trait == BlendPixelTrait)
13108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13115 if (p->storage_class == PseudoClass)
13116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13117 " Storage class: PseudoClass");
13120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13121 " Storage class: DirectClass");
13124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13125 " Number of colors: %.20g",(double) p->colors);
13128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13129 " Number of colors: unspecified");
13131 if (mng_info->adjoin == MagickFalse)
13136 use_global_plte=MagickFalse;
13137 all_images_are_gray=MagickFalse;
13138 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13139 need_local_plte=MagickTrue;
13141 need_defi=MagickFalse;
13142 need_matte=MagickFalse;
13143 mng_info->framing_mode=1;
13144 mng_info->old_framing_mode=1;
13147 if (image_info->page != (char *) NULL)
13150 Determine image bounding box.
13152 SetGeometry(image,&mng_info->page);
13153 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13154 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13166 mng_info->page=image->page;
13167 need_geom=MagickTrue;
13168 if (mng_info->page.width || mng_info->page.height)
13169 need_geom=MagickFalse;
13171 Check all the scenes.
13173 initial_delay=image->delay;
13174 need_iterations=MagickFalse;
13175 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13176 mng_info->equal_physs=MagickTrue,
13177 mng_info->equal_gammas=MagickTrue;
13178 mng_info->equal_srgbs=MagickTrue;
13179 mng_info->equal_backgrounds=MagickTrue;
13181 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13182 defined(PNG_MNG_FEATURES_SUPPORTED)
13183 all_images_are_gray=MagickTrue;
13184 mng_info->equal_palettes=MagickFalse;
13185 need_local_plte=MagickFalse;
13187 for (next_image=image; next_image != (Image *) NULL; )
13191 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13192 mng_info->page.width=next_image->columns+next_image->page.x;
13194 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13195 mng_info->page.height=next_image->rows+next_image->page.y;
13198 if (next_image->page.x || next_image->page.y)
13199 need_defi=MagickTrue;
13201 if (next_image->alpha_trait == BlendPixelTrait)
13202 need_matte=MagickTrue;
13204 if ((int) next_image->dispose >= BackgroundDispose)
13205 if ((next_image->alpha_trait == BlendPixelTrait) ||
13206 next_image->page.x || next_image->page.y ||
13207 ((next_image->columns < mng_info->page.width) &&
13208 (next_image->rows < mng_info->page.height)))
13209 mng_info->need_fram=MagickTrue;
13211 if (next_image->iterations)
13212 need_iterations=MagickTrue;
13214 final_delay=next_image->delay;
13216 if (final_delay != initial_delay || final_delay > 1UL*
13217 next_image->ticks_per_second)
13218 mng_info->need_fram=1;
13220 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13221 defined(PNG_MNG_FEATURES_SUPPORTED)
13223 check for global palette possibility.
13225 if (image->alpha_trait == BlendPixelTrait)
13226 need_local_plte=MagickTrue;
13228 if (need_local_plte == 0)
13230 if (IsImageGray(image,exception) == MagickFalse)
13231 all_images_are_gray=MagickFalse;
13232 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13233 if (use_global_plte == 0)
13234 use_global_plte=mng_info->equal_palettes;
13235 need_local_plte=!mng_info->equal_palettes;
13238 if (GetNextImageInList(next_image) != (Image *) NULL)
13240 if (next_image->background_color.red !=
13241 next_image->next->background_color.red ||
13242 next_image->background_color.green !=
13243 next_image->next->background_color.green ||
13244 next_image->background_color.blue !=
13245 next_image->next->background_color.blue)
13246 mng_info->equal_backgrounds=MagickFalse;
13248 if (next_image->gamma != next_image->next->gamma)
13249 mng_info->equal_gammas=MagickFalse;
13251 if (next_image->rendering_intent !=
13252 next_image->next->rendering_intent)
13253 mng_info->equal_srgbs=MagickFalse;
13255 if ((next_image->units != next_image->next->units) ||
13256 (next_image->resolution.x != next_image->next->resolution.x) ||
13257 (next_image->resolution.y != next_image->next->resolution.y))
13258 mng_info->equal_physs=MagickFalse;
13260 if (mng_info->equal_chrms)
13262 if (next_image->chromaticity.red_primary.x !=
13263 next_image->next->chromaticity.red_primary.x ||
13264 next_image->chromaticity.red_primary.y !=
13265 next_image->next->chromaticity.red_primary.y ||
13266 next_image->chromaticity.green_primary.x !=
13267 next_image->next->chromaticity.green_primary.x ||
13268 next_image->chromaticity.green_primary.y !=
13269 next_image->next->chromaticity.green_primary.y ||
13270 next_image->chromaticity.blue_primary.x !=
13271 next_image->next->chromaticity.blue_primary.x ||
13272 next_image->chromaticity.blue_primary.y !=
13273 next_image->next->chromaticity.blue_primary.y ||
13274 next_image->chromaticity.white_point.x !=
13275 next_image->next->chromaticity.white_point.x ||
13276 next_image->chromaticity.white_point.y !=
13277 next_image->next->chromaticity.white_point.y)
13278 mng_info->equal_chrms=MagickFalse;
13282 next_image=GetNextImageInList(next_image);
13284 if (image_count < 2)
13286 mng_info->equal_backgrounds=MagickFalse;
13287 mng_info->equal_chrms=MagickFalse;
13288 mng_info->equal_gammas=MagickFalse;
13289 mng_info->equal_srgbs=MagickFalse;
13290 mng_info->equal_physs=MagickFalse;
13291 use_global_plte=MagickFalse;
13292 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13293 need_local_plte=MagickTrue;
13295 need_iterations=MagickFalse;
13298 if (mng_info->need_fram == MagickFalse)
13301 Only certain framing rates 100/n are exactly representable without
13302 the FRAM chunk but we'll allow some slop in VLC files
13304 if (final_delay == 0)
13306 if (need_iterations != MagickFalse)
13309 It's probably a GIF with loop; don't run it *too* fast.
13311 if (mng_info->adjoin)
13314 (void) ThrowMagickException(exception,GetMagickModule(),
13316 "input has zero delay between all frames; assuming",
13321 mng_info->ticks_per_second=0;
13323 if (final_delay != 0)
13324 mng_info->ticks_per_second=(png_uint_32)
13325 (image->ticks_per_second/final_delay);
13326 if (final_delay > 50)
13327 mng_info->ticks_per_second=2;
13329 if (final_delay > 75)
13330 mng_info->ticks_per_second=1;
13332 if (final_delay > 125)
13333 mng_info->need_fram=MagickTrue;
13335 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13336 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13337 (final_delay != 25) && (final_delay != 50) && (final_delay !=
13338 1UL*image->ticks_per_second))
13339 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13342 if (mng_info->need_fram != MagickFalse)
13343 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13345 If pseudocolor, we should also check to see if all the
13346 palettes are identical and write a global PLTE if they are.
13350 Write the MNG version 1.0 signature and MHDR chunk.
13352 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13353 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13354 PNGType(chunk,mng_MHDR);
13355 LogPNGChunk(logging,mng_MHDR,28L);
13356 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13357 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13358 PNGLong(chunk+12,mng_info->ticks_per_second);
13359 PNGLong(chunk+16,0L); /* layer count=unknown */
13360 PNGLong(chunk+20,0L); /* frame count=unknown */
13361 PNGLong(chunk+24,0L); /* play time=unknown */
13366 if (need_defi || mng_info->need_fram || use_global_plte)
13367 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13370 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13375 if (need_defi || mng_info->need_fram || use_global_plte)
13376 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13379 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13387 if (need_defi || mng_info->need_fram || use_global_plte)
13388 PNGLong(chunk+28,11L); /* simplicity=LC */
13391 PNGLong(chunk+28,9L); /* simplicity=VLC */
13396 if (need_defi || mng_info->need_fram || use_global_plte)
13397 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13400 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13403 (void) WriteBlob(image,32,chunk);
13404 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13405 option=GetImageOption(image_info,"mng:need-cacheoff");
13406 if (option != (const char *) NULL)
13412 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13414 PNGType(chunk,mng_nEED);
13415 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13416 (void) WriteBlobMSBULong(image,(size_t) length);
13417 LogPNGChunk(logging,mng_nEED,(size_t) length);
13419 (void) WriteBlob(image,length,chunk);
13420 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13422 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13423 (GetNextImageInList(image) != (Image *) NULL) &&
13424 (image->iterations != 1))
13427 Write MNG TERM chunk
13429 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13430 PNGType(chunk,mng_TERM);
13431 LogPNGChunk(logging,mng_TERM,10L);
13432 chunk[4]=3; /* repeat animation */
13433 chunk[5]=0; /* show last frame when done */
13434 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13435 final_delay/MagickMax(image->ticks_per_second,1)));
13437 if (image->iterations == 0)
13438 PNGLong(chunk+10,PNG_UINT_31_MAX);
13441 PNGLong(chunk+10,(png_uint_32) image->iterations);
13443 if (logging != MagickFalse)
13445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13446 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13447 final_delay/MagickMax(image->ticks_per_second,1)));
13449 if (image->iterations == 0)
13450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13451 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13455 " Image iterations: %.20g",(double) image->iterations);
13457 (void) WriteBlob(image,14,chunk);
13458 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13461 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13463 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13464 mng_info->equal_srgbs)
13467 Write MNG sRGB chunk
13469 (void) WriteBlobMSBULong(image,1L);
13470 PNGType(chunk,mng_sRGB);
13471 LogPNGChunk(logging,mng_sRGB,1L);
13473 if (image->rendering_intent != UndefinedIntent)
13474 chunk[4]=(unsigned char)
13475 Magick_RenderingIntent_to_PNG_RenderingIntent(
13476 (image->rendering_intent));
13479 chunk[4]=(unsigned char)
13480 Magick_RenderingIntent_to_PNG_RenderingIntent(
13481 (PerceptualIntent));
13483 (void) WriteBlob(image,5,chunk);
13484 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13485 mng_info->have_write_global_srgb=MagickTrue;
13490 if (image->gamma && mng_info->equal_gammas)
13493 Write MNG gAMA chunk
13495 (void) WriteBlobMSBULong(image,4L);
13496 PNGType(chunk,mng_gAMA);
13497 LogPNGChunk(logging,mng_gAMA,4L);
13498 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13499 (void) WriteBlob(image,8,chunk);
13500 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13501 mng_info->have_write_global_gama=MagickTrue;
13503 if (mng_info->equal_chrms)
13509 Write MNG cHRM chunk
13511 (void) WriteBlobMSBULong(image,32L);
13512 PNGType(chunk,mng_cHRM);
13513 LogPNGChunk(logging,mng_cHRM,32L);
13514 primary=image->chromaticity.white_point;
13515 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13516 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13517 primary=image->chromaticity.red_primary;
13518 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13519 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13520 primary=image->chromaticity.green_primary;
13521 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13522 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13523 primary=image->chromaticity.blue_primary;
13524 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13525 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13526 (void) WriteBlob(image,36,chunk);
13527 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13528 mng_info->have_write_global_chrm=MagickTrue;
13531 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13534 Write MNG pHYs chunk
13536 (void) WriteBlobMSBULong(image,9L);
13537 PNGType(chunk,mng_pHYs);
13538 LogPNGChunk(logging,mng_pHYs,9L);
13540 if (image->units == PixelsPerInchResolution)
13542 PNGLong(chunk+4,(png_uint_32)
13543 (image->resolution.x*100.0/2.54+0.5));
13545 PNGLong(chunk+8,(png_uint_32)
13546 (image->resolution.y*100.0/2.54+0.5));
13553 if (image->units == PixelsPerCentimeterResolution)
13555 PNGLong(chunk+4,(png_uint_32)
13556 (image->resolution.x*100.0+0.5));
13558 PNGLong(chunk+8,(png_uint_32)
13559 (image->resolution.y*100.0+0.5));
13566 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13567 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13571 (void) WriteBlob(image,13,chunk);
13572 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13575 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13576 or does not cover the entire frame.
13578 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13579 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13580 (image->page.width+image->page.x < mng_info->page.width))
13581 || (image->page.height && (image->page.height+image->page.y
13582 < mng_info->page.height))))
13584 (void) WriteBlobMSBULong(image,6L);
13585 PNGType(chunk,mng_BACK);
13586 LogPNGChunk(logging,mng_BACK,6L);
13587 red=ScaleQuantumToShort(image->background_color.red);
13588 green=ScaleQuantumToShort(image->background_color.green);
13589 blue=ScaleQuantumToShort(image->background_color.blue);
13590 PNGShort(chunk+4,red);
13591 PNGShort(chunk+6,green);
13592 PNGShort(chunk+8,blue);
13593 (void) WriteBlob(image,10,chunk);
13594 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13595 if (mng_info->equal_backgrounds)
13597 (void) WriteBlobMSBULong(image,6L);
13598 PNGType(chunk,mng_bKGD);
13599 LogPNGChunk(logging,mng_bKGD,6L);
13600 (void) WriteBlob(image,10,chunk);
13601 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13605 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13606 if ((need_local_plte == MagickFalse) &&
13607 (image->storage_class == PseudoClass) &&
13608 (all_images_are_gray == MagickFalse))
13614 Write MNG PLTE chunk
13616 data_length=3*image->colors;
13617 (void) WriteBlobMSBULong(image,data_length);
13618 PNGType(chunk,mng_PLTE);
13619 LogPNGChunk(logging,mng_PLTE,data_length);
13621 for (i=0; i < (ssize_t) image->colors; i++)
13623 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13624 image->colormap[i].red) & 0xff);
13625 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13626 image->colormap[i].green) & 0xff);
13627 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13628 image->colormap[i].blue) & 0xff);
13631 (void) WriteBlob(image,data_length+4,chunk);
13632 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13633 mng_info->have_write_global_plte=MagickTrue;
13639 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13640 defined(PNG_MNG_FEATURES_SUPPORTED)
13641 mng_info->equal_palettes=MagickFalse;
13645 if (mng_info->adjoin)
13647 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13648 defined(PNG_MNG_FEATURES_SUPPORTED)
13650 If we aren't using a global palette for the entire MNG, check to
13651 see if we can use one for two or more consecutive images.
13653 if (need_local_plte && use_global_plte && !all_images_are_gray)
13655 if (mng_info->IsPalette)
13658 When equal_palettes is true, this image has the same palette
13659 as the previous PseudoClass image
13661 mng_info->have_write_global_plte=mng_info->equal_palettes;
13662 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13663 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13666 Write MNG PLTE chunk
13671 data_length=3*image->colors;
13672 (void) WriteBlobMSBULong(image,data_length);
13673 PNGType(chunk,mng_PLTE);
13674 LogPNGChunk(logging,mng_PLTE,data_length);
13676 for (i=0; i < (ssize_t) image->colors; i++)
13678 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13679 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13680 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13683 (void) WriteBlob(image,data_length+4,chunk);
13684 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13685 (uInt) (data_length+4)));
13686 mng_info->have_write_global_plte=MagickTrue;
13690 mng_info->have_write_global_plte=MagickFalse;
13701 previous_x=mng_info->page.x;
13702 previous_y=mng_info->page.y;
13709 mng_info->page=image->page;
13710 if ((mng_info->page.x != previous_x) ||
13711 (mng_info->page.y != previous_y))
13713 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13714 PNGType(chunk,mng_DEFI);
13715 LogPNGChunk(logging,mng_DEFI,12L);
13716 chunk[4]=0; /* object 0 MSB */
13717 chunk[5]=0; /* object 0 LSB */
13718 chunk[6]=0; /* visible */
13719 chunk[7]=0; /* abstract */
13720 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13721 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13722 (void) WriteBlob(image,16,chunk);
13723 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13728 mng_info->write_mng=write_mng;
13730 if ((int) image->dispose >= 3)
13731 mng_info->framing_mode=3;
13733 if (mng_info->need_fram && mng_info->adjoin &&
13734 ((image->delay != mng_info->delay) ||
13735 (mng_info->framing_mode != mng_info->old_framing_mode)))
13737 if (image->delay == mng_info->delay)
13740 Write a MNG FRAM chunk with the new framing mode.
13742 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13743 PNGType(chunk,mng_FRAM);
13744 LogPNGChunk(logging,mng_FRAM,1L);
13745 chunk[4]=(unsigned char) mng_info->framing_mode;
13746 (void) WriteBlob(image,5,chunk);
13747 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13752 Write a MNG FRAM chunk with the delay.
13754 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13755 PNGType(chunk,mng_FRAM);
13756 LogPNGChunk(logging,mng_FRAM,10L);
13757 chunk[4]=(unsigned char) mng_info->framing_mode;
13758 chunk[5]=0; /* frame name separator (no name) */
13759 chunk[6]=2; /* flag for changing default delay */
13760 chunk[7]=0; /* flag for changing frame timeout */
13761 chunk[8]=0; /* flag for changing frame clipping */
13762 chunk[9]=0; /* flag for changing frame sync_id */
13763 PNGLong(chunk+10,(png_uint_32)
13764 ((mng_info->ticks_per_second*
13765 image->delay)/MagickMax(image->ticks_per_second,1)));
13766 (void) WriteBlob(image,14,chunk);
13767 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13768 mng_info->delay=(png_uint_32) image->delay;
13770 mng_info->old_framing_mode=mng_info->framing_mode;
13773 #if defined(JNG_SUPPORTED)
13774 if (image_info->compression == JPEGCompression)
13779 if (logging != MagickFalse)
13780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13781 " Writing JNG object.");
13782 /* To do: specify the desired alpha compression method. */
13783 write_info=CloneImageInfo(image_info);
13784 write_info->compression=UndefinedCompression;
13785 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13786 write_info=DestroyImageInfo(write_info);
13791 if (logging != MagickFalse)
13792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13793 " Writing PNG object.");
13795 mng_info->need_blob = MagickFalse;
13796 mng_info->ping_preserve_colormap = MagickFalse;
13798 /* We don't want any ancillary chunks written */
13799 mng_info->ping_exclude_bKGD=MagickTrue;
13800 mng_info->ping_exclude_cHRM=MagickTrue;
13801 mng_info->ping_exclude_date=MagickTrue;
13802 mng_info->ping_exclude_EXIF=MagickTrue;
13803 mng_info->ping_exclude_gAMA=MagickTrue;
13804 mng_info->ping_exclude_iCCP=MagickTrue;
13805 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13806 mng_info->ping_exclude_oFFs=MagickTrue;
13807 mng_info->ping_exclude_pHYs=MagickTrue;
13808 mng_info->ping_exclude_sRGB=MagickTrue;
13809 mng_info->ping_exclude_tEXt=MagickTrue;
13810 mng_info->ping_exclude_tRNS=MagickTrue;
13811 mng_info->ping_exclude_vpAg=MagickTrue;
13812 mng_info->ping_exclude_zCCP=MagickTrue;
13813 mng_info->ping_exclude_zTXt=MagickTrue;
13815 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13818 if (status == MagickFalse)
13820 MngInfoFreeStruct(mng_info,&have_mng_structure);
13821 (void) CloseBlob(image);
13822 return(MagickFalse);
13824 (void) CatchImageException(image);
13825 if (GetNextImageInList(image) == (Image *) NULL)
13827 image=SyncNextImageInList(image);
13828 status=SetImageProgress(image,SaveImagesTag,scene++,
13829 GetImageListLength(image));
13831 if (status == MagickFalse)
13834 } while (mng_info->adjoin);
13838 while (GetPreviousImageInList(image) != (Image *) NULL)
13839 image=GetPreviousImageInList(image);
13841 Write the MEND chunk.
13843 (void) WriteBlobMSBULong(image,0x00000000L);
13844 PNGType(chunk,mng_MEND);
13845 LogPNGChunk(logging,mng_MEND,0L);
13846 (void) WriteBlob(image,4,chunk);
13847 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13850 Relinquish resources.
13852 (void) CloseBlob(image);
13853 MngInfoFreeStruct(mng_info,&have_mng_structure);
13855 if (logging != MagickFalse)
13856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13858 return(MagickTrue);
13860 #else /* PNG_LIBPNG_VER > 10011 */
13862 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13865 printf("Your PNG library is too old: You have libpng-%s\n",
13866 PNG_LIBPNG_VER_STRING);
13868 ThrowBinaryException(CoderError,"PNG library is too old",
13869 image_info->filename);
13872 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13874 return(WritePNGImage(image_info,image));
13876 #endif /* PNG_LIBPNG_VER > 10011 */