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 #ifdef PNG_SETJMP_SUPPORTED
632 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
633 # define IMPNG_SETJMP_NOT_THREAD_SAFE
636 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
638 *ping_semaphore = (SemaphoreInfo *) NULL;
643 This temporary until I set up malloc'ed object attributes array.
644 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
647 #define MNG_MAX_OBJECTS 256
650 If this not defined, spec is interpreted strictly. If it is
651 defined, an attempt will be made to recover from some errors,
653 o global PLTE too short
658 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
659 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
660 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
661 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
662 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
663 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
664 will be enabled by default in libpng-1.2.0.
666 #ifdef PNG_MNG_FEATURES_SUPPORTED
667 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
668 # define PNG_READ_EMPTY_PLTE_SUPPORTED
670 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
671 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
676 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
677 This macro is only defined in libpng-1.0.3 and later.
678 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
680 #ifndef PNG_UINT_31_MAX
681 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
685 Constant strings for known chunk types. If you need to add a chunk,
686 add a string holding the name here. To make the code more
687 portable, we use ASCII numbers like this, not characters.
690 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
691 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
692 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
693 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
694 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
695 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
696 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
697 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
698 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
699 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
700 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
701 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
702 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
703 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
704 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
705 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
706 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
707 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
708 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
709 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
710 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
711 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
712 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
713 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
714 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
715 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
716 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
717 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
718 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
719 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
720 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
721 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
722 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
723 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
725 #if defined(JNG_SUPPORTED)
726 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
727 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
728 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
729 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
730 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
731 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
735 /* Other known chunks that are not yet supported by ImageMagick: */
736 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
737 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
738 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
739 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
740 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
741 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
742 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
745 typedef struct _MngBox
754 typedef struct _MngPair
761 #ifdef MNG_OBJECT_BUFFERS
762 typedef struct _MngBuffer
794 typedef struct _MngInfo
797 #ifdef MNG_OBJECT_BUFFERS
799 *ob[MNG_MAX_OBJECTS];
810 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
811 bytes_in_read_buffer,
817 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
818 defined(PNG_MNG_FEATURES_SUPPORTED)
830 have_saved_bkgd_index,
831 have_write_global_chrm,
832 have_write_global_gama,
833 have_write_global_plte,
834 have_write_global_srgb,
848 x_off[MNG_MAX_OBJECTS],
849 y_off[MNG_MAX_OBJECTS];
855 object_clip[MNG_MAX_OBJECTS];
858 /* These flags could be combined into one byte */
859 exists[MNG_MAX_OBJECTS],
860 frozen[MNG_MAX_OBJECTS],
862 invisible[MNG_MAX_OBJECTS],
863 viewable[MNG_MAX_OBJECTS];
875 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
893 global_x_pixels_per_unit,
894 global_y_pixels_per_unit,
904 global_phys_unit_type,
919 write_png_compression_level,
920 write_png_compression_strategy,
921 write_png_compression_filter,
928 #ifdef MNG_BASI_SUPPORTED
936 basi_compression_method,
938 basi_interlace_method,
961 /* Added at version 6.6.6-7 */
969 /* ping_exclude_iTXt, */
976 ping_exclude_zCCP, /* hex-encoded iCCP */
978 ping_preserve_colormap,
979 /* Added at version 6.8.5-7 */
986 Forward declarations.
988 static MagickBooleanType
989 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
991 static MagickBooleanType
992 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
994 #if defined(JNG_SUPPORTED)
995 static MagickBooleanType
996 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
999 #if PNG_LIBPNG_VER > 10011
1002 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1003 static MagickBooleanType
1004 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
1006 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1008 * This is true if the high byte and the next highest byte of
1009 * each sample of the image, the colormap, and the background color
1010 * are equal to each other. We check this by seeing if the samples
1011 * are unchanged when we scale them down to 8 and back up to Quantum.
1013 * We don't use the method GetImageDepth() because it doesn't check
1014 * background and doesn't handle PseudoClass specially.
1017 #define QuantumToCharToQuantumEqQuantum(quantum) \
1018 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1021 ok_to_reduce=MagickFalse;
1023 if (image->depth >= 16)
1030 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1031 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1032 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1033 MagickTrue : MagickFalse;
1035 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1039 for (indx=0; indx < (ssize_t) image->colors; indx++)
1042 QuantumToCharToQuantumEqQuantum(
1043 image->colormap[indx].red) &&
1044 QuantumToCharToQuantumEqQuantum(
1045 image->colormap[indx].green) &&
1046 QuantumToCharToQuantumEqQuantum(
1047 image->colormap[indx].blue)) ?
1048 MagickTrue : MagickFalse;
1050 if (ok_to_reduce == MagickFalse)
1055 if ((ok_to_reduce != MagickFalse) &&
1056 (image->storage_class != PseudoClass))
1064 for (y=0; y < (ssize_t) image->rows; y++)
1066 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1068 if (p == (const Quantum *) NULL)
1070 ok_to_reduce = MagickFalse;
1074 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1077 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1078 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1079 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1080 MagickTrue : MagickFalse;
1082 if (ok_to_reduce == MagickFalse)
1085 p+=GetPixelChannels(image);
1092 if (ok_to_reduce != MagickFalse)
1094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1095 " OK to reduce PNG bit depth to 8 without loss of info");
1099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1100 " Not OK to reduce PNG bit depth to 8 without loss of info");
1104 return ok_to_reduce;
1106 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1108 static const char* PngColorTypeToString(const unsigned int color_type)
1111 *result = "Unknown";
1115 case PNG_COLOR_TYPE_GRAY:
1118 case PNG_COLOR_TYPE_GRAY_ALPHA:
1119 result = "Gray+Alpha";
1121 case PNG_COLOR_TYPE_PALETTE:
1124 case PNG_COLOR_TYPE_RGB:
1127 case PNG_COLOR_TYPE_RGB_ALPHA:
1128 result = "RGB+Alpha";
1136 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1140 case PerceptualIntent:
1143 case RelativeIntent:
1146 case SaturationIntent:
1149 case AbsoluteIntent:
1157 static RenderingIntent
1158 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1160 switch (ping_intent)
1163 return PerceptualIntent;
1166 return RelativeIntent;
1169 return SaturationIntent;
1172 return AbsoluteIntent;
1175 return UndefinedIntent;
1180 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1182 switch (ping_intent)
1185 return "Perceptual Intent";
1188 return "Relative Intent";
1191 return "Saturation Intent";
1194 return "Absolute Intent";
1197 return "Undefined Intent";
1201 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1210 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1212 switch (ping_colortype)
1230 return "UndefinedColorType";
1235 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1242 #endif /* PNG_LIBPNG_VER > 10011 */
1243 #endif /* MAGICKCORE_PNG_DELEGATE */
1246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256 % IsMNG() returns MagickTrue if the image format type, identified by the
1257 % magick string, is MNG.
1259 % The format of the IsMNG method is:
1261 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1263 % A description of each parameter follows:
1265 % o magick: compare image format pattern against these bytes.
1267 % o length: Specifies the length of the magick string.
1271 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1274 return(MagickFalse);
1276 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1279 return(MagickFalse);
1283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293 % IsJNG() returns MagickTrue if the image format type, identified by the
1294 % magick string, is JNG.
1296 % The format of the IsJNG method is:
1298 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1300 % A description of each parameter follows:
1302 % o magick: compare image format pattern against these bytes.
1304 % o length: Specifies the length of the magick string.
1308 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1311 return(MagickFalse);
1313 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1316 return(MagickFalse);
1320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1330 % IsPNG() returns MagickTrue if the image format type, identified by the
1331 % magick string, is PNG.
1333 % The format of the IsPNG method is:
1335 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1337 % A description of each parameter follows:
1339 % o magick: compare image format pattern against these bytes.
1341 % o length: Specifies the length of the magick string.
1344 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1347 return(MagickFalse);
1349 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1352 return(MagickFalse);
1355 #if defined(MAGICKCORE_PNG_DELEGATE)
1356 #if defined(__cplusplus) || defined(c_plusplus)
1360 #if (PNG_LIBPNG_VER > 10011)
1361 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1366 assert(image != (Image *) NULL);
1367 assert(image->signature == MagickSignature);
1368 buffer[0]=(unsigned char) (value >> 24);
1369 buffer[1]=(unsigned char) (value >> 16);
1370 buffer[2]=(unsigned char) (value >> 8);
1371 buffer[3]=(unsigned char) value;
1372 return((size_t) WriteBlob(image,4,buffer));
1375 static void PNGLong(png_bytep p,png_uint_32 value)
1377 *p++=(png_byte) ((value >> 24) & 0xff);
1378 *p++=(png_byte) ((value >> 16) & 0xff);
1379 *p++=(png_byte) ((value >> 8) & 0xff);
1380 *p++=(png_byte) (value & 0xff);
1383 #if defined(JNG_SUPPORTED)
1384 static void PNGsLong(png_bytep p,png_int_32 value)
1386 *p++=(png_byte) ((value >> 24) & 0xff);
1387 *p++=(png_byte) ((value >> 16) & 0xff);
1388 *p++=(png_byte) ((value >> 8) & 0xff);
1389 *p++=(png_byte) (value & 0xff);
1393 static void PNGShort(png_bytep p,png_uint_16 value)
1395 *p++=(png_byte) ((value >> 8) & 0xff);
1396 *p++=(png_byte) (value & 0xff);
1399 static void PNGType(png_bytep p,png_bytep type)
1401 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1404 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1407 if (logging != MagickFalse)
1408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1409 " Writing %c%c%c%c chunk, length: %.20g",
1410 type[0],type[1],type[2],type[3],(double) length);
1412 #endif /* PNG_LIBPNG_VER > 10011 */
1414 #if defined(__cplusplus) || defined(c_plusplus)
1418 #if PNG_LIBPNG_VER > 10011
1420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1424 % R e a d P N G I m a g e %
1428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1431 % Multiple-image Network Graphics (MNG) image file and returns it. It
1432 % allocates the memory necessary for the new Image structure and returns a
1433 % pointer to the new image or set of images.
1435 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1437 % The format of the ReadPNGImage method is:
1439 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1441 % A description of each parameter follows:
1443 % o image_info: the image info.
1445 % o exception: return any errors or warnings in this structure.
1447 % To do, more or less in chronological order (as of version 5.5.2,
1448 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1450 % Get 16-bit cheap transparency working.
1452 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1454 % Preserve all unknown and not-yet-handled known chunks found in input
1455 % PNG file and copy them into output PNG files according to the PNG
1458 % (At this point, PNG encoding should be in full MNG compliance)
1460 % Provide options for choice of background to use when the MNG BACK
1461 % chunk is not present or is not mandatory (i.e., leave transparent,
1462 % user specified, MNG BACK, PNG bKGD)
1464 % Implement LOOP/ENDL [done, but could do discretionary loops more
1465 % efficiently by linking in the duplicate frames.].
1467 % Decode and act on the MHDR simplicity profile (offer option to reject
1468 % files or attempt to process them anyway when the profile isn't LC or VLC).
1470 % Upgrade to full MNG without Delta-PNG.
1472 % o BACK [done a while ago except for background image ID]
1473 % o MOVE [done 15 May 1999]
1474 % o CLIP [done 15 May 1999]
1475 % o DISC [done 19 May 1999]
1476 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1477 % o SEEK [partially done 19 May 1999 (discard function only)]
1481 % o MNG-level tEXt/iTXt/zTXt
1486 % o iTXt (wait for libpng implementation).
1488 % Use the scene signature to discover when an identical scene is
1489 % being reused, and just point to the original image->exception instead
1490 % of storing another set of pixels. This not specific to MNG
1491 % but could be applied generally.
1493 % Upgrade to full MNG with Delta-PNG.
1495 % JNG tEXt/iTXt/zTXt
1497 % We will not attempt to read files containing the CgBI chunk.
1498 % They are really Xcode files meant for display on the iPhone.
1499 % These are not valid PNG files and it is impossible to recover
1500 % the original PNG from files that have been converted to Xcode-PNG,
1501 % since irretrievable loss of color data has occurred due to the
1502 % use of premultiplied alpha.
1505 #if defined(__cplusplus) || defined(c_plusplus)
1510 This the function that does the actual reading of data. It is
1511 the same as the one supplied in libpng, except that it receives the
1512 datastream from the ReadBlob() function instead of standard input.
1514 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1519 image=(Image *) png_get_io_ptr(png_ptr);
1525 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1526 if (check != length)
1531 (void) FormatLocaleString(msg,MaxTextExtent,
1532 "Expected %.20g bytes; found %.20g bytes",(double) length,
1534 png_warning(png_ptr,msg);
1535 png_error(png_ptr,"Read Exception");
1540 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1541 !defined(PNG_MNG_FEATURES_SUPPORTED)
1542 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1543 * older than libpng-1.0.3a, which was the first to allow the empty
1544 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1545 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1546 * encountered after an empty PLTE, so we have to look ahead for bKGD
1547 * chunks and remove them from the datastream that is passed to libpng,
1548 * and store their contents for later use.
1550 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1565 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1566 image=(Image *) mng_info->image;
1567 while (mng_info->bytes_in_read_buffer && length)
1569 data[i]=mng_info->read_buffer[i];
1570 mng_info->bytes_in_read_buffer--;
1576 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1578 if (check != length)
1579 png_error(png_ptr,"Read Exception");
1583 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1586 check=(png_size_t) ReadBlob(image,(size_t) length,
1587 (char *) mng_info->read_buffer);
1588 mng_info->read_buffer[4]=0;
1589 mng_info->bytes_in_read_buffer=4;
1590 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1591 mng_info->found_empty_plte=MagickTrue;
1592 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1594 mng_info->found_empty_plte=MagickFalse;
1595 mng_info->have_saved_bkgd_index=MagickFalse;
1599 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1602 check=(png_size_t) ReadBlob(image,(size_t) length,
1603 (char *) mng_info->read_buffer);
1604 mng_info->read_buffer[4]=0;
1605 mng_info->bytes_in_read_buffer=4;
1606 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1607 if (mng_info->found_empty_plte)
1610 Skip the bKGD data byte and CRC.
1613 ReadBlob(image,5,(char *) mng_info->read_buffer);
1614 check=(png_size_t) ReadBlob(image,(size_t) length,
1615 (char *) mng_info->read_buffer);
1616 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1617 mng_info->have_saved_bkgd_index=MagickTrue;
1618 mng_info->bytes_in_read_buffer=0;
1626 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1631 image=(Image *) png_get_io_ptr(png_ptr);
1637 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1639 if (check != length)
1640 png_error(png_ptr,"WriteBlob Failed");
1644 static void png_flush_data(png_structp png_ptr)
1649 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1650 static int PalettesAreEqual(Image *a,Image *b)
1655 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1656 return((int) MagickFalse);
1658 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1659 return((int) MagickFalse);
1661 if (a->colors != b->colors)
1662 return((int) MagickFalse);
1664 for (i=0; i < (ssize_t) a->colors; i++)
1666 if ((a->colormap[i].red != b->colormap[i].red) ||
1667 (a->colormap[i].green != b->colormap[i].green) ||
1668 (a->colormap[i].blue != b->colormap[i].blue))
1669 return((int) MagickFalse);
1672 return((int) MagickTrue);
1676 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1678 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1679 mng_info->exists[i] && !mng_info->frozen[i])
1681 #ifdef MNG_OBJECT_BUFFERS
1682 if (mng_info->ob[i] != (MngBuffer *) NULL)
1684 if (mng_info->ob[i]->reference_count > 0)
1685 mng_info->ob[i]->reference_count--;
1687 if (mng_info->ob[i]->reference_count == 0)
1689 if (mng_info->ob[i]->image != (Image *) NULL)
1690 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1692 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1695 mng_info->ob[i]=(MngBuffer *) NULL;
1697 mng_info->exists[i]=MagickFalse;
1698 mng_info->invisible[i]=MagickFalse;
1699 mng_info->viewable[i]=MagickFalse;
1700 mng_info->frozen[i]=MagickFalse;
1701 mng_info->x_off[i]=0;
1702 mng_info->y_off[i]=0;
1703 mng_info->object_clip[i].left=0;
1704 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1705 mng_info->object_clip[i].top=0;
1706 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1710 static void MngInfoFreeStruct(MngInfo *mng_info,
1711 MagickBooleanType *have_mng_structure)
1713 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1718 for (i=1; i < MNG_MAX_OBJECTS; i++)
1719 MngInfoDiscardObject(mng_info,i);
1721 if (mng_info->global_plte != (png_colorp) NULL)
1722 mng_info->global_plte=(png_colorp)
1723 RelinquishMagickMemory(mng_info->global_plte);
1725 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1726 *have_mng_structure=MagickFalse;
1730 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1736 if (box.left < box2.left)
1739 if (box.top < box2.top)
1742 if (box.right > box2.right)
1743 box.right=box2.right;
1745 if (box.bottom > box2.bottom)
1746 box.bottom=box2.bottom;
1751 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1757 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1759 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1760 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1761 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1762 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1763 if (delta_type != 0)
1765 box.left+=previous_box.left;
1766 box.right+=previous_box.right;
1767 box.top+=previous_box.top;
1768 box.bottom+=previous_box.bottom;
1774 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1780 Read two ssize_ts from CLON, MOVE or PAST chunk
1782 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1783 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1785 if (delta_type != 0)
1787 pair.a+=previous_pair.a;
1788 pair.b+=previous_pair.b;
1794 static long mng_get_long(unsigned char *p)
1796 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1799 typedef struct _PNGErrorInfo
1808 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1819 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1820 image=error_info->image;
1821 exception=error_info->exception;
1823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1826 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1827 "`%s'",image->filename);
1829 #if (PNG_LIBPNG_VER < 10500)
1830 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1831 * are building with libpng-1.4.x and can be ignored.
1833 longjmp(ping->jmpbuf,1);
1835 png_longjmp(ping,1);
1839 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1850 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1851 png_error(ping, message);
1853 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1854 image=error_info->image;
1855 exception=error_info->exception;
1856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1857 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1859 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1860 message,"`%s'",image->filename);
1863 #ifdef PNG_USER_MEM_SUPPORTED
1864 #if PNG_LIBPNG_VER >= 10400
1865 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1867 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1871 return((png_voidp) AcquireMagickMemory((size_t) size));
1875 Free a pointer. It is removed from the list at the same time.
1877 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1880 ptr=RelinquishMagickMemory(ptr);
1881 return((png_free_ptr) NULL);
1885 #if defined(__cplusplus) || defined(c_plusplus)
1890 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1891 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1896 register unsigned char
1910 unhex[103]={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,0, 0,0,0,0,0,0,0,0,0,0,
1912 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1913 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1914 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1918 /* look for newline */
1922 /* look for length */
1923 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1926 length=(png_uint_32) StringToLong(sp);
1928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1929 " length: %lu",(unsigned long) length);
1931 while (*sp != ' ' && *sp != '\n')
1934 /* allocate space */
1937 png_warning(ping,"invalid profile length");
1938 return(MagickFalse);
1941 profile=BlobToStringInfo((const void *) NULL,length);
1943 if (profile == (StringInfo *) NULL)
1945 png_warning(ping, "unable to copy profile");
1946 return(MagickFalse);
1949 /* copy profile, skipping white space and column 1 "=" signs */
1950 dp=GetStringInfoDatum(profile);
1953 for (i=0; i < (ssize_t) nibbles; i++)
1955 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1959 png_warning(ping, "ran out of profile data");
1960 profile=DestroyStringInfo(profile);
1961 return(MagickFalse);
1967 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1970 (*dp++)+=unhex[(int) *sp++];
1973 We have already read "Raw profile type.
1975 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1976 profile=DestroyStringInfo(profile);
1978 if (image_info->verbose)
1979 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1984 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1985 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1991 /* The unknown chunk structure contains the chunk data:
1996 Note that libpng has already taken care of the CRC handling.
1999 LogMagickEvent(CoderEvent,GetMagickModule(),
2000 " read_vpag_chunk: found %c%c%c%c chunk",
2001 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2003 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2004 chunk->name[2] != 65 ||chunk-> name[3] != 103)
2005 return(0); /* Did not recognize */
2007 /* recognized vpAg */
2009 if (chunk->size != 9)
2010 return(-1); /* Error return */
2012 if (chunk->data[8] != 0)
2013 return(0); /* ImageMagick requires pixel units */
2015 image=(Image *) png_get_user_chunk_ptr(ping);
2017 image->page.width=(size_t) ((chunk->data[0] << 24) |
2018 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2020 image->page.height=(size_t) ((chunk->data[4] << 24) |
2021 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2023 /* Return one of the following: */
2024 /* return(-n); chunk had an error */
2025 /* return(0); did not recognize */
2026 /* return(n); success */
2034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2038 % R e a d O n e P N G I m a g e %
2042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2044 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2045 % (minus the 8-byte signature) and returns it. It allocates the memory
2046 % necessary for the new Image structure and returns a pointer to the new
2049 % The format of the ReadOnePNGImage method is:
2051 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2052 % ExceptionInfo *exception)
2054 % A description of each parameter follows:
2056 % o mng_info: Specifies a pointer to a MngInfo structure.
2058 % o image_info: the image info.
2060 % o exception: return any errors or warnings in this structure.
2063 static Image *ReadOnePNGImage(MngInfo *mng_info,
2064 const ImageInfo *image_info, ExceptionInfo *exception)
2066 /* Read one PNG image */
2068 /* To do: Read the tIME chunk into the date:modify property */
2069 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2082 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2092 ping_interlace_method,
2093 ping_compression_method,
2107 ping_found_sRGB_cHRM,
2112 *volatile pixel_info;
2150 register unsigned char
2170 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2171 png_byte unused_chunks[]=
2173 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2174 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2175 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2176 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2177 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2178 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2179 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2180 /* ignore the APNG chunks */
2181 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2182 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2183 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2188 /* Define these outside of the following "if logging()" block so they will
2189 * show in debuggers.
2192 (void) ConcatenateMagickString(im_vers,
2193 MagickLibVersionText,32);
2194 (void) ConcatenateMagickString(im_vers,
2195 MagickLibAddendum,32);
2198 (void) ConcatenateMagickString(libpng_vers,
2199 PNG_LIBPNG_VER_STRING,32);
2201 (void) ConcatenateMagickString(libpng_runv,
2202 png_get_libpng_ver(NULL),32);
2205 (void) ConcatenateMagickString(zlib_vers,
2208 (void) ConcatenateMagickString(zlib_runv,
2211 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2212 " Enter ReadOnePNGImage()\n"
2213 " IM version = %s\n"
2214 " Libpng version = %s",
2215 im_vers, libpng_vers);
2217 if (logging != MagickFalse)
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:\n"
2258 " image->alpha_trait=%d"
2259 " image->rendering_intent=%d\n"
2260 " image->colorspace=%d\n"
2262 (int) image->alpha_trait, (int) image->rendering_intent,
2263 (int) image->colorspace, image->gamma);
2265 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2267 /* Set to an out-of-range color unless tRNS chunk is present */
2268 transparent_color.red=65537;
2269 transparent_color.green=65537;
2270 transparent_color.blue=65537;
2271 transparent_color.alpha=65537;
2276 num_raw_profiles = 0;
2278 ping_found_cHRM = MagickFalse;
2279 ping_found_gAMA = MagickFalse;
2280 ping_found_iCCP = MagickFalse;
2281 ping_found_sRGB = MagickFalse;
2282 ping_found_sRGB_cHRM = MagickFalse;
2283 ping_preserve_iCCP = MagickFalse;
2287 Allocate the PNG structures
2289 #ifdef PNG_USER_MEM_SUPPORTED
2290 error_info.image=image;
2291 error_info.exception=exception;
2292 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2293 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2294 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2296 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2297 MagickPNGErrorHandler,MagickPNGWarningHandler);
2299 if (ping == (png_struct *) NULL)
2300 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2302 ping_info=png_create_info_struct(ping);
2304 if (ping_info == (png_info *) NULL)
2306 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2307 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2310 end_info=png_create_info_struct(ping);
2312 if (end_info == (png_info *) NULL)
2314 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2315 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2318 pixel_info=(MemoryInfo *) NULL;
2320 if (setjmp(png_jmpbuf(ping)))
2323 PNG image is corrupt.
2325 png_destroy_read_struct(&ping,&ping_info,&end_info);
2327 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2328 UnlockSemaphoreInfo(ping_semaphore);
2331 if (pixel_info != (MemoryInfo *) NULL)
2332 pixel_info=RelinquishVirtualMemory(pixel_info);
2334 if (logging != MagickFalse)
2335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2336 " exit ReadOnePNGImage() with error.");
2338 if (image != (Image *) NULL)
2340 InheritException(exception,exception);
2344 return(GetFirstImageInList(image));
2347 /* { For navigation to end of SETJMP-protected block. Within this
2348 * block, use png_error() instead of Throwing an Exception, to ensure
2349 * that libpng is able to clean up, and that the semaphore is unlocked.
2352 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2353 LockSemaphoreInfo(ping_semaphore);
2356 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2357 /* Allow benign errors */
2358 png_set_benign_errors(ping, 1);
2362 Prepare PNG for reading.
2365 mng_info->image_found++;
2366 png_set_sig_bytes(ping,8);
2368 if (LocaleCompare(image_info->magick,"MNG") == 0)
2370 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2371 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2372 png_set_read_fn(ping,image,png_get_data);
2374 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2375 png_permit_empty_plte(ping,MagickTrue);
2376 png_set_read_fn(ping,image,png_get_data);
2378 mng_info->image=image;
2379 mng_info->bytes_in_read_buffer=0;
2380 mng_info->found_empty_plte=MagickFalse;
2381 mng_info->have_saved_bkgd_index=MagickFalse;
2382 png_set_read_fn(ping,mng_info,mng_get_data);
2388 png_set_read_fn(ping,image,png_get_data);
2394 value=GetImageOption(image_info,"profile:skip");
2396 if (IsOptionMember("ICC",value) == MagickFalse)
2399 value=GetImageOption(image_info,"png:preserve-iCCP");
2402 value=GetImageArtifact(image,"png:preserve-iCCP");
2405 ping_preserve_iCCP=MagickTrue;
2407 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2408 /* Don't let libpng check for ICC/sRGB profile because we're going
2409 * to do that anyway. This feature was added at libpng-1.6.12.
2410 * If logging, go ahead and check and issue a warning as appropriate.
2412 if (logging == MagickFalse)
2413 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2416 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2419 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2423 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2424 /* Ignore unused chunks and all unknown chunks except for vpAg */
2425 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2426 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2428 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2430 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2431 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2432 (int)sizeof(unused_chunks)/5);
2433 /* Callback for other unknown chunks */
2434 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2437 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2438 # if (PNG_LIBPNG_VER >= 10400)
2439 /* Limit the size of the chunk storage cache used for sPLT, text,
2440 * and unknown chunks.
2442 png_set_chunk_cache_max(ping, 32767);
2446 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2447 /* Disable new libpng-1.5.10 feature */
2448 png_set_check_for_invalid_index (ping, 0);
2451 #if (PNG_LIBPNG_VER < 10400)
2452 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2453 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2454 /* Disable thread-unsafe features of pnggccrd */
2455 if (png_access_version_number() >= 10200)
2457 png_uint_32 mmx_disable_mask=0;
2458 png_uint_32 asm_flags;
2460 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2461 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2462 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2463 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2464 asm_flags=png_get_asm_flags(ping);
2465 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2470 png_read_info(ping,ping_info);
2472 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2473 &ping_bit_depth,&ping_color_type,
2474 &ping_interlace_method,&ping_compression_method,
2475 &ping_filter_method);
2477 ping_file_depth = ping_bit_depth;
2479 /* Swap bytes if requested */
2480 if (ping_file_depth == 16)
2485 value=GetImageOption(image_info,"png:swap-bytes");
2488 value=GetImageArtifact(image,"png:swap-bytes");
2494 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2499 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2500 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2502 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2503 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2506 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2509 (void) png_get_bKGD(ping, ping_info, &ping_background);
2511 if (ping_bit_depth < 8)
2513 png_set_packing(ping);
2517 image->depth=ping_bit_depth;
2518 image->depth=GetImageQuantumDepth(image,MagickFalse);
2519 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2521 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2522 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2524 image->rendering_intent=UndefinedIntent;
2525 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2527 (void) ResetMagickMemory(&image->chromaticity,0,
2528 sizeof(image->chromaticity));
2531 if (logging != MagickFalse)
2533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2534 " PNG width: %.20g, height: %.20g\n"
2535 " PNG color_type: %d, bit_depth: %d\n"
2536 " PNG compression_method: %d\n"
2537 " PNG interlace_method: %d, filter_method: %d",
2538 (double) ping_width, (double) ping_height,
2539 ping_color_type, ping_bit_depth,
2540 ping_compression_method,
2541 ping_interlace_method,ping_filter_method);
2545 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2547 ping_found_iCCP=MagickTrue;
2548 if (logging != MagickFalse)
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550 " Found PNG iCCP chunk.");
2553 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2555 ping_found_gAMA=MagickTrue;
2556 if (logging != MagickFalse)
2557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2558 " Found PNG gAMA chunk.");
2561 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2563 ping_found_cHRM=MagickTrue;
2564 if (logging != MagickFalse)
2565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2566 " Found PNG cHRM chunk.");
2569 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2572 ping_found_sRGB=MagickTrue;
2573 if (logging != MagickFalse)
2574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575 " Found PNG sRGB chunk.");
2578 #ifdef PNG_READ_iCCP_SUPPORTED
2579 if (ping_found_iCCP !=MagickTrue &&
2580 ping_found_sRGB != MagickTrue &&
2581 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2583 ping_found_iCCP=MagickTrue;
2584 if (logging != MagickFalse)
2585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586 " Found PNG iCCP chunk.");
2589 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2594 #if (PNG_LIBPNG_VER < 10500)
2608 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2611 if (profile_length != 0)
2616 if (logging != MagickFalse)
2617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2618 " Reading PNG iCCP chunk.");
2620 profile=BlobToStringInfo(info,profile_length);
2622 if (profile == (StringInfo *) NULL)
2624 png_warning(ping, "ICC profile is NULL");
2625 profile=DestroyStringInfo(profile);
2629 if (ping_preserve_iCCP == MagickFalse)
2643 length=(png_uint_32) GetStringInfoLength(profile);
2645 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2647 if (length == sRGB_info[icheck].len)
2651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2652 " Got a %lu-byte ICC profile (potentially sRGB)",
2653 (unsigned long) length);
2655 data=GetStringInfoDatum(profile);
2656 profile_crc=crc32(0,data,length);
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2659 " with crc=%8x",(unsigned int) profile_crc);
2663 if (profile_crc == sRGB_info[icheck].crc)
2665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2666 " It is sRGB with rendering intent = %s",
2667 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2668 sRGB_info[icheck].intent));
2669 if (image->rendering_intent==UndefinedIntent)
2671 image->rendering_intent=
2672 Magick_RenderingIntent_from_PNG_RenderingIntent(
2673 sRGB_info[icheck].intent);
2679 if (sRGB_info[icheck].len == 0)
2681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2682 " Got a %lu-byte ICC profile not recognized as sRGB",
2683 (unsigned long) length);
2684 (void) SetImageProfile(image,"icc",profile,exception);
2687 else /* Preserve-iCCP */
2689 (void) SetImageProfile(image,"icc",profile,exception);
2692 profile=DestroyStringInfo(profile);
2698 #if defined(PNG_READ_sRGB_SUPPORTED)
2700 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2703 if (png_get_sRGB(ping,ping_info,&intent))
2705 if (image->rendering_intent == UndefinedIntent)
2706 image->rendering_intent=
2707 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2715 else if (mng_info->have_global_srgb)
2717 if (image->rendering_intent == UndefinedIntent)
2718 image->rendering_intent=
2719 Magick_RenderingIntent_from_PNG_RenderingIntent
2720 (mng_info->global_srgb_intent);
2727 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2728 if (mng_info->have_global_gama)
2729 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2731 if (png_get_gAMA(ping,ping_info,&file_gamma))
2733 image->gamma=(float) file_gamma;
2734 if (logging != MagickFalse)
2735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2736 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2740 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2742 if (mng_info->have_global_chrm != MagickFalse)
2744 (void) png_set_cHRM(ping,ping_info,
2745 mng_info->global_chrm.white_point.x,
2746 mng_info->global_chrm.white_point.y,
2747 mng_info->global_chrm.red_primary.x,
2748 mng_info->global_chrm.red_primary.y,
2749 mng_info->global_chrm.green_primary.x,
2750 mng_info->global_chrm.green_primary.y,
2751 mng_info->global_chrm.blue_primary.x,
2752 mng_info->global_chrm.blue_primary.y);
2756 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2758 (void) png_get_cHRM(ping,ping_info,
2759 &image->chromaticity.white_point.x,
2760 &image->chromaticity.white_point.y,
2761 &image->chromaticity.red_primary.x,
2762 &image->chromaticity.red_primary.y,
2763 &image->chromaticity.green_primary.x,
2764 &image->chromaticity.green_primary.y,
2765 &image->chromaticity.blue_primary.x,
2766 &image->chromaticity.blue_primary.y);
2768 ping_found_cHRM=MagickTrue;
2770 if (image->chromaticity.red_primary.x>0.6399f &&
2771 image->chromaticity.red_primary.x<0.6401f &&
2772 image->chromaticity.red_primary.y>0.3299f &&
2773 image->chromaticity.red_primary.y<0.3301f &&
2774 image->chromaticity.green_primary.x>0.2999f &&
2775 image->chromaticity.green_primary.x<0.3001f &&
2776 image->chromaticity.green_primary.y>0.5999f &&
2777 image->chromaticity.green_primary.y<0.6001f &&
2778 image->chromaticity.blue_primary.x>0.1499f &&
2779 image->chromaticity.blue_primary.x<0.1501f &&
2780 image->chromaticity.blue_primary.y>0.0599f &&
2781 image->chromaticity.blue_primary.y<0.0601f &&
2782 image->chromaticity.white_point.x>0.3126f &&
2783 image->chromaticity.white_point.x<0.3128f &&
2784 image->chromaticity.white_point.y>0.3289f &&
2785 image->chromaticity.white_point.y<0.3291f)
2786 ping_found_sRGB_cHRM=MagickTrue;
2789 if (image->rendering_intent != UndefinedIntent)
2791 if (ping_found_sRGB != MagickTrue &&
2792 (ping_found_gAMA != MagickTrue ||
2793 (image->gamma > .45 && image->gamma < .46)) &&
2794 (ping_found_cHRM != MagickTrue ||
2795 ping_found_sRGB_cHRM != MagickFalse) &&
2796 ping_found_iCCP != MagickTrue)
2798 png_set_sRGB(ping,ping_info,
2799 Magick_RenderingIntent_to_PNG_RenderingIntent
2800 (image->rendering_intent));
2801 file_gamma=1.000f/2.200f;
2802 ping_found_sRGB=MagickTrue;
2803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2804 " Setting sRGB as if in input");
2808 #if defined(PNG_oFFs_SUPPORTED)
2809 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2811 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2812 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2814 if (logging != MagickFalse)
2815 if (image->page.x || image->page.y)
2816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2817 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2818 image->page.x,(double) image->page.y);
2821 #if defined(PNG_pHYs_SUPPORTED)
2822 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2824 if (mng_info->have_global_phys)
2826 png_set_pHYs(ping,ping_info,
2827 mng_info->global_x_pixels_per_unit,
2828 mng_info->global_y_pixels_per_unit,
2829 mng_info->global_phys_unit_type);
2836 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2839 Set image resolution.
2841 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2843 image->resolution.x=(double) x_resolution;
2844 image->resolution.y=(double) y_resolution;
2846 if (unit_type == PNG_RESOLUTION_METER)
2848 image->units=PixelsPerCentimeterResolution;
2849 image->resolution.x=(double) x_resolution/100.0;
2850 image->resolution.y=(double) y_resolution/100.0;
2853 if (logging != MagickFalse)
2854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2855 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2856 (double) x_resolution,(double) y_resolution,unit_type);
2860 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2865 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2867 if ((number_colors == 0) &&
2868 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2870 if (mng_info->global_plte_length)
2872 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2873 (int) mng_info->global_plte_length);
2875 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2877 if (mng_info->global_trns_length)
2880 "global tRNS has more entries than global PLTE");
2884 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2885 (int) mng_info->global_trns_length,NULL);
2888 #ifdef PNG_READ_bKGD_SUPPORTED
2890 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2891 mng_info->have_saved_bkgd_index ||
2893 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2898 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2899 if (mng_info->have_saved_bkgd_index)
2900 background.index=mng_info->saved_bkgd_index;
2902 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2903 background.index=ping_background->index;
2905 background.red=(png_uint_16)
2906 mng_info->global_plte[background.index].red;
2908 background.green=(png_uint_16)
2909 mng_info->global_plte[background.index].green;
2911 background.blue=(png_uint_16)
2912 mng_info->global_plte[background.index].blue;
2914 background.gray=(png_uint_16)
2915 mng_info->global_plte[background.index].green;
2917 png_set_bKGD(ping,ping_info,&background);
2922 png_error(ping,"No global PLTE in file");
2926 #ifdef PNG_READ_bKGD_SUPPORTED
2927 if (mng_info->have_global_bkgd &&
2928 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2929 image->background_color=mng_info->mng_global_bkgd;
2931 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2936 /* Set image background color.
2937 * Scale background components to 16-bit, then scale
2943 if (ping_file_depth == 1)
2946 else if (ping_file_depth == 2)
2949 else if (ping_file_depth == 4)
2952 if (ping_file_depth <= 8)
2955 ping_background->red *= bkgd_scale;
2956 ping_background->green *= bkgd_scale;
2957 ping_background->blue *= bkgd_scale;
2959 if (logging != MagickFalse)
2961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2964 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2965 ping_background->red,ping_background->green,
2966 ping_background->blue,
2967 bkgd_scale,ping_background->red,
2968 ping_background->green,ping_background->blue);
2971 image->background_color.red=
2972 ScaleShortToQuantum(ping_background->red);
2974 image->background_color.green=
2975 ScaleShortToQuantum(ping_background->green);
2977 image->background_color.blue=
2978 ScaleShortToQuantum(ping_background->blue);
2980 image->background_color.alpha=OpaqueAlpha;
2982 if (logging != MagickFalse)
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984 " image->background_color=(%.20g,%.20g,%.20g).",
2985 (double) image->background_color.red,
2986 (double) image->background_color.green,
2987 (double) image->background_color.blue);
2989 #endif /* PNG_READ_bKGD_SUPPORTED */
2991 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2994 Image has a tRNS chunk.
3002 if (logging != MagickFalse)
3003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3004 " Reading PNG tRNS chunk.");
3006 max_sample = (int) ((one << ping_file_depth) - 1);
3008 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3009 (int)ping_trans_color->gray > max_sample) ||
3010 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3011 ((int)ping_trans_color->red > max_sample ||
3012 (int)ping_trans_color->green > max_sample ||
3013 (int)ping_trans_color->blue > max_sample)))
3015 if (logging != MagickFalse)
3016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3017 " Ignoring PNG tRNS chunk with out-of-range sample.");
3018 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3019 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3020 image->alpha_trait=UndefinedPixelTrait;
3027 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3029 /* Scale transparent_color to short */
3030 transparent_color.red= scale_to_short*ping_trans_color->red;
3031 transparent_color.green= scale_to_short*ping_trans_color->green;
3032 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3033 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3035 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3037 if (logging != MagickFalse)
3039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3040 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3041 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3044 transparent_color.red=transparent_color.alpha;
3045 transparent_color.green=transparent_color.alpha;
3046 transparent_color.blue=transparent_color.alpha;
3050 #if defined(PNG_READ_sBIT_SUPPORTED)
3051 if (mng_info->have_global_sbit)
3053 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3054 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3057 num_passes=png_set_interlace_handling(ping);
3059 png_read_update_info(ping,ping_info);
3061 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3064 Initialize image structure.
3066 mng_info->image_box.left=0;
3067 mng_info->image_box.right=(ssize_t) ping_width;
3068 mng_info->image_box.top=0;
3069 mng_info->image_box.bottom=(ssize_t) ping_height;
3070 if (mng_info->mng_type == 0)
3072 mng_info->mng_width=ping_width;
3073 mng_info->mng_height=ping_height;
3074 mng_info->frame=mng_info->image_box;
3075 mng_info->clip=mng_info->image_box;
3080 image->page.y=mng_info->y_off[mng_info->object_id];
3083 image->compression=ZipCompression;
3084 image->columns=ping_width;
3085 image->rows=ping_height;
3087 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3088 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3091 image_gamma = image->gamma;
3093 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3094 " image->gamma=%f",(float) image_gamma);
3096 if (image_gamma > 0.75)
3098 /* Set image->rendering_intent to Undefined,
3099 * image->colorspace to GRAY, and reset image->chromaticity.
3101 image->intensity = Rec709LuminancePixelIntensityMethod;
3102 SetImageColorspace(image,GRAYColorspace,exception);
3103 image->gamma = image_gamma;
3107 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3108 " image->colorspace=%d",(int) image->colorspace);
3110 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3111 ((int) ping_bit_depth < 16 &&
3112 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3117 image->storage_class=PseudoClass;
3119 image->colors=one << ping_file_depth;
3120 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3121 if (image->colors > 256)
3124 if (image->colors > 65536L)
3125 image->colors=65536L;
3127 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3132 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3133 image->colors=(size_t) number_colors;
3135 if (logging != MagickFalse)
3136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3137 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3141 if (image->storage_class == PseudoClass)
3144 Initialize image colormap.
3146 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3147 png_error(ping,"Memory allocation failed");
3149 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3154 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3156 for (i=0; i < (ssize_t) number_colors; i++)
3158 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3159 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3160 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3163 for ( ; i < (ssize_t) image->colors; i++)
3165 image->colormap[i].red=0;
3166 image->colormap[i].green=0;
3167 image->colormap[i].blue=0;
3176 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3181 for (i=0; i < (ssize_t) image->colors; i++)
3183 image->colormap[i].red=(Quantum) (i*scale);
3184 image->colormap[i].green=(Quantum) (i*scale);
3185 image->colormap[i].blue=(Quantum) (i*scale);
3190 /* Set some properties for reporting by "identify" */
3195 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3196 ping_interlace_method in value */
3198 (void) FormatLocaleString(msg,MaxTextExtent,
3199 "%d, %d",(int) ping_width, (int) ping_height);
3200 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3202 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3203 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3205 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3206 (int) ping_color_type,
3207 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3208 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3210 if (ping_interlace_method == 0)
3212 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3213 (int) ping_interlace_method);
3215 else if (ping_interlace_method == 1)
3217 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3218 (int) ping_interlace_method);
3222 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3223 (int) ping_interlace_method);
3225 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3227 if (number_colors != 0)
3229 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3230 (int) number_colors);
3231 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3237 Read image scanlines.
3239 if (image->delay != 0)
3240 mng_info->scenes_found++;
3242 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3243 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3244 (image_info->first_scene+image_info->number_scenes))))
3246 /* This happens later in non-ping decodes */
3247 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3248 image->storage_class=DirectClass;
3250 if (logging != MagickFalse)
3251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3252 " Skipping PNG image data for scene %.20g",(double)
3253 mng_info->scenes_found-1);
3254 png_destroy_read_struct(&ping,&ping_info,&end_info);
3256 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3257 UnlockSemaphoreInfo(ping_semaphore);
3260 if (logging != MagickFalse)
3261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3262 " exit ReadOnePNGImage().");
3267 if (logging != MagickFalse)
3268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3269 " Reading PNG IDAT chunk(s)");
3272 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3273 sizeof(*ping_pixels));
3275 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3277 if (pixel_info == (MemoryInfo *) NULL)
3278 png_error(ping,"Memory allocation failed");
3279 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3281 if (logging != MagickFalse)
3282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3283 " Converting PNG pixels to pixel packets");
3285 Convert PNG pixels to pixel packets.
3287 quantum_info=AcquireQuantumInfo(image_info,image);
3289 if (quantum_info == (QuantumInfo *) NULL)
3290 png_error(ping,"Failed to allocate quantum_info");
3292 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3297 found_transparent_pixel;
3299 found_transparent_pixel=MagickFalse;
3301 if (image->storage_class == DirectClass)
3303 for (pass=0; pass < num_passes; pass++)
3306 Convert image to DirectClass pixel packets.
3308 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3309 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3310 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3311 BlendPixelTrait : UndefinedPixelTrait;
3313 for (y=0; y < (ssize_t) image->rows; y++)
3316 row_offset=ping_rowbytes*y;
3321 png_read_row(ping,ping_pixels+row_offset,NULL);
3323 if (pass < num_passes-1)
3326 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3328 if (q == (Quantum *) NULL)
3331 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3332 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3333 GrayQuantum,ping_pixels+row_offset,exception);
3335 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3336 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3337 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3339 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3340 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3341 RGBAQuantum,ping_pixels+row_offset,exception);
3343 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3344 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3345 IndexQuantum,ping_pixels+row_offset,exception);
3347 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3348 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3349 RGBQuantum,ping_pixels+row_offset,exception);
3351 if (found_transparent_pixel == MagickFalse)
3353 /* Is there a transparent pixel in the row? */
3354 if (y== 0 && logging != MagickFalse)
3355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3356 " Looking for cheap transparent pixel");
3358 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3360 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3361 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3362 (GetPixelAlpha(image,q) != OpaqueAlpha))
3364 if (logging != MagickFalse)
3365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368 found_transparent_pixel = MagickTrue;
3371 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3372 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3373 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3374 transparent_color.red &&
3375 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3376 transparent_color.green &&
3377 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3378 transparent_color.blue))
3380 if (logging != MagickFalse)
3381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 found_transparent_pixel = MagickTrue;
3386 q+=GetPixelChannels(image);
3390 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3392 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3395 if (status == MagickFalse)
3398 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3402 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3404 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3405 if (status == MagickFalse)
3411 else /* image->storage_class != DirectClass */
3413 for (pass=0; pass < num_passes; pass++)
3422 Convert grayscale image to PseudoClass pixel packets.
3424 if (logging != MagickFalse)
3425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3426 " Converting grayscale pixels to pixel packets");
3428 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3429 BlendPixelTrait : UndefinedPixelTrait;
3431 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3432 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3433 sizeof(*quantum_scanline));
3435 if (quantum_scanline == (Quantum *) NULL)
3436 png_error(ping,"Memory allocation failed");
3438 for (y=0; y < (ssize_t) image->rows; y++)
3444 row_offset=ping_rowbytes*y;
3449 png_read_row(ping,ping_pixels+row_offset,NULL);
3451 if (pass < num_passes-1)
3454 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3456 if (q == (Quantum *) NULL)
3459 p=ping_pixels+row_offset;
3462 switch (ping_bit_depth)
3467 if (ping_color_type == 4)
3468 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3472 alpha=ScaleCharToQuantum((unsigned char)*p++);
3474 SetPixelAlpha(image,alpha,q);
3476 if (alpha != OpaqueAlpha)
3477 found_transparent_pixel = MagickTrue;
3479 q+=GetPixelChannels(image);
3483 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3491 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3493 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3497 if (image->colors > 256)
3498 quantum=((*p++) << 8);
3504 *r=ScaleShortToQuantum(quantum);
3507 if (ping_color_type == 4)
3509 if (image->colors > 256)
3510 quantum=((*p++) << 8);
3516 alpha=ScaleShortToQuantum(quantum);
3517 SetPixelAlpha(image,alpha,q);
3519 if (alpha != OpaqueAlpha)
3520 found_transparent_pixel = MagickTrue;
3522 q+=GetPixelChannels(image);
3525 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3527 p++; /* strip low byte */
3529 if (ping_color_type == 4)
3531 SetPixelAlpha(image,*p++,q);
3533 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3534 found_transparent_pixel = MagickTrue;
3537 q+=GetPixelChannels(image);
3550 Transfer image scanline.
3554 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3556 if (q == (Quantum *) NULL)
3558 for (x=0; x < (ssize_t) image->columns; x++)
3560 SetPixelIndex(image,*r++,q);
3561 q+=GetPixelChannels(image);
3564 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3567 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3569 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3572 if (status == MagickFalse)
3577 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3579 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3581 if (status == MagickFalse)
3585 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3588 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3589 UndefinedPixelTrait;
3591 if (logging != MagickFalse)
3593 if (found_transparent_pixel != MagickFalse)
3594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3595 " Found transparent pixel");
3598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3599 " No transparent pixel was found");
3601 ping_color_type&=0x03;
3606 if (quantum_info != (QuantumInfo *) NULL)
3607 quantum_info=DestroyQuantumInfo(quantum_info);
3609 if (image->storage_class == PseudoClass)
3614 alpha_trait=image->alpha_trait;
3615 image->alpha_trait=UndefinedPixelTrait;
3616 (void) SyncImage(image,exception);
3617 image->alpha_trait=alpha_trait;
3620 png_read_end(ping,end_info);
3622 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3623 (ssize_t) image_info->first_scene && image->delay != 0)
3625 png_destroy_read_struct(&ping,&ping_info,&end_info);
3626 pixel_info=RelinquishVirtualMemory(pixel_info);
3628 (void) SetImageBackgroundColor(image,exception);
3629 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3630 UnlockSemaphoreInfo(ping_semaphore);
3632 if (logging != MagickFalse)
3633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3634 " exit ReadOnePNGImage() early.");
3638 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3644 Image has a transparent background.
3646 storage_class=image->storage_class;
3647 image->alpha_trait=BlendPixelTrait;
3649 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3651 if (storage_class == PseudoClass)
3653 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3655 for (x=0; x < ping_num_trans; x++)
3657 image->colormap[x].alpha_trait=BlendPixelTrait;
3658 image->colormap[x].alpha =
3659 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3663 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3665 for (x=0; x < (int) image->colors; x++)
3667 if (ScaleQuantumToShort(image->colormap[x].red) ==
3668 transparent_color.alpha)
3670 image->colormap[x].alpha_trait=BlendPixelTrait;
3671 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3675 (void) SyncImage(image,exception);
3678 #if 1 /* Should have already been done above, but glennrp problem P10
3683 for (y=0; y < (ssize_t) image->rows; y++)
3685 image->storage_class=storage_class;
3686 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3688 if (q == (Quantum *) NULL)
3692 /* Caution: on a Q8 build, this does not distinguish between
3693 * 16-bit colors that differ only in the low byte
3695 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3697 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3698 transparent_color.red &&
3699 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3700 transparent_color.green &&
3701 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3702 transparent_color.blue)
3704 SetPixelAlpha(image,TransparentAlpha,q);
3707 #if 0 /* I have not found a case where this is needed. */
3710 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3714 q+=GetPixelChannels(image);
3717 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3723 image->storage_class=DirectClass;
3726 for (j = 0; j < 2; j++)
3729 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3730 MagickTrue : MagickFalse;
3732 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3733 MagickTrue : MagickFalse;
3735 if (status != MagickFalse)
3736 for (i=0; i < (ssize_t) num_text; i++)
3738 /* Check for a profile */
3740 if (logging != MagickFalse)
3741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742 " Reading PNG text chunk");
3744 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3749 value=GetImageOption(image_info,"profile:skip");
3751 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3753 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3756 if (logging != MagickFalse)
3757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3758 " Read raw profile %s",text[i].key+17);
3762 if (logging != MagickFalse)
3763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3764 " Skipping raw profile %s",text[i].key+17);
3773 length=text[i].text_length;
3774 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3776 if (value == (char *) NULL)
3778 png_error(ping,"Memory allocation failed");
3782 (void) ConcatenateMagickString(value,text[i].text,length+2);
3784 /* Don't save "density" or "units" property if we have a pHYs
3787 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3788 (LocaleCompare(text[i].key,"density") != 0 &&
3789 LocaleCompare(text[i].key,"units") != 0))
3790 (void) SetImageProperty(image,text[i].key,value,exception);
3792 if (logging != MagickFalse)
3794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3797 (unsigned long) length,
3801 value=DestroyString(value);
3804 num_text_total += num_text;
3807 #ifdef MNG_OBJECT_BUFFERS
3809 Store the object if necessary.
3811 if (object_id && !mng_info->frozen[object_id])
3813 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3816 create a new object buffer.
3818 mng_info->ob[object_id]=(MngBuffer *)
3819 AcquireMagickMemory(sizeof(MngBuffer));
3821 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3823 mng_info->ob[object_id]->image=(Image *) NULL;
3824 mng_info->ob[object_id]->reference_count=1;
3828 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3829 mng_info->ob[object_id]->frozen)
3831 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3832 png_error(ping,"Memory allocation failed");
3834 if (mng_info->ob[object_id]->frozen)
3835 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3841 if (mng_info->ob[object_id]->image != (Image *) NULL)
3842 mng_info->ob[object_id]->image=DestroyImage
3843 (mng_info->ob[object_id]->image);
3845 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3848 if (mng_info->ob[object_id]->image != (Image *) NULL)
3849 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3852 png_error(ping, "Cloning image for object buffer failed");
3854 if (ping_width > 250000L || ping_height > 250000L)
3855 png_error(ping,"PNG Image dimensions are too large.");
3857 mng_info->ob[object_id]->width=ping_width;
3858 mng_info->ob[object_id]->height=ping_height;
3859 mng_info->ob[object_id]->color_type=ping_color_type;
3860 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3861 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3862 mng_info->ob[object_id]->compression_method=
3863 ping_compression_method;
3864 mng_info->ob[object_id]->filter_method=ping_filter_method;
3866 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3872 Copy the PLTE to the object buffer.
3874 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3875 mng_info->ob[object_id]->plte_length=number_colors;
3877 for (i=0; i < number_colors; i++)
3879 mng_info->ob[object_id]->plte[i]=plte[i];
3884 mng_info->ob[object_id]->plte_length=0;
3889 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3890 * alpha or if a valid tRNS chunk is present, no matter whether there
3891 * is actual transparency present.
3893 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3894 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3895 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3896 BlendPixelTrait : UndefinedPixelTrait;
3898 #if 0 /* I'm not sure what's wrong here but it does not work. */
3899 if (image->alpha_trait == BlendPixelTrait)
3901 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3902 (void) SetImageType(image,GrayscaleMatteType,exception);
3904 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3905 (void) SetImageType(image,PaletteMatteType,exception);
3908 (void) SetImageType(image,TrueColorMatteType,exception);
3913 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3914 (void) SetImageType(image,GrayscaleType,exception);
3916 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3917 (void) SetImageType(image,PaletteType,exception);
3920 (void) SetImageType(image,TrueColorType,exception);
3924 /* Set more properties for identify to retrieve */
3929 if (num_text_total != 0)
3931 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3932 (void) FormatLocaleString(msg,MaxTextExtent,
3933 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3934 (void) SetImageProperty(image,"png:text",msg,
3938 if (num_raw_profiles != 0)
3940 (void) FormatLocaleString(msg,MaxTextExtent,
3941 "%d were found", num_raw_profiles);
3942 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3946 if (ping_found_cHRM != MagickFalse)
3948 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3949 "chunk was found (see Chromaticity, above)");
3950 (void) SetImageProperty(image,"png:cHRM",msg,
3954 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3956 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3957 "chunk was found (see Background color, above)");
3958 (void) SetImageProperty(image,"png:bKGD",msg,
3962 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3965 #if defined(PNG_iCCP_SUPPORTED)
3966 if (ping_found_iCCP != MagickFalse)
3967 (void) SetImageProperty(image,"png:iCCP",msg,
3971 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3972 (void) SetImageProperty(image,"png:tRNS",msg,
3975 #if defined(PNG_sRGB_SUPPORTED)
3976 if (ping_found_sRGB != MagickFalse)
3978 (void) FormatLocaleString(msg,MaxTextExtent,
3981 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3982 (void) SetImageProperty(image,"png:sRGB",msg,
3987 if (ping_found_gAMA != MagickFalse)
3989 (void) FormatLocaleString(msg,MaxTextExtent,
3990 "gamma=%.8g (See Gamma, above)",
3992 (void) SetImageProperty(image,"png:gAMA",msg,
3996 #if defined(PNG_pHYs_SUPPORTED)
3997 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3999 (void) FormatLocaleString(msg,MaxTextExtent,
4000 "x_res=%.10g, y_res=%.10g, units=%d",
4001 (double) x_resolution,(double) y_resolution, unit_type);
4002 (void) SetImageProperty(image,"png:pHYs",msg,
4007 #if defined(PNG_oFFs_SUPPORTED)
4008 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4010 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
4011 (double) image->page.x,(double) image->page.y);
4012 (void) SetImageProperty(image,"png:oFFs",msg,
4017 if ((image->page.width != 0 && image->page.width != image->columns) ||
4018 (image->page.height != 0 && image->page.height != image->rows))
4020 (void) FormatLocaleString(msg,MaxTextExtent,
4021 "width=%.20g, height=%.20g",
4022 (double) image->page.width,(double) image->page.height);
4023 (void) SetImageProperty(image,"png:vpAg",msg,
4029 Relinquish resources.
4031 png_destroy_read_struct(&ping,&ping_info,&end_info);
4033 pixel_info=RelinquishVirtualMemory(pixel_info);
4035 if (logging != MagickFalse)
4036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4037 " exit ReadOnePNGImage()");
4039 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4040 UnlockSemaphoreInfo(ping_semaphore);
4043 /* } for navigation to beginning of SETJMP-protected block, revert to
4044 * Throwing an Exception when an error occurs.
4049 /* end of reading one PNG image */
4052 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4067 magic_number[MaxTextExtent];
4075 assert(image_info != (const ImageInfo *) NULL);
4076 assert(image_info->signature == MagickSignature);
4078 if (image_info->debug != MagickFalse)
4079 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4080 image_info->filename);
4082 assert(exception != (ExceptionInfo *) NULL);
4083 assert(exception->signature == MagickSignature);
4084 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4085 image=AcquireImage(image_info,exception);
4086 mng_info=(MngInfo *) NULL;
4087 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4089 if (status == MagickFalse)
4090 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4093 Verify PNG signature.
4095 count=ReadBlob(image,8,(unsigned char *) magic_number);
4097 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4098 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4101 Allocate a MngInfo structure.
4103 have_mng_structure=MagickFalse;
4104 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4106 if (mng_info == (MngInfo *) NULL)
4107 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4110 Initialize members of the MngInfo structure.
4112 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4113 mng_info->image=image;
4114 have_mng_structure=MagickTrue;
4117 image=ReadOnePNGImage(mng_info,image_info,exception);
4118 MngInfoFreeStruct(mng_info,&have_mng_structure);
4120 if (image == (Image *) NULL)
4122 if (previous != (Image *) NULL)
4124 if (previous->signature != MagickSignature)
4125 ThrowReaderException(CorruptImageError,"CorruptImage");
4127 (void) CloseBlob(previous);
4128 (void) DestroyImageList(previous);
4131 if (logging != MagickFalse)
4132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4133 "exit ReadPNGImage() with error");
4135 return((Image *) NULL);
4138 (void) CloseBlob(image);
4140 if ((image->columns == 0) || (image->rows == 0))
4142 if (logging != MagickFalse)
4143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4144 "exit ReadPNGImage() with error.");
4146 ThrowReaderException(CorruptImageError,"CorruptImage");
4149 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4150 ((image->gamma < .45) || (image->gamma > .46)) &&
4151 !(image->chromaticity.red_primary.x>0.6399f &&
4152 image->chromaticity.red_primary.x<0.6401f &&
4153 image->chromaticity.red_primary.y>0.3299f &&
4154 image->chromaticity.red_primary.y<0.3301f &&
4155 image->chromaticity.green_primary.x>0.2999f &&
4156 image->chromaticity.green_primary.x<0.3001f &&
4157 image->chromaticity.green_primary.y>0.5999f &&
4158 image->chromaticity.green_primary.y<0.6001f &&
4159 image->chromaticity.blue_primary.x>0.1499f &&
4160 image->chromaticity.blue_primary.x<0.1501f &&
4161 image->chromaticity.blue_primary.y>0.0599f &&
4162 image->chromaticity.blue_primary.y<0.0601f &&
4163 image->chromaticity.white_point.x>0.3126f &&
4164 image->chromaticity.white_point.x<0.3128f &&
4165 image->chromaticity.white_point.y>0.3289f &&
4166 image->chromaticity.white_point.y<0.3291f))
4167 SetImageColorspace(image,RGBColorspace,exception);
4169 if (logging != MagickFalse)
4170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4171 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4172 (double) image->page.width,(double) image->page.height,
4173 (double) image->page.x,(double) image->page.y);
4175 if (logging != MagickFalse)
4176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4183 #if defined(JNG_SUPPORTED)
4185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4189 % R e a d O n e J N G I m a g e %
4193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4195 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4196 % (minus the 8-byte signature) and returns it. It allocates the memory
4197 % necessary for the new Image structure and returns a pointer to the new
4200 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4202 % The format of the ReadOneJNGImage method is:
4204 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4205 % ExceptionInfo *exception)
4207 % A description of each parameter follows:
4209 % o mng_info: Specifies a pointer to a MngInfo structure.
4211 % o image_info: the image info.
4213 % o exception: return any errors or warnings in this structure.
4216 static Image *ReadOneJNGImage(MngInfo *mng_info,
4217 const ImageInfo *image_info, ExceptionInfo *exception)
4244 jng_image_sample_depth,
4245 jng_image_compression_method,
4246 jng_image_interlace_method,
4247 jng_alpha_sample_depth,
4248 jng_alpha_compression_method,
4249 jng_alpha_filter_method,
4250 jng_alpha_interlace_method;
4252 register const Quantum
4262 register unsigned char
4272 jng_alpha_compression_method=0;
4273 jng_alpha_sample_depth=8;
4277 alpha_image=(Image *) NULL;
4278 color_image=(Image *) NULL;
4279 alpha_image_info=(ImageInfo *) NULL;
4280 color_image_info=(ImageInfo *) NULL;
4282 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4283 " Enter ReadOneJNGImage()");
4285 image=mng_info->image;
4287 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4290 Allocate next image structure.
4292 if (logging != MagickFalse)
4293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4294 " AcquireNextImage()");
4296 AcquireNextImage(image_info,image,exception);
4298 if (GetNextImageInList(image) == (Image *) NULL)
4299 return((Image *) NULL);
4301 image=SyncNextImageInList(image);
4303 mng_info->image=image;
4306 Signature bytes have already been read.
4309 read_JSEP=MagickFalse;
4310 reading_idat=MagickFalse;
4314 type[MaxTextExtent];
4323 Read a new JNG chunk.
4325 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4326 2*GetBlobSize(image));
4328 if (status == MagickFalse)
4332 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4333 length=ReadBlobMSBLong(image);
4334 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4336 if (logging != MagickFalse)
4337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4338 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4339 type[0],type[1],type[2],type[3],(double) length);
4341 if (length > PNG_UINT_31_MAX || count == 0)
4342 ThrowReaderException(CorruptImageError,"CorruptImage");
4345 chunk=(unsigned char *) NULL;
4349 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4351 if (chunk == (unsigned char *) NULL)
4352 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4354 for (i=0; i < (ssize_t) length; i++)
4355 chunk[i]=(unsigned char) ReadBlobByte(image);
4360 (void) ReadBlobMSBLong(image); /* read crc word */
4362 if (memcmp(type,mng_JHDR,4) == 0)
4366 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4367 (p[2] << 8) | p[3]);
4368 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4369 (p[6] << 8) | p[7]);
4370 if ((jng_width == 0) || (jng_height == 0))
4371 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4372 jng_color_type=p[8];
4373 jng_image_sample_depth=p[9];
4374 jng_image_compression_method=p[10];
4375 jng_image_interlace_method=p[11];
4377 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4380 jng_alpha_sample_depth=p[12];
4381 jng_alpha_compression_method=p[13];
4382 jng_alpha_filter_method=p[14];
4383 jng_alpha_interlace_method=p[15];
4385 if (logging != MagickFalse)
4387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4388 " jng_width: %16lu, jng_height: %16lu\n"
4389 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4390 " jng_image_compression_method:%3d",
4391 (unsigned long) jng_width, (unsigned long) jng_height,
4392 jng_color_type, jng_image_sample_depth,
4393 jng_image_compression_method);
4395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396 " jng_image_interlace_method: %3d"
4397 " jng_alpha_sample_depth: %3d",
4398 jng_image_interlace_method,
4399 jng_alpha_sample_depth);
4401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4402 " jng_alpha_compression_method:%3d\n"
4403 " jng_alpha_filter_method: %3d\n"
4404 " jng_alpha_interlace_method: %3d",
4405 jng_alpha_compression_method,
4406 jng_alpha_filter_method,
4407 jng_alpha_interlace_method);
4412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4418 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4419 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4420 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4423 o create color_image
4424 o open color_blob, attached to color_image
4425 o if (color type has alpha)
4426 open alpha_blob, attached to alpha_image
4429 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4431 if (color_image_info == (ImageInfo *) NULL)
4432 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4434 GetImageInfo(color_image_info);
4435 color_image=AcquireImage(color_image_info,exception);
4437 if (color_image == (Image *) NULL)
4438 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4440 if (logging != MagickFalse)
4441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4442 " Creating color_blob.");
4444 (void) AcquireUniqueFilename(color_image->filename);
4445 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4448 if (status == MagickFalse)
4449 return((Image *) NULL);
4451 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4453 alpha_image_info=(ImageInfo *)
4454 AcquireMagickMemory(sizeof(ImageInfo));
4456 if (alpha_image_info == (ImageInfo *) NULL)
4457 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4459 GetImageInfo(alpha_image_info);
4460 alpha_image=AcquireImage(alpha_image_info,exception);
4462 if (alpha_image == (Image *) NULL)
4464 alpha_image=DestroyImage(alpha_image);
4465 ThrowReaderException(ResourceLimitError,
4466 "MemoryAllocationFailed");
4469 if (logging != MagickFalse)
4470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4471 " Creating alpha_blob.");
4473 (void) AcquireUniqueFilename(alpha_image->filename);
4474 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4477 if (status == MagickFalse)
4478 return((Image *) NULL);
4480 if (jng_alpha_compression_method == 0)
4485 if (logging != MagickFalse)
4486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4487 " Writing IHDR chunk to alpha_blob.");
4489 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4490 "\211PNG\r\n\032\n");
4492 (void) WriteBlobMSBULong(alpha_image,13L);
4493 PNGType(data,mng_IHDR);
4494 LogPNGChunk(logging,mng_IHDR,13L);
4495 PNGLong(data+4,jng_width);
4496 PNGLong(data+8,jng_height);
4497 data[12]=jng_alpha_sample_depth;
4498 data[13]=0; /* color_type gray */
4499 data[14]=0; /* compression method 0 */
4500 data[15]=0; /* filter_method 0 */
4501 data[16]=0; /* interlace_method 0 */
4502 (void) WriteBlob(alpha_image,17,data);
4503 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4506 reading_idat=MagickTrue;
4509 if (memcmp(type,mng_JDAT,4) == 0)
4511 /* Copy chunk to color_image->blob */
4513 if (logging != MagickFalse)
4514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4515 " Copying JDAT chunk data to color_blob.");
4517 (void) WriteBlob(color_image,length,chunk);
4520 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4525 if (memcmp(type,mng_IDAT,4) == 0)
4530 /* Copy IDAT header and chunk data to alpha_image->blob */
4532 if (image_info->ping == MagickFalse)
4534 if (logging != MagickFalse)
4535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4536 " Copying IDAT chunk data to alpha_blob.");
4538 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4539 PNGType(data,mng_IDAT);
4540 LogPNGChunk(logging,mng_IDAT,length);
4541 (void) WriteBlob(alpha_image,4,data);
4542 (void) WriteBlob(alpha_image,length,chunk);
4543 (void) WriteBlobMSBULong(alpha_image,
4544 crc32(crc32(0,data,4),chunk,(uInt) length));
4548 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4553 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4555 /* Copy chunk data to alpha_image->blob */
4557 if (image_info->ping == MagickFalse)
4559 if (logging != MagickFalse)
4560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4561 " Copying JDAA chunk data to alpha_blob.");
4563 (void) WriteBlob(alpha_image,length,chunk);
4567 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4572 if (memcmp(type,mng_JSEP,4) == 0)
4574 read_JSEP=MagickTrue;
4577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4582 if (memcmp(type,mng_bKGD,4) == 0)
4586 image->background_color.red=ScaleCharToQuantum(p[1]);
4587 image->background_color.green=image->background_color.red;
4588 image->background_color.blue=image->background_color.red;
4593 image->background_color.red=ScaleCharToQuantum(p[1]);
4594 image->background_color.green=ScaleCharToQuantum(p[3]);
4595 image->background_color.blue=ScaleCharToQuantum(p[5]);
4598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4602 if (memcmp(type,mng_gAMA,4) == 0)
4605 image->gamma=((float) mng_get_long(p))*0.00001;
4607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4611 if (memcmp(type,mng_cHRM,4) == 0)
4615 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4616 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4617 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4618 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4619 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4620 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4621 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4622 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4625 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4629 if (memcmp(type,mng_sRGB,4) == 0)
4633 image->rendering_intent=
4634 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4635 image->gamma=1.000f/2.200f;
4636 image->chromaticity.red_primary.x=0.6400f;
4637 image->chromaticity.red_primary.y=0.3300f;
4638 image->chromaticity.green_primary.x=0.3000f;
4639 image->chromaticity.green_primary.y=0.6000f;
4640 image->chromaticity.blue_primary.x=0.1500f;
4641 image->chromaticity.blue_primary.y=0.0600f;
4642 image->chromaticity.white_point.x=0.3127f;
4643 image->chromaticity.white_point.y=0.3290f;
4646 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4650 if (memcmp(type,mng_oFFs,4) == 0)
4654 image->page.x=(ssize_t) mng_get_long(p);
4655 image->page.y=(ssize_t) mng_get_long(&p[4]);
4657 if ((int) p[8] != 0)
4659 image->page.x/=10000;
4660 image->page.y/=10000;
4665 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4670 if (memcmp(type,mng_pHYs,4) == 0)
4674 image->resolution.x=(double) mng_get_long(p);
4675 image->resolution.y=(double) mng_get_long(&p[4]);
4676 if ((int) p[8] == PNG_RESOLUTION_METER)
4678 image->units=PixelsPerCentimeterResolution;
4679 image->resolution.x=image->resolution.x/100.0f;
4680 image->resolution.y=image->resolution.y/100.0f;
4684 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4689 if (memcmp(type,mng_iCCP,4) == 0)
4693 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4700 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4702 if (memcmp(type,mng_IEND,4))
4712 Finish up reading image data:
4714 o read main image from color_blob.
4718 o if (color_type has alpha)
4719 if alpha_encoding is PNG
4720 read secondary image from alpha_blob via ReadPNG
4721 if alpha_encoding is JPEG
4722 read secondary image from alpha_blob via ReadJPEG
4726 o copy intensity of secondary image into
4727 alpha samples of main image.
4729 o destroy the secondary image.
4732 (void) CloseBlob(color_image);
4734 if (logging != MagickFalse)
4735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4736 " Reading jng_image from color_blob.");
4738 assert(color_image_info != (ImageInfo *) NULL);
4739 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4740 color_image->filename);
4742 color_image_info->ping=MagickFalse; /* To do: avoid this */
4743 jng_image=ReadImage(color_image_info,exception);
4745 if (jng_image == (Image *) NULL)
4746 return((Image *) NULL);
4748 (void) RelinquishUniqueFileResource(color_image->filename);
4749 color_image=DestroyImage(color_image);
4750 color_image_info=DestroyImageInfo(color_image_info);
4752 if (jng_image == (Image *) NULL)
4753 return((Image *) NULL);
4755 if (logging != MagickFalse)
4756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4757 " Copying jng_image pixels to main image.");
4759 image->rows=jng_height;
4760 image->columns=jng_width;
4762 for (y=0; y < (ssize_t) image->rows; y++)
4764 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4765 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4766 for (x=(ssize_t) image->columns; x != 0; x--)
4768 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4769 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4770 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4771 q+=GetPixelChannels(image);
4772 s+=GetPixelChannels(jng_image);
4775 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4779 jng_image=DestroyImage(jng_image);
4781 if (image_info->ping == MagickFalse)
4783 if (jng_color_type >= 12)
4785 if (jng_alpha_compression_method == 0)
4789 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4790 PNGType(data,mng_IEND);
4791 LogPNGChunk(logging,mng_IEND,0L);
4792 (void) WriteBlob(alpha_image,4,data);
4793 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4796 (void) CloseBlob(alpha_image);
4798 if (logging != MagickFalse)
4799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4800 " Reading alpha from alpha_blob.");
4802 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4803 "%s",alpha_image->filename);
4805 jng_image=ReadImage(alpha_image_info,exception);
4807 if (jng_image != (Image *) NULL)
4808 for (y=0; y < (ssize_t) image->rows; y++)
4810 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4812 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4814 if (image->alpha_trait == BlendPixelTrait)
4815 for (x=(ssize_t) image->columns; x != 0; x--)
4817 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4818 q+=GetPixelChannels(image);
4819 s+=GetPixelChannels(jng_image);
4823 for (x=(ssize_t) image->columns; x != 0; x--)
4825 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4826 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4827 image->alpha_trait=BlendPixelTrait;
4828 q+=GetPixelChannels(image);
4829 s+=GetPixelChannels(jng_image);
4832 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4835 (void) RelinquishUniqueFileResource(alpha_image->filename);
4836 alpha_image=DestroyImage(alpha_image);
4837 alpha_image_info=DestroyImageInfo(alpha_image_info);
4838 if (jng_image != (Image *) NULL)
4839 jng_image=DestroyImage(jng_image);
4843 /* Read the JNG image. */
4845 if (mng_info->mng_type == 0)
4847 mng_info->mng_width=jng_width;
4848 mng_info->mng_height=jng_height;
4851 if (image->page.width == 0 && image->page.height == 0)
4853 image->page.width=jng_width;
4854 image->page.height=jng_height;
4857 if (image->page.x == 0 && image->page.y == 0)
4859 image->page.x=mng_info->x_off[mng_info->object_id];
4860 image->page.y=mng_info->y_off[mng_info->object_id];
4865 image->page.y=mng_info->y_off[mng_info->object_id];
4868 mng_info->image_found++;
4869 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4870 2*GetBlobSize(image));
4872 if (logging != MagickFalse)
4873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4874 " exit ReadOneJNGImage()");
4880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4884 % R e a d J N G I m a g e %
4888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4890 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4891 % (including the 8-byte signature) and returns it. It allocates the memory
4892 % necessary for the new Image structure and returns a pointer to the new
4895 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4897 % The format of the ReadJNGImage method is:
4899 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4902 % A description of each parameter follows:
4904 % o image_info: the image info.
4906 % o exception: return any errors or warnings in this structure.
4910 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4925 magic_number[MaxTextExtent];
4933 assert(image_info != (const ImageInfo *) NULL);
4934 assert(image_info->signature == MagickSignature);
4935 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4936 assert(exception != (ExceptionInfo *) NULL);
4937 assert(exception->signature == MagickSignature);
4938 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4939 image=AcquireImage(image_info,exception);
4940 mng_info=(MngInfo *) NULL;
4941 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4943 if (status == MagickFalse)
4944 return((Image *) NULL);
4946 if (LocaleCompare(image_info->magick,"JNG") != 0)
4947 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4949 /* Verify JNG signature. */
4951 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4953 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4954 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4956 /* Allocate a MngInfo structure. */
4958 have_mng_structure=MagickFalse;
4959 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4961 if (mng_info == (MngInfo *) NULL)
4962 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4964 /* Initialize members of the MngInfo structure. */
4966 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4967 have_mng_structure=MagickTrue;
4969 mng_info->image=image;
4971 image=ReadOneJNGImage(mng_info,image_info,exception);
4972 MngInfoFreeStruct(mng_info,&have_mng_structure);
4974 if (image == (Image *) NULL)
4976 if (IsImageObject(previous) != MagickFalse)
4978 (void) CloseBlob(previous);
4979 (void) DestroyImageList(previous);
4982 if (logging != MagickFalse)
4983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4984 "exit ReadJNGImage() with error");
4986 return((Image *) NULL);
4988 (void) CloseBlob(image);
4990 if (image->columns == 0 || image->rows == 0)
4992 if (logging != MagickFalse)
4993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4994 "exit ReadJNGImage() with error");
4996 ThrowReaderException(CorruptImageError,"CorruptImage");
4999 if (logging != MagickFalse)
5000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5006 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5009 page_geometry[MaxTextExtent];
5042 #if defined(MNG_INSERT_LAYERS)
5044 mng_background_color;
5047 register unsigned char
5062 #if defined(MNG_INSERT_LAYERS)
5067 volatile unsigned int
5068 #ifdef MNG_OBJECT_BUFFERS
5069 mng_background_object=0,
5071 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5074 default_frame_timeout,
5076 #if defined(MNG_INSERT_LAYERS)
5082 /* These delays are all measured in image ticks_per_second,
5083 * not in MNG ticks_per_second
5086 default_frame_delay,
5090 #if defined(MNG_INSERT_LAYERS)
5099 previous_fb.bottom=0;
5101 previous_fb.right=0;
5103 default_fb.bottom=0;
5107 /* Open image file. */
5109 assert(image_info != (const ImageInfo *) NULL);
5110 assert(image_info->signature == MagickSignature);
5111 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5112 assert(exception != (ExceptionInfo *) NULL);
5113 assert(exception->signature == MagickSignature);
5114 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5115 image=AcquireImage(image_info,exception);
5116 mng_info=(MngInfo *) NULL;
5117 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5119 if (status == MagickFalse)
5120 return((Image *) NULL);
5122 first_mng_object=MagickFalse;
5124 have_mng_structure=MagickFalse;
5126 /* Allocate a MngInfo structure. */
5128 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5130 if (mng_info == (MngInfo *) NULL)
5131 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5133 /* Initialize members of the MngInfo structure. */
5135 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5136 mng_info->image=image;
5137 have_mng_structure=MagickTrue;
5139 if (LocaleCompare(image_info->magick,"MNG") == 0)
5142 magic_number[MaxTextExtent];
5144 /* Verify MNG signature. */
5145 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5146 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5147 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5149 /* Initialize some nonzero members of the MngInfo structure. */
5150 for (i=0; i < MNG_MAX_OBJECTS; i++)
5152 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5153 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5155 mng_info->exists[0]=MagickTrue;
5158 first_mng_object=MagickTrue;
5160 #if defined(MNG_INSERT_LAYERS)
5161 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5163 default_frame_delay=0;
5164 default_frame_timeout=0;
5167 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5169 skip_to_iend=MagickFalse;
5170 term_chunk_found=MagickFalse;
5171 mng_info->framing_mode=1;
5172 #if defined(MNG_INSERT_LAYERS)
5173 mandatory_back=MagickFalse;
5175 #if defined(MNG_INSERT_LAYERS)
5176 mng_background_color=image->background_color;
5178 default_fb=mng_info->frame;
5179 previous_fb=mng_info->frame;
5183 type[MaxTextExtent];
5185 if (LocaleCompare(image_info->magick,"MNG") == 0)
5194 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5195 length=ReadBlobMSBLong(image);
5196 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5198 if (logging != MagickFalse)
5199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5200 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5201 type[0],type[1],type[2],type[3],(double) length);
5203 if (length > PNG_UINT_31_MAX)
5207 ThrowReaderException(CorruptImageError,"CorruptImage");
5210 chunk=(unsigned char *) NULL;
5214 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5216 if (chunk == (unsigned char *) NULL)
5217 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5219 for (i=0; i < (ssize_t) length; i++)
5220 chunk[i]=(unsigned char) ReadBlobByte(image);
5225 (void) ReadBlobMSBLong(image); /* read crc word */
5227 #if !defined(JNG_SUPPORTED)
5228 if (memcmp(type,mng_JHDR,4) == 0)
5230 skip_to_iend=MagickTrue;
5232 if (mng_info->jhdr_warning == 0)
5233 (void) ThrowMagickException(exception,GetMagickModule(),
5234 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5236 mng_info->jhdr_warning++;
5239 if (memcmp(type,mng_DHDR,4) == 0)
5241 skip_to_iend=MagickTrue;
5243 if (mng_info->dhdr_warning == 0)
5244 (void) ThrowMagickException(exception,GetMagickModule(),
5245 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5247 mng_info->dhdr_warning++;
5249 if (memcmp(type,mng_MEND,4) == 0)
5254 if (memcmp(type,mng_IEND,4) == 0)
5255 skip_to_iend=MagickFalse;
5258 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5260 if (logging != MagickFalse)
5261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5267 if (memcmp(type,mng_MHDR,4) == 0)
5269 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5270 (p[2] << 8) | p[3]);
5272 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5273 (p[6] << 8) | p[7]);
5275 if (logging != MagickFalse)
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5278 " MNG width: %.20g",(double) mng_info->mng_width);
5279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5280 " MNG height: %.20g",(double) mng_info->mng_height);
5284 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5286 if (mng_info->ticks_per_second == 0)
5287 default_frame_delay=0;
5290 default_frame_delay=1UL*image->ticks_per_second/
5291 mng_info->ticks_per_second;
5293 frame_delay=default_frame_delay;
5299 simplicity=(size_t) mng_get_long(p);
5302 mng_type=1; /* Full MNG */
5304 if ((simplicity != 0) && ((simplicity | 11) == 11))
5305 mng_type=2; /* LC */
5307 if ((simplicity != 0) && ((simplicity | 9) == 9))
5308 mng_type=3; /* VLC */
5310 #if defined(MNG_INSERT_LAYERS)
5312 insert_layers=MagickTrue;
5314 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5316 /* Allocate next image structure. */
5317 AcquireNextImage(image_info,image,exception);
5319 if (GetNextImageInList(image) == (Image *) NULL)
5320 return((Image *) NULL);
5322 image=SyncNextImageInList(image);
5323 mng_info->image=image;
5326 if ((mng_info->mng_width > 65535L) ||
5327 (mng_info->mng_height > 65535L))
5328 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5330 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5331 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5332 mng_info->mng_height);
5334 mng_info->frame.left=0;
5335 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5336 mng_info->frame.top=0;
5337 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5338 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5340 for (i=0; i < MNG_MAX_OBJECTS; i++)
5341 mng_info->object_clip[i]=mng_info->frame;
5343 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5347 if (memcmp(type,mng_TERM,4) == 0)
5358 final_delay=(png_uint_32) mng_get_long(&p[2]);
5359 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5361 if (mng_iterations == PNG_UINT_31_MAX)
5364 image->iterations=mng_iterations;
5365 term_chunk_found=MagickTrue;
5368 if (logging != MagickFalse)
5370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5371 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5372 repeat,(double) final_delay, (double) image->iterations);
5375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5378 if (memcmp(type,mng_DEFI,4) == 0)
5381 (void) ThrowMagickException(exception,GetMagickModule(),
5382 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5385 object_id=(p[0] << 8) | p[1];
5387 if (mng_type == 2 && object_id != 0)
5388 (void) ThrowMagickException(exception,GetMagickModule(),
5389 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5392 if (object_id > MNG_MAX_OBJECTS)
5395 Instead of using a warning we should allocate a larger
5396 MngInfo structure and continue.
5398 (void) ThrowMagickException(exception,GetMagickModule(),
5399 CoderError,"object id too large","`%s'",image->filename);
5400 object_id=MNG_MAX_OBJECTS;
5403 if (mng_info->exists[object_id])
5404 if (mng_info->frozen[object_id])
5406 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5407 (void) ThrowMagickException(exception,
5408 GetMagickModule(),CoderError,
5409 "DEFI cannot redefine a frozen MNG object","`%s'",
5414 mng_info->exists[object_id]=MagickTrue;
5417 mng_info->invisible[object_id]=p[2];
5420 Extract object offset info.
5424 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5425 (p[5] << 16) | (p[6] << 8) | p[7]);
5427 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5428 (p[9] << 16) | (p[10] << 8) | p[11]);
5430 if (logging != MagickFalse)
5432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5433 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5434 object_id,(double) mng_info->x_off[object_id],
5435 object_id,(double) mng_info->y_off[object_id]);
5440 Extract object clipping info.
5443 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5446 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5449 if (memcmp(type,mng_bKGD,4) == 0)
5451 mng_info->have_global_bkgd=MagickFalse;
5455 mng_info->mng_global_bkgd.red=
5456 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5458 mng_info->mng_global_bkgd.green=
5459 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5461 mng_info->mng_global_bkgd.blue=
5462 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5464 mng_info->have_global_bkgd=MagickTrue;
5467 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5470 if (memcmp(type,mng_BACK,4) == 0)
5472 #if defined(MNG_INSERT_LAYERS)
5474 mandatory_back=p[6];
5479 if (mandatory_back && length > 5)
5481 mng_background_color.red=
5482 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5484 mng_background_color.green=
5485 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5487 mng_background_color.blue=
5488 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5490 mng_background_color.alpha=OpaqueAlpha;
5493 #ifdef MNG_OBJECT_BUFFERS
5495 mng_background_object=(p[7] << 8) | p[8];
5498 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5502 if (memcmp(type,mng_PLTE,4) == 0)
5504 /* Read global PLTE. */
5506 if (length && (length < 769))
5508 if (mng_info->global_plte == (png_colorp) NULL)
5509 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5510 sizeof(*mng_info->global_plte));
5512 for (i=0; i < (ssize_t) (length/3); i++)
5514 mng_info->global_plte[i].red=p[3*i];
5515 mng_info->global_plte[i].green=p[3*i+1];
5516 mng_info->global_plte[i].blue=p[3*i+2];
5519 mng_info->global_plte_length=(unsigned int) (length/3);
5522 for ( ; i < 256; i++)
5524 mng_info->global_plte[i].red=i;
5525 mng_info->global_plte[i].green=i;
5526 mng_info->global_plte[i].blue=i;
5530 mng_info->global_plte_length=256;
5533 mng_info->global_plte_length=0;
5535 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539 if (memcmp(type,mng_tRNS,4) == 0)
5541 /* read global tRNS */
5544 for (i=0; i < (ssize_t) length; i++)
5545 mng_info->global_trns[i]=p[i];
5548 for ( ; i < 256; i++)
5549 mng_info->global_trns[i]=255;
5551 mng_info->global_trns_length=(unsigned int) length;
5552 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5555 if (memcmp(type,mng_gAMA,4) == 0)
5562 igamma=mng_get_long(p);
5563 mng_info->global_gamma=((float) igamma)*0.00001;
5564 mng_info->have_global_gama=MagickTrue;
5568 mng_info->have_global_gama=MagickFalse;
5570 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5574 if (memcmp(type,mng_cHRM,4) == 0)
5576 /* Read global cHRM */
5580 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5581 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5582 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5583 mng_info->global_chrm.red_primary.y=0.00001*
5584 mng_get_long(&p[12]);
5585 mng_info->global_chrm.green_primary.x=0.00001*
5586 mng_get_long(&p[16]);
5587 mng_info->global_chrm.green_primary.y=0.00001*
5588 mng_get_long(&p[20]);
5589 mng_info->global_chrm.blue_primary.x=0.00001*
5590 mng_get_long(&p[24]);
5591 mng_info->global_chrm.blue_primary.y=0.00001*
5592 mng_get_long(&p[28]);
5593 mng_info->have_global_chrm=MagickTrue;
5596 mng_info->have_global_chrm=MagickFalse;
5598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5602 if (memcmp(type,mng_sRGB,4) == 0)
5609 mng_info->global_srgb_intent=
5610 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5611 mng_info->have_global_srgb=MagickTrue;
5614 mng_info->have_global_srgb=MagickFalse;
5616 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5620 if (memcmp(type,mng_iCCP,4) == 0)
5628 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5633 if (memcmp(type,mng_FRAM,4) == 0)
5636 (void) ThrowMagickException(exception,GetMagickModule(),
5637 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5640 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5641 image->delay=frame_delay;
5643 frame_delay=default_frame_delay;
5644 frame_timeout=default_frame_timeout;
5649 mng_info->framing_mode=p[0];
5651 if (logging != MagickFalse)
5652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5653 " Framing_mode=%d",mng_info->framing_mode);
5657 /* Note the delay and frame clipping boundaries. */
5659 p++; /* framing mode */
5661 while (*p && ((p-chunk) < (ssize_t) length))
5662 p++; /* frame name */
5664 p++; /* frame name terminator */
5666 if ((p-chunk) < (ssize_t) (length-4))
5673 change_delay=(*p++);
5674 change_timeout=(*p++);
5675 change_clipping=(*p++);
5676 p++; /* change_sync */
5680 frame_delay=1UL*image->ticks_per_second*
5683 if (mng_info->ticks_per_second != 0)
5684 frame_delay/=mng_info->ticks_per_second;
5687 frame_delay=PNG_UINT_31_MAX;
5689 if (change_delay == 2)
5690 default_frame_delay=frame_delay;
5694 if (logging != MagickFalse)
5695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5696 " Framing_delay=%.20g",(double) frame_delay);
5701 frame_timeout=1UL*image->ticks_per_second*
5704 if (mng_info->ticks_per_second != 0)
5705 frame_timeout/=mng_info->ticks_per_second;
5708 frame_timeout=PNG_UINT_31_MAX;
5710 if (change_delay == 2)
5711 default_frame_timeout=frame_timeout;
5715 if (logging != MagickFalse)
5716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5717 " Framing_timeout=%.20g",(double) frame_timeout);
5720 if (change_clipping)
5722 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5726 if (logging != MagickFalse)
5727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5728 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5729 (double) fb.left,(double) fb.right,(double) fb.top,
5730 (double) fb.bottom);
5732 if (change_clipping == 2)
5738 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5740 subframe_width=(size_t) (mng_info->clip.right
5741 -mng_info->clip.left);
5743 subframe_height=(size_t) (mng_info->clip.bottom
5744 -mng_info->clip.top);
5746 Insert a background layer behind the frame if framing_mode is 4.
5748 #if defined(MNG_INSERT_LAYERS)
5749 if (logging != MagickFalse)
5750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5751 " subframe_width=%.20g, subframe_height=%.20g",(double)
5752 subframe_width,(double) subframe_height);
5754 if (insert_layers && (mng_info->framing_mode == 4) &&
5755 (subframe_width) && (subframe_height))
5757 /* Allocate next image structure. */
5758 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5760 AcquireNextImage(image_info,image,exception);
5762 if (GetNextImageInList(image) == (Image *) NULL)
5764 image=DestroyImageList(image);
5765 MngInfoFreeStruct(mng_info,&have_mng_structure);
5766 return((Image *) NULL);
5769 image=SyncNextImageInList(image);
5772 mng_info->image=image;
5774 if (term_chunk_found)
5776 image->start_loop=MagickTrue;
5777 image->iterations=mng_iterations;
5778 term_chunk_found=MagickFalse;
5782 image->start_loop=MagickFalse;
5784 image->columns=subframe_width;
5785 image->rows=subframe_height;
5786 image->page.width=subframe_width;
5787 image->page.height=subframe_height;
5788 image->page.x=mng_info->clip.left;
5789 image->page.y=mng_info->clip.top;
5790 image->background_color=mng_background_color;
5791 image->alpha_trait=UndefinedPixelTrait;
5793 (void) SetImageBackgroundColor(image,exception);
5795 if (logging != MagickFalse)
5796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5797 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5798 (double) mng_info->clip.left,(double) mng_info->clip.right,
5799 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5802 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5805 if (memcmp(type,mng_CLIP,4) == 0)
5814 first_object=(p[0] << 8) | p[1];
5815 last_object=(p[2] << 8) | p[3];
5817 for (i=(int) first_object; i <= (int) last_object; i++)
5819 if (mng_info->exists[i] && !mng_info->frozen[i])
5824 box=mng_info->object_clip[i];
5825 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5829 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5832 if (memcmp(type,mng_SAVE,4) == 0)
5834 for (i=1; i < MNG_MAX_OBJECTS; i++)
5835 if (mng_info->exists[i])
5837 mng_info->frozen[i]=MagickTrue;
5838 #ifdef MNG_OBJECT_BUFFERS
5839 if (mng_info->ob[i] != (MngBuffer *) NULL)
5840 mng_info->ob[i]->frozen=MagickTrue;
5845 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5850 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5852 /* Read DISC or SEEK. */
5854 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5856 for (i=1; i < MNG_MAX_OBJECTS; i++)
5857 MngInfoDiscardObject(mng_info,i);
5865 for (j=0; j < (ssize_t) length; j+=2)
5867 i=p[j] << 8 | p[j+1];
5868 MngInfoDiscardObject(mng_info,i);
5873 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5878 if (memcmp(type,mng_MOVE,4) == 0)
5886 first_object=(p[0] << 8) | p[1];
5887 last_object=(p[2] << 8) | p[3];
5888 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5890 if (mng_info->exists[i] && !mng_info->frozen[i])
5898 old_pair.a=mng_info->x_off[i];
5899 old_pair.b=mng_info->y_off[i];
5900 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5901 mng_info->x_off[i]=new_pair.a;
5902 mng_info->y_off[i]=new_pair.b;
5906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5910 if (memcmp(type,mng_LOOP,4) == 0)
5912 ssize_t loop_iters=1;
5913 loop_level=chunk[0];
5914 mng_info->loop_active[loop_level]=1; /* mark loop active */
5916 /* Record starting point. */
5917 loop_iters=mng_get_long(&chunk[1]);
5919 if (logging != MagickFalse)
5920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5921 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5922 (double) loop_iters);
5924 if (loop_iters == 0)
5925 skipping_loop=loop_level;
5929 mng_info->loop_jump[loop_level]=TellBlob(image);
5930 mng_info->loop_count[loop_level]=loop_iters;
5933 mng_info->loop_iteration[loop_level]=0;
5934 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5938 if (memcmp(type,mng_ENDL,4) == 0)
5940 loop_level=chunk[0];
5942 if (skipping_loop > 0)
5944 if (skipping_loop == loop_level)
5947 Found end of zero-iteration loop.
5950 mng_info->loop_active[loop_level]=0;
5956 if (mng_info->loop_active[loop_level] == 1)
5958 mng_info->loop_count[loop_level]--;
5959 mng_info->loop_iteration[loop_level]++;
5961 if (logging != MagickFalse)
5962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5963 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5964 (double) loop_level,(double)
5965 mng_info->loop_count[loop_level]);
5967 if (mng_info->loop_count[loop_level] != 0)
5969 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5973 ThrowReaderException(CorruptImageError,
5974 "ImproperImageHeader");
5985 mng_info->loop_active[loop_level]=0;
5987 for (i=0; i < loop_level; i++)
5988 if (mng_info->loop_active[i] == 1)
5989 last_level=(short) i;
5990 loop_level=last_level;
5995 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5999 if (memcmp(type,mng_CLON,4) == 0)
6001 if (mng_info->clon_warning == 0)
6002 (void) ThrowMagickException(exception,GetMagickModule(),
6003 CoderError,"CLON is not implemented yet","`%s'",
6006 mng_info->clon_warning++;
6009 if (memcmp(type,mng_MAGN,4) == 0)
6024 magn_first=(p[0] << 8) | p[1];
6030 magn_last=(p[2] << 8) | p[3];
6033 magn_last=magn_first;
6034 #ifndef MNG_OBJECT_BUFFERS
6035 if (magn_first || magn_last)
6036 if (mng_info->magn_warning == 0)
6038 (void) ThrowMagickException(exception,
6039 GetMagickModule(),CoderError,
6040 "MAGN is not implemented yet for nonzero objects",
6041 "`%s'",image->filename);
6043 mng_info->magn_warning++;
6053 magn_mx=(p[5] << 8) | p[6];
6062 magn_my=(p[7] << 8) | p[8];
6071 magn_ml=(p[9] << 8) | p[10];
6080 magn_mr=(p[11] << 8) | p[12];
6089 magn_mt=(p[13] << 8) | p[14];
6098 magn_mb=(p[15] << 8) | p[16];
6110 magn_methy=magn_methx;
6113 if (magn_methx > 5 || magn_methy > 5)
6114 if (mng_info->magn_warning == 0)
6116 (void) ThrowMagickException(exception,
6117 GetMagickModule(),CoderError,
6118 "Unknown MAGN method in MNG datastream","`%s'",
6121 mng_info->magn_warning++;
6123 #ifdef MNG_OBJECT_BUFFERS
6124 /* Magnify existing objects in the range magn_first to magn_last */
6126 if (magn_first == 0 || magn_last == 0)
6128 /* Save the magnification factors for object 0 */
6129 mng_info->magn_mb=magn_mb;
6130 mng_info->magn_ml=magn_ml;
6131 mng_info->magn_mr=magn_mr;
6132 mng_info->magn_mt=magn_mt;
6133 mng_info->magn_mx=magn_mx;
6134 mng_info->magn_my=magn_my;
6135 mng_info->magn_methx=magn_methx;
6136 mng_info->magn_methy=magn_methy;
6140 if (memcmp(type,mng_PAST,4) == 0)
6142 if (mng_info->past_warning == 0)
6143 (void) ThrowMagickException(exception,GetMagickModule(),
6144 CoderError,"PAST is not implemented yet","`%s'",
6147 mng_info->past_warning++;
6150 if (memcmp(type,mng_SHOW,4) == 0)
6152 if (mng_info->show_warning == 0)
6153 (void) ThrowMagickException(exception,GetMagickModule(),
6154 CoderError,"SHOW is not implemented yet","`%s'",
6157 mng_info->show_warning++;
6160 if (memcmp(type,mng_sBIT,4) == 0)
6163 mng_info->have_global_sbit=MagickFalse;
6167 mng_info->global_sbit.gray=p[0];
6168 mng_info->global_sbit.red=p[0];
6169 mng_info->global_sbit.green=p[1];
6170 mng_info->global_sbit.blue=p[2];
6171 mng_info->global_sbit.alpha=p[3];
6172 mng_info->have_global_sbit=MagickTrue;
6175 if (memcmp(type,mng_pHYs,4) == 0)
6179 mng_info->global_x_pixels_per_unit=
6180 (size_t) mng_get_long(p);
6181 mng_info->global_y_pixels_per_unit=
6182 (size_t) mng_get_long(&p[4]);
6183 mng_info->global_phys_unit_type=p[8];
6184 mng_info->have_global_phys=MagickTrue;
6188 mng_info->have_global_phys=MagickFalse;
6190 if (memcmp(type,mng_pHYg,4) == 0)
6192 if (mng_info->phyg_warning == 0)
6193 (void) ThrowMagickException(exception,GetMagickModule(),
6194 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6196 mng_info->phyg_warning++;
6198 if (memcmp(type,mng_BASI,4) == 0)
6200 skip_to_iend=MagickTrue;
6202 if (mng_info->basi_warning == 0)
6203 (void) ThrowMagickException(exception,GetMagickModule(),
6204 CoderError,"BASI is not implemented yet","`%s'",
6207 mng_info->basi_warning++;
6208 #ifdef MNG_BASI_SUPPORTED
6209 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6210 (p[2] << 8) | p[3]);
6211 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6212 (p[6] << 8) | p[7]);
6213 basi_color_type=p[8];
6214 basi_compression_method=p[9];
6215 basi_filter_type=p[10];
6216 basi_interlace_method=p[11];
6218 basi_red=(p[12] << 8) & p[13];
6224 basi_green=(p[14] << 8) & p[15];
6230 basi_blue=(p[16] << 8) & p[17];
6236 basi_alpha=(p[18] << 8) & p[19];
6240 if (basi_sample_depth == 16)
6247 basi_viewable=p[20];
6253 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6257 if (memcmp(type,mng_IHDR,4)
6258 #if defined(JNG_SUPPORTED)
6259 && memcmp(type,mng_JHDR,4)
6263 /* Not an IHDR or JHDR chunk */
6265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6270 if (logging != MagickFalse)
6271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6272 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6274 mng_info->exists[object_id]=MagickTrue;
6275 mng_info->viewable[object_id]=MagickTrue;
6277 if (mng_info->invisible[object_id])
6279 if (logging != MagickFalse)
6280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6281 " Skipping invisible object");
6283 skip_to_iend=MagickTrue;
6284 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6287 #if defined(MNG_INSERT_LAYERS)
6289 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6291 image_width=(size_t) mng_get_long(p);
6292 image_height=(size_t) mng_get_long(&p[4]);
6294 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6297 Insert a transparent background layer behind the entire animation
6298 if it is not full screen.
6300 #if defined(MNG_INSERT_LAYERS)
6301 if (insert_layers && mng_type && first_mng_object)
6303 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6304 (image_width < mng_info->mng_width) ||
6305 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6306 (image_height < mng_info->mng_height) ||
6307 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6309 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6312 Allocate next image structure.
6314 AcquireNextImage(image_info,image,exception);
6316 if (GetNextImageInList(image) == (Image *) NULL)
6318 image=DestroyImageList(image);
6319 MngInfoFreeStruct(mng_info,&have_mng_structure);
6320 return((Image *) NULL);
6323 image=SyncNextImageInList(image);
6325 mng_info->image=image;
6327 if (term_chunk_found)
6329 image->start_loop=MagickTrue;
6330 image->iterations=mng_iterations;
6331 term_chunk_found=MagickFalse;
6335 image->start_loop=MagickFalse;
6337 /* Make a background rectangle. */
6340 image->columns=mng_info->mng_width;
6341 image->rows=mng_info->mng_height;
6342 image->page.width=mng_info->mng_width;
6343 image->page.height=mng_info->mng_height;
6346 image->background_color=mng_background_color;
6347 (void) SetImageBackgroundColor(image,exception);
6348 if (logging != MagickFalse)
6349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6350 " Inserted transparent background layer, W=%.20g, H=%.20g",
6351 (double) mng_info->mng_width,(double) mng_info->mng_height);
6355 Insert a background layer behind the upcoming image if
6356 framing_mode is 3, and we haven't already inserted one.
6358 if (insert_layers && (mng_info->framing_mode == 3) &&
6359 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6360 (simplicity & 0x08)))
6362 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6365 Allocate next image structure.
6367 AcquireNextImage(image_info,image,exception);
6369 if (GetNextImageInList(image) == (Image *) NULL)
6371 image=DestroyImageList(image);
6372 MngInfoFreeStruct(mng_info,&have_mng_structure);
6373 return((Image *) NULL);
6376 image=SyncNextImageInList(image);
6379 mng_info->image=image;
6381 if (term_chunk_found)
6383 image->start_loop=MagickTrue;
6384 image->iterations=mng_iterations;
6385 term_chunk_found=MagickFalse;
6389 image->start_loop=MagickFalse;
6392 image->columns=subframe_width;
6393 image->rows=subframe_height;
6394 image->page.width=subframe_width;
6395 image->page.height=subframe_height;
6396 image->page.x=mng_info->clip.left;
6397 image->page.y=mng_info->clip.top;
6398 image->background_color=mng_background_color;
6399 image->alpha_trait=UndefinedPixelTrait;
6400 (void) SetImageBackgroundColor(image,exception);
6402 if (logging != MagickFalse)
6403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6404 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6405 (double) mng_info->clip.left,(double) mng_info->clip.right,
6406 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6408 #endif /* MNG_INSERT_LAYERS */
6409 first_mng_object=MagickFalse;
6411 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6414 Allocate next image structure.
6416 AcquireNextImage(image_info,image,exception);
6418 if (GetNextImageInList(image) == (Image *) NULL)
6420 image=DestroyImageList(image);
6421 MngInfoFreeStruct(mng_info,&have_mng_structure);
6422 return((Image *) NULL);
6425 image=SyncNextImageInList(image);
6427 mng_info->image=image;
6428 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6429 GetBlobSize(image));
6431 if (status == MagickFalse)
6434 if (term_chunk_found)
6436 image->start_loop=MagickTrue;
6437 term_chunk_found=MagickFalse;
6441 image->start_loop=MagickFalse;
6443 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6445 image->delay=frame_delay;
6446 frame_delay=default_frame_delay;
6452 image->page.width=mng_info->mng_width;
6453 image->page.height=mng_info->mng_height;
6454 image->page.x=mng_info->x_off[object_id];
6455 image->page.y=mng_info->y_off[object_id];
6456 image->iterations=mng_iterations;
6459 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6462 if (logging != MagickFalse)
6463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6464 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6467 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6470 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6474 mng_info->image=image;
6475 mng_info->mng_type=mng_type;
6476 mng_info->object_id=object_id;
6478 if (memcmp(type,mng_IHDR,4) == 0)
6479 image=ReadOnePNGImage(mng_info,image_info,exception);
6481 #if defined(JNG_SUPPORTED)
6483 image=ReadOneJNGImage(mng_info,image_info,exception);
6486 if (image == (Image *) NULL)
6488 if (IsImageObject(previous) != MagickFalse)
6490 (void) DestroyImageList(previous);
6491 (void) CloseBlob(previous);
6494 MngInfoFreeStruct(mng_info,&have_mng_structure);
6495 return((Image *) NULL);
6498 if (image->columns == 0 || image->rows == 0)
6500 (void) CloseBlob(image);
6501 image=DestroyImageList(image);
6502 MngInfoFreeStruct(mng_info,&have_mng_structure);
6503 return((Image *) NULL);
6506 mng_info->image=image;
6513 if (mng_info->magn_methx || mng_info->magn_methy)
6519 if (logging != MagickFalse)
6520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6521 " Processing MNG MAGN chunk");
6523 if (mng_info->magn_methx == 1)
6525 magnified_width=mng_info->magn_ml;
6527 if (image->columns > 1)
6528 magnified_width += mng_info->magn_mr;
6530 if (image->columns > 2)
6531 magnified_width += (png_uint_32)
6532 ((image->columns-2)*(mng_info->magn_mx));
6537 magnified_width=(png_uint_32) image->columns;
6539 if (image->columns > 1)
6540 magnified_width += mng_info->magn_ml-1;
6542 if (image->columns > 2)
6543 magnified_width += mng_info->magn_mr-1;
6545 if (image->columns > 3)
6546 magnified_width += (png_uint_32)
6547 ((image->columns-3)*(mng_info->magn_mx-1));
6550 if (mng_info->magn_methy == 1)
6552 magnified_height=mng_info->magn_mt;
6554 if (image->rows > 1)
6555 magnified_height += mng_info->magn_mb;
6557 if (image->rows > 2)
6558 magnified_height += (png_uint_32)
6559 ((image->rows-2)*(mng_info->magn_my));
6564 magnified_height=(png_uint_32) image->rows;
6566 if (image->rows > 1)
6567 magnified_height += mng_info->magn_mt-1;
6569 if (image->rows > 2)
6570 magnified_height += mng_info->magn_mb-1;
6572 if (image->rows > 3)
6573 magnified_height += (png_uint_32)
6574 ((image->rows-3)*(mng_info->magn_my-1));
6577 if (magnified_height > image->rows ||
6578 magnified_width > image->columns)
6605 /* Allocate next image structure. */
6607 if (logging != MagickFalse)
6608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6609 " Allocate magnified image");
6611 AcquireNextImage(image_info,image,exception);
6613 if (GetNextImageInList(image) == (Image *) NULL)
6615 image=DestroyImageList(image);
6616 MngInfoFreeStruct(mng_info,&have_mng_structure);
6617 return((Image *) NULL);
6620 large_image=SyncNextImageInList(image);
6622 large_image->columns=magnified_width;
6623 large_image->rows=magnified_height;
6625 magn_methx=mng_info->magn_methx;
6626 magn_methy=mng_info->magn_methy;
6628 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6629 #define QM unsigned short
6630 if (magn_methx != 1 || magn_methy != 1)
6633 Scale pixels to unsigned shorts to prevent
6634 overflow of intermediate values of interpolations
6636 for (y=0; y < (ssize_t) image->rows; y++)
6638 q=GetAuthenticPixels(image,0,y,image->columns,1,
6641 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6643 SetPixelRed(image,ScaleQuantumToShort(
6644 GetPixelRed(image,q)),q);
6645 SetPixelGreen(image,ScaleQuantumToShort(
6646 GetPixelGreen(image,q)),q);
6647 SetPixelBlue(image,ScaleQuantumToShort(
6648 GetPixelBlue(image,q)),q);
6649 SetPixelAlpha(image,ScaleQuantumToShort(
6650 GetPixelAlpha(image,q)),q);
6651 q+=GetPixelChannels(image);
6654 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6662 if (image->alpha_trait == BlendPixelTrait)
6663 (void) SetImageBackgroundColor(large_image,exception);
6667 large_image->background_color.alpha=OpaqueAlpha;
6668 (void) SetImageBackgroundColor(large_image,exception);
6670 if (magn_methx == 4)
6673 if (magn_methx == 5)
6676 if (magn_methy == 4)
6679 if (magn_methy == 5)
6683 /* magnify the rows into the right side of the large image */
6685 if (logging != MagickFalse)
6686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687 " Magnify the rows to %.20g",(double) large_image->rows);
6688 m=(ssize_t) mng_info->magn_mt;
6690 length=(size_t) image->columns*GetPixelChannels(image);
6691 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6692 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6694 if ((prev == (Quantum *) NULL) ||
6695 (next == (Quantum *) NULL))
6697 image=DestroyImageList(image);
6698 MngInfoFreeStruct(mng_info,&have_mng_structure);
6699 ThrowReaderException(ResourceLimitError,
6700 "MemoryAllocationFailed");
6703 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6704 (void) CopyMagickMemory(next,n,length);
6706 for (y=0; y < (ssize_t) image->rows; y++)
6709 m=(ssize_t) mng_info->magn_mt;
6711 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6712 m=(ssize_t) mng_info->magn_mb;
6714 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6715 m=(ssize_t) mng_info->magn_mb;
6717 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6721 m=(ssize_t) mng_info->magn_my;
6727 if (y < (ssize_t) image->rows-1)
6729 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6731 (void) CopyMagickMemory(next,n,length);
6734 for (i=0; i < m; i++, yy++)
6739 assert(yy < (ssize_t) large_image->rows);
6742 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6744 q+=(large_image->columns-image->columns)*
6745 GetPixelChannels(large_image);
6747 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6749 /* To do: get color as function of indexes[x] */
6751 if (image->storage_class == PseudoClass)
6756 if (magn_methy <= 1)
6758 /* replicate previous */
6759 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6760 SetPixelGreen(large_image,GetPixelGreen(image,
6762 SetPixelBlue(large_image,GetPixelBlue(image,
6764 SetPixelAlpha(large_image,GetPixelAlpha(image,
6768 else if (magn_methy == 2 || magn_methy == 4)
6772 SetPixelRed(large_image,GetPixelRed(image,
6774 SetPixelGreen(large_image,GetPixelGreen(image,
6776 SetPixelBlue(large_image,GetPixelBlue(image,
6778 SetPixelAlpha(large_image,GetPixelAlpha(image,
6785 SetPixelRed(large_image,((QM) (((ssize_t)
6786 (2*i*(GetPixelRed(image,n)
6787 -GetPixelRed(image,pixels)+m))/
6789 +GetPixelRed(image,pixels)))),q);
6790 SetPixelGreen(large_image,((QM) (((ssize_t)
6791 (2*i*(GetPixelGreen(image,n)
6792 -GetPixelGreen(image,pixels)+m))/
6794 +GetPixelGreen(image,pixels)))),q);
6795 SetPixelBlue(large_image,((QM) (((ssize_t)
6796 (2*i*(GetPixelBlue(image,n)
6797 -GetPixelBlue(image,pixels)+m))/
6799 +GetPixelBlue(image,pixels)))),q);
6801 if (image->alpha_trait == BlendPixelTrait)
6802 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6803 (2*i*(GetPixelAlpha(image,n)
6804 -GetPixelAlpha(image,pixels)+m))
6806 GetPixelAlpha(image,pixels)))),q);
6809 if (magn_methy == 4)
6811 /* Replicate nearest */
6812 if (i <= ((m+1) << 1))
6813 SetPixelAlpha(large_image,GetPixelAlpha(image,
6816 SetPixelAlpha(large_image,GetPixelAlpha(image,
6821 else /* if (magn_methy == 3 || magn_methy == 5) */
6823 /* Replicate nearest */
6824 if (i <= ((m+1) << 1))
6826 SetPixelRed(large_image,GetPixelRed(image,
6828 SetPixelGreen(large_image,GetPixelGreen(image,
6830 SetPixelBlue(large_image,GetPixelBlue(image,
6832 SetPixelAlpha(large_image,GetPixelAlpha(image,
6838 SetPixelRed(large_image,GetPixelRed(image,n),q);
6839 SetPixelGreen(large_image,GetPixelGreen(image,n),
6841 SetPixelBlue(large_image,GetPixelBlue(image,n),
6843 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6847 if (magn_methy == 5)
6849 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6850 (GetPixelAlpha(image,n)
6851 -GetPixelAlpha(image,pixels))
6852 +m))/((ssize_t) (m*2))
6853 +GetPixelAlpha(image,pixels)),q);
6856 n+=GetPixelChannels(image);
6857 q+=GetPixelChannels(large_image);
6858 pixels+=GetPixelChannels(image);
6861 if (SyncAuthenticPixels(large_image,exception) == 0)
6867 prev=(Quantum *) RelinquishMagickMemory(prev);
6868 next=(Quantum *) RelinquishMagickMemory(next);
6870 length=image->columns;
6872 if (logging != MagickFalse)
6873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6874 " Delete original image");
6876 DeleteImageFromList(&image);
6880 mng_info->image=image;
6882 /* magnify the columns */
6883 if (logging != MagickFalse)
6884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6885 " Magnify the columns to %.20g",(double) image->columns);
6887 for (y=0; y < (ssize_t) image->rows; y++)
6892 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6893 pixels=q+(image->columns-length)*GetPixelChannels(image);
6894 n=pixels+GetPixelChannels(image);
6896 for (x=(ssize_t) (image->columns-length);
6897 x < (ssize_t) image->columns; x++)
6899 /* To do: Rewrite using Get/Set***PixelChannel() */
6901 if (x == (ssize_t) (image->columns-length))
6902 m=(ssize_t) mng_info->magn_ml;
6904 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6905 m=(ssize_t) mng_info->magn_mr;
6907 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6908 m=(ssize_t) mng_info->magn_mr;
6910 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6914 m=(ssize_t) mng_info->magn_mx;
6916 for (i=0; i < m; i++)
6918 if (magn_methx <= 1)
6920 /* replicate previous */
6921 SetPixelRed(image,GetPixelRed(image,pixels),q);
6922 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6923 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6924 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6927 else if (magn_methx == 2 || magn_methx == 4)
6931 SetPixelRed(image,GetPixelRed(image,pixels),q);
6932 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6933 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6934 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6937 /* To do: Rewrite using Get/Set***PixelChannel() */
6941 SetPixelRed(image,(QM) ((2*i*(
6942 GetPixelRed(image,n)
6943 -GetPixelRed(image,pixels))+m)
6945 GetPixelRed(image,pixels)),q);
6947 SetPixelGreen(image,(QM) ((2*i*(
6948 GetPixelGreen(image,n)
6949 -GetPixelGreen(image,pixels))+m)
6951 GetPixelGreen(image,pixels)),q);
6953 SetPixelBlue(image,(QM) ((2*i*(
6954 GetPixelBlue(image,n)
6955 -GetPixelBlue(image,pixels))+m)
6957 GetPixelBlue(image,pixels)),q);
6958 if (image->alpha_trait == BlendPixelTrait)
6959 SetPixelAlpha(image,(QM) ((2*i*(
6960 GetPixelAlpha(image,n)
6961 -GetPixelAlpha(image,pixels))+m)
6963 GetPixelAlpha(image,pixels)),q);
6966 if (magn_methx == 4)
6968 /* Replicate nearest */
6969 if (i <= ((m+1) << 1))
6971 SetPixelAlpha(image,
6972 GetPixelAlpha(image,pixels)+0,q);
6976 SetPixelAlpha(image,
6977 GetPixelAlpha(image,n)+0,q);
6982 else /* if (magn_methx == 3 || magn_methx == 5) */
6984 /* Replicate nearest */
6985 if (i <= ((m+1) << 1))
6987 SetPixelRed(image,GetPixelRed(image,pixels),q);
6988 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6989 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6990 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6995 SetPixelRed(image,GetPixelRed(image,n),q);
6996 SetPixelGreen(image,GetPixelGreen(image,n),q);
6997 SetPixelBlue(image,GetPixelBlue(image,n),q);
6998 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7001 if (magn_methx == 5)
7004 SetPixelAlpha(image,
7005 (QM) ((2*i*( GetPixelAlpha(image,n)
7006 -GetPixelAlpha(image,pixels))+m)/
7008 +GetPixelAlpha(image,pixels)),q);
7011 q+=GetPixelChannels(image);
7013 n+=GetPixelChannels(image);
7016 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7019 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7020 if (magn_methx != 1 || magn_methy != 1)
7023 Rescale pixels to Quantum
7025 for (y=0; y < (ssize_t) image->rows; y++)
7027 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7029 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7031 SetPixelRed(image,ScaleShortToQuantum(
7032 GetPixelRed(image,q)),q);
7033 SetPixelGreen(image,ScaleShortToQuantum(
7034 GetPixelGreen(image,q)),q);
7035 SetPixelBlue(image,ScaleShortToQuantum(
7036 GetPixelBlue(image,q)),q);
7037 SetPixelAlpha(image,ScaleShortToQuantum(
7038 GetPixelAlpha(image,q)),q);
7039 q+=GetPixelChannels(image);
7042 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7047 if (logging != MagickFalse)
7048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049 " Finished MAGN processing");
7054 Crop_box is with respect to the upper left corner of the MNG.
7056 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7057 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7058 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7059 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7060 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7061 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7062 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7063 if ((crop_box.left != (mng_info->image_box.left
7064 +mng_info->x_off[object_id])) ||
7065 (crop_box.right != (mng_info->image_box.right
7066 +mng_info->x_off[object_id])) ||
7067 (crop_box.top != (mng_info->image_box.top
7068 +mng_info->y_off[object_id])) ||
7069 (crop_box.bottom != (mng_info->image_box.bottom
7070 +mng_info->y_off[object_id])))
7072 if (logging != MagickFalse)
7073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074 " Crop the PNG image");
7076 if ((crop_box.left < crop_box.right) &&
7077 (crop_box.top < crop_box.bottom))
7086 Crop_info is with respect to the upper left corner of
7089 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7090 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7091 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7092 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7093 image->page.width=image->columns;
7094 image->page.height=image->rows;
7097 im=CropImage(image,&crop_info,exception);
7099 if (im != (Image *) NULL)
7101 image->columns=im->columns;
7102 image->rows=im->rows;
7103 im=DestroyImage(im);
7104 image->page.width=image->columns;
7105 image->page.height=image->rows;
7106 image->page.x=crop_box.left;
7107 image->page.y=crop_box.top;
7114 No pixels in crop area. The MNG spec still requires
7115 a layer, though, so make a single transparent pixel in
7116 the top left corner.
7121 (void) SetImageBackgroundColor(image,exception);
7122 image->page.width=1;
7123 image->page.height=1;
7128 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7129 image=mng_info->image;
7133 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7134 /* PNG does not handle depths greater than 16 so reduce it even
7137 if (image->depth > 16)
7141 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7142 if (image->depth > 8)
7144 /* To do: fill low byte properly */
7148 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7152 if (image_info->number_scenes != 0)
7154 if (mng_info->scenes_found >
7155 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7159 if (logging != MagickFalse)
7160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7161 " Finished reading image datastream.");
7163 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7165 (void) CloseBlob(image);
7167 if (logging != MagickFalse)
7168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7169 " Finished reading all image datastreams.");
7171 #if defined(MNG_INSERT_LAYERS)
7172 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7173 (mng_info->mng_height))
7176 Insert a background layer if nothing else was found.
7178 if (logging != MagickFalse)
7179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7180 " No images found. Inserting a background layer.");
7182 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7185 Allocate next image structure.
7187 AcquireNextImage(image_info,image,exception);
7188 if (GetNextImageInList(image) == (Image *) NULL)
7190 image=DestroyImageList(image);
7191 MngInfoFreeStruct(mng_info,&have_mng_structure);
7193 if (logging != MagickFalse)
7194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7195 " Allocation failed, returning NULL.");
7197 return((Image *) NULL);
7199 image=SyncNextImageInList(image);
7201 image->columns=mng_info->mng_width;
7202 image->rows=mng_info->mng_height;
7203 image->page.width=mng_info->mng_width;
7204 image->page.height=mng_info->mng_height;
7207 image->background_color=mng_background_color;
7208 image->alpha_trait=UndefinedPixelTrait;
7210 if (image_info->ping == MagickFalse)
7211 (void) SetImageBackgroundColor(image,exception);
7213 mng_info->image_found++;
7216 image->iterations=mng_iterations;
7218 if (mng_iterations == 1)
7219 image->start_loop=MagickTrue;
7221 while (GetPreviousImageInList(image) != (Image *) NULL)
7224 if (image_count > 10*mng_info->image_found)
7226 if (logging != MagickFalse)
7227 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7229 (void) ThrowMagickException(exception,GetMagickModule(),
7230 CoderError,"Linked list is corrupted, beginning of list not found",
7231 "`%s'",image_info->filename);
7233 return((Image *) NULL);
7236 image=GetPreviousImageInList(image);
7238 if (GetNextImageInList(image) == (Image *) NULL)
7240 if (logging != MagickFalse)
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7243 (void) ThrowMagickException(exception,GetMagickModule(),
7244 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7245 image_info->filename);
7249 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7250 GetNextImageInList(image) ==
7253 if (logging != MagickFalse)
7254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7255 " First image null");
7257 (void) ThrowMagickException(exception,GetMagickModule(),
7258 CoderError,"image->next for first image is NULL but shouldn't be.",
7259 "`%s'",image_info->filename);
7262 if (mng_info->image_found == 0)
7264 if (logging != MagickFalse)
7265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7266 " No visible images found.");
7268 (void) ThrowMagickException(exception,GetMagickModule(),
7269 CoderError,"No visible images in file","`%s'",image_info->filename);
7271 if (image != (Image *) NULL)
7272 image=DestroyImageList(image);
7274 MngInfoFreeStruct(mng_info,&have_mng_structure);
7275 return((Image *) NULL);
7278 if (mng_info->ticks_per_second)
7279 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7280 final_delay/mng_info->ticks_per_second;
7283 image->start_loop=MagickTrue;
7285 /* Find final nonzero image delay */
7286 final_image_delay=0;
7288 while (GetNextImageInList(image) != (Image *) NULL)
7291 final_image_delay=image->delay;
7293 image=GetNextImageInList(image);
7296 if (final_delay < final_image_delay)
7297 final_delay=final_image_delay;
7299 image->delay=final_delay;
7301 if (logging != MagickFalse)
7302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7303 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7304 (double) final_delay);
7306 if (logging != MagickFalse)
7312 image=GetFirstImageInList(image);
7314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7315 " Before coalesce:");
7317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7318 " scene 0 delay=%.20g",(double) image->delay);
7320 while (GetNextImageInList(image) != (Image *) NULL)
7322 image=GetNextImageInList(image);
7323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7324 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7328 image=GetFirstImageInList(image);
7329 #ifdef MNG_COALESCE_LAYERS
7339 if (logging != MagickFalse)
7340 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7343 next_image=CoalesceImages(image,exception);
7345 if (next_image == (Image *) NULL)
7346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7348 image=DestroyImageList(image);
7351 for (next=image; next != (Image *) NULL; next=next_image)
7353 next->page.width=mng_info->mng_width;
7354 next->page.height=mng_info->mng_height;
7357 next->scene=scene++;
7358 next_image=GetNextImageInList(next);
7360 if (next_image == (Image *) NULL)
7363 if (next->delay == 0)
7366 next_image->previous=GetPreviousImageInList(next);
7367 if (GetPreviousImageInList(next) == (Image *) NULL)
7370 next->previous->next=next_image;
7371 next=DestroyImage(next);
7377 while (GetNextImageInList(image) != (Image *) NULL)
7378 image=GetNextImageInList(image);
7380 image->dispose=BackgroundDispose;
7382 if (logging != MagickFalse)
7388 image=GetFirstImageInList(image);
7390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7391 " After coalesce:");
7393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7394 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7395 (double) image->dispose);
7397 while (GetNextImageInList(image) != (Image *) NULL)
7399 image=GetNextImageInList(image);
7401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7402 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7403 (double) image->delay,(double) image->dispose);
7407 image=GetFirstImageInList(image);
7408 MngInfoFreeStruct(mng_info,&have_mng_structure);
7409 have_mng_structure=MagickFalse;
7411 if (logging != MagickFalse)
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7414 return(GetFirstImageInList(image));
7416 #else /* PNG_LIBPNG_VER > 10011 */
7417 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7419 printf("Your PNG library is too old: You have libpng-%s\n",
7420 PNG_LIBPNG_VER_STRING);
7422 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7423 "PNG library is too old","`%s'",image_info->filename);
7425 return(Image *) NULL;
7428 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7430 return(ReadPNGImage(image_info,exception));
7432 #endif /* PNG_LIBPNG_VER > 10011 */
7436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7440 % R e g i s t e r P N G I m a g e %
7444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7446 % RegisterPNGImage() adds properties for the PNG image format to
7447 % the list of supported formats. The properties include the image format
7448 % tag, a method to read and/or write the format, whether the format
7449 % supports the saving of more than one frame to the same file or blob,
7450 % whether the format supports native in-memory I/O, and a brief
7451 % description of the format.
7453 % The format of the RegisterPNGImage method is:
7455 % size_t RegisterPNGImage(void)
7458 ModuleExport size_t RegisterPNGImage(void)
7461 version[MaxTextExtent];
7469 "See http://www.libpng.org/ for details about the PNG format."
7474 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7480 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7486 #if defined(PNG_LIBPNG_VER_STRING)
7487 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7488 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7490 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7492 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7493 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7498 entry=SetMagickInfo("MNG");
7499 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7501 #if defined(MAGICKCORE_PNG_DELEGATE)
7502 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7503 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7506 entry->magick=(IsImageFormatHandler *) IsMNG;
7507 entry->description=ConstantString("Multiple-image Network Graphics");
7509 if (*version != '\0')
7510 entry->version=ConstantString(version);
7512 entry->mime_type=ConstantString("video/x-mng");
7513 entry->module=ConstantString("PNG");
7514 entry->note=ConstantString(MNGNote);
7515 (void) RegisterMagickInfo(entry);
7517 entry=SetMagickInfo("PNG");
7519 #if defined(MAGICKCORE_PNG_DELEGATE)
7520 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7521 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7524 entry->magick=(IsImageFormatHandler *) IsPNG;
7525 entry->adjoin=MagickFalse;
7526 entry->description=ConstantString("Portable Network Graphics");
7527 entry->mime_type=ConstantString("image/png");
7528 entry->module=ConstantString("PNG");
7530 if (*version != '\0')
7531 entry->version=ConstantString(version);
7533 entry->note=ConstantString(PNGNote);
7534 (void) RegisterMagickInfo(entry);
7536 entry=SetMagickInfo("PNG8");
7538 #if defined(MAGICKCORE_PNG_DELEGATE)
7539 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7540 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7543 entry->magick=(IsImageFormatHandler *) IsPNG;
7544 entry->adjoin=MagickFalse;
7545 entry->description=ConstantString(
7546 "8-bit indexed with optional binary transparency");
7547 entry->mime_type=ConstantString("image/png");
7548 entry->module=ConstantString("PNG");
7549 (void) RegisterMagickInfo(entry);
7551 entry=SetMagickInfo("PNG24");
7554 #if defined(ZLIB_VERSION)
7555 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7556 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7558 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7560 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7561 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7565 if (*version != '\0')
7566 entry->version=ConstantString(version);
7568 #if defined(MAGICKCORE_PNG_DELEGATE)
7569 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7570 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7573 entry->magick=(IsImageFormatHandler *) IsPNG;
7574 entry->adjoin=MagickFalse;
7575 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7576 entry->mime_type=ConstantString("image/png");
7577 entry->module=ConstantString("PNG");
7578 (void) RegisterMagickInfo(entry);
7580 entry=SetMagickInfo("PNG32");
7582 #if defined(MAGICKCORE_PNG_DELEGATE)
7583 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7584 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7587 entry->magick=(IsImageFormatHandler *) IsPNG;
7588 entry->adjoin=MagickFalse;
7589 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7590 entry->mime_type=ConstantString("image/png");
7591 entry->module=ConstantString("PNG");
7592 (void) RegisterMagickInfo(entry);
7594 entry=SetMagickInfo("PNG48");
7596 #if defined(MAGICKCORE_PNG_DELEGATE)
7597 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7598 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7601 entry->magick=(IsImageFormatHandler *) IsPNG;
7602 entry->adjoin=MagickFalse;
7603 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7604 entry->mime_type=ConstantString("image/png");
7605 entry->module=ConstantString("PNG");
7606 (void) RegisterMagickInfo(entry);
7608 entry=SetMagickInfo("PNG64");
7610 #if defined(MAGICKCORE_PNG_DELEGATE)
7611 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7612 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7615 entry->magick=(IsImageFormatHandler *) IsPNG;
7616 entry->adjoin=MagickFalse;
7617 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7618 entry->mime_type=ConstantString("image/png");
7619 entry->module=ConstantString("PNG");
7620 (void) RegisterMagickInfo(entry);
7622 entry=SetMagickInfo("PNG00");
7624 #if defined(MAGICKCORE_PNG_DELEGATE)
7625 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7626 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7629 entry->magick=(IsImageFormatHandler *) IsPNG;
7630 entry->adjoin=MagickFalse;
7631 entry->description=ConstantString(
7632 "PNG inheriting bit-depth and color-type from original");
7633 entry->mime_type=ConstantString("image/png");
7634 entry->module=ConstantString("PNG");
7635 (void) RegisterMagickInfo(entry);
7637 entry=SetMagickInfo("JNG");
7639 #if defined(JNG_SUPPORTED)
7640 #if defined(MAGICKCORE_PNG_DELEGATE)
7641 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7642 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7646 entry->magick=(IsImageFormatHandler *) IsJNG;
7647 entry->adjoin=MagickFalse;
7648 entry->description=ConstantString("JPEG Network Graphics");
7649 entry->mime_type=ConstantString("image/x-jng");
7650 entry->module=ConstantString("PNG");
7651 entry->note=ConstantString(JNGNote);
7652 (void) RegisterMagickInfo(entry);
7654 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7655 ping_semaphore=AcquireSemaphoreInfo();
7658 return(MagickImageCoderSignature);
7662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7666 % U n r e g i s t e r P N G I m a g e %
7670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7672 % UnregisterPNGImage() removes format registrations made by the
7673 % PNG module from the list of supported formats.
7675 % The format of the UnregisterPNGImage method is:
7677 % UnregisterPNGImage(void)
7680 ModuleExport void UnregisterPNGImage(void)
7682 (void) UnregisterMagickInfo("MNG");
7683 (void) UnregisterMagickInfo("PNG");
7684 (void) UnregisterMagickInfo("PNG8");
7685 (void) UnregisterMagickInfo("PNG24");
7686 (void) UnregisterMagickInfo("PNG32");
7687 (void) UnregisterMagickInfo("PNG48");
7688 (void) UnregisterMagickInfo("PNG64");
7689 (void) UnregisterMagickInfo("PNG00");
7690 (void) UnregisterMagickInfo("JNG");
7692 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7693 if (ping_semaphore != (SemaphoreInfo *) NULL)
7694 RelinquishSemaphoreInfo(&ping_semaphore);
7698 #if defined(MAGICKCORE_PNG_DELEGATE)
7699 #if PNG_LIBPNG_VER > 10011
7701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7705 % W r i t e M N G I m a g e %
7709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7711 % WriteMNGImage() writes an image in the Portable Network Graphics
7712 % Group's "Multiple-image Network Graphics" encoded image format.
7714 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7716 % The format of the WriteMNGImage method is:
7718 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7719 % Image *image,ExceptionInfo *exception)
7721 % A description of each parameter follows.
7723 % o image_info: the image info.
7725 % o image: The image.
7727 % o exception: return any errors or warnings in this structure.
7729 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7730 % "To do" under ReadPNGImage):
7732 % Preserve all unknown and not-yet-handled known chunks found in input
7733 % PNG file and copy them into output PNG files according to the PNG
7736 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7738 % Improve selection of color type (use indexed-colour or indexed-colour
7739 % with tRNS when 256 or fewer unique RGBA values are present).
7741 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7742 % This will be complicated if we limit ourselves to generating MNG-LC
7743 % files. For now we ignore disposal method 3 and simply overlay the next
7746 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7747 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7748 % [mostly done 15 June 1999 but still need to take care of tRNS]
7750 % Check for identical sRGB and replace with a global sRGB (and remove
7751 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7752 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7753 % local gAMA/cHRM with local sRGB if appropriate).
7755 % Check for identical sBIT chunks and write global ones.
7757 % Provide option to skip writing the signature tEXt chunks.
7759 % Use signatures to detect identical objects and reuse the first
7760 % instance of such objects instead of writing duplicate objects.
7762 % Use a smaller-than-32k value of compression window size when
7765 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7766 % ancillary text chunks and save profiles.
7768 % Provide an option to force LC files (to ensure exact framing rate)
7771 % Provide an option to force VLC files instead of LC, even when offsets
7772 % are present. This will involve expanding the embedded images with a
7773 % transparent region at the top and/or left.
7777 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7778 png_info *ping_info, unsigned char *profile_type, unsigned char
7779 *profile_description, unsigned char *profile_data, png_uint_32 length)
7798 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7800 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7803 if (image_info->verbose)
7805 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7806 (char *) profile_type, (double) length);
7809 #if PNG_LIBPNG_VER >= 10400
7810 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7812 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7814 description_length=(png_uint_32) strlen((const char *) profile_description);
7815 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7816 + description_length);
7817 #if PNG_LIBPNG_VER >= 10400
7818 text[0].text=(png_charp) png_malloc(ping,
7819 (png_alloc_size_t) allocated_length);
7820 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7822 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7823 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7825 text[0].key[0]='\0';
7826 (void) ConcatenateMagickString(text[0].key,
7827 "Raw profile type ",MaxTextExtent);
7828 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7832 (void) CopyMagickString(dp,(const char *) profile_description,
7834 dp+=description_length;
7836 (void) FormatLocaleString(dp,allocated_length-
7837 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7840 for (i=0; i < (ssize_t) length; i++)
7844 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7845 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7850 text[0].text_length=(png_size_t) (dp-text[0].text);
7851 text[0].compression=image_info->compression == NoCompression ||
7852 (image_info->compression == UndefinedCompression &&
7853 text[0].text_length < 128) ? -1 : 0;
7855 if (text[0].text_length <= allocated_length)
7856 png_set_text(ping,ping_info,text,1);
7858 png_free(ping,text[0].text);
7859 png_free(ping,text[0].key);
7860 png_free(ping,text);
7863 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7864 const char *string, MagickBooleanType logging)
7877 ResetImageProfileIterator(image);
7879 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7881 profile=GetImageProfile(image,name);
7883 if (profile != (const StringInfo *) NULL)
7888 if (LocaleNCompare(name,string,11) == 0)
7890 if (logging != MagickFalse)
7891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7892 " Found %s profile",name);
7894 ping_profile=CloneStringInfo(profile);
7895 data=GetStringInfoDatum(ping_profile),
7896 length=(png_uint_32) GetStringInfoLength(ping_profile);
7901 (void) WriteBlobMSBULong(image,length-5); /* data length */
7902 (void) WriteBlob(image,length-1,data+1);
7903 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7904 ping_profile=DestroyStringInfo(ping_profile);
7908 name=GetNextImageProfile(image);
7915 /* Write one PNG image */
7916 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7917 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7948 ping_trans_alpha[256];
7976 ping_have_cheap_transparency,
7989 /* ping_exclude_EXIF, */
7992 /* ping_exclude_iTXt, */
7997 /* ping_exclude_tRNS, */
7999 ping_exclude_zCCP, /* hex-encoded iCCP */
8002 ping_preserve_colormap,
8004 ping_need_colortype_warning,
8012 *volatile pixel_info;
8031 ping_interlace_method,
8032 ping_compression_method,
8049 number_semitransparent,
8051 ping_pHYs_unit_type;
8054 ping_pHYs_x_resolution,
8055 ping_pHYs_y_resolution;
8057 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8058 " Enter WriteOnePNGImage()");
8060 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8061 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8062 if (image_info == (ImageInfo *) NULL)
8063 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8065 /* Define these outside of the following "if logging()" block so they will
8066 * show in debuggers.
8069 (void) ConcatenateMagickString(im_vers,
8070 MagickLibVersionText,MaxTextExtent);
8071 (void) ConcatenateMagickString(im_vers,
8072 MagickLibAddendum,MaxTextExtent);
8075 (void) ConcatenateMagickString(libpng_vers,
8076 PNG_LIBPNG_VER_STRING,32);
8078 (void) ConcatenateMagickString(libpng_runv,
8079 png_get_libpng_ver(NULL),32);
8082 (void) ConcatenateMagickString(zlib_vers,
8085 (void) ConcatenateMagickString(zlib_runv,
8090 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8092 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8094 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8096 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8099 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8101 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8103 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8108 /* Initialize some stuff */
8111 ping_interlace_method=0,
8112 ping_compression_method=0,
8113 ping_filter_method=0,
8116 ping_background.red = 0;
8117 ping_background.green = 0;
8118 ping_background.blue = 0;
8119 ping_background.gray = 0;
8120 ping_background.index = 0;
8122 ping_trans_color.red=0;
8123 ping_trans_color.green=0;
8124 ping_trans_color.blue=0;
8125 ping_trans_color.gray=0;
8127 ping_pHYs_unit_type = 0;
8128 ping_pHYs_x_resolution = 0;
8129 ping_pHYs_y_resolution = 0;
8131 ping_have_blob=MagickFalse;
8132 ping_have_cheap_transparency=MagickFalse;
8133 ping_have_color=MagickTrue;
8134 ping_have_non_bw=MagickTrue;
8135 ping_have_PLTE=MagickFalse;
8136 ping_have_bKGD=MagickFalse;
8137 ping_have_iCCP=MagickFalse;
8138 ping_have_pHYs=MagickFalse;
8139 ping_have_sRGB=MagickFalse;
8140 ping_have_tRNS=MagickFalse;
8142 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8143 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8144 ping_exclude_date=mng_info->ping_exclude_date;
8145 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8146 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8147 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8148 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8149 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8150 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8151 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8152 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8153 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8154 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8155 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8156 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8158 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8159 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8160 ping_need_colortype_warning = MagickFalse;
8162 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8163 * i.e., eliminate the ICC profile and set image->rendering_intent.
8164 * Note that this will not involve any changes to the actual pixels
8165 * but merely passes information to applications that read the resulting
8168 * To do: recognize other variants of the sRGB profile, using the CRC to
8169 * verify all recognized variants including the 7 already known.
8171 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8173 * Use something other than image->rendering_intent to record the fact
8174 * that the sRGB profile was found.
8176 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8177 * profile. Record the Blackpoint Compensation, if any.
8179 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8187 ResetImageProfileIterator(image);
8188 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8190 profile=GetImageProfile(image,name);
8192 if (profile != (StringInfo *) NULL)
8194 if ((LocaleCompare(name,"ICC") == 0) ||
8195 (LocaleCompare(name,"ICM") == 0))
8210 length=(png_uint_32) GetStringInfoLength(profile);
8212 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8214 if (length == sRGB_info[icheck].len)
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " Got a %lu-byte ICC profile (potentially sRGB)",
8220 (unsigned long) length);
8222 data=GetStringInfoDatum(profile);
8223 profile_crc=crc32(0,data,length);
8225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226 " with crc=%8x",(unsigned int) profile_crc);
8230 if (profile_crc == sRGB_info[icheck].crc)
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " It is sRGB with rendering intent = %s",
8234 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8235 sRGB_info[icheck].intent));
8236 if (image->rendering_intent==UndefinedIntent)
8238 image->rendering_intent=
8239 Magick_RenderingIntent_from_PNG_RenderingIntent(
8240 sRGB_info[icheck].intent);
8242 ping_exclude_iCCP = MagickTrue;
8243 ping_exclude_zCCP = MagickTrue;
8244 ping_have_sRGB = MagickTrue;
8249 if (sRGB_info[icheck].len == 0)
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251 " Got a %lu-byte ICC profile not recognized as sRGB",
8252 (unsigned long) length);
8255 name=GetNextImageProfile(image);
8260 number_semitransparent = 0;
8261 number_transparent = 0;
8263 if (logging != MagickFalse)
8265 if (image->storage_class == UndefinedClass)
8266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " storage_class=UndefinedClass");
8268 if (image->storage_class == DirectClass)
8269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8270 " storage_class=DirectClass");
8271 if (image->storage_class == PseudoClass)
8272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8273 " storage_class=PseudoClass");
8276 if (image->storage_class == PseudoClass &&
8277 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8278 mng_info->write_png48 || mng_info->write_png64 ||
8279 (mng_info->write_png_colortype != 1 &&
8280 mng_info->write_png_colortype != 5)))
8282 (void) SyncImage(image,exception);
8283 image->storage_class = DirectClass;
8286 if (ping_preserve_colormap == MagickFalse)
8288 if (image->storage_class != PseudoClass && image->colormap != NULL)
8290 /* Free the bogus colormap; it can cause trouble later */
8291 if (logging != MagickFalse)
8292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293 " Freeing bogus colormap");
8294 (void) RelinquishMagickMemory(image->colormap);
8295 image->colormap=NULL;
8299 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8300 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8303 Sometimes we get PseudoClass images whose RGB values don't match
8304 the colors in the colormap. This code syncs the RGB values.
8306 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8307 (void) SyncImage(image,exception);
8309 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8310 if (image->depth > 8)
8312 if (logging != MagickFalse)
8313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8320 /* Respect the -depth option */
8321 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8326 if (image->depth > 8)
8328 #if MAGICKCORE_QUANTUM_DEPTH > 16
8329 /* Scale to 16-bit */
8330 LBR16PacketRGBO(image->background_color);
8332 for (y=0; y < (ssize_t) image->rows; y++)
8334 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8336 if (r == (Quantum *) NULL)
8339 for (x=0; x < (ssize_t) image->columns; x++)
8342 r+=GetPixelChannels(image);
8345 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8349 if (image->storage_class == PseudoClass && image->colormap != NULL)
8351 for (i=0; i < (ssize_t) image->colors; i++)
8353 LBR16PacketRGBO(image->colormap[i]);
8356 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8359 else if (image->depth > 4)
8361 #if MAGICKCORE_QUANTUM_DEPTH > 8
8362 /* Scale to 8-bit */
8363 LBR08PacketRGBO(image->background_color);
8365 for (y=0; y < (ssize_t) image->rows; y++)
8367 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8369 if (r == (Quantum *) NULL)
8372 for (x=0; x < (ssize_t) image->columns; x++)
8375 r+=GetPixelChannels(image);
8378 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8382 if (image->storage_class == PseudoClass && image->colormap != NULL)
8384 for (i=0; i < (ssize_t) image->colors; i++)
8386 LBR08PacketRGBO(image->colormap[i]);
8389 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8392 if (image->depth > 2)
8394 /* Scale to 4-bit */
8395 LBR04PacketRGBO(image->background_color);
8397 for (y=0; y < (ssize_t) image->rows; y++)
8399 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8401 if (r == (Quantum *) NULL)
8404 for (x=0; x < (ssize_t) image->columns; x++)
8407 r+=GetPixelChannels(image);
8410 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8414 if (image->storage_class == PseudoClass && image->colormap != NULL)
8416 for (i=0; i < (ssize_t) image->colors; i++)
8418 LBR04PacketRGBO(image->colormap[i]);
8423 else if (image->depth > 1)
8425 /* Scale to 2-bit */
8426 LBR02PacketRGBO(image->background_color);
8428 for (y=0; y < (ssize_t) image->rows; y++)
8430 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8432 if (r == (Quantum *) NULL)
8435 for (x=0; x < (ssize_t) image->columns; x++)
8438 r+=GetPixelChannels(image);
8441 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8445 if (image->storage_class == PseudoClass && image->colormap != NULL)
8447 for (i=0; i < (ssize_t) image->colors; i++)
8449 LBR02PacketRGBO(image->colormap[i]);
8455 /* Scale to 1-bit */
8456 LBR01PacketRGBO(image->background_color);
8458 for (y=0; y < (ssize_t) image->rows; y++)
8460 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8462 if (r == (Quantum *) NULL)
8465 for (x=0; x < (ssize_t) image->columns; x++)
8468 r+=GetPixelChannels(image);
8471 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8475 if (image->storage_class == PseudoClass && image->colormap != NULL)
8477 for (i=0; i < (ssize_t) image->colors; i++)
8479 LBR01PacketRGBO(image->colormap[i]);
8485 /* To do: set to next higher multiple of 8 */
8486 if (image->depth < 8)
8489 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8490 /* PNG does not handle depths greater than 16 so reduce it even
8493 if (image->depth > 8)
8497 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8498 if (image->depth > 8)
8500 /* To do: fill low byte properly */
8504 if (image->depth == 16 && mng_info->write_png_depth != 16)
8505 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8509 image_colors = (int) image->colors;
8510 number_opaque = (int) image->colors;
8511 number_transparent = 0;
8512 number_semitransparent = 0;
8514 if (mng_info->write_png_colortype &&
8515 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8516 mng_info->write_png_colortype < 4 &&
8517 image->alpha_trait != BlendPixelTrait)))
8519 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8520 * are not going to need the result.
8522 if (mng_info->write_png_colortype == 1 ||
8523 mng_info->write_png_colortype == 5)
8524 ping_have_color=MagickFalse;
8526 if (image->alpha_trait == BlendPixelTrait)
8528 number_transparent = 2;
8529 number_semitransparent = 1;
8533 if (mng_info->write_png_colortype < 7)
8537 * Normally we run this just once, but in the case of writing PNG8
8538 * we reduce the transparency to binary and run again, then if there
8539 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8540 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8541 * palette. Then (To do) we take care of a final reduction that is only
8542 * needed if there are still 256 colors present and one of them has both
8543 * transparent and opaque instances.
8546 tried_332 = MagickFalse;
8547 tried_333 = MagickFalse;
8548 tried_444 = MagickFalse;
8553 * Sometimes we get DirectClass images that have 256 colors or fewer.
8554 * This code will build a colormap.
8556 * Also, sometimes we get PseudoClass images with an out-of-date
8557 * colormap. This code will replace the colormap with a new one.
8558 * Sometimes we get PseudoClass images that have more than 256 colors.
8559 * This code will delete the colormap and change the image to
8562 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8563 * even though it sometimes contains left-over non-opaque values.
8565 * Also we gather some information (number of opaque, transparent,
8566 * and semitransparent pixels, and whether the image has any non-gray
8567 * pixels or only black-and-white pixels) that we might need later.
8569 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8570 * we need to check for bogus non-opaque values, at least.
8578 semitransparent[260],
8581 register const Quantum
8588 if (logging != MagickFalse)
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590 " Enter BUILD_PALETTE:");
8592 if (logging != MagickFalse)
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595 " image->columns=%.20g",(double) image->columns);
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " image->rows=%.20g",(double) image->rows);
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8599 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " image->depth=%.20g",(double) image->depth);
8603 if (image->storage_class == PseudoClass && image->colormap != NULL)
8605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8606 " Original colormap:");
8607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608 " i (red,green,blue,alpha)");
8610 for (i=0; i < 256; i++)
8612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8613 " %d (%d,%d,%d,%d)",
8615 (int) image->colormap[i].red,
8616 (int) image->colormap[i].green,
8617 (int) image->colormap[i].blue,
8618 (int) image->colormap[i].alpha);
8621 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626 " %d (%d,%d,%d,%d)",
8628 (int) image->colormap[i].red,
8629 (int) image->colormap[i].green,
8630 (int) image->colormap[i].blue,
8631 (int) image->colormap[i].alpha);
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637 " image->colors=%d",(int) image->colors);
8639 if (image->colors == 0)
8640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8641 " (zero means unknown)");
8643 if (ping_preserve_colormap == MagickFalse)
8644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8645 " Regenerate the colormap");
8650 number_semitransparent = 0;
8651 number_transparent = 0;
8653 for (y=0; y < (ssize_t) image->rows; y++)
8655 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8657 if (q == (Quantum *) NULL)
8660 for (x=0; x < (ssize_t) image->columns; x++)
8662 if (image->alpha_trait != BlendPixelTrait ||
8663 GetPixelAlpha(image,q) == OpaqueAlpha)
8665 if (number_opaque < 259)
8667 if (number_opaque == 0)
8669 GetPixelInfoPixel(image, q, opaque);
8670 opaque[0].alpha=OpaqueAlpha;
8674 for (i=0; i< (ssize_t) number_opaque; i++)
8676 if (IsPixelEquivalent(image,q, opaque+i))
8680 if (i == (ssize_t) number_opaque && number_opaque < 259)
8683 GetPixelInfoPixel(image, q, opaque+i);
8684 opaque[i].alpha=OpaqueAlpha;
8688 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8690 if (number_transparent < 259)
8692 if (number_transparent == 0)
8694 GetPixelInfoPixel(image, q, transparent);
8695 ping_trans_color.red=(unsigned short)
8696 GetPixelRed(image,q);
8697 ping_trans_color.green=(unsigned short)
8698 GetPixelGreen(image,q);
8699 ping_trans_color.blue=(unsigned short)
8700 GetPixelBlue(image,q);
8701 ping_trans_color.gray=(unsigned short)
8702 GetPixelGray(image,q);
8703 number_transparent = 1;
8706 for (i=0; i< (ssize_t) number_transparent; i++)
8708 if (IsPixelEquivalent(image,q, transparent+i))
8712 if (i == (ssize_t) number_transparent &&
8713 number_transparent < 259)
8715 number_transparent++;
8716 GetPixelInfoPixel(image,q,transparent+i);
8722 if (number_semitransparent < 259)
8724 if (number_semitransparent == 0)
8726 GetPixelInfoPixel(image,q,semitransparent);
8727 number_semitransparent = 1;
8730 for (i=0; i< (ssize_t) number_semitransparent; i++)
8732 if (IsPixelEquivalent(image,q, semitransparent+i)
8733 && GetPixelAlpha(image,q) ==
8734 semitransparent[i].alpha)
8738 if (i == (ssize_t) number_semitransparent &&
8739 number_semitransparent < 259)
8741 number_semitransparent++;
8742 GetPixelInfoPixel(image, q, semitransparent+i);
8746 q+=GetPixelChannels(image);
8750 if (mng_info->write_png8 == MagickFalse &&
8751 ping_exclude_bKGD == MagickFalse)
8753 /* Add the background color to the palette, if it
8754 * isn't already there.
8756 if (logging != MagickFalse)
8758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8759 " Check colormap for background (%d,%d,%d)",
8760 (int) image->background_color.red,
8761 (int) image->background_color.green,
8762 (int) image->background_color.blue);
8764 for (i=0; i<number_opaque; i++)
8766 if (opaque[i].red == image->background_color.red &&
8767 opaque[i].green == image->background_color.green &&
8768 opaque[i].blue == image->background_color.blue)
8771 if (number_opaque < 259 && i == number_opaque)
8773 opaque[i] = image->background_color;
8774 ping_background.index = i;
8776 if (logging != MagickFalse)
8778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8779 " background_color index is %d",(int) i);
8783 else if (logging != MagickFalse)
8784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785 " No room in the colormap to add background color");
8788 image_colors=number_opaque+number_transparent+number_semitransparent;
8790 if (logging != MagickFalse)
8792 if (image_colors > 256)
8793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8794 " image has more than 256 colors");
8797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798 " image has %d colors",image_colors);
8801 if (ping_preserve_colormap != MagickFalse)
8804 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8806 ping_have_color=MagickFalse;
8807 ping_have_non_bw=MagickFalse;
8809 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8812 "incompatible colorspace");
8813 ping_have_color=MagickTrue;
8814 ping_have_non_bw=MagickTrue;
8817 if(image_colors > 256)
8819 for (y=0; y < (ssize_t) image->rows; y++)
8821 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8823 if (q == (Quantum *) NULL)
8827 for (x=0; x < (ssize_t) image->columns; x++)
8829 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8830 GetPixelRed(image,s) != GetPixelBlue(image,s))
8832 ping_have_color=MagickTrue;
8833 ping_have_non_bw=MagickTrue;
8836 s+=GetPixelChannels(image);
8839 if (ping_have_color != MagickFalse)
8842 /* Worst case is black-and-white; we are looking at every
8846 if (ping_have_non_bw == MagickFalse)
8849 for (x=0; x < (ssize_t) image->columns; x++)
8851 if (GetPixelRed(image,s) != 0 &&
8852 GetPixelRed(image,s) != QuantumRange)
8854 ping_have_non_bw=MagickTrue;
8857 s+=GetPixelChannels(image);
8864 if (image_colors < 257)
8870 * Initialize image colormap.
8873 if (logging != MagickFalse)
8874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8875 " Sort the new colormap");
8877 /* Sort palette, transparent first */;
8881 for (i=0; i<number_transparent; i++)
8882 colormap[n++] = transparent[i];
8884 for (i=0; i<number_semitransparent; i++)
8885 colormap[n++] = semitransparent[i];
8887 for (i=0; i<number_opaque; i++)
8888 colormap[n++] = opaque[i];
8890 ping_background.index +=
8891 (number_transparent + number_semitransparent);
8893 /* image_colors < 257; search the colormap instead of the pixels
8894 * to get ping_have_color and ping_have_non_bw
8898 if (ping_have_color == MagickFalse)
8900 if (colormap[i].red != colormap[i].green ||
8901 colormap[i].red != colormap[i].blue)
8903 ping_have_color=MagickTrue;
8904 ping_have_non_bw=MagickTrue;
8909 if (ping_have_non_bw == MagickFalse)
8911 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8912 ping_have_non_bw=MagickTrue;
8916 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8917 (number_transparent == 0 && number_semitransparent == 0)) &&
8918 (((mng_info->write_png_colortype-1) ==
8919 PNG_COLOR_TYPE_PALETTE) ||
8920 (mng_info->write_png_colortype == 0)))
8922 if (logging != MagickFalse)
8924 if (n != (ssize_t) image_colors)
8925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8926 " image_colors (%d) and n (%d) don't match",
8929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930 " AcquireImageColormap");
8933 image->colors = image_colors;
8935 if (AcquireImageColormap(image,image_colors,exception) ==
8937 ThrowWriterException(ResourceLimitError,
8938 "MemoryAllocationFailed");
8940 for (i=0; i< (ssize_t) image_colors; i++)
8941 image->colormap[i] = colormap[i];
8943 if (logging != MagickFalse)
8945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8946 " image->colors=%d (%d)",
8947 (int) image->colors, image_colors);
8949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8950 " Update the pixel indexes");
8953 /* Sync the pixel indices with the new colormap */
8955 for (y=0; y < (ssize_t) image->rows; y++)
8957 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8959 if (q == (Quantum *) NULL)
8962 for (x=0; x < (ssize_t) image->columns; x++)
8964 for (i=0; i< (ssize_t) image_colors; i++)
8966 if ((image->alpha_trait != BlendPixelTrait ||
8967 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8968 image->colormap[i].red == GetPixelRed(image,q) &&
8969 image->colormap[i].green == GetPixelGreen(image,q) &&
8970 image->colormap[i].blue == GetPixelBlue(image,q))
8972 SetPixelIndex(image,i,q);
8976 q+=GetPixelChannels(image);
8979 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8985 if (logging != MagickFalse)
8987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988 " image->colors=%d", (int) image->colors);
8990 if (image->colormap != NULL)
8992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993 " i (red,green,blue,alpha)");
8995 for (i=0; i < (ssize_t) image->colors; i++)
8997 if (i < 300 || i >= (ssize_t) image->colors - 10)
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " %d (%d,%d,%d,%d)",
9002 (int) image->colormap[i].red,
9003 (int) image->colormap[i].green,
9004 (int) image->colormap[i].blue,
9005 (int) image->colormap[i].alpha);
9010 if (number_transparent < 257)
9011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012 " number_transparent = %d",
9013 number_transparent);
9016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017 " number_transparent > 256");
9019 if (number_opaque < 257)
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 " number_opaque = %d",
9025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026 " number_opaque > 256");
9028 if (number_semitransparent < 257)
9029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030 " number_semitransparent = %d",
9031 number_semitransparent);
9034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035 " number_semitransparent > 256");
9037 if (ping_have_non_bw == MagickFalse)
9038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9039 " All pixels and the background are black or white");
9041 else if (ping_have_color == MagickFalse)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " All pixels and the background are gray");
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " At least one pixel or the background is non-gray");
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " Exit BUILD_PALETTE:");
9053 if (mng_info->write_png8 == MagickFalse)
9056 /* Make any reductions necessary for the PNG8 format */
9057 if (image_colors <= 256 &&
9058 image_colors != 0 && image->colormap != NULL &&
9059 number_semitransparent == 0 &&
9060 number_transparent <= 1)
9063 /* PNG8 can't have semitransparent colors so we threshold the
9064 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9065 * transparent color so if more than one is transparent we merge
9066 * them into image->background_color.
9068 if (number_semitransparent != 0 || number_transparent > 1)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Thresholding the alpha channel to binary");
9073 for (y=0; y < (ssize_t) image->rows; y++)
9075 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9077 if (r == (Quantum *) NULL)
9080 for (x=0; x < (ssize_t) image->columns; x++)
9082 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9084 SetPixelInfoPixel(image,&image->background_color,r);
9085 SetPixelAlpha(image,TransparentAlpha,r);
9088 SetPixelAlpha(image,OpaqueAlpha,r);
9089 r+=GetPixelChannels(image);
9092 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9095 if (image_colors != 0 && image_colors <= 256 &&
9096 image->colormap != NULL)
9097 for (i=0; i<image_colors; i++)
9098 image->colormap[i].alpha =
9099 (image->colormap[i].alpha > TransparentAlpha/2 ?
9100 TransparentAlpha : OpaqueAlpha);
9105 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9106 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9107 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9110 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9112 if (logging != MagickFalse)
9113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9114 " Quantizing the background color to 4-4-4");
9116 tried_444 = MagickTrue;
9118 LBR04PacketRGB(image->background_color);
9120 if (logging != MagickFalse)
9121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9122 " Quantizing the pixel colors to 4-4-4");
9124 if (image->colormap == NULL)
9126 for (y=0; y < (ssize_t) image->rows; y++)
9128 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9130 if (r == (Quantum *) NULL)
9133 for (x=0; x < (ssize_t) image->columns; x++)
9135 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9137 r+=GetPixelChannels(image);
9140 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9145 else /* Should not reach this; colormap already exists and
9148 if (logging != MagickFalse)
9149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9150 " Quantizing the colormap to 4-4-4");
9152 for (i=0; i<image_colors; i++)
9154 LBR04PacketRGB(image->colormap[i]);
9160 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9162 if (logging != MagickFalse)
9163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164 " Quantizing the background color to 3-3-3");
9166 tried_333 = MagickTrue;
9168 LBR03PacketRGB(image->background_color);
9170 if (logging != MagickFalse)
9171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9172 " Quantizing the pixel colors to 3-3-3-1");
9174 if (image->colormap == NULL)
9176 for (y=0; y < (ssize_t) image->rows; y++)
9178 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9180 if (r == (Quantum *) NULL)
9183 for (x=0; x < (ssize_t) image->columns; x++)
9185 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9187 r+=GetPixelChannels(image);
9190 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9195 else /* Should not reach this; colormap already exists and
9198 if (logging != MagickFalse)
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200 " Quantizing the colormap to 3-3-3-1");
9201 for (i=0; i<image_colors; i++)
9203 LBR03PacketRGB(image->colormap[i]);
9209 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9211 if (logging != MagickFalse)
9212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213 " Quantizing the background color to 3-3-2");
9215 tried_332 = MagickTrue;
9217 /* Red and green were already done so we only quantize the blue
9221 LBR02PacketBlue(image->background_color);
9223 if (logging != MagickFalse)
9224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9225 " Quantizing the pixel colors to 3-3-2-1");
9227 if (image->colormap == NULL)
9229 for (y=0; y < (ssize_t) image->rows; y++)
9231 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9233 if (r == (Quantum *) NULL)
9236 for (x=0; x < (ssize_t) image->columns; x++)
9238 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9240 r+=GetPixelChannels(image);
9243 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9248 else /* Should not reach this; colormap already exists and
9251 if (logging != MagickFalse)
9252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9253 " Quantizing the colormap to 3-3-2-1");
9254 for (i=0; i<image_colors; i++)
9256 LBR02PacketBlue(image->colormap[i]);
9262 if (image_colors == 0 || image_colors > 256)
9264 /* Take care of special case with 256 opaque colors + 1 transparent
9265 * color. We don't need to quantize to 2-3-2-1; we only need to
9266 * eliminate one color, so we'll merge the two darkest red
9267 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9269 if (logging != MagickFalse)
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271 " Merging two dark red background colors to 3-3-2-1");
9273 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9274 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9275 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9277 image->background_color.red=ScaleCharToQuantum(0x24);
9280 if (logging != MagickFalse)
9281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9282 " Merging two dark red pixel colors to 3-3-2-1");
9284 if (image->colormap == NULL)
9286 for (y=0; y < (ssize_t) image->rows; y++)
9288 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9290 if (r == (Quantum *) NULL)
9293 for (x=0; x < (ssize_t) image->columns; x++)
9295 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9296 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9297 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9298 GetPixelAlpha(image,r) == OpaqueAlpha)
9300 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9302 r+=GetPixelChannels(image);
9305 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9313 for (i=0; i<image_colors; i++)
9315 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9316 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9317 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9319 image->colormap[i].red=ScaleCharToQuantum(0x24);
9326 /* END OF BUILD_PALETTE */
9328 /* If we are excluding the tRNS chunk and there is transparency,
9329 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9332 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9333 (number_transparent != 0 || number_semitransparent != 0))
9335 unsigned int colortype=mng_info->write_png_colortype;
9337 if (ping_have_color == MagickFalse)
9338 mng_info->write_png_colortype = 5;
9341 mng_info->write_png_colortype = 7;
9343 if (colortype != 0 &&
9344 mng_info->write_png_colortype != colortype)
9345 ping_need_colortype_warning=MagickTrue;
9349 /* See if cheap transparency is possible. It is only possible
9350 * when there is a single transparent color, no semitransparent
9351 * color, and no opaque color that has the same RGB components
9352 * as the transparent color. We only need this information if
9353 * we are writing a PNG with colortype 0 or 2, and we have not
9354 * excluded the tRNS chunk.
9356 if (number_transparent == 1 &&
9357 mng_info->write_png_colortype < 4)
9359 ping_have_cheap_transparency = MagickTrue;
9361 if (number_semitransparent != 0)
9362 ping_have_cheap_transparency = MagickFalse;
9364 else if (image_colors == 0 || image_colors > 256 ||
9365 image->colormap == NULL)
9367 register const Quantum
9370 for (y=0; y < (ssize_t) image->rows; y++)
9372 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9374 if (q == (Quantum *) NULL)
9377 for (x=0; x < (ssize_t) image->columns; x++)
9379 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9380 (unsigned short) GetPixelRed(image,q) ==
9381 ping_trans_color.red &&
9382 (unsigned short) GetPixelGreen(image,q) ==
9383 ping_trans_color.green &&
9384 (unsigned short) GetPixelBlue(image,q) ==
9385 ping_trans_color.blue)
9387 ping_have_cheap_transparency = MagickFalse;
9391 q+=GetPixelChannels(image);
9394 if (ping_have_cheap_transparency == MagickFalse)
9400 /* Assuming that image->colormap[0] is the one transparent color
9401 * and that all others are opaque.
9403 if (image_colors > 1)
9404 for (i=1; i<image_colors; i++)
9405 if (image->colormap[i].red == image->colormap[0].red &&
9406 image->colormap[i].green == image->colormap[0].green &&
9407 image->colormap[i].blue == image->colormap[0].blue)
9409 ping_have_cheap_transparency = MagickFalse;
9414 if (logging != MagickFalse)
9416 if (ping_have_cheap_transparency == MagickFalse)
9417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418 " Cheap transparency is not possible.");
9421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422 " Cheap transparency is possible.");
9426 ping_have_cheap_transparency = MagickFalse;
9428 image_depth=image->depth;
9430 quantum_info = (QuantumInfo *) NULL;
9432 image_colors=(int) image->colors;
9433 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9435 if (mng_info->write_png_colortype < 5)
9436 mng_info->IsPalette=image->storage_class == PseudoClass &&
9437 image_colors <= 256 && image->colormap != NULL;
9439 mng_info->IsPalette = MagickFalse;
9441 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9442 (image->colors == 0 || image->colormap == NULL))
9444 image_info=DestroyImageInfo(image_info);
9445 image=DestroyImage(image);
9446 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9447 "Cannot write PNG8 or color-type 3; colormap is NULL",
9448 "`%s'",IMimage->filename);
9449 return(MagickFalse);
9453 Allocate the PNG structures
9455 #ifdef PNG_USER_MEM_SUPPORTED
9456 error_info.image=image;
9457 error_info.exception=exception;
9458 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9459 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9460 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9463 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9464 MagickPNGErrorHandler,MagickPNGWarningHandler);
9467 if (ping == (png_struct *) NULL)
9468 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9470 ping_info=png_create_info_struct(ping);
9472 if (ping_info == (png_info *) NULL)
9474 png_destroy_write_struct(&ping,(png_info **) NULL);
9475 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9478 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9479 pixel_info=(MemoryInfo *) NULL;
9481 if (setjmp(png_jmpbuf(ping)))
9487 if (image_info->verbose)
9488 (void) printf("PNG write has failed.\n");
9490 png_destroy_write_struct(&ping,&ping_info);
9491 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9492 UnlockSemaphoreInfo(ping_semaphore);
9495 if (pixel_info != (MemoryInfo *) NULL)
9496 pixel_info=RelinquishVirtualMemory(pixel_info);
9498 if (quantum_info != (QuantumInfo *) NULL)
9499 quantum_info=DestroyQuantumInfo(quantum_info);
9501 if (ping_have_blob != MagickFalse)
9502 (void) CloseBlob(image);
9503 image_info=DestroyImageInfo(image_info);
9504 image=DestroyImage(image);
9505 return(MagickFalse);
9508 /* { For navigation to end of SETJMP-protected block. Within this
9509 * block, use png_error() instead of Throwing an Exception, to ensure
9510 * that libpng is able to clean up, and that the semaphore is unlocked.
9513 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9514 LockSemaphoreInfo(ping_semaphore);
9517 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9518 /* Allow benign errors */
9519 png_set_benign_errors(ping, 1);
9523 Prepare PNG for writing.
9526 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9527 if (mng_info->write_mng)
9529 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9530 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9531 /* Disable new libpng-1.5.10 feature when writing a MNG because
9532 * zero-length PLTE is OK
9534 png_set_check_for_invalid_index (ping, 0);
9539 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9540 if (mng_info->write_mng)
9541 png_permit_empty_plte(ping,MagickTrue);
9548 ping_width=(png_uint_32) image->columns;
9549 ping_height=(png_uint_32) image->rows;
9551 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9554 if (mng_info->write_png48 || mng_info->write_png64)
9557 if (mng_info->write_png_depth != 0)
9558 image_depth=mng_info->write_png_depth;
9560 /* Adjust requested depth to next higher valid depth if necessary */
9561 if (image_depth > 8)
9564 if ((image_depth > 4) && (image_depth < 8))
9567 if (image_depth == 3)
9570 if (logging != MagickFalse)
9572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9573 " width=%.20g",(double) ping_width);
9574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9575 " height=%.20g",(double) ping_height);
9576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577 " image_matte=%.20g",(double) image->alpha_trait);
9578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579 " image->depth=%.20g",(double) image->depth);
9580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9581 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9584 save_image_depth=image_depth;
9585 ping_bit_depth=(png_byte) save_image_depth;
9588 #if defined(PNG_pHYs_SUPPORTED)
9589 if (ping_exclude_pHYs == MagickFalse)
9591 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9592 (!mng_info->write_mng || !mng_info->equal_physs))
9594 if (logging != MagickFalse)
9595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9596 " Setting up pHYs chunk");
9598 if (image->units == PixelsPerInchResolution)
9600 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9601 ping_pHYs_x_resolution=
9602 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9603 ping_pHYs_y_resolution=
9604 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9607 else if (image->units == PixelsPerCentimeterResolution)
9609 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9610 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9611 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9616 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9617 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9618 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9621 if (logging != MagickFalse)
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9624 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9625 (int) ping_pHYs_unit_type);
9626 ping_have_pHYs = MagickTrue;
9631 if (ping_exclude_bKGD == MagickFalse)
9633 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9639 if (ping_bit_depth == 8)
9642 if (ping_bit_depth == 4)
9645 if (ping_bit_depth == 2)
9648 if (ping_bit_depth == 1)
9651 ping_background.red=(png_uint_16)
9652 (ScaleQuantumToShort(image->background_color.red) & mask);
9654 ping_background.green=(png_uint_16)
9655 (ScaleQuantumToShort(image->background_color.green) & mask);
9657 ping_background.blue=(png_uint_16)
9658 (ScaleQuantumToShort(image->background_color.blue) & mask);
9660 ping_background.gray=(png_uint_16) ping_background.green;
9663 if (logging != MagickFalse)
9665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9666 " Setting up bKGD chunk (1)");
9667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9668 " background_color index is %d",
9669 (int) ping_background.index);
9671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9672 " ping_bit_depth=%d",ping_bit_depth);
9675 ping_have_bKGD = MagickTrue;
9679 Select the color type.
9684 if (mng_info->IsPalette && mng_info->write_png8)
9686 /* To do: make this a function cause it's used twice, except
9687 for reducing the sample depth from 8. */
9689 number_colors=image_colors;
9691 ping_have_tRNS=MagickFalse;
9696 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9698 if (logging != MagickFalse)
9699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9700 " Setting up PLTE chunk with %d colors (%d)",
9701 number_colors, image_colors);
9703 for (i=0; i < (ssize_t) number_colors; i++)
9705 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9706 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9707 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9708 if (logging != MagickFalse)
9709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9710 #if MAGICKCORE_QUANTUM_DEPTH == 8
9711 " %3ld (%3d,%3d,%3d)",
9713 " %5ld (%5d,%5d,%5d)",
9715 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9719 ping_have_PLTE=MagickTrue;
9720 image_depth=ping_bit_depth;
9723 if (matte != MagickFalse)
9726 Identify which colormap entry is transparent.
9728 assert(number_colors <= 256);
9729 assert(image->colormap != NULL);
9731 for (i=0; i < (ssize_t) number_transparent; i++)
9732 ping_trans_alpha[i]=0;
9735 ping_num_trans=(unsigned short) (number_transparent +
9736 number_semitransparent);
9738 if (ping_num_trans == 0)
9739 ping_have_tRNS=MagickFalse;
9742 ping_have_tRNS=MagickTrue;
9745 if (ping_exclude_bKGD == MagickFalse)
9748 * Identify which colormap entry is the background color.
9751 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9752 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9755 ping_background.index=(png_byte) i;
9757 if (logging != MagickFalse)
9759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9760 " background_color index is %d",
9761 (int) ping_background.index);
9764 } /* end of write_png8 */
9766 else if (mng_info->write_png_colortype == 1)
9768 image_matte=MagickFalse;
9769 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9772 else if (mng_info->write_png24 || mng_info->write_png48 ||
9773 mng_info->write_png_colortype == 3)
9775 image_matte=MagickFalse;
9776 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9779 else if (mng_info->write_png32 || mng_info->write_png64 ||
9780 mng_info->write_png_colortype == 7)
9782 image_matte=MagickTrue;
9783 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9786 else /* mng_info->write_pngNN not specified */
9788 image_depth=ping_bit_depth;
9790 if (mng_info->write_png_colortype != 0)
9792 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9794 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9795 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9796 image_matte=MagickTrue;
9799 image_matte=MagickFalse;
9801 if (logging != MagickFalse)
9802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9803 " PNG colortype %d was specified:",(int) ping_color_type);
9806 else /* write_png_colortype not specified */
9808 if (logging != MagickFalse)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9810 " Selecting PNG colortype:");
9812 ping_color_type=(png_byte) ((matte != MagickFalse)?
9813 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9815 if (image_info->type == TrueColorType)
9817 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9818 image_matte=MagickFalse;
9821 if (image_info->type == TrueColorMatteType)
9823 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9824 image_matte=MagickTrue;
9827 if (image_info->type == PaletteType ||
9828 image_info->type == PaletteMatteType)
9829 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9831 if (mng_info->write_png_colortype == 0 &&
9832 (image_info->type == UndefinedType ||
9833 image_info->type == OptimizeType))
9835 if (ping_have_color == MagickFalse)
9837 if (image_matte == MagickFalse)
9839 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9840 image_matte=MagickFalse;
9845 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9846 image_matte=MagickTrue;
9851 if (image_matte == MagickFalse)
9853 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9854 image_matte=MagickFalse;
9859 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9860 image_matte=MagickTrue;
9867 if (logging != MagickFalse)
9868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9869 " Selected PNG colortype=%d",ping_color_type);
9871 if (ping_bit_depth < 8)
9873 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9874 ping_color_type == PNG_COLOR_TYPE_RGB ||
9875 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9879 old_bit_depth=ping_bit_depth;
9881 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9883 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9887 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9892 if (image->colors == 0)
9895 png_error(ping,"image has 0 colors");
9898 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9899 ping_bit_depth <<= 1;
9902 if (logging != MagickFalse)
9904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9905 " Number of colors: %.20g",(double) image_colors);
9907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9908 " Tentative PNG bit depth: %d",ping_bit_depth);
9911 if (ping_bit_depth < (int) mng_info->write_png_depth)
9912 ping_bit_depth = mng_info->write_png_depth;
9915 image_depth=ping_bit_depth;
9917 if (logging != MagickFalse)
9919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9920 " Tentative PNG color type: %s (%.20g)",
9921 PngColorTypeToString(ping_color_type),
9922 (double) ping_color_type);
9924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9925 " image_info->type: %.20g",(double) image_info->type);
9927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928 " image_depth: %.20g",(double) image_depth);
9930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9932 " image->depth: %.20g",(double) image->depth);
9934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9935 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9938 if (matte != MagickFalse)
9940 if (mng_info->IsPalette)
9942 if (mng_info->write_png_colortype == 0)
9944 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9946 if (ping_have_color != MagickFalse)
9947 ping_color_type=PNG_COLOR_TYPE_RGBA;
9951 * Determine if there is any transparent color.
9953 if (number_transparent + number_semitransparent == 0)
9956 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9959 image_matte=MagickFalse;
9961 if (mng_info->write_png_colortype == 0)
9962 ping_color_type&=0x03;
9972 if (ping_bit_depth == 8)
9975 if (ping_bit_depth == 4)
9978 if (ping_bit_depth == 2)
9981 if (ping_bit_depth == 1)
9984 ping_trans_color.red=(png_uint_16)
9985 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9987 ping_trans_color.green=(png_uint_16)
9988 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9990 ping_trans_color.blue=(png_uint_16)
9991 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9993 ping_trans_color.gray=(png_uint_16)
9994 (ScaleQuantumToShort(GetPixelInfoIntensity(
9995 image->colormap)) & mask);
9997 ping_trans_color.index=(png_byte) 0;
9999 ping_have_tRNS=MagickTrue;
10002 if (ping_have_tRNS != MagickFalse)
10005 * Determine if there is one and only one transparent color
10006 * and if so if it is fully transparent.
10008 if (ping_have_cheap_transparency == MagickFalse)
10009 ping_have_tRNS=MagickFalse;
10012 if (ping_have_tRNS != MagickFalse)
10014 if (mng_info->write_png_colortype == 0)
10015 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10017 if (image_depth == 8)
10019 ping_trans_color.red&=0xff;
10020 ping_trans_color.green&=0xff;
10021 ping_trans_color.blue&=0xff;
10022 ping_trans_color.gray&=0xff;
10028 if (image_depth == 8)
10030 ping_trans_color.red&=0xff;
10031 ping_trans_color.green&=0xff;
10032 ping_trans_color.blue&=0xff;
10033 ping_trans_color.gray&=0xff;
10040 if (ping_have_tRNS != MagickFalse)
10041 image_matte=MagickFalse;
10043 if ((mng_info->IsPalette) &&
10044 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10045 ping_have_color == MagickFalse &&
10046 (image_matte == MagickFalse || image_depth >= 8))
10050 if (image_matte != MagickFalse)
10051 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10053 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10055 ping_color_type=PNG_COLOR_TYPE_GRAY;
10057 if (save_image_depth == 16 && image_depth == 8)
10059 if (logging != MagickFalse)
10061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10062 " Scaling ping_trans_color (0)");
10064 ping_trans_color.gray*=0x0101;
10068 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10069 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10071 if ((image_colors == 0) ||
10072 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10073 image_colors=(int) (one << image_depth);
10075 if (image_depth > 8)
10081 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10083 if(!mng_info->write_png_depth)
10087 while ((int) (one << ping_bit_depth)
10088 < (ssize_t) image_colors)
10089 ping_bit_depth <<= 1;
10093 else if (ping_color_type ==
10094 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10095 mng_info->IsPalette)
10097 /* Check if grayscale is reducible */
10100 depth_4_ok=MagickTrue,
10101 depth_2_ok=MagickTrue,
10102 depth_1_ok=MagickTrue;
10104 for (i=0; i < (ssize_t) image_colors; i++)
10109 intensity=ScaleQuantumToChar(image->colormap[i].red);
10111 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10112 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10113 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10114 depth_2_ok=depth_1_ok=MagickFalse;
10115 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10116 depth_1_ok=MagickFalse;
10119 if (depth_1_ok && mng_info->write_png_depth <= 1)
10122 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10125 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10130 image_depth=ping_bit_depth;
10135 if (mng_info->IsPalette)
10137 number_colors=image_colors;
10139 if (image_depth <= 8)
10144 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10146 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10148 for (i=0; i < (ssize_t) number_colors; i++)
10150 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10151 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10152 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10155 if (logging != MagickFalse)
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " Setting up PLTE chunk with %d colors",
10160 ping_have_PLTE=MagickTrue;
10163 /* color_type is PNG_COLOR_TYPE_PALETTE */
10164 if (mng_info->write_png_depth == 0)
10172 while ((one << ping_bit_depth) < (size_t) number_colors)
10173 ping_bit_depth <<= 1;
10178 if (matte != MagickFalse)
10181 * Set up trans_colors array.
10183 assert(number_colors <= 256);
10185 ping_num_trans=(unsigned short) (number_transparent +
10186 number_semitransparent);
10188 if (ping_num_trans == 0)
10189 ping_have_tRNS=MagickFalse;
10193 if (logging != MagickFalse)
10195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10196 " Scaling ping_trans_color (1)");
10198 ping_have_tRNS=MagickTrue;
10200 for (i=0; i < ping_num_trans; i++)
10202 ping_trans_alpha[i]= (png_byte)
10203 ScaleQuantumToChar(image->colormap[i].alpha);
10213 if (image_depth < 8)
10216 if ((save_image_depth == 16) && (image_depth == 8))
10218 if (logging != MagickFalse)
10220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10221 " Scaling ping_trans_color from (%d,%d,%d)",
10222 (int) ping_trans_color.red,
10223 (int) ping_trans_color.green,
10224 (int) ping_trans_color.blue);
10227 ping_trans_color.red*=0x0101;
10228 ping_trans_color.green*=0x0101;
10229 ping_trans_color.blue*=0x0101;
10230 ping_trans_color.gray*=0x0101;
10232 if (logging != MagickFalse)
10234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10236 (int) ping_trans_color.red,
10237 (int) ping_trans_color.green,
10238 (int) ping_trans_color.blue);
10243 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10244 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10247 Adjust background and transparency samples in sub-8-bit grayscale files.
10249 if (ping_bit_depth < 8 && ping_color_type ==
10250 PNG_COLOR_TYPE_GRAY)
10258 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10260 if (ping_exclude_bKGD == MagickFalse)
10263 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10264 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10265 &image->background_color))) +.5)));
10267 if (logging != MagickFalse)
10268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10269 " Setting up bKGD chunk (2)");
10270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10271 " background_color index is %d",
10272 (int) ping_background.index);
10274 ping_have_bKGD = MagickTrue;
10277 if (logging != MagickFalse)
10278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10279 " Scaling ping_trans_color.gray from %d",
10280 (int)ping_trans_color.gray);
10282 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10283 ping_trans_color.gray)+.5);
10285 if (logging != MagickFalse)
10286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287 " to %d", (int)ping_trans_color.gray);
10290 if (ping_exclude_bKGD == MagickFalse)
10292 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10295 Identify which colormap entry is the background color.
10298 number_colors=image_colors;
10300 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10301 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10304 ping_background.index=(png_byte) i;
10306 if (logging != MagickFalse)
10308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10309 " Setting up bKGD chunk with index=%d",(int) i);
10312 if (i < (ssize_t) number_colors)
10314 ping_have_bKGD = MagickTrue;
10316 if (logging != MagickFalse)
10318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319 " background =(%d,%d,%d)",
10320 (int) ping_background.red,
10321 (int) ping_background.green,
10322 (int) ping_background.blue);
10326 else /* Can't happen */
10328 if (logging != MagickFalse)
10329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330 " No room in PLTE to add bKGD color");
10331 ping_have_bKGD = MagickFalse;
10336 if (logging != MagickFalse)
10337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10338 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10341 Initialize compression level and filtering.
10343 if (logging != MagickFalse)
10345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346 " Setting up deflate compression");
10348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10349 " Compression buffer size: 32768");
10352 png_set_compression_buffer_size(ping,32768L);
10354 if (logging != MagickFalse)
10355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10356 " Compression mem level: 9");
10358 png_set_compression_mem_level(ping, 9);
10360 /* Untangle the "-quality" setting:
10362 Undefined is 0; the default is used.
10367 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10368 zlib default compression level
10370 1-9: the zlib compression level
10374 0-4: the PNG filter method
10376 5: libpng adaptive filtering if compression level > 5
10377 libpng filter type "none" if compression level <= 5
10378 or if image is grayscale or palette
10380 6: libpng adaptive filtering
10382 7: "LOCO" filtering (intrapixel differing) if writing
10383 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10384 and earlier because of a missing "else".
10386 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10387 filtering. Unused prior to IM-6.7.0-10, was same as 6
10389 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10390 Unused prior to IM-6.7.0-10, was same as 6
10392 Note that using the -quality option, not all combinations of
10393 PNG filter type, zlib compression level, and zlib compression
10394 strategy are possible. This will be addressed soon in a
10395 release that accomodates "-define png:compression-strategy", etc.
10399 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10400 image_info->quality;
10404 if (mng_info->write_png_compression_strategy == 0)
10405 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10408 else if (mng_info->write_png_compression_level == 0)
10413 level=(int) MagickMin((ssize_t) quality/10,9);
10415 mng_info->write_png_compression_level = level+1;
10418 if (mng_info->write_png_compression_strategy == 0)
10420 if ((quality %10) == 8 || (quality %10) == 9)
10421 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10422 mng_info->write_png_compression_strategy=Z_RLE+1;
10424 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10428 if (mng_info->write_png_compression_filter == 0)
10429 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10431 if (logging != MagickFalse)
10433 if (mng_info->write_png_compression_level)
10434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10435 " Compression level: %d",
10436 (int) mng_info->write_png_compression_level-1);
10438 if (mng_info->write_png_compression_strategy)
10439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10440 " Compression strategy: %d",
10441 (int) mng_info->write_png_compression_strategy-1);
10443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444 " Setting up filtering");
10446 if (mng_info->write_png_compression_filter == 6)
10447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448 " Base filter method: ADAPTIVE");
10449 else if (mng_info->write_png_compression_filter == 0 ||
10450 mng_info->write_png_compression_filter == 1)
10451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452 " Base filter method: NONE");
10454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455 " Base filter method: %d",
10456 (int) mng_info->write_png_compression_filter-1);
10459 if (mng_info->write_png_compression_level != 0)
10460 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10462 if (mng_info->write_png_compression_filter == 6)
10464 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10465 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10467 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10469 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10471 else if (mng_info->write_png_compression_filter == 7 ||
10472 mng_info->write_png_compression_filter == 10)
10473 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10475 else if (mng_info->write_png_compression_filter == 8)
10477 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10478 if (mng_info->write_mng)
10480 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10481 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10482 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10485 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10488 else if (mng_info->write_png_compression_filter == 9)
10489 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10491 else if (mng_info->write_png_compression_filter != 0)
10492 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10493 mng_info->write_png_compression_filter-1);
10495 if (mng_info->write_png_compression_strategy != 0)
10496 png_set_compression_strategy(ping,
10497 mng_info->write_png_compression_strategy-1);
10499 ping_interlace_method=image_info->interlace != NoInterlace;
10501 if (mng_info->write_mng)
10502 png_set_sig_bytes(ping,8);
10504 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10506 if (mng_info->write_png_colortype != 0)
10508 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10509 if (ping_have_color != MagickFalse)
10511 ping_color_type = PNG_COLOR_TYPE_RGB;
10513 if (ping_bit_depth < 8)
10517 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10518 if (ping_have_color != MagickFalse)
10519 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10522 if (ping_need_colortype_warning != MagickFalse ||
10523 ((mng_info->write_png_depth &&
10524 (int) mng_info->write_png_depth != ping_bit_depth) ||
10525 (mng_info->write_png_colortype &&
10526 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10527 mng_info->write_png_colortype != 7 &&
10528 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10530 if (logging != MagickFalse)
10532 if (ping_need_colortype_warning != MagickFalse)
10534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10535 " Image has transparency but tRNS chunk was excluded");
10538 if (mng_info->write_png_depth)
10540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10541 " Defined png:bit-depth=%u, Computed depth=%u",
10542 mng_info->write_png_depth,
10546 if (mng_info->write_png_colortype)
10548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10549 " Defined png:color-type=%u, Computed color type=%u",
10550 mng_info->write_png_colortype-1,
10556 "Cannot write image with defined png:bit-depth or png:color-type.");
10559 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10561 /* Add an opaque matte channel */
10562 image->alpha_trait = BlendPixelTrait;
10563 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10565 if (logging != MagickFalse)
10566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567 " Added an opaque matte channel");
10570 if (number_transparent != 0 || number_semitransparent != 0)
10572 if (ping_color_type < 4)
10574 ping_have_tRNS=MagickTrue;
10575 if (logging != MagickFalse)
10576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10577 " Setting ping_have_tRNS=MagickTrue.");
10581 if (logging != MagickFalse)
10582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583 " Writing PNG header chunks");
10585 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10586 ping_bit_depth,ping_color_type,
10587 ping_interlace_method,ping_compression_method,
10588 ping_filter_method);
10590 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10592 png_set_PLTE(ping,ping_info,palette,number_colors);
10594 if (logging != MagickFalse)
10596 for (i=0; i< (ssize_t) number_colors; i++)
10598 if (i < ping_num_trans)
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10602 (int) palette[i].red,
10603 (int) palette[i].green,
10604 (int) palette[i].blue,
10606 (int) ping_trans_alpha[i]);
10608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609 " PLTE[%d] = (%d,%d,%d)",
10611 (int) palette[i].red,
10612 (int) palette[i].green,
10613 (int) palette[i].blue);
10618 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10619 if (ping_exclude_sRGB != MagickFalse ||
10620 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10622 if ((ping_exclude_tEXt == MagickFalse ||
10623 ping_exclude_zTXt == MagickFalse) &&
10624 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10626 ResetImageProfileIterator(image);
10627 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10629 profile=GetImageProfile(image,name);
10631 if (profile != (StringInfo *) NULL)
10633 #ifdef PNG_WRITE_iCCP_SUPPORTED
10634 if ((LocaleCompare(name,"ICC") == 0) ||
10635 (LocaleCompare(name,"ICM") == 0))
10638 if (ping_exclude_iCCP == MagickFalse)
10640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641 " Setting up iCCP chunk");
10643 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10644 #if (PNG_LIBPNG_VER < 10500)
10645 (png_charp) GetStringInfoDatum(profile),
10647 (png_const_bytep) GetStringInfoDatum(profile),
10649 (png_uint_32) GetStringInfoLength(profile));
10650 ping_have_iCCP = MagickTrue;
10656 if (ping_exclude_zCCP == MagickFalse)
10658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10659 " Setting up zTXT chunk with uuencoded ICC");
10660 Magick_png_write_raw_profile(image_info,ping,ping_info,
10661 (unsigned char *) name,(unsigned char *) name,
10662 GetStringInfoDatum(profile),
10663 (png_uint_32) GetStringInfoLength(profile));
10664 ping_have_iCCP = MagickTrue;
10668 if (logging != MagickFalse)
10669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670 " Setting up text chunk with %s profile",name);
10672 name=GetNextImageProfile(image);
10677 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10678 if ((mng_info->have_write_global_srgb == 0) &&
10679 ping_have_iCCP != MagickTrue &&
10680 (ping_have_sRGB != MagickFalse ||
10681 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10683 if (ping_exclude_sRGB == MagickFalse)
10686 Note image rendering intent.
10688 if (logging != MagickFalse)
10689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10690 " Setting up sRGB chunk");
10692 (void) png_set_sRGB(ping,ping_info,(
10693 Magick_RenderingIntent_to_PNG_RenderingIntent(
10694 image->rendering_intent)));
10696 ping_have_sRGB = MagickTrue;
10700 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10703 if (ping_exclude_gAMA == MagickFalse &&
10704 ping_have_iCCP == MagickFalse &&
10705 ping_have_sRGB == MagickFalse &&
10706 (ping_exclude_sRGB == MagickFalse ||
10707 (image->gamma < .45 || image->gamma > .46)))
10709 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10713 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10715 if (logging != MagickFalse)
10716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10717 " Setting up gAMA chunk");
10719 png_set_gAMA(ping,ping_info,image->gamma);
10723 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10725 if ((mng_info->have_write_global_chrm == 0) &&
10726 (image->chromaticity.red_primary.x != 0.0))
10729 Note image chromaticity.
10730 Note: if cHRM+gAMA == sRGB write sRGB instead.
10738 wp=image->chromaticity.white_point;
10739 rp=image->chromaticity.red_primary;
10740 gp=image->chromaticity.green_primary;
10741 bp=image->chromaticity.blue_primary;
10743 if (logging != MagickFalse)
10744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10745 " Setting up cHRM chunk");
10747 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10753 if (ping_exclude_bKGD == MagickFalse)
10755 if (ping_have_bKGD != MagickFalse)
10757 png_set_bKGD(ping,ping_info,&ping_background);
10760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10761 " Setting up bKGD chunk");
10762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10763 " background color = (%d,%d,%d)",
10764 (int) ping_background.red,
10765 (int) ping_background.green,
10766 (int) ping_background.blue);
10767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10768 " index = %d, gray=%d",
10769 (int) ping_background.index,
10770 (int) ping_background.gray);
10775 if (ping_exclude_pHYs == MagickFalse)
10777 if (ping_have_pHYs != MagickFalse)
10779 png_set_pHYs(ping,ping_info,
10780 ping_pHYs_x_resolution,
10781 ping_pHYs_y_resolution,
10782 ping_pHYs_unit_type);
10786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10787 " Setting up pHYs chunk");
10788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " x_resolution=%lu",
10790 (unsigned long) ping_pHYs_x_resolution);
10791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792 " y_resolution=%lu",
10793 (unsigned long) ping_pHYs_y_resolution);
10794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796 (unsigned long) ping_pHYs_unit_type);
10801 #if defined(PNG_oFFs_SUPPORTED)
10802 if (ping_exclude_oFFs == MagickFalse)
10804 if (image->page.x || image->page.y)
10806 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10807 (png_int_32) image->page.y, 0);
10809 if (logging != MagickFalse)
10810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10811 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10812 (int) image->page.x, (int) image->page.y);
10817 if (mng_info->need_blob != MagickFalse)
10819 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10821 png_error(ping,"WriteBlob Failed");
10823 ping_have_blob=MagickTrue;
10826 png_write_info_before_PLTE(ping, ping_info);
10828 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10830 if (logging != MagickFalse)
10832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10833 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10836 if (ping_color_type == 3)
10837 (void) png_set_tRNS(ping, ping_info,
10844 (void) png_set_tRNS(ping, ping_info,
10847 &ping_trans_color);
10849 if (logging != MagickFalse)
10851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10852 " tRNS color =(%d,%d,%d)",
10853 (int) ping_trans_color.red,
10854 (int) ping_trans_color.green,
10855 (int) ping_trans_color.blue);
10860 /* write any png-chunk-b profiles */
10861 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10863 png_write_info(ping,ping_info);
10865 /* write any PNG-chunk-m profiles */
10866 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10868 if (ping_exclude_vpAg == MagickFalse)
10870 if ((image->page.width != 0 && image->page.width != image->columns) ||
10871 (image->page.height != 0 && image->page.height != image->rows))
10876 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10877 PNGType(chunk,mng_vpAg);
10878 LogPNGChunk(logging,mng_vpAg,9L);
10879 PNGLong(chunk+4,(png_uint_32) image->page.width);
10880 PNGLong(chunk+8,(png_uint_32) image->page.height);
10881 chunk[12]=0; /* unit = pixels */
10882 (void) WriteBlob(image,13,chunk);
10883 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10887 #if (PNG_LIBPNG_VER == 10206)
10888 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10889 #define PNG_HAVE_IDAT 0x04
10890 ping->mode |= PNG_HAVE_IDAT;
10891 #undef PNG_HAVE_IDAT
10894 png_set_packing(ping);
10898 rowbytes=image->columns;
10899 if (image_depth > 8)
10901 switch (ping_color_type)
10903 case PNG_COLOR_TYPE_RGB:
10907 case PNG_COLOR_TYPE_GRAY_ALPHA:
10911 case PNG_COLOR_TYPE_RGBA:
10919 if (logging != MagickFalse)
10921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10922 " Writing PNG image data");
10924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10925 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10927 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10928 if (pixel_info == (MemoryInfo *) NULL)
10929 png_error(ping,"Allocation of memory for pixels failed");
10930 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10933 Initialize image scanlines.
10935 quantum_info=AcquireQuantumInfo(image_info,image);
10936 if (quantum_info == (QuantumInfo *) NULL)
10937 png_error(ping,"Memory allocation for quantum_info failed");
10938 quantum_info->format=UndefinedQuantumFormat;
10939 quantum_info->depth=image_depth;
10940 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10941 num_passes=png_set_interlace_handling(ping);
10943 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10944 !mng_info->write_png48 && !mng_info->write_png64 &&
10945 !mng_info->write_png32) &&
10946 (mng_info->IsPalette ||
10947 (image_info->type == BilevelType)) &&
10948 image_matte == MagickFalse &&
10949 ping_have_non_bw == MagickFalse)
10951 /* Palette, Bilevel, or Opaque Monochrome */
10952 register const Quantum
10955 quantum_info->depth=8;
10956 for (pass=0; pass < num_passes; pass++)
10959 Convert PseudoClass image to a PNG monochrome image.
10961 for (y=0; y < (ssize_t) image->rows; y++)
10963 if (logging != MagickFalse && y == 0)
10964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10965 " Writing row of pixels (0)");
10967 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10969 if (p == (const Quantum *) NULL)
10972 if (mng_info->IsPalette)
10974 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10975 quantum_info,GrayQuantum,ping_pixels,exception);
10976 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10977 mng_info->write_png_depth &&
10978 mng_info->write_png_depth != old_bit_depth)
10980 /* Undo pixel scaling */
10981 for (i=0; i < (ssize_t) image->columns; i++)
10982 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10983 >> (8-old_bit_depth));
10989 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10990 quantum_info,RedQuantum,ping_pixels,exception);
10993 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10994 for (i=0; i < (ssize_t) image->columns; i++)
10995 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10998 if (logging != MagickFalse && y == 0)
10999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11000 " Writing row of pixels (1)");
11002 png_write_row(ping,ping_pixels);
11004 if (image->previous == (Image *) NULL)
11006 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11007 if (status == MagickFalse)
11013 else /* Not Palette, Bilevel, or Opaque Monochrome */
11015 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11016 !mng_info->write_png48 && !mng_info->write_png64 &&
11017 !mng_info->write_png32) && (image_matte != MagickFalse ||
11018 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11019 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11021 register const Quantum
11024 for (pass=0; pass < num_passes; pass++)
11027 for (y=0; y < (ssize_t) image->rows; y++)
11029 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11031 if (p == (const Quantum *) NULL)
11034 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11036 if (mng_info->IsPalette)
11037 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11038 quantum_info,GrayQuantum,ping_pixels,exception);
11041 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11042 quantum_info,RedQuantum,ping_pixels,exception);
11044 if (logging != MagickFalse && y == 0)
11045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11046 " Writing GRAY PNG pixels (2)");
11049 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11051 if (logging != MagickFalse && y == 0)
11052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053 " Writing GRAY_ALPHA PNG pixels (2)");
11055 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11059 if (logging != MagickFalse && y == 0)
11060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11061 " Writing row of pixels (2)");
11063 png_write_row(ping,ping_pixels);
11066 if (image->previous == (Image *) NULL)
11068 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11069 if (status == MagickFalse)
11077 register const Quantum
11080 for (pass=0; pass < num_passes; pass++)
11082 if ((image_depth > 8) ||
11083 mng_info->write_png24 ||
11084 mng_info->write_png32 ||
11085 mng_info->write_png48 ||
11086 mng_info->write_png64 ||
11087 (!mng_info->write_png8 && !mng_info->IsPalette))
11089 for (y=0; y < (ssize_t) image->rows; y++)
11091 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11093 if (p == (const Quantum *) NULL)
11096 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11098 if (image->storage_class == DirectClass)
11099 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11100 quantum_info,RedQuantum,ping_pixels,exception);
11103 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11104 quantum_info,GrayQuantum,ping_pixels,exception);
11107 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11109 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11110 quantum_info,GrayAlphaQuantum,ping_pixels,
11113 if (logging != MagickFalse && y == 0)
11114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11115 " Writing GRAY_ALPHA PNG pixels (3)");
11118 else if (image_matte != MagickFalse)
11119 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11120 quantum_info,RGBAQuantum,ping_pixels,exception);
11123 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11124 quantum_info,RGBQuantum,ping_pixels,exception);
11126 if (logging != MagickFalse && y == 0)
11127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11128 " Writing row of pixels (3)");
11130 png_write_row(ping,ping_pixels);
11135 /* not ((image_depth > 8) ||
11136 mng_info->write_png24 || mng_info->write_png32 ||
11137 mng_info->write_png48 || mng_info->write_png64 ||
11138 (!mng_info->write_png8 && !mng_info->IsPalette))
11141 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11142 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11144 if (logging != MagickFalse)
11145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11146 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11148 quantum_info->depth=8;
11152 for (y=0; y < (ssize_t) image->rows; y++)
11154 if (logging != MagickFalse && y == 0)
11155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11158 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11160 if (p == (const Quantum *) NULL)
11163 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11165 quantum_info->depth=image->depth;
11167 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11168 quantum_info,GrayQuantum,ping_pixels,exception);
11171 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11173 if (logging != MagickFalse && y == 0)
11174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175 " Writing GRAY_ALPHA PNG pixels (4)");
11177 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11178 quantum_info,GrayAlphaQuantum,ping_pixels,
11184 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11185 quantum_info,IndexQuantum,ping_pixels,exception);
11187 if (logging != MagickFalse && y <= 2)
11189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190 " Writing row of non-gray pixels (4)");
11192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11193 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11194 (int)ping_pixels[0],(int)ping_pixels[1]);
11197 png_write_row(ping,ping_pixels);
11201 if (image->previous == (Image *) NULL)
11203 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11204 if (status == MagickFalse)
11211 if (quantum_info != (QuantumInfo *) NULL)
11212 quantum_info=DestroyQuantumInfo(quantum_info);
11214 if (logging != MagickFalse)
11216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11217 " Wrote PNG image data");
11219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11220 " Width: %.20g",(double) ping_width);
11222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11223 " Height: %.20g",(double) ping_height);
11225 if (mng_info->write_png_depth)
11227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11228 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11232 " PNG bit-depth written: %d",ping_bit_depth);
11234 if (mng_info->write_png_colortype)
11236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11237 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11241 " PNG color-type written: %d",ping_color_type);
11243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11244 " PNG Interlace method: %d",ping_interlace_method);
11247 Generate text chunks after IDAT.
11249 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11251 ResetImagePropertyIterator(image);
11252 property=GetNextImageProperty(image);
11253 while (property != (const char *) NULL)
11258 value=GetImageProperty(image,property,exception);
11260 /* Don't write any "png:" or "jpeg:" properties; those are just for
11261 * "identify" or for passing through to another JPEG
11263 if ((LocaleNCompare(property,"png:",4) != 0 &&
11264 LocaleNCompare(property,"jpeg:",5) != 0) &&
11267 /* Suppress density and units if we wrote a pHYs chunk */
11268 (ping_exclude_pHYs != MagickFalse ||
11269 LocaleCompare(property,"density") != 0 ||
11270 LocaleCompare(property,"units") != 0) &&
11272 /* Suppress the IM-generated Date:create and Date:modify */
11273 (ping_exclude_date == MagickFalse ||
11274 LocaleNCompare(property, "Date:",5) != 0))
11276 if (value != (const char *) NULL)
11279 #if PNG_LIBPNG_VER >= 10400
11280 text=(png_textp) png_malloc(ping,
11281 (png_alloc_size_t) sizeof(png_text));
11283 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11285 text[0].key=(char *) property;
11286 text[0].text=(char *) value;
11287 text[0].text_length=strlen(value);
11289 if (ping_exclude_tEXt != MagickFalse)
11290 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11292 else if (ping_exclude_zTXt != MagickFalse)
11293 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11297 text[0].compression=image_info->compression == NoCompression ||
11298 (image_info->compression == UndefinedCompression &&
11299 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11300 PNG_TEXT_COMPRESSION_zTXt ;
11303 if (logging != MagickFalse)
11305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11306 " Setting up text chunk");
11308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11309 " keyword: '%s'",text[0].key);
11312 png_set_text(ping,ping_info,text,1);
11313 png_free(ping,text);
11316 property=GetNextImageProperty(image);
11320 /* write any PNG-chunk-e profiles */
11321 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11323 if (logging != MagickFalse)
11324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11325 " Writing PNG end info");
11327 png_write_end(ping,ping_info);
11329 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11331 if (mng_info->page.x || mng_info->page.y ||
11332 (ping_width != mng_info->page.width) ||
11333 (ping_height != mng_info->page.height))
11339 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11341 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11342 PNGType(chunk,mng_FRAM);
11343 LogPNGChunk(logging,mng_FRAM,27L);
11345 chunk[5]=0; /* frame name separator (no name) */
11346 chunk[6]=1; /* flag for changing delay, for next frame only */
11347 chunk[7]=0; /* flag for changing frame timeout */
11348 chunk[8]=1; /* flag for changing frame clipping for next frame */
11349 chunk[9]=0; /* flag for changing frame sync_id */
11350 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11351 chunk[14]=0; /* clipping boundaries delta type */
11352 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11354 (png_uint_32) (mng_info->page.x + ping_width));
11355 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11357 (png_uint_32) (mng_info->page.y + ping_height));
11358 (void) WriteBlob(image,31,chunk);
11359 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11360 mng_info->old_framing_mode=4;
11361 mng_info->framing_mode=1;
11365 mng_info->framing_mode=3;
11367 if (mng_info->write_mng && !mng_info->need_fram &&
11368 ((int) image->dispose == 3))
11369 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11372 Free PNG resources.
11375 png_destroy_write_struct(&ping,&ping_info);
11377 pixel_info=RelinquishVirtualMemory(pixel_info);
11379 if (ping_have_blob != MagickFalse)
11380 (void) CloseBlob(image);
11382 image_info=DestroyImageInfo(image_info);
11383 image=DestroyImage(image);
11385 /* Store bit depth actually written */
11386 s[0]=(char) ping_bit_depth;
11389 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11391 if (logging != MagickFalse)
11392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11393 " exit WriteOnePNGImage()");
11395 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11396 UnlockSemaphoreInfo(ping_semaphore);
11399 /* } for navigation to beginning of SETJMP-protected block. Revert to
11400 * Throwing an Exception when an error occurs.
11403 return(MagickTrue);
11404 /* End write one PNG image */
11409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11413 % W r i t e P N G I m a g e %
11417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11419 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11420 % Multiple-image Network Graphics (MNG) image file.
11422 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11424 % The format of the WritePNGImage method is:
11426 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11427 % Image *image,ExceptionInfo *exception)
11429 % A description of each parameter follows:
11431 % o image_info: the image info.
11433 % o image: The image.
11435 % o exception: return any errors or warnings in this structure.
11437 % Returns MagickTrue on success, MagickFalse on failure.
11439 % Communicating with the PNG encoder:
11441 % While the datastream written is always in PNG format and normally would
11442 % be given the "png" file extension, this method also writes the following
11443 % pseudo-formats which are subsets of png:
11445 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11446 % a depth greater than 8, the depth is reduced. If transparency
11447 % is present, the tRNS chunk must only have values 0 and 255
11448 % (i.e., transparency is binary: fully opaque or fully
11449 % transparent). If other values are present they will be
11450 % 50%-thresholded to binary transparency. If more than 256
11451 % colors are present, they will be quantized to the 4-4-4-1,
11452 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11453 % of any resulting fully-transparent pixels is changed to
11454 % the image's background color.
11456 % If you want better quantization or dithering of the colors
11457 % or alpha than that, you need to do it before calling the
11458 % PNG encoder. The pixels contain 8-bit indices even if
11459 % they could be represented with 1, 2, or 4 bits. Grayscale
11460 % images will be written as indexed PNG files even though the
11461 % PNG grayscale type might be slightly more efficient. Please
11462 % note that writing to the PNG8 format may result in loss
11463 % of color and alpha data.
11465 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11466 % chunk can be present to convey binary transparency by naming
11467 % one of the colors as transparent. The only loss incurred
11468 % is reduction of sample depth to 8. If the image has more
11469 % than one transparent color, has semitransparent pixels, or
11470 % has an opaque pixel with the same RGB components as the
11471 % transparent color, an image is not written.
11473 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11474 % transparency is permitted, i.e., the alpha sample for
11475 % each pixel can have any value from 0 to 255. The alpha
11476 % channel is present even if the image is fully opaque.
11477 % The only loss in data is the reduction of the sample depth
11480 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11481 % chunk can be present to convey binary transparency by naming
11482 % one of the colors as transparent. If the image has more
11483 % than one transparent color, has semitransparent pixels, or
11484 % has an opaque pixel with the same RGB components as the
11485 % transparent color, an image is not written.
11487 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11488 % transparency is permitted, i.e., the alpha sample for
11489 % each pixel can have any value from 0 to 65535. The alpha
11490 % channel is present even if the image is fully opaque.
11492 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11493 % image, if the input was a PNG, is written. If these values
11494 % cannot be found, then "PNG00" falls back to the regular "PNG"
11497 % o -define: For more precise control of the PNG output, you can use the
11498 % Image options "png:bit-depth" and "png:color-type". These
11499 % can be set from the commandline with "-define" and also
11500 % from the application programming interfaces. The options
11501 % are case-independent and are converted to lowercase before
11502 % being passed to this encoder.
11504 % png:color-type can be 0, 2, 3, 4, or 6.
11506 % When png:color-type is 0 (Grayscale), png:bit-depth can
11507 % be 1, 2, 4, 8, or 16.
11509 % When png:color-type is 2 (RGB), png:bit-depth can
11512 % When png:color-type is 3 (Indexed), png:bit-depth can
11513 % be 1, 2, 4, or 8. This refers to the number of bits
11514 % used to store the index. The color samples always have
11515 % bit-depth 8 in indexed PNG files.
11517 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11518 % png:bit-depth can be 8 or 16.
11520 % If the image cannot be written without loss with the
11521 % requested bit-depth and color-type, a PNG file will not
11522 % be written, a warning will be issued, and the encoder will
11523 % return MagickFalse.
11525 % Since image encoders should not be responsible for the "heavy lifting",
11526 % the user should make sure that ImageMagick has already reduced the
11527 % image depth and number of colors and limit transparency to binary
11528 % transparency prior to attempting to write the image with depth, color,
11529 % or transparency limitations.
11531 % Note that another definition, "png:bit-depth-written" exists, but it
11532 % is not intended for external use. It is only used internally by the
11533 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11535 % It is possible to request that the PNG encoder write previously-formatted
11536 % ancillary chunks in the output PNG file, using the "-profile" commandline
11537 % option as shown below or by setting the profile via a programming
11540 % -profile PNG-chunk-x:<file>
11542 % where x is a location flag and <file> is a file containing the chunk
11543 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11544 % This encoder will compute the chunk length and CRC, so those must not
11545 % be included in the file.
11547 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11548 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11549 % of the same type, then add a short unique string after the "x" to prevent
11550 % subsequent profiles from overwriting the preceding ones, e.g.,
11552 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11554 % As of version 6.6.6 the following optimizations are always done:
11556 % o 32-bit depth is reduced to 16.
11557 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11558 % high byte and low byte are identical.
11559 % o Palette is sorted to remove unused entries and to put a
11560 % transparent color first, if BUILD_PNG_PALETTE is defined.
11561 % o Opaque matte channel is removed when writing an indexed PNG.
11562 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11563 % this can be done without loss and a larger bit depth N was not
11564 % requested via the "-define png:bit-depth=N" option.
11565 % o If matte channel is present but only one transparent color is
11566 % present, RGB+tRNS is written instead of RGBA
11567 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11568 % was requested when converting an opaque image).
11570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11572 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11573 Image *image,ExceptionInfo *exception)
11578 have_mng_structure,
11593 assert(image_info != (const ImageInfo *) NULL);
11594 assert(image_info->signature == MagickSignature);
11595 assert(image != (Image *) NULL);
11596 assert(image->signature == MagickSignature);
11597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11598 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11600 Allocate a MngInfo structure.
11602 have_mng_structure=MagickFalse;
11603 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11605 if (mng_info == (MngInfo *) NULL)
11606 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11609 Initialize members of the MngInfo structure.
11611 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11612 mng_info->image=image;
11613 mng_info->equal_backgrounds=MagickTrue;
11614 have_mng_structure=MagickTrue;
11616 /* See if user has requested a specific PNG subformat */
11618 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11619 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11620 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11621 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11622 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11624 value=GetImageOption(image_info,"png:format");
11625 if (value == (char *) NULL)
11626 if (LocaleCompare(image_info->magick,"PNG00") == 0)
11628 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631 " Format=%s",value);
11633 mng_info->write_png8 = MagickFalse;
11634 mng_info->write_png24 = MagickFalse;
11635 mng_info->write_png32 = MagickFalse;
11636 mng_info->write_png48 = MagickFalse;
11637 mng_info->write_png64 = MagickFalse;
11639 if (LocaleCompare(value,"png8") == 0)
11640 mng_info->write_png8 = MagickTrue;
11642 else if (LocaleCompare(value,"png24") == 0)
11643 mng_info->write_png24 = MagickTrue;
11645 else if (LocaleCompare(value,"png32") == 0)
11646 mng_info->write_png32 = MagickTrue;
11648 else if (LocaleCompare(value,"png48") == 0)
11649 mng_info->write_png48 = MagickTrue;
11651 else if (LocaleCompare(value,"png64") == 0)
11652 mng_info->write_png64 = MagickTrue;
11654 else if ((LocaleCompare(value,"png00") == 0) ||
11655 LocaleCompare(image_info->magick,"PNG00") == 0)
11657 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11658 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11660 if (value != (char *) NULL)
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11663 " png00 inherited bit depth=%s",value);
11665 if (LocaleCompare(value,"1") == 0)
11666 mng_info->write_png_depth = 1;
11668 else if (LocaleCompare(value,"2") == 0)
11669 mng_info->write_png_depth = 2;
11671 else if (LocaleCompare(value,"4") == 0)
11672 mng_info->write_png_depth = 4;
11674 else if (LocaleCompare(value,"8") == 0)
11675 mng_info->write_png_depth = 8;
11677 else if (LocaleCompare(value,"16") == 0)
11678 mng_info->write_png_depth = 16;
11681 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11683 if (value != (char *) NULL)
11685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11686 " png00 inherited color type=%s",value);
11688 if (LocaleCompare(value,"0") == 0)
11689 mng_info->write_png_colortype = 1;
11691 else if (LocaleCompare(value,"2") == 0)
11692 mng_info->write_png_colortype = 3;
11694 else if (LocaleCompare(value,"3") == 0)
11695 mng_info->write_png_colortype = 4;
11697 else if (LocaleCompare(value,"4") == 0)
11698 mng_info->write_png_colortype = 5;
11700 else if (LocaleCompare(value,"6") == 0)
11701 mng_info->write_png_colortype = 7;
11706 if (mng_info->write_png8)
11708 mng_info->write_png_colortype = /* 3 */ 4;
11709 mng_info->write_png_depth = 8;
11713 if (mng_info->write_png24)
11715 mng_info->write_png_colortype = /* 2 */ 3;
11716 mng_info->write_png_depth = 8;
11719 if (image->alpha_trait == BlendPixelTrait)
11720 (void) SetImageType(image,TrueColorMatteType,exception);
11723 (void) SetImageType(image,TrueColorType,exception);
11725 (void) SyncImage(image,exception);
11728 if (mng_info->write_png32)
11730 mng_info->write_png_colortype = /* 6 */ 7;
11731 mng_info->write_png_depth = 8;
11733 image->alpha_trait = BlendPixelTrait;
11735 (void) SetImageType(image,TrueColorMatteType,exception);
11736 (void) SyncImage(image,exception);
11739 if (mng_info->write_png48)
11741 mng_info->write_png_colortype = /* 2 */ 3;
11742 mng_info->write_png_depth = 16;
11745 if (image->alpha_trait == BlendPixelTrait)
11746 (void) SetImageType(image,TrueColorMatteType,exception);
11749 (void) SetImageType(image,TrueColorType,exception);
11751 (void) SyncImage(image,exception);
11754 if (mng_info->write_png64)
11756 mng_info->write_png_colortype = /* 6 */ 7;
11757 mng_info->write_png_depth = 16;
11759 image->alpha_trait = BlendPixelTrait;
11761 (void) SetImageType(image,TrueColorMatteType,exception);
11762 (void) SyncImage(image,exception);
11765 value=GetImageOption(image_info,"png:bit-depth");
11767 if (value != (char *) NULL)
11769 if (LocaleCompare(value,"1") == 0)
11770 mng_info->write_png_depth = 1;
11772 else if (LocaleCompare(value,"2") == 0)
11773 mng_info->write_png_depth = 2;
11775 else if (LocaleCompare(value,"4") == 0)
11776 mng_info->write_png_depth = 4;
11778 else if (LocaleCompare(value,"8") == 0)
11779 mng_info->write_png_depth = 8;
11781 else if (LocaleCompare(value,"16") == 0)
11782 mng_info->write_png_depth = 16;
11785 (void) ThrowMagickException(exception,
11786 GetMagickModule(),CoderWarning,
11787 "ignoring invalid defined png:bit-depth",
11790 if (logging != MagickFalse)
11791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11795 value=GetImageOption(image_info,"png:color-type");
11797 if (value != (char *) NULL)
11799 /* We must store colortype+1 because 0 is a valid colortype */
11800 if (LocaleCompare(value,"0") == 0)
11801 mng_info->write_png_colortype = 1;
11803 else if (LocaleCompare(value,"1") == 0)
11804 mng_info->write_png_colortype = 2;
11806 else if (LocaleCompare(value,"2") == 0)
11807 mng_info->write_png_colortype = 3;
11809 else if (LocaleCompare(value,"3") == 0)
11810 mng_info->write_png_colortype = 4;
11812 else if (LocaleCompare(value,"4") == 0)
11813 mng_info->write_png_colortype = 5;
11815 else if (LocaleCompare(value,"6") == 0)
11816 mng_info->write_png_colortype = 7;
11819 (void) ThrowMagickException(exception,
11820 GetMagickModule(),CoderWarning,
11821 "ignoring invalid defined png:color-type",
11824 if (logging != MagickFalse)
11825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11826 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11829 /* Check for chunks to be excluded:
11831 * The default is to not exclude any known chunks except for any
11832 * listed in the "unused_chunks" array, above.
11834 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11835 * define (in the image properties or in the image artifacts)
11836 * or via a mng_info member. For convenience, in addition
11837 * to or instead of a comma-separated list of chunks, the
11838 * "exclude-chunk" string can be simply "all" or "none".
11840 * The exclude-chunk define takes priority over the mng_info.
11842 * A "png:include-chunk" define takes priority over both the
11843 * mng_info and the "png:exclude-chunk" define. Like the
11844 * "exclude-chunk" string, it can define "all" or "none" as
11845 * well as a comma-separated list. Chunks that are unknown to
11846 * ImageMagick are always excluded, regardless of their "copy-safe"
11847 * status according to the PNG specification, and even if they
11848 * appear in the "include-chunk" list. Such defines appearing among
11849 * the image options take priority over those found among the image
11852 * Finally, all chunks listed in the "unused_chunks" array are
11853 * automatically excluded, regardless of the other instructions
11856 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11857 * will not be written and the gAMA chunk will only be written if it
11858 * is not between .45 and .46, or approximately (1.0/2.2).
11860 * If you exclude tRNS and the image has transparency, the colortype
11861 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11863 * The -strip option causes StripImage() to set the png:include-chunk
11864 * artifact to "none,trns,gama".
11867 mng_info->ping_exclude_bKGD=MagickFalse;
11868 mng_info->ping_exclude_cHRM=MagickFalse;
11869 mng_info->ping_exclude_date=MagickFalse;
11870 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11871 mng_info->ping_exclude_gAMA=MagickFalse;
11872 mng_info->ping_exclude_iCCP=MagickFalse;
11873 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11874 mng_info->ping_exclude_oFFs=MagickFalse;
11875 mng_info->ping_exclude_pHYs=MagickFalse;
11876 mng_info->ping_exclude_sRGB=MagickFalse;
11877 mng_info->ping_exclude_tEXt=MagickFalse;
11878 mng_info->ping_exclude_tRNS=MagickFalse;
11879 mng_info->ping_exclude_vpAg=MagickFalse;
11880 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11881 mng_info->ping_exclude_zTXt=MagickFalse;
11883 mng_info->ping_preserve_colormap=MagickFalse;
11885 value=GetImageOption(image_info,"png:preserve-colormap");
11887 value=GetImageArtifact(image,"png:preserve-colormap");
11889 mng_info->ping_preserve_colormap=MagickTrue;
11891 mng_info->ping_preserve_iCCP=MagickFalse;
11893 value=GetImageOption(image_info,"png:preserve-iCCP");
11895 value=GetImageArtifact(image,"png:preserve-iCCP");
11897 mng_info->ping_preserve_iCCP=MagickTrue;
11899 /* These compression-level, compression-strategy, and compression-filter
11900 * defines take precedence over values from the -quality option.
11902 value=GetImageOption(image_info,"png:compression-level");
11904 value=GetImageArtifact(image,"png:compression-level");
11907 /* We have to add 1 to everything because 0 is a valid input,
11908 * and we want to use 0 (the default) to mean undefined.
11910 if (LocaleCompare(value,"0") == 0)
11911 mng_info->write_png_compression_level = 1;
11913 else if (LocaleCompare(value,"1") == 0)
11914 mng_info->write_png_compression_level = 2;
11916 else if (LocaleCompare(value,"2") == 0)
11917 mng_info->write_png_compression_level = 3;
11919 else if (LocaleCompare(value,"3") == 0)
11920 mng_info->write_png_compression_level = 4;
11922 else if (LocaleCompare(value,"4") == 0)
11923 mng_info->write_png_compression_level = 5;
11925 else if (LocaleCompare(value,"5") == 0)
11926 mng_info->write_png_compression_level = 6;
11928 else if (LocaleCompare(value,"6") == 0)
11929 mng_info->write_png_compression_level = 7;
11931 else if (LocaleCompare(value,"7") == 0)
11932 mng_info->write_png_compression_level = 8;
11934 else if (LocaleCompare(value,"8") == 0)
11935 mng_info->write_png_compression_level = 9;
11937 else if (LocaleCompare(value,"9") == 0)
11938 mng_info->write_png_compression_level = 10;
11941 (void) ThrowMagickException(exception,
11942 GetMagickModule(),CoderWarning,
11943 "ignoring invalid defined png:compression-level",
11947 value=GetImageOption(image_info,"png:compression-strategy");
11949 value=GetImageArtifact(image,"png:compression-strategy");
11952 if (LocaleCompare(value,"0") == 0)
11953 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11955 else if (LocaleCompare(value,"1") == 0)
11956 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11958 else if (LocaleCompare(value,"2") == 0)
11959 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11961 else if (LocaleCompare(value,"3") == 0)
11962 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11963 mng_info->write_png_compression_strategy = Z_RLE+1;
11965 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11968 else if (LocaleCompare(value,"4") == 0)
11969 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11970 mng_info->write_png_compression_strategy = Z_FIXED+1;
11972 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11976 (void) ThrowMagickException(exception,
11977 GetMagickModule(),CoderWarning,
11978 "ignoring invalid defined png:compression-strategy",
11982 value=GetImageOption(image_info,"png:compression-filter");
11984 value=GetImageArtifact(image,"png:compression-filter");
11987 /* To do: combinations of filters allowed by libpng
11988 * masks 0x08 through 0xf8
11990 * Implement this as a comma-separated list of 0,1,2,3,4,5
11991 * where 5 is a special case meaning PNG_ALL_FILTERS.
11994 if (LocaleCompare(value,"0") == 0)
11995 mng_info->write_png_compression_filter = 1;
11997 else if (LocaleCompare(value,"1") == 0)
11998 mng_info->write_png_compression_filter = 2;
12000 else if (LocaleCompare(value,"2") == 0)
12001 mng_info->write_png_compression_filter = 3;
12003 else if (LocaleCompare(value,"3") == 0)
12004 mng_info->write_png_compression_filter = 4;
12006 else if (LocaleCompare(value,"4") == 0)
12007 mng_info->write_png_compression_filter = 5;
12009 else if (LocaleCompare(value,"5") == 0)
12010 mng_info->write_png_compression_filter = 6;
12013 (void) ThrowMagickException(exception,
12014 GetMagickModule(),CoderWarning,
12015 "ignoring invalid defined png:compression-filter",
12019 for (source=0; source<8; source++)
12024 value=GetImageOption(image_info,"png:exclude-chunks");
12027 value=GetImageArtifact(image,"png:exclude-chunks");
12030 value=GetImageOption(image_info,"png:exclude-chunk");
12033 value=GetImageArtifact(image,"png:exclude-chunk");
12036 value=GetImageOption(image_info,"png:include-chunks");
12039 value=GetImageArtifact(image,"png:include-chunks");
12042 value=GetImageOption(image_info,"png:include-chunk");
12045 value=GetImageArtifact(image,"png:include-chunk");
12051 excluding = MagickTrue;
12053 excluding = MagickFalse;
12055 if (logging != MagickFalse)
12057 if (source == 0 || source == 2)
12058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12059 " png:exclude-chunk=%s found in image options.\n", value);
12060 else if (source == 1 || source == 3)
12061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12062 " png:exclude-chunk=%s found in image artifacts.\n", value);
12063 else if (source == 4 || source == 6)
12064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12065 " png:include-chunk=%s found in image options.\n", value);
12066 else /* if (source == 5 || source == 7) */
12067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12068 " png:include-chunk=%s found in image artifacts.\n", value);
12071 if (IsOptionMember("all",value) != MagickFalse)
12073 mng_info->ping_exclude_bKGD=excluding;
12074 mng_info->ping_exclude_cHRM=excluding;
12075 mng_info->ping_exclude_date=excluding;
12076 mng_info->ping_exclude_EXIF=excluding;
12077 mng_info->ping_exclude_gAMA=excluding;
12078 mng_info->ping_exclude_iCCP=excluding;
12079 /* mng_info->ping_exclude_iTXt=excluding; */
12080 mng_info->ping_exclude_oFFs=excluding;
12081 mng_info->ping_exclude_pHYs=excluding;
12082 mng_info->ping_exclude_sRGB=excluding;
12083 mng_info->ping_exclude_tEXt=excluding;
12084 mng_info->ping_exclude_tRNS=excluding;
12085 mng_info->ping_exclude_vpAg=excluding;
12086 mng_info->ping_exclude_zCCP=excluding;
12087 mng_info->ping_exclude_zTXt=excluding;
12090 if (IsOptionMember("none",value) != MagickFalse)
12092 mng_info->ping_exclude_bKGD=!excluding;
12093 mng_info->ping_exclude_cHRM=!excluding;
12094 mng_info->ping_exclude_date=!excluding;
12095 mng_info->ping_exclude_EXIF=!excluding;
12096 mng_info->ping_exclude_gAMA=!excluding;
12097 mng_info->ping_exclude_iCCP=!excluding;
12098 /* mng_info->ping_exclude_iTXt=!excluding; */
12099 mng_info->ping_exclude_oFFs=!excluding;
12100 mng_info->ping_exclude_pHYs=!excluding;
12101 mng_info->ping_exclude_sRGB=!excluding;
12102 mng_info->ping_exclude_tEXt=!excluding;
12103 mng_info->ping_exclude_tRNS=!excluding;
12104 mng_info->ping_exclude_vpAg=!excluding;
12105 mng_info->ping_exclude_zCCP=!excluding;
12106 mng_info->ping_exclude_zTXt=!excluding;
12109 if (IsOptionMember("bkgd",value) != MagickFalse)
12110 mng_info->ping_exclude_bKGD=excluding;
12112 if (IsOptionMember("chrm",value) != MagickFalse)
12113 mng_info->ping_exclude_cHRM=excluding;
12115 if (IsOptionMember("date",value) != MagickFalse)
12116 mng_info->ping_exclude_date=excluding;
12118 if (IsOptionMember("exif",value) != MagickFalse)
12119 mng_info->ping_exclude_EXIF=excluding;
12121 if (IsOptionMember("gama",value) != MagickFalse)
12122 mng_info->ping_exclude_gAMA=excluding;
12124 if (IsOptionMember("iccp",value) != MagickFalse)
12125 mng_info->ping_exclude_iCCP=excluding;
12128 if (IsOptionMember("itxt",value) != MagickFalse)
12129 mng_info->ping_exclude_iTXt=excluding;
12132 if (IsOptionMember("offs",value) != MagickFalse)
12133 mng_info->ping_exclude_oFFs=excluding;
12135 if (IsOptionMember("phys",value) != MagickFalse)
12136 mng_info->ping_exclude_pHYs=excluding;
12138 if (IsOptionMember("srgb",value) != MagickFalse)
12139 mng_info->ping_exclude_sRGB=excluding;
12141 if (IsOptionMember("text",value) != MagickFalse)
12142 mng_info->ping_exclude_tEXt=excluding;
12144 if (IsOptionMember("trns",value) != MagickFalse)
12145 mng_info->ping_exclude_tRNS=excluding;
12147 if (IsOptionMember("vpag",value) != MagickFalse)
12148 mng_info->ping_exclude_vpAg=excluding;
12150 if (IsOptionMember("zccp",value) != MagickFalse)
12151 mng_info->ping_exclude_zCCP=excluding;
12153 if (IsOptionMember("ztxt",value) != MagickFalse)
12154 mng_info->ping_exclude_zTXt=excluding;
12157 if (logging != MagickFalse)
12159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12160 " Chunks to be excluded from the output png:");
12161 if (mng_info->ping_exclude_bKGD != MagickFalse)
12162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12164 if (mng_info->ping_exclude_cHRM != MagickFalse)
12165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12167 if (mng_info->ping_exclude_date != MagickFalse)
12168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12170 if (mng_info->ping_exclude_EXIF != MagickFalse)
12171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12173 if (mng_info->ping_exclude_gAMA != MagickFalse)
12174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176 if (mng_info->ping_exclude_iCCP != MagickFalse)
12177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180 if (mng_info->ping_exclude_iTXt != MagickFalse)
12181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12185 if (mng_info->ping_exclude_oFFs != MagickFalse)
12186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12188 if (mng_info->ping_exclude_pHYs != MagickFalse)
12189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12191 if (mng_info->ping_exclude_sRGB != MagickFalse)
12192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12194 if (mng_info->ping_exclude_tEXt != MagickFalse)
12195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12197 if (mng_info->ping_exclude_tRNS != MagickFalse)
12198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12200 if (mng_info->ping_exclude_vpAg != MagickFalse)
12201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12203 if (mng_info->ping_exclude_zCCP != MagickFalse)
12204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12206 if (mng_info->ping_exclude_zTXt != MagickFalse)
12207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12211 mng_info->need_blob = MagickTrue;
12213 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12215 MngInfoFreeStruct(mng_info,&have_mng_structure);
12217 if (logging != MagickFalse)
12218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12223 #if defined(JNG_SUPPORTED)
12225 /* Write one JNG image */
12226 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12227 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12248 jng_alpha_compression_method,
12249 jng_alpha_sample_depth,
12257 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12258 " Enter WriteOneJNGImage()");
12260 blob=(unsigned char *) NULL;
12261 jpeg_image=(Image *) NULL;
12262 jpeg_image_info=(ImageInfo *) NULL;
12265 transparent=image_info->type==GrayscaleMatteType ||
12266 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12268 jng_alpha_sample_depth = 0;
12270 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12272 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12274 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12275 image_info->quality;
12277 if (jng_alpha_quality >= 1000)
12278 jng_alpha_quality /= 1000;
12286 /* Create JPEG blob, image, and image_info */
12287 if (logging != MagickFalse)
12288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12289 " Creating jpeg_image_info for alpha.");
12291 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12293 if (jpeg_image_info == (ImageInfo *) NULL)
12294 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12296 if (logging != MagickFalse)
12297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12298 " Creating jpeg_image.");
12300 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12301 if (jpeg_image == (Image *) NULL)
12302 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12303 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12304 jpeg_image->alpha_trait=UndefinedPixelTrait;
12305 jpeg_image->quality=jng_alpha_quality;
12306 jpeg_image_info->type=GrayscaleType;
12307 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12308 (void) AcquireUniqueFilename(jpeg_image->filename);
12309 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12310 "%s",jpeg_image->filename);
12314 jng_alpha_compression_method=0;
12316 jng_alpha_sample_depth=0;
12319 /* To do: check bit depth of PNG alpha channel */
12321 /* Check if image is grayscale. */
12322 if (image_info->type != TrueColorMatteType && image_info->type !=
12323 TrueColorType && IsImageGray(image,exception))
12326 if (logging != MagickFalse)
12328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12329 " JNG Quality = %d",(int) jng_quality);
12330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12331 " JNG Color Type = %d",jng_color_type);
12334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12335 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12337 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12345 if (jng_alpha_compression_method==0)
12350 /* Encode alpha as a grayscale PNG blob */
12351 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12353 if (logging != MagickFalse)
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355 " Creating PNG blob.");
12357 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12358 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12359 jpeg_image_info->interlace=NoInterlace;
12361 /* Exclude all ancillary chunks */
12362 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12364 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12367 /* Retrieve sample depth used */
12368 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12369 if (value != (char *) NULL)
12370 jng_alpha_sample_depth= (unsigned int) value[0];
12374 /* Encode alpha as a grayscale JPEG blob */
12376 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12379 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12380 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12381 jpeg_image_info->interlace=NoInterlace;
12382 if (logging != MagickFalse)
12383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12384 " Creating blob.");
12385 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12387 jng_alpha_sample_depth=8;
12389 if (logging != MagickFalse)
12390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12391 " Successfully read jpeg_image into a blob, length=%.20g.",
12395 /* Destroy JPEG image and image_info */
12396 jpeg_image=DestroyImage(jpeg_image);
12397 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12398 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12401 /* Write JHDR chunk */
12402 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12403 PNGType(chunk,mng_JHDR);
12404 LogPNGChunk(logging,mng_JHDR,16L);
12405 PNGLong(chunk+4,(png_uint_32) image->columns);
12406 PNGLong(chunk+8,(png_uint_32) image->rows);
12407 chunk[12]=jng_color_type;
12408 chunk[13]=8; /* sample depth */
12409 chunk[14]=8; /*jng_image_compression_method */
12410 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12411 chunk[16]=jng_alpha_sample_depth;
12412 chunk[17]=jng_alpha_compression_method;
12413 chunk[18]=0; /*jng_alpha_filter_method */
12414 chunk[19]=0; /*jng_alpha_interlace_method */
12415 (void) WriteBlob(image,20,chunk);
12416 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12417 if (logging != MagickFalse)
12419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420 " JNG width:%15lu",(unsigned long) image->columns);
12422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12423 " JNG height:%14lu",(unsigned long) image->rows);
12425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12426 " JNG color type:%10d",jng_color_type);
12428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12429 " JNG sample depth:%8d",8);
12431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12432 " JNG compression:%9d",8);
12434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12435 " JNG interlace:%11d",0);
12437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12438 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12441 " JNG alpha compression:%3d",jng_alpha_compression_method);
12443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12444 " JNG alpha filter:%8d",0);
12446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12447 " JNG alpha interlace:%5d",0);
12450 /* Write any JNG-chunk-b profiles */
12451 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12454 Write leading ancillary chunks
12460 Write JNG bKGD chunk
12471 if (jng_color_type == 8 || jng_color_type == 12)
12475 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12476 PNGType(chunk,mng_bKGD);
12477 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12478 red=ScaleQuantumToChar(image->background_color.red);
12479 green=ScaleQuantumToChar(image->background_color.green);
12480 blue=ScaleQuantumToChar(image->background_color.blue);
12487 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12488 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12491 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12494 Write JNG sRGB chunk
12496 (void) WriteBlobMSBULong(image,1L);
12497 PNGType(chunk,mng_sRGB);
12498 LogPNGChunk(logging,mng_sRGB,1L);
12500 if (image->rendering_intent != UndefinedIntent)
12501 chunk[4]=(unsigned char)
12502 Magick_RenderingIntent_to_PNG_RenderingIntent(
12503 (image->rendering_intent));
12506 chunk[4]=(unsigned char)
12507 Magick_RenderingIntent_to_PNG_RenderingIntent(
12508 (PerceptualIntent));
12510 (void) WriteBlob(image,5,chunk);
12511 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12515 if (image->gamma != 0.0)
12518 Write JNG gAMA chunk
12520 (void) WriteBlobMSBULong(image,4L);
12521 PNGType(chunk,mng_gAMA);
12522 LogPNGChunk(logging,mng_gAMA,4L);
12523 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12524 (void) WriteBlob(image,8,chunk);
12525 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12528 if ((mng_info->equal_chrms == MagickFalse) &&
12529 (image->chromaticity.red_primary.x != 0.0))
12535 Write JNG cHRM chunk
12537 (void) WriteBlobMSBULong(image,32L);
12538 PNGType(chunk,mng_cHRM);
12539 LogPNGChunk(logging,mng_cHRM,32L);
12540 primary=image->chromaticity.white_point;
12541 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12542 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12543 primary=image->chromaticity.red_primary;
12544 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12545 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12546 primary=image->chromaticity.green_primary;
12547 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12548 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12549 primary=image->chromaticity.blue_primary;
12550 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12551 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12552 (void) WriteBlob(image,36,chunk);
12553 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12557 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12560 Write JNG pHYs chunk
12562 (void) WriteBlobMSBULong(image,9L);
12563 PNGType(chunk,mng_pHYs);
12564 LogPNGChunk(logging,mng_pHYs,9L);
12565 if (image->units == PixelsPerInchResolution)
12567 PNGLong(chunk+4,(png_uint_32)
12568 (image->resolution.x*100.0/2.54+0.5));
12570 PNGLong(chunk+8,(png_uint_32)
12571 (image->resolution.y*100.0/2.54+0.5));
12578 if (image->units == PixelsPerCentimeterResolution)
12580 PNGLong(chunk+4,(png_uint_32)
12581 (image->resolution.x*100.0+0.5));
12583 PNGLong(chunk+8,(png_uint_32)
12584 (image->resolution.y*100.0+0.5));
12591 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12592 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12596 (void) WriteBlob(image,13,chunk);
12597 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12600 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12603 Write JNG oFFs chunk
12605 (void) WriteBlobMSBULong(image,9L);
12606 PNGType(chunk,mng_oFFs);
12607 LogPNGChunk(logging,mng_oFFs,9L);
12608 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12609 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12611 (void) WriteBlob(image,13,chunk);
12612 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12614 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12616 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12617 PNGType(chunk,mng_vpAg);
12618 LogPNGChunk(logging,mng_vpAg,9L);
12619 PNGLong(chunk+4,(png_uint_32) image->page.width);
12620 PNGLong(chunk+8,(png_uint_32) image->page.height);
12621 chunk[12]=0; /* unit = pixels */
12622 (void) WriteBlob(image,13,chunk);
12623 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12629 if (jng_alpha_compression_method==0)
12637 /* Write IDAT chunk header */
12638 if (logging != MagickFalse)
12639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12640 " Write IDAT chunks from blob, length=%.20g.",(double)
12643 /* Copy IDAT chunks */
12646 for (i=8; i<(ssize_t) length; i+=len+12)
12648 len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12651 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12653 /* Found an IDAT chunk. */
12654 (void) WriteBlobMSBULong(image,len);
12655 LogPNGChunk(logging,mng_IDAT,len);
12656 (void) WriteBlob(image,len+4,p);
12657 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12662 if (logging != MagickFalse)
12663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12664 " Skipping %c%c%c%c chunk, length=%.20g.",
12665 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12672 /* Write JDAA chunk header */
12673 if (logging != MagickFalse)
12674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12675 " Write JDAA chunk, length=%.20g.",(double) length);
12676 (void) WriteBlobMSBULong(image,(size_t) length);
12677 PNGType(chunk,mng_JDAA);
12678 LogPNGChunk(logging,mng_JDAA,length);
12679 /* Write JDAT chunk(s) data */
12680 (void) WriteBlob(image,4,chunk);
12681 (void) WriteBlob(image,length,blob);
12682 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12685 blob=(unsigned char *) RelinquishMagickMemory(blob);
12688 /* Encode image as a JPEG blob */
12689 if (logging != MagickFalse)
12690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12691 " Creating jpeg_image_info.");
12692 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12693 if (jpeg_image_info == (ImageInfo *) NULL)
12694 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12696 if (logging != MagickFalse)
12697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698 " Creating jpeg_image.");
12700 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12701 if (jpeg_image == (Image *) NULL)
12702 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12703 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12705 (void) AcquireUniqueFilename(jpeg_image->filename);
12706 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12707 jpeg_image->filename);
12709 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12712 if (logging != MagickFalse)
12713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12714 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12715 (double) jpeg_image->rows);
12717 if (jng_color_type == 8 || jng_color_type == 12)
12718 jpeg_image_info->type=GrayscaleType;
12720 jpeg_image_info->quality=jng_quality;
12721 jpeg_image->quality=jng_quality;
12722 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12723 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12725 if (logging != MagickFalse)
12726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12727 " Creating blob.");
12729 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12731 if (logging != MagickFalse)
12733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12734 " Successfully read jpeg_image into a blob, length=%.20g.",
12737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12738 " Write JDAT chunk, length=%.20g.",(double) length);
12741 /* Write JDAT chunk(s) */
12742 (void) WriteBlobMSBULong(image,(size_t) length);
12743 PNGType(chunk,mng_JDAT);
12744 LogPNGChunk(logging,mng_JDAT,length);
12745 (void) WriteBlob(image,4,chunk);
12746 (void) WriteBlob(image,length,blob);
12747 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12749 jpeg_image=DestroyImage(jpeg_image);
12750 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12751 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12752 blob=(unsigned char *) RelinquishMagickMemory(blob);
12754 /* Write any JNG-chunk-e profiles */
12755 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12757 /* Write IEND chunk */
12758 (void) WriteBlobMSBULong(image,0L);
12759 PNGType(chunk,mng_IEND);
12760 LogPNGChunk(logging,mng_IEND,0);
12761 (void) WriteBlob(image,4,chunk);
12762 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12764 if (logging != MagickFalse)
12765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12766 " exit WriteOneJNGImage()");
12773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12777 % W r i t e J N G I m a g e %
12781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12783 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12785 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12787 % The format of the WriteJNGImage method is:
12789 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12790 % Image *image,ExceptionInfo *exception)
12792 % A description of each parameter follows:
12794 % o image_info: the image info.
12796 % o image: The image.
12798 % o exception: return any errors or warnings in this structure.
12800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12802 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12803 ExceptionInfo *exception)
12806 have_mng_structure,
12816 assert(image_info != (const ImageInfo *) NULL);
12817 assert(image_info->signature == MagickSignature);
12818 assert(image != (Image *) NULL);
12819 assert(image->signature == MagickSignature);
12820 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12821 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12822 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12823 if (status == MagickFalse)
12827 Allocate a MngInfo structure.
12829 have_mng_structure=MagickFalse;
12830 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12831 if (mng_info == (MngInfo *) NULL)
12832 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12834 Initialize members of the MngInfo structure.
12836 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12837 mng_info->image=image;
12838 have_mng_structure=MagickTrue;
12840 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12842 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12843 (void) CloseBlob(image);
12845 (void) CatchImageException(image);
12846 MngInfoFreeStruct(mng_info,&have_mng_structure);
12847 if (logging != MagickFalse)
12848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12853 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12854 ExceptionInfo *exception)
12863 have_mng_structure,
12866 volatile MagickBooleanType
12878 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12879 defined(PNG_MNG_FEATURES_SUPPORTED)
12882 all_images_are_gray,
12892 volatile unsigned int
12903 #if (PNG_LIBPNG_VER < 10200)
12904 if (image_info->verbose)
12905 printf("Your PNG library (libpng-%s) is rather old.\n",
12906 PNG_LIBPNG_VER_STRING);
12912 assert(image_info != (const ImageInfo *) NULL);
12913 assert(image_info->signature == MagickSignature);
12914 assert(image != (Image *) NULL);
12915 assert(image->signature == MagickSignature);
12916 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12917 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12918 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12919 if (status == MagickFalse)
12923 Allocate a MngInfo structure.
12925 have_mng_structure=MagickFalse;
12926 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12927 if (mng_info == (MngInfo *) NULL)
12928 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12930 Initialize members of the MngInfo structure.
12932 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12933 mng_info->image=image;
12934 have_mng_structure=MagickTrue;
12935 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12938 * See if user has requested a specific PNG subformat to be used
12939 * for all of the PNGs in the MNG being written, e.g.,
12941 * convert *.png png8:animation.mng
12943 * To do: check -define png:bit_depth and png:color_type as well,
12944 * or perhaps use mng:bit_depth and mng:color_type instead for
12948 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12949 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12950 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12952 write_jng=MagickFalse;
12953 if (image_info->compression == JPEGCompression)
12954 write_jng=MagickTrue;
12956 mng_info->adjoin=image_info->adjoin &&
12957 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12959 if (logging != MagickFalse)
12961 /* Log some info about the input */
12965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12966 " Checking input image(s)\n"
12967 " Image_info depth: %.20g, Type: %d",
12968 (double) image_info->depth, image_info->type);
12971 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12975 " Scene: %.20g\n, Image depth: %.20g",
12976 (double) scene++, (double) p->depth);
12978 if (p->alpha_trait == BlendPixelTrait)
12979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12986 if (p->storage_class == PseudoClass)
12987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12988 " Storage class: PseudoClass");
12991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12992 " Storage class: DirectClass");
12995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12996 " Number of colors: %.20g",(double) p->colors);
12999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13000 " Number of colors: unspecified");
13002 if (mng_info->adjoin == MagickFalse)
13007 use_global_plte=MagickFalse;
13008 all_images_are_gray=MagickFalse;
13009 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13010 need_local_plte=MagickTrue;
13012 need_defi=MagickFalse;
13013 need_matte=MagickFalse;
13014 mng_info->framing_mode=1;
13015 mng_info->old_framing_mode=1;
13018 if (image_info->page != (char *) NULL)
13021 Determine image bounding box.
13023 SetGeometry(image,&mng_info->page);
13024 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13025 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13037 mng_info->page=image->page;
13038 need_geom=MagickTrue;
13039 if (mng_info->page.width || mng_info->page.height)
13040 need_geom=MagickFalse;
13042 Check all the scenes.
13044 initial_delay=image->delay;
13045 need_iterations=MagickFalse;
13046 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13047 mng_info->equal_physs=MagickTrue,
13048 mng_info->equal_gammas=MagickTrue;
13049 mng_info->equal_srgbs=MagickTrue;
13050 mng_info->equal_backgrounds=MagickTrue;
13052 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13053 defined(PNG_MNG_FEATURES_SUPPORTED)
13054 all_images_are_gray=MagickTrue;
13055 mng_info->equal_palettes=MagickFalse;
13056 need_local_plte=MagickFalse;
13058 for (next_image=image; next_image != (Image *) NULL; )
13062 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13063 mng_info->page.width=next_image->columns+next_image->page.x;
13065 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13066 mng_info->page.height=next_image->rows+next_image->page.y;
13069 if (next_image->page.x || next_image->page.y)
13070 need_defi=MagickTrue;
13072 if (next_image->alpha_trait == BlendPixelTrait)
13073 need_matte=MagickTrue;
13075 if ((int) next_image->dispose >= BackgroundDispose)
13076 if ((next_image->alpha_trait == BlendPixelTrait) ||
13077 next_image->page.x || next_image->page.y ||
13078 ((next_image->columns < mng_info->page.width) &&
13079 (next_image->rows < mng_info->page.height)))
13080 mng_info->need_fram=MagickTrue;
13082 if (next_image->iterations)
13083 need_iterations=MagickTrue;
13085 final_delay=next_image->delay;
13087 if (final_delay != initial_delay || final_delay > 1UL*
13088 next_image->ticks_per_second)
13089 mng_info->need_fram=1;
13091 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13092 defined(PNG_MNG_FEATURES_SUPPORTED)
13094 check for global palette possibility.
13096 if (image->alpha_trait == BlendPixelTrait)
13097 need_local_plte=MagickTrue;
13099 if (need_local_plte == 0)
13101 if (IsImageGray(image,exception) == MagickFalse)
13102 all_images_are_gray=MagickFalse;
13103 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13104 if (use_global_plte == 0)
13105 use_global_plte=mng_info->equal_palettes;
13106 need_local_plte=!mng_info->equal_palettes;
13109 if (GetNextImageInList(next_image) != (Image *) NULL)
13111 if (next_image->background_color.red !=
13112 next_image->next->background_color.red ||
13113 next_image->background_color.green !=
13114 next_image->next->background_color.green ||
13115 next_image->background_color.blue !=
13116 next_image->next->background_color.blue)
13117 mng_info->equal_backgrounds=MagickFalse;
13119 if (next_image->gamma != next_image->next->gamma)
13120 mng_info->equal_gammas=MagickFalse;
13122 if (next_image->rendering_intent !=
13123 next_image->next->rendering_intent)
13124 mng_info->equal_srgbs=MagickFalse;
13126 if ((next_image->units != next_image->next->units) ||
13127 (next_image->resolution.x != next_image->next->resolution.x) ||
13128 (next_image->resolution.y != next_image->next->resolution.y))
13129 mng_info->equal_physs=MagickFalse;
13131 if (mng_info->equal_chrms)
13133 if (next_image->chromaticity.red_primary.x !=
13134 next_image->next->chromaticity.red_primary.x ||
13135 next_image->chromaticity.red_primary.y !=
13136 next_image->next->chromaticity.red_primary.y ||
13137 next_image->chromaticity.green_primary.x !=
13138 next_image->next->chromaticity.green_primary.x ||
13139 next_image->chromaticity.green_primary.y !=
13140 next_image->next->chromaticity.green_primary.y ||
13141 next_image->chromaticity.blue_primary.x !=
13142 next_image->next->chromaticity.blue_primary.x ||
13143 next_image->chromaticity.blue_primary.y !=
13144 next_image->next->chromaticity.blue_primary.y ||
13145 next_image->chromaticity.white_point.x !=
13146 next_image->next->chromaticity.white_point.x ||
13147 next_image->chromaticity.white_point.y !=
13148 next_image->next->chromaticity.white_point.y)
13149 mng_info->equal_chrms=MagickFalse;
13153 next_image=GetNextImageInList(next_image);
13155 if (image_count < 2)
13157 mng_info->equal_backgrounds=MagickFalse;
13158 mng_info->equal_chrms=MagickFalse;
13159 mng_info->equal_gammas=MagickFalse;
13160 mng_info->equal_srgbs=MagickFalse;
13161 mng_info->equal_physs=MagickFalse;
13162 use_global_plte=MagickFalse;
13163 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13164 need_local_plte=MagickTrue;
13166 need_iterations=MagickFalse;
13169 if (mng_info->need_fram == MagickFalse)
13172 Only certain framing rates 100/n are exactly representable without
13173 the FRAM chunk but we'll allow some slop in VLC files
13175 if (final_delay == 0)
13177 if (need_iterations != MagickFalse)
13180 It's probably a GIF with loop; don't run it *too* fast.
13182 if (mng_info->adjoin)
13185 (void) ThrowMagickException(exception,GetMagickModule(),
13187 "input has zero delay between all frames; assuming",
13192 mng_info->ticks_per_second=0;
13194 if (final_delay != 0)
13195 mng_info->ticks_per_second=(png_uint_32)
13196 (image->ticks_per_second/final_delay);
13197 if (final_delay > 50)
13198 mng_info->ticks_per_second=2;
13200 if (final_delay > 75)
13201 mng_info->ticks_per_second=1;
13203 if (final_delay > 125)
13204 mng_info->need_fram=MagickTrue;
13206 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13207 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13208 (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
13209 1UL*image->ticks_per_second))
13210 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13213 if (mng_info->need_fram != MagickFalse)
13214 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13216 If pseudocolor, we should also check to see if all the
13217 palettes are identical and write a global PLTE if they are.
13221 Write the MNG version 1.0 signature and MHDR chunk.
13223 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13224 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13225 PNGType(chunk,mng_MHDR);
13226 LogPNGChunk(logging,mng_MHDR,28L);
13227 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13228 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13229 PNGLong(chunk+12,mng_info->ticks_per_second);
13230 PNGLong(chunk+16,0L); /* layer count=unknown */
13231 PNGLong(chunk+20,0L); /* frame count=unknown */
13232 PNGLong(chunk+24,0L); /* play time=unknown */
13237 if (need_defi || mng_info->need_fram || use_global_plte)
13238 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13241 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13246 if (need_defi || mng_info->need_fram || use_global_plte)
13247 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13250 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13258 if (need_defi || mng_info->need_fram || use_global_plte)
13259 PNGLong(chunk+28,11L); /* simplicity=LC */
13262 PNGLong(chunk+28,9L); /* simplicity=VLC */
13267 if (need_defi || mng_info->need_fram || use_global_plte)
13268 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13271 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13274 (void) WriteBlob(image,32,chunk);
13275 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13276 option=GetImageOption(image_info,"mng:need-cacheoff");
13277 if (option != (const char *) NULL)
13283 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13285 PNGType(chunk,mng_nEED);
13286 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13287 (void) WriteBlobMSBULong(image,(size_t) length);
13288 LogPNGChunk(logging,mng_nEED,(size_t) length);
13290 (void) WriteBlob(image,length,chunk);
13291 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13293 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13294 (GetNextImageInList(image) != (Image *) NULL) &&
13295 (image->iterations != 1))
13298 Write MNG TERM chunk
13300 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13301 PNGType(chunk,mng_TERM);
13302 LogPNGChunk(logging,mng_TERM,10L);
13303 chunk[4]=3; /* repeat animation */
13304 chunk[5]=0; /* show last frame when done */
13305 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13306 final_delay/MagickMax(image->ticks_per_second,1)));
13308 if (image->iterations == 0)
13309 PNGLong(chunk+10,PNG_UINT_31_MAX);
13312 PNGLong(chunk+10,(png_uint_32) image->iterations);
13314 if (logging != MagickFalse)
13316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13317 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13318 final_delay/MagickMax(image->ticks_per_second,1)));
13320 if (image->iterations == 0)
13321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13322 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13326 " Image iterations: %.20g",(double) image->iterations);
13328 (void) WriteBlob(image,14,chunk);
13329 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13332 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13334 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13335 mng_info->equal_srgbs)
13338 Write MNG sRGB chunk
13340 (void) WriteBlobMSBULong(image,1L);
13341 PNGType(chunk,mng_sRGB);
13342 LogPNGChunk(logging,mng_sRGB,1L);
13344 if (image->rendering_intent != UndefinedIntent)
13345 chunk[4]=(unsigned char)
13346 Magick_RenderingIntent_to_PNG_RenderingIntent(
13347 (image->rendering_intent));
13350 chunk[4]=(unsigned char)
13351 Magick_RenderingIntent_to_PNG_RenderingIntent(
13352 (PerceptualIntent));
13354 (void) WriteBlob(image,5,chunk);
13355 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13356 mng_info->have_write_global_srgb=MagickTrue;
13361 if (image->gamma && mng_info->equal_gammas)
13364 Write MNG gAMA chunk
13366 (void) WriteBlobMSBULong(image,4L);
13367 PNGType(chunk,mng_gAMA);
13368 LogPNGChunk(logging,mng_gAMA,4L);
13369 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13370 (void) WriteBlob(image,8,chunk);
13371 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13372 mng_info->have_write_global_gama=MagickTrue;
13374 if (mng_info->equal_chrms)
13380 Write MNG cHRM chunk
13382 (void) WriteBlobMSBULong(image,32L);
13383 PNGType(chunk,mng_cHRM);
13384 LogPNGChunk(logging,mng_cHRM,32L);
13385 primary=image->chromaticity.white_point;
13386 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13387 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13388 primary=image->chromaticity.red_primary;
13389 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13390 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13391 primary=image->chromaticity.green_primary;
13392 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13393 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13394 primary=image->chromaticity.blue_primary;
13395 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13396 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13397 (void) WriteBlob(image,36,chunk);
13398 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13399 mng_info->have_write_global_chrm=MagickTrue;
13402 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13405 Write MNG pHYs chunk
13407 (void) WriteBlobMSBULong(image,9L);
13408 PNGType(chunk,mng_pHYs);
13409 LogPNGChunk(logging,mng_pHYs,9L);
13411 if (image->units == PixelsPerInchResolution)
13413 PNGLong(chunk+4,(png_uint_32)
13414 (image->resolution.x*100.0/2.54+0.5));
13416 PNGLong(chunk+8,(png_uint_32)
13417 (image->resolution.y*100.0/2.54+0.5));
13424 if (image->units == PixelsPerCentimeterResolution)
13426 PNGLong(chunk+4,(png_uint_32)
13427 (image->resolution.x*100.0+0.5));
13429 PNGLong(chunk+8,(png_uint_32)
13430 (image->resolution.y*100.0+0.5));
13437 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13438 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13442 (void) WriteBlob(image,13,chunk);
13443 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13446 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13447 or does not cover the entire frame.
13449 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13450 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13451 (image->page.width+image->page.x < mng_info->page.width))
13452 || (image->page.height && (image->page.height+image->page.y
13453 < mng_info->page.height))))
13455 (void) WriteBlobMSBULong(image,6L);
13456 PNGType(chunk,mng_BACK);
13457 LogPNGChunk(logging,mng_BACK,6L);
13458 red=ScaleQuantumToShort(image->background_color.red);
13459 green=ScaleQuantumToShort(image->background_color.green);
13460 blue=ScaleQuantumToShort(image->background_color.blue);
13461 PNGShort(chunk+4,red);
13462 PNGShort(chunk+6,green);
13463 PNGShort(chunk+8,blue);
13464 (void) WriteBlob(image,10,chunk);
13465 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13466 if (mng_info->equal_backgrounds)
13468 (void) WriteBlobMSBULong(image,6L);
13469 PNGType(chunk,mng_bKGD);
13470 LogPNGChunk(logging,mng_bKGD,6L);
13471 (void) WriteBlob(image,10,chunk);
13472 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13476 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13477 if ((need_local_plte == MagickFalse) &&
13478 (image->storage_class == PseudoClass) &&
13479 (all_images_are_gray == MagickFalse))
13485 Write MNG PLTE chunk
13487 data_length=3*image->colors;
13488 (void) WriteBlobMSBULong(image,data_length);
13489 PNGType(chunk,mng_PLTE);
13490 LogPNGChunk(logging,mng_PLTE,data_length);
13492 for (i=0; i < (ssize_t) image->colors; i++)
13494 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13495 image->colormap[i].red) & 0xff);
13496 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13497 image->colormap[i].green) & 0xff);
13498 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13499 image->colormap[i].blue) & 0xff);
13502 (void) WriteBlob(image,data_length+4,chunk);
13503 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13504 mng_info->have_write_global_plte=MagickTrue;
13510 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13511 defined(PNG_MNG_FEATURES_SUPPORTED)
13512 mng_info->equal_palettes=MagickFalse;
13516 if (mng_info->adjoin)
13518 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13519 defined(PNG_MNG_FEATURES_SUPPORTED)
13521 If we aren't using a global palette for the entire MNG, check to
13522 see if we can use one for two or more consecutive images.
13524 if (need_local_plte && use_global_plte && !all_images_are_gray)
13526 if (mng_info->IsPalette)
13529 When equal_palettes is true, this image has the same palette
13530 as the previous PseudoClass image
13532 mng_info->have_write_global_plte=mng_info->equal_palettes;
13533 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13534 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13537 Write MNG PLTE chunk
13542 data_length=3*image->colors;
13543 (void) WriteBlobMSBULong(image,data_length);
13544 PNGType(chunk,mng_PLTE);
13545 LogPNGChunk(logging,mng_PLTE,data_length);
13547 for (i=0; i < (ssize_t) image->colors; i++)
13549 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13550 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13551 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13554 (void) WriteBlob(image,data_length+4,chunk);
13555 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13556 (uInt) (data_length+4)));
13557 mng_info->have_write_global_plte=MagickTrue;
13561 mng_info->have_write_global_plte=MagickFalse;
13572 previous_x=mng_info->page.x;
13573 previous_y=mng_info->page.y;
13580 mng_info->page=image->page;
13581 if ((mng_info->page.x != previous_x) ||
13582 (mng_info->page.y != previous_y))
13584 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13585 PNGType(chunk,mng_DEFI);
13586 LogPNGChunk(logging,mng_DEFI,12L);
13587 chunk[4]=0; /* object 0 MSB */
13588 chunk[5]=0; /* object 0 LSB */
13589 chunk[6]=0; /* visible */
13590 chunk[7]=0; /* abstract */
13591 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13592 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13593 (void) WriteBlob(image,16,chunk);
13594 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13599 mng_info->write_mng=write_mng;
13601 if ((int) image->dispose >= 3)
13602 mng_info->framing_mode=3;
13604 if (mng_info->need_fram && mng_info->adjoin &&
13605 ((image->delay != mng_info->delay) ||
13606 (mng_info->framing_mode != mng_info->old_framing_mode)))
13608 if (image->delay == mng_info->delay)
13611 Write a MNG FRAM chunk with the new framing mode.
13613 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13614 PNGType(chunk,mng_FRAM);
13615 LogPNGChunk(logging,mng_FRAM,1L);
13616 chunk[4]=(unsigned char) mng_info->framing_mode;
13617 (void) WriteBlob(image,5,chunk);
13618 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13623 Write a MNG FRAM chunk with the delay.
13625 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13626 PNGType(chunk,mng_FRAM);
13627 LogPNGChunk(logging,mng_FRAM,10L);
13628 chunk[4]=(unsigned char) mng_info->framing_mode;
13629 chunk[5]=0; /* frame name separator (no name) */
13630 chunk[6]=2; /* flag for changing default delay */
13631 chunk[7]=0; /* flag for changing frame timeout */
13632 chunk[8]=0; /* flag for changing frame clipping */
13633 chunk[9]=0; /* flag for changing frame sync_id */
13634 PNGLong(chunk+10,(png_uint_32)
13635 ((mng_info->ticks_per_second*
13636 image->delay)/MagickMax(image->ticks_per_second,1)));
13637 (void) WriteBlob(image,14,chunk);
13638 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13639 mng_info->delay=(png_uint_32) image->delay;
13641 mng_info->old_framing_mode=mng_info->framing_mode;
13644 #if defined(JNG_SUPPORTED)
13645 if (image_info->compression == JPEGCompression)
13650 if (logging != MagickFalse)
13651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13652 " Writing JNG object.");
13653 /* To do: specify the desired alpha compression method. */
13654 write_info=CloneImageInfo(image_info);
13655 write_info->compression=UndefinedCompression;
13656 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13657 write_info=DestroyImageInfo(write_info);
13662 if (logging != MagickFalse)
13663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13664 " Writing PNG object.");
13666 mng_info->need_blob = MagickFalse;
13667 mng_info->ping_preserve_colormap = MagickFalse;
13669 /* We don't want any ancillary chunks written */
13670 mng_info->ping_exclude_bKGD=MagickTrue;
13671 mng_info->ping_exclude_cHRM=MagickTrue;
13672 mng_info->ping_exclude_date=MagickTrue;
13673 mng_info->ping_exclude_EXIF=MagickTrue;
13674 mng_info->ping_exclude_gAMA=MagickTrue;
13675 mng_info->ping_exclude_iCCP=MagickTrue;
13676 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13677 mng_info->ping_exclude_oFFs=MagickTrue;
13678 mng_info->ping_exclude_pHYs=MagickTrue;
13679 mng_info->ping_exclude_sRGB=MagickTrue;
13680 mng_info->ping_exclude_tEXt=MagickTrue;
13681 mng_info->ping_exclude_tRNS=MagickTrue;
13682 mng_info->ping_exclude_vpAg=MagickTrue;
13683 mng_info->ping_exclude_zCCP=MagickTrue;
13684 mng_info->ping_exclude_zTXt=MagickTrue;
13686 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13689 if (status == MagickFalse)
13691 MngInfoFreeStruct(mng_info,&have_mng_structure);
13692 (void) CloseBlob(image);
13693 return(MagickFalse);
13695 (void) CatchImageException(image);
13696 if (GetNextImageInList(image) == (Image *) NULL)
13698 image=SyncNextImageInList(image);
13699 status=SetImageProgress(image,SaveImagesTag,scene++,
13700 GetImageListLength(image));
13702 if (status == MagickFalse)
13705 } while (mng_info->adjoin);
13709 while (GetPreviousImageInList(image) != (Image *) NULL)
13710 image=GetPreviousImageInList(image);
13712 Write the MEND chunk.
13714 (void) WriteBlobMSBULong(image,0x00000000L);
13715 PNGType(chunk,mng_MEND);
13716 LogPNGChunk(logging,mng_MEND,0L);
13717 (void) WriteBlob(image,4,chunk);
13718 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13721 Relinquish resources.
13723 (void) CloseBlob(image);
13724 MngInfoFreeStruct(mng_info,&have_mng_structure);
13726 if (logging != MagickFalse)
13727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13729 return(MagickTrue);
13731 #else /* PNG_LIBPNG_VER > 10011 */
13733 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13736 printf("Your PNG library is too old: You have libpng-%s\n",
13737 PNG_LIBPNG_VER_STRING);
13739 ThrowBinaryException(CoderError,"PNG library is too old",
13740 image_info->filename);
13743 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13745 return(WritePNGImage(image_info,image));
13747 #endif /* PNG_LIBPNG_VER > 10011 */