2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
106 /* ImageMagick differences */
107 #define first_scene scene
109 #if PNG_LIBPNG_VER > 10011
111 Optional declarations. Define or undefine them as you like.
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116 Features under construction. Define these to work on them.
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
132 /* Macros for left-bit-replication to ensure that pixels
133 * and PixelInfos all have the same image->depth, and for use
134 * in PNG8 quantization.
138 /* LBR01: Replicate top bit */
140 #define LBR01PacketRed(pixelpacket) \
141 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
144 #define LBR01PacketGreen(pixelpacket) \
145 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
148 #define LBR01PacketBlue(pixelpacket) \
149 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
152 #define LBR01PacketAlpha(pixelpacket) \
153 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
156 #define LBR01PacketRGB(pixelpacket) \
158 LBR01PacketRed((pixelpacket)); \
159 LBR01PacketGreen((pixelpacket)); \
160 LBR01PacketBlue((pixelpacket)); \
163 #define LBR01PacketRGBO(pixelpacket) \
165 LBR01PacketRGB((pixelpacket)); \
166 LBR01PacketAlpha((pixelpacket)); \
169 #define LBR01PixelRed(pixel) \
170 (SetPixelRed(image, \
171 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
172 0 : QuantumRange,(pixel)));
174 #define LBR01PixelGreen(pixel) \
175 (SetPixelGreen(image, \
176 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177 0 : QuantumRange,(pixel)));
179 #define LBR01PixelBlue(pixel) \
180 (SetPixelBlue(image, \
181 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
182 0 : QuantumRange,(pixel)));
184 #define LBR01PixelAlpha(pixel) \
185 (SetPixelAlpha(image, \
186 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
187 0 : QuantumRange,(pixel)));
189 #define LBR01PixelRGB(pixel) \
191 LBR01PixelRed((pixel)); \
192 LBR01PixelGreen((pixel)); \
193 LBR01PixelBlue((pixel)); \
196 #define LBR01PixelRGBA(pixel) \
198 LBR01PixelRGB((pixel)); \
199 LBR01PixelAlpha((pixel)); \
202 /* LBR02: Replicate top 2 bits */
204 #define LBR02PacketRed(pixelpacket) \
206 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
207 (pixelpacket).red=ScaleCharToQuantum( \
208 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 #define LBR02PacketGreen(pixelpacket) \
212 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
213 (pixelpacket).green=ScaleCharToQuantum( \
214 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 #define LBR02PacketBlue(pixelpacket) \
218 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
219 (pixelpacket).blue=ScaleCharToQuantum( \
220 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 #define LBR02PacketAlpha(pixelpacket) \
224 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
225 (pixelpacket).alpha=ScaleCharToQuantum( \
226 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
229 #define LBR02PacketRGB(pixelpacket) \
231 LBR02PacketRed((pixelpacket)); \
232 LBR02PacketGreen((pixelpacket)); \
233 LBR02PacketBlue((pixelpacket)); \
236 #define LBR02PacketRGBO(pixelpacket) \
238 LBR02PacketRGB((pixelpacket)); \
239 LBR02PacketAlpha((pixelpacket)); \
242 #define LBR02PixelRed(pixel) \
244 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
246 SetPixelRed(image, ScaleCharToQuantum( \
247 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
250 #define LBR02PixelGreen(pixel) \
252 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
254 SetPixelGreen(image, ScaleCharToQuantum( \
255 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
258 #define LBR02PixelBlue(pixel) \
260 unsigned char lbr_bits= \
261 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
262 SetPixelBlue(image, ScaleCharToQuantum( \
263 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
266 #define LBR02PixelAlpha(pixel) \
268 unsigned char lbr_bits= \
269 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
270 SetPixelAlpha(image, ScaleCharToQuantum( \
271 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
275 #define LBR02PixelRGB(pixel) \
277 LBR02PixelRed((pixel)); \
278 LBR02PixelGreen((pixel)); \
279 LBR02PixelBlue((pixel)); \
282 #define LBR02PixelRGBA(pixel) \
284 LBR02PixelRGB((pixel)); \
285 LBR02PixelAlpha((pixel)); \
288 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
289 PNG8 quantization) */
291 #define LBR03PacketRed(pixelpacket) \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
294 (pixelpacket).red=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 #define LBR03PacketGreen(pixelpacket) \
299 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
300 (pixelpacket).green=ScaleCharToQuantum( \
301 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 #define LBR03PacketBlue(pixelpacket) \
305 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
306 (pixelpacket).blue=ScaleCharToQuantum( \
307 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
310 #define LBR03PacketRGB(pixelpacket) \
312 LBR03PacketRed((pixelpacket)); \
313 LBR03PacketGreen((pixelpacket)); \
314 LBR03PacketBlue((pixelpacket)); \
317 #define LBR03PixelRed(pixel) \
319 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
321 SetPixelRed(image, ScaleCharToQuantum( \
322 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
324 #define LBR03Green(pixel) \
326 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
328 SetPixelGreen(image, ScaleCharToQuantum( \
329 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
331 #define LBR03Blue(pixel) \
333 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
335 SetPixelBlue(image, ScaleCharToQuantum( \
336 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
339 #define LBR03RGB(pixel) \
341 LBR03PixelRed((pixel)); \
342 LBR03Green((pixel)); \
343 LBR03Blue((pixel)); \
346 /* LBR04: Replicate top 4 bits */
348 #define LBR04PacketRed(pixelpacket) \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
351 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
353 #define LBR04PacketGreen(pixelpacket) \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
356 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
358 #define LBR04PacketBlue(pixelpacket) \
360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
361 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
363 #define LBR04PacketAlpha(pixelpacket) \
365 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
366 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
369 #define LBR04PacketRGB(pixelpacket) \
371 LBR04PacketRed((pixelpacket)); \
372 LBR04PacketGreen((pixelpacket)); \
373 LBR04PacketBlue((pixelpacket)); \
376 #define LBR04PacketRGBO(pixelpacket) \
378 LBR04PacketRGB((pixelpacket)); \
379 LBR04PacketAlpha((pixelpacket)); \
382 #define LBR04PixelRed(pixel) \
384 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
387 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
389 #define LBR04PixelGreen(pixel) \
391 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
393 SetPixelGreen(image,\
394 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
396 #define LBR04PixelBlue(pixel) \
398 unsigned char lbr_bits= \
399 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
401 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
403 #define LBR04PixelAlpha(pixel) \
405 unsigned char lbr_bits= \
406 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
407 SetPixelAlpha(image,\
408 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
411 #define LBR04PixelRGB(pixel) \
413 LBR04PixelRed((pixel)); \
414 LBR04PixelGreen((pixel)); \
415 LBR04PixelBlue((pixel)); \
418 #define LBR04PixelRGBA(pixel) \
420 LBR04PixelRGB((pixel)); \
421 LBR04PixelAlpha((pixel)); \
425 #if MAGICKCORE_QUANTUM_DEPTH > 8
426 /* LBR08: Replicate top 8 bits */
428 #define LBR08PacketRed(pixelpacket) \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
431 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
433 #define LBR08PacketGreen(pixelpacket) \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
436 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
438 #define LBR08PacketBlue(pixelpacket) \
440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
441 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
443 #define LBR08PacketAlpha(pixelpacket) \
445 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
446 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
449 #define LBR08PacketRGB(pixelpacket) \
451 LBR08PacketRed((pixelpacket)); \
452 LBR08PacketGreen((pixelpacket)); \
453 LBR08PacketBlue((pixelpacket)); \
456 #define LBR08PacketRGBO(pixelpacket) \
458 LBR08PacketRGB((pixelpacket)); \
459 LBR08PacketAlpha((pixelpacket)); \
462 #define LBR08PixelRed(pixel) \
464 unsigned char lbr_bits= \
465 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
467 ScaleCharToQuantum((lbr_bits)), (pixel)); \
469 #define LBR08PixelGreen(pixel) \
471 unsigned char lbr_bits= \
472 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
473 SetPixelGreen(image,\
474 ScaleCharToQuantum((lbr_bits)), (pixel)); \
476 #define LBR08PixelBlue(pixel) \
478 unsigned char lbr_bits= \
479 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
481 ScaleCharToQuantum((lbr_bits)), (pixel)); \
483 #define LBR08PixelAlpha(pixel) \
485 unsigned char lbr_bits= \
486 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
487 SetPixelAlpha(image,\
488 ScaleCharToQuantum((lbr_bits)), (pixel)); \
491 #define LBR08PixelRGB(pixel) \
493 LBR08PixelRed((pixel)); \
494 LBR08PixelGreen((pixel)); \
495 LBR08PixelBlue((pixel)); \
498 #define LBR08PixelRGBA(pixel) \
500 LBR08PixelRGB((pixel)); \
501 LBR08PixelAlpha((pixel)); \
503 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
506 #if MAGICKCORE_QUANTUM_DEPTH > 16
507 /* LBR16: Replicate top 16 bits */
509 #define LBR16PacketRed(pixelpacket) \
511 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
512 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
514 #define LBR16PacketGreen(pixelpacket) \
516 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
517 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
519 #define LBR16PacketBlue(pixelpacket) \
521 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
522 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
524 #define LBR16PacketAlpha(pixelpacket) \
526 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
527 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
530 #define LBR16PacketRGB(pixelpacket) \
532 LBR16PacketRed((pixelpacket)); \
533 LBR16PacketGreen((pixelpacket)); \
534 LBR16PacketBlue((pixelpacket)); \
537 #define LBR16PacketRGBO(pixelpacket) \
539 LBR16PacketRGB((pixelpacket)); \
540 LBR16PacketAlpha((pixelpacket)); \
543 #define LBR16PixelRed(pixel) \
545 unsigned short lbr_bits= \
546 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
550 #define LBR16PixelGreen(pixel) \
552 unsigned short lbr_bits= \
553 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
554 SetPixelGreen(image,\
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
557 #define LBR16PixelBlue(pixel) \
559 unsigned short lbr_bits= \
560 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
564 #define LBR16PixelAlpha(pixel) \
566 unsigned short lbr_bits= \
567 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
568 SetPixelAlpha(image,\
569 ScaleShortToQuantum((lbr_bits)),(pixel)); \
572 #define LBR16PixelRGB(pixel) \
574 LBR16PixelRed((pixel)); \
575 LBR16PixelGreen((pixel)); \
576 LBR16PixelBlue((pixel)); \
579 #define LBR16PixelRGBA(pixel) \
581 LBR16PixelRGB((pixel)); \
582 LBR16PixelAlpha((pixel)); \
584 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
587 Establish thread safety.
588 setjmp/longjmp is claimed to be safe on these platforms:
589 setjmp/longjmp is alleged to be unsafe on these platforms:
591 #ifndef SETJMP_IS_THREAD_SAFE
592 #define PNG_SETJMP_NOT_THREAD_SAFE
595 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
597 *ping_semaphore = (SemaphoreInfo *) NULL;
601 This temporary until I set up malloc'ed object attributes array.
602 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
605 #define MNG_MAX_OBJECTS 256
608 If this not defined, spec is interpreted strictly. If it is
609 defined, an attempt will be made to recover from some errors,
611 o global PLTE too short
616 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
617 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
618 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
619 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
620 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
621 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
622 will be enabled by default in libpng-1.2.0.
624 #ifdef PNG_MNG_FEATURES_SUPPORTED
625 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
626 # define PNG_READ_EMPTY_PLTE_SUPPORTED
628 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
629 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
634 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
635 This macro is only defined in libpng-1.0.3 and later.
636 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
638 #ifndef PNG_UINT_31_MAX
639 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
643 Constant strings for known chunk types. If you need to add a chunk,
644 add a string holding the name here. To make the code more
645 portable, we use ASCII numbers like this, not characters.
648 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
649 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
650 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
651 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
652 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
653 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
654 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
655 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
656 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
657 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
658 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
659 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
660 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
661 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
662 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
663 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
664 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
665 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
666 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
667 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
668 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
669 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
670 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
671 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
672 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
673 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
674 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
675 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
676 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
677 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
678 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
679 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
680 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
681 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
683 #if defined(JNG_SUPPORTED)
684 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
685 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
686 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
687 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
688 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
689 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
693 Other known chunks that are not yet supported by ImageMagick:
694 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
695 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
696 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
697 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
698 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
699 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
700 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
701 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
704 typedef struct _MngBox
713 typedef struct _MngPair
720 #ifdef MNG_OBJECT_BUFFERS
721 typedef struct _MngBuffer
753 typedef struct _MngInfo
756 #ifdef MNG_OBJECT_BUFFERS
758 *ob[MNG_MAX_OBJECTS];
769 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
770 bytes_in_read_buffer,
776 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
777 defined(PNG_MNG_FEATURES_SUPPORTED)
789 have_saved_bkgd_index,
790 have_write_global_chrm,
791 have_write_global_gama,
792 have_write_global_plte,
793 have_write_global_srgb,
807 x_off[MNG_MAX_OBJECTS],
808 y_off[MNG_MAX_OBJECTS];
814 object_clip[MNG_MAX_OBJECTS];
817 /* These flags could be combined into one byte */
818 exists[MNG_MAX_OBJECTS],
819 frozen[MNG_MAX_OBJECTS],
821 invisible[MNG_MAX_OBJECTS],
822 viewable[MNG_MAX_OBJECTS];
834 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
852 global_x_pixels_per_unit,
853 global_y_pixels_per_unit,
863 global_phys_unit_type,
878 write_png_compression_level,
879 write_png_compression_strategy,
880 write_png_compression_filter,
887 #ifdef MNG_BASI_SUPPORTED
895 basi_compression_method,
897 basi_interlace_method,
920 /* Added at version 6.6.6-7 */
928 /* ping_exclude_iTXt, */
935 ping_exclude_zCCP, /* hex-encoded iCCP */
937 ping_preserve_colormap;
943 Forward declarations.
945 static MagickBooleanType
946 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
948 static MagickBooleanType
949 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
951 #if defined(JNG_SUPPORTED)
952 static MagickBooleanType
953 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
956 #if PNG_LIBPNG_VER > 10011
959 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
960 static MagickBooleanType
961 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
963 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
965 * This is true if the high byte and the next highest byte of
966 * each sample of the image, the colormap, and the background color
967 * are equal to each other. We check this by seeing if the samples
968 * are unchanged when we scale them down to 8 and back up to Quantum.
970 * We don't use the method GetImageDepth() because it doesn't check
971 * background and doesn't handle PseudoClass specially.
974 #define QuantumToCharToQuantumEqQuantum(quantum) \
975 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
978 ok_to_reduce=MagickFalse;
980 if (image->depth >= 16)
987 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
988 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
989 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
990 MagickTrue : MagickFalse;
992 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
996 for (indx=0; indx < (ssize_t) image->colors; indx++)
999 QuantumToCharToQuantumEqQuantum(
1000 image->colormap[indx].red) &&
1001 QuantumToCharToQuantumEqQuantum(
1002 image->colormap[indx].green) &&
1003 QuantumToCharToQuantumEqQuantum(
1004 image->colormap[indx].blue)) ?
1005 MagickTrue : MagickFalse;
1007 if (ok_to_reduce == MagickFalse)
1012 if ((ok_to_reduce != MagickFalse) &&
1013 (image->storage_class != PseudoClass))
1021 for (y=0; y < (ssize_t) image->rows; y++)
1023 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1025 if (p == (const Quantum *) NULL)
1027 ok_to_reduce = MagickFalse;
1031 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1034 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1035 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1036 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1037 MagickTrue : MagickFalse;
1039 if (ok_to_reduce == MagickFalse)
1042 p+=GetPixelChannels(image);
1049 if (ok_to_reduce != MagickFalse)
1051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1052 " OK to reduce PNG bit depth to 8 without loss of info");
1056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1057 " Not OK to reduce PNG bit depth to 8 without loss of info");
1061 return ok_to_reduce;
1063 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1065 static const char* PngColorTypeToString(const unsigned int color_type)
1068 *result = "Unknown";
1072 case PNG_COLOR_TYPE_GRAY:
1075 case PNG_COLOR_TYPE_GRAY_ALPHA:
1076 result = "Gray+Alpha";
1078 case PNG_COLOR_TYPE_PALETTE:
1081 case PNG_COLOR_TYPE_RGB:
1084 case PNG_COLOR_TYPE_RGB_ALPHA:
1085 result = "RGB+Alpha";
1093 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1097 case PerceptualIntent:
1100 case RelativeIntent:
1103 case SaturationIntent:
1106 case AbsoluteIntent:
1114 static RenderingIntent
1115 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1117 switch (ping_intent)
1120 return PerceptualIntent;
1123 return RelativeIntent;
1126 return SaturationIntent;
1129 return AbsoluteIntent;
1132 return UndefinedIntent;
1137 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1139 switch (ping_intent)
1142 return "Perceptual Intent";
1145 return "Relative Intent";
1148 return "Saturation Intent";
1151 return "Absolute Intent";
1154 return "Undefined Intent";
1158 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1167 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1169 switch (ping_colortype)
1187 return "UndefinedColorType";
1192 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1199 #endif /* PNG_LIBPNG_VER > 10011 */
1200 #endif /* MAGICKCORE_PNG_DELEGATE */
1203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 % IsMNG() returns MagickTrue if the image format type, identified by the
1214 % magick string, is MNG.
1216 % The format of the IsMNG method is:
1218 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1220 % A description of each parameter follows:
1222 % o magick: compare image format pattern against these bytes.
1224 % o length: Specifies the length of the magick string.
1228 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1231 return(MagickFalse);
1233 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1236 return(MagickFalse);
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1250 % IsJNG() returns MagickTrue if the image format type, identified by the
1251 % magick string, is JNG.
1253 % The format of the IsJNG method is:
1255 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1257 % A description of each parameter follows:
1259 % o magick: compare image format pattern against these bytes.
1261 % o length: Specifies the length of the magick string.
1265 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1268 return(MagickFalse);
1270 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1273 return(MagickFalse);
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287 % IsPNG() returns MagickTrue if the image format type, identified by the
1288 % magick string, is PNG.
1290 % The format of the IsPNG method is:
1292 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1294 % A description of each parameter follows:
1296 % o magick: compare image format pattern against these bytes.
1298 % o length: Specifies the length of the magick string.
1301 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1304 return(MagickFalse);
1306 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1309 return(MagickFalse);
1312 #if defined(MAGICKCORE_PNG_DELEGATE)
1313 #if defined(__cplusplus) || defined(c_plusplus)
1317 #if (PNG_LIBPNG_VER > 10011)
1318 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1323 assert(image != (Image *) NULL);
1324 assert(image->signature == MagickSignature);
1325 buffer[0]=(unsigned char) (value >> 24);
1326 buffer[1]=(unsigned char) (value >> 16);
1327 buffer[2]=(unsigned char) (value >> 8);
1328 buffer[3]=(unsigned char) value;
1329 return((size_t) WriteBlob(image,4,buffer));
1332 static void PNGLong(png_bytep p,png_uint_32 value)
1334 *p++=(png_byte) ((value >> 24) & 0xff);
1335 *p++=(png_byte) ((value >> 16) & 0xff);
1336 *p++=(png_byte) ((value >> 8) & 0xff);
1337 *p++=(png_byte) (value & 0xff);
1340 #if defined(JNG_SUPPORTED)
1341 static void PNGsLong(png_bytep p,png_int_32 value)
1343 *p++=(png_byte) ((value >> 24) & 0xff);
1344 *p++=(png_byte) ((value >> 16) & 0xff);
1345 *p++=(png_byte) ((value >> 8) & 0xff);
1346 *p++=(png_byte) (value & 0xff);
1350 static void PNGShort(png_bytep p,png_uint_16 value)
1352 *p++=(png_byte) ((value >> 8) & 0xff);
1353 *p++=(png_byte) (value & 0xff);
1356 static void PNGType(png_bytep p,png_bytep type)
1358 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1361 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1364 if (logging != MagickFalse)
1365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1366 " Writing %c%c%c%c chunk, length: %.20g",
1367 type[0],type[1],type[2],type[3],(double) length);
1369 #endif /* PNG_LIBPNG_VER > 10011 */
1371 #if defined(__cplusplus) || defined(c_plusplus)
1375 #if PNG_LIBPNG_VER > 10011
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1381 % R e a d P N G I m a g e %
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1388 % Multiple-image Network Graphics (MNG) image file and returns it. It
1389 % allocates the memory necessary for the new Image structure and returns a
1390 % pointer to the new image or set of images.
1392 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1394 % The format of the ReadPNGImage method is:
1396 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1398 % A description of each parameter follows:
1400 % o image_info: the image info.
1402 % o exception: return any errors or warnings in this structure.
1404 % To do, more or less in chronological order (as of version 5.5.2,
1405 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1407 % Get 16-bit cheap transparency working.
1409 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1411 % Preserve all unknown and not-yet-handled known chunks found in input
1412 % PNG file and copy them into output PNG files according to the PNG
1415 % (At this point, PNG encoding should be in full MNG compliance)
1417 % Provide options for choice of background to use when the MNG BACK
1418 % chunk is not present or is not mandatory (i.e., leave transparent,
1419 % user specified, MNG BACK, PNG bKGD)
1421 % Implement LOOP/ENDL [done, but could do discretionary loops more
1422 % efficiently by linking in the duplicate frames.].
1424 % Decode and act on the MHDR simplicity profile (offer option to reject
1425 % files or attempt to process them anyway when the profile isn't LC or VLC).
1427 % Upgrade to full MNG without Delta-PNG.
1429 % o BACK [done a while ago except for background image ID]
1430 % o MOVE [done 15 May 1999]
1431 % o CLIP [done 15 May 1999]
1432 % o DISC [done 19 May 1999]
1433 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1434 % o SEEK [partially done 19 May 1999 (discard function only)]
1438 % o MNG-level tEXt/iTXt/zTXt
1443 % o iTXt (wait for libpng implementation).
1445 % Use the scene signature to discover when an identical scene is
1446 % being reused, and just point to the original image->exception instead
1447 % of storing another set of pixels. This not specific to MNG
1448 % but could be applied generally.
1450 % Upgrade to full MNG with Delta-PNG.
1452 % JNG tEXt/iTXt/zTXt
1454 % We will not attempt to read files containing the CgBI chunk.
1455 % They are really Xcode files meant for display on the iPhone.
1456 % These are not valid PNG files and it is impossible to recover
1457 % the original PNG from files that have been converted to Xcode-PNG,
1458 % since irretrievable loss of color data has occurred due to the
1459 % use of premultiplied alpha.
1462 #if defined(__cplusplus) || defined(c_plusplus)
1467 This the function that does the actual reading of data. It is
1468 the same as the one supplied in libpng, except that it receives the
1469 datastream from the ReadBlob() function instead of standard input.
1471 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1476 image=(Image *) png_get_io_ptr(png_ptr);
1482 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1483 if (check != length)
1488 (void) FormatLocaleString(msg,MaxTextExtent,
1489 "Expected %.20g bytes; found %.20g bytes",(double) length,
1491 png_warning(png_ptr,msg);
1492 png_error(png_ptr,"Read Exception");
1497 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1498 !defined(PNG_MNG_FEATURES_SUPPORTED)
1499 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1500 * older than libpng-1.0.3a, which was the first to allow the empty
1501 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1502 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1503 * encountered after an empty PLTE, so we have to look ahead for bKGD
1504 * chunks and remove them from the datastream that is passed to libpng,
1505 * and store their contents for later use.
1507 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1522 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1523 image=(Image *) mng_info->image;
1524 while (mng_info->bytes_in_read_buffer && length)
1526 data[i]=mng_info->read_buffer[i];
1527 mng_info->bytes_in_read_buffer--;
1533 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1535 if (check != length)
1536 png_error(png_ptr,"Read Exception");
1540 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1543 check=(png_size_t) ReadBlob(image,(size_t) length,
1544 (char *) mng_info->read_buffer);
1545 mng_info->read_buffer[4]=0;
1546 mng_info->bytes_in_read_buffer=4;
1547 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1548 mng_info->found_empty_plte=MagickTrue;
1549 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1551 mng_info->found_empty_plte=MagickFalse;
1552 mng_info->have_saved_bkgd_index=MagickFalse;
1556 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1559 check=(png_size_t) ReadBlob(image,(size_t) length,
1560 (char *) mng_info->read_buffer);
1561 mng_info->read_buffer[4]=0;
1562 mng_info->bytes_in_read_buffer=4;
1563 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1564 if (mng_info->found_empty_plte)
1567 Skip the bKGD data byte and CRC.
1570 ReadBlob(image,5,(char *) mng_info->read_buffer);
1571 check=(png_size_t) ReadBlob(image,(size_t) length,
1572 (char *) mng_info->read_buffer);
1573 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1574 mng_info->have_saved_bkgd_index=MagickTrue;
1575 mng_info->bytes_in_read_buffer=0;
1583 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1588 image=(Image *) png_get_io_ptr(png_ptr);
1594 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1596 if (check != length)
1597 png_error(png_ptr,"WriteBlob Failed");
1601 static void png_flush_data(png_structp png_ptr)
1606 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1607 static int PalettesAreEqual(Image *a,Image *b)
1612 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1613 return((int) MagickFalse);
1615 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1616 return((int) MagickFalse);
1618 if (a->colors != b->colors)
1619 return((int) MagickFalse);
1621 for (i=0; i < (ssize_t) a->colors; i++)
1623 if ((a->colormap[i].red != b->colormap[i].red) ||
1624 (a->colormap[i].green != b->colormap[i].green) ||
1625 (a->colormap[i].blue != b->colormap[i].blue))
1626 return((int) MagickFalse);
1629 return((int) MagickTrue);
1633 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1635 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1636 mng_info->exists[i] && !mng_info->frozen[i])
1638 #ifdef MNG_OBJECT_BUFFERS
1639 if (mng_info->ob[i] != (MngBuffer *) NULL)
1641 if (mng_info->ob[i]->reference_count > 0)
1642 mng_info->ob[i]->reference_count--;
1644 if (mng_info->ob[i]->reference_count == 0)
1646 if (mng_info->ob[i]->image != (Image *) NULL)
1647 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1649 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1652 mng_info->ob[i]=(MngBuffer *) NULL;
1654 mng_info->exists[i]=MagickFalse;
1655 mng_info->invisible[i]=MagickFalse;
1656 mng_info->viewable[i]=MagickFalse;
1657 mng_info->frozen[i]=MagickFalse;
1658 mng_info->x_off[i]=0;
1659 mng_info->y_off[i]=0;
1660 mng_info->object_clip[i].left=0;
1661 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1662 mng_info->object_clip[i].top=0;
1663 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1667 static void MngInfoFreeStruct(MngInfo *mng_info,
1668 MagickBooleanType *have_mng_structure)
1670 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1675 for (i=1; i < MNG_MAX_OBJECTS; i++)
1676 MngInfoDiscardObject(mng_info,i);
1678 if (mng_info->global_plte != (png_colorp) NULL)
1679 mng_info->global_plte=(png_colorp)
1680 RelinquishMagickMemory(mng_info->global_plte);
1682 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1683 *have_mng_structure=MagickFalse;
1687 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1693 if (box.left < box2.left)
1696 if (box.top < box2.top)
1699 if (box.right > box2.right)
1700 box.right=box2.right;
1702 if (box.bottom > box2.bottom)
1703 box.bottom=box2.bottom;
1708 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1714 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1716 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1717 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1718 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1719 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1720 if (delta_type != 0)
1722 box.left+=previous_box.left;
1723 box.right+=previous_box.right;
1724 box.top+=previous_box.top;
1725 box.bottom+=previous_box.bottom;
1731 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1737 Read two ssize_ts from CLON, MOVE or PAST chunk
1739 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1740 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1742 if (delta_type != 0)
1744 pair.a+=previous_pair.a;
1745 pair.b+=previous_pair.b;
1751 static long mng_get_long(unsigned char *p)
1753 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1756 typedef struct _PNGErrorInfo
1765 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1776 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1777 image=error_info->image;
1778 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1783 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1784 "`%s'",image->filename);
1786 #if (PNG_LIBPNG_VER < 10500)
1787 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1788 * are building with libpng-1.4.x and can be ignored.
1790 longjmp(ping->jmpbuf,1);
1792 png_longjmp(ping,1);
1796 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1807 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1808 png_error(ping, message);
1810 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1811 image=error_info->image;
1812 exception=error_info->exception;
1813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1816 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1817 message,"`%s'",image->filename);
1820 #ifdef PNG_USER_MEM_SUPPORTED
1821 #if PNG_LIBPNG_VER >= 10400
1822 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1824 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1828 return((png_voidp) AcquireMagickMemory((size_t) size));
1832 Free a pointer. It is removed from the list at the same time.
1834 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1837 ptr=RelinquishMagickMemory(ptr);
1838 return((png_free_ptr) NULL);
1842 #if defined(__cplusplus) || defined(c_plusplus)
1847 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1848 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1853 register unsigned char
1867 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1868 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1869 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1870 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1871 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1875 /* look for newline */
1879 /* look for length */
1880 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1883 length=(png_uint_32) StringToLong(sp);
1885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1886 " length: %lu",(unsigned long) length);
1888 while (*sp != ' ' && *sp != '\n')
1891 /* allocate space */
1894 png_warning(ping,"invalid profile length");
1895 return(MagickFalse);
1898 profile=BlobToStringInfo((const void *) NULL,length);
1900 if (profile == (StringInfo *) NULL)
1902 png_warning(ping, "unable to copy profile");
1903 return(MagickFalse);
1906 /* copy profile, skipping white space and column 1 "=" signs */
1907 dp=GetStringInfoDatum(profile);
1910 for (i=0; i < (ssize_t) nibbles; i++)
1912 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1916 png_warning(ping, "ran out of profile data");
1917 profile=DestroyStringInfo(profile);
1918 return(MagickFalse);
1924 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1927 (*dp++)+=unhex[(int) *sp++];
1930 We have already read "Raw profile type.
1932 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1933 profile=DestroyStringInfo(profile);
1935 if (image_info->verbose)
1936 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1941 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1942 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1948 /* The unknown chunk structure contains the chunk data:
1953 Note that libpng has already taken care of the CRC handling.
1956 LogMagickEvent(CoderEvent,GetMagickModule(),
1957 " read_vpag_chunk: found %c%c%c%c chunk",
1958 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1960 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1961 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1962 return(0); /* Did not recognize */
1964 /* recognized vpAg */
1966 if (chunk->size != 9)
1967 return(-1); /* Error return */
1969 if (chunk->data[8] != 0)
1970 return(0); /* ImageMagick requires pixel units */
1972 image=(Image *) png_get_user_chunk_ptr(ping);
1974 image->page.width=(size_t) ((chunk->data[0] << 24) |
1975 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1977 image->page.height=(size_t) ((chunk->data[4] << 24) |
1978 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1980 /* Return one of the following: */
1981 /* return(-n); chunk had an error */
1982 /* return(0); did not recognize */
1983 /* return(n); success */
1991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1995 % R e a d O n e P N G I m a g e %
1999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2001 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2002 % (minus the 8-byte signature) and returns it. It allocates the memory
2003 % necessary for the new Image structure and returns a pointer to the new
2006 % The format of the ReadOnePNGImage method is:
2008 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2009 % ExceptionInfo *exception)
2011 % A description of each parameter follows:
2013 % o mng_info: Specifies a pointer to a MngInfo structure.
2015 % o image_info: the image info.
2017 % o exception: return any errors or warnings in this structure.
2020 static Image *ReadOnePNGImage(MngInfo *mng_info,
2021 const ImageInfo *image_info, ExceptionInfo *exception)
2023 /* Read one PNG image */
2025 /* To do: Read the tIME chunk into the date:modify property */
2026 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2039 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2049 ping_interlace_method,
2050 ping_compression_method,
2064 ping_found_sRGB_cHRM,
2103 register unsigned char
2121 *volatile ping_pixels;
2123 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2124 png_byte unused_chunks[]=
2126 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2127 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2128 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2129 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2130 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2131 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2132 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2133 /* ignore the APNG chunks */
2134 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2135 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2136 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2141 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2142 " Enter ReadOnePNGImage()");
2144 /* Define these outside of the following "if logging()" block so they will
2145 * show in debuggers.
2148 (void) ConcatenateMagickString(im_vers,
2149 MagickLibVersionText,32);
2150 (void) ConcatenateMagickString(im_vers,
2151 MagickLibAddendum,32);
2154 (void) ConcatenateMagickString(libpng_vers,
2155 PNG_LIBPNG_VER_STRING,32);
2157 (void) ConcatenateMagickString(libpng_runv,
2158 png_get_libpng_ver(NULL),32);
2161 (void) ConcatenateMagickString(zlib_vers,
2164 (void) ConcatenateMagickString(zlib_runv,
2169 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
2171 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
2173 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2175 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2178 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2180 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2182 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2187 #if (PNG_LIBPNG_VER < 10200)
2188 if (image_info->verbose)
2189 printf("Your PNG library (libpng-%s) is rather old.\n",
2190 PNG_LIBPNG_VER_STRING);
2193 #if (PNG_LIBPNG_VER >= 10400)
2194 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2195 if (image_info->verbose)
2197 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2198 PNG_LIBPNG_VER_STRING);
2199 printf("Please update it.\n");
2205 quantum_info = (QuantumInfo *) NULL;
2206 image=mng_info->image;
2208 if (logging != MagickFalse)
2210 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2211 " Before reading:");
2213 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2214 " image->alpha_trait=%d",(int) image->alpha_trait);
2216 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2217 " image->rendering_intent=%d",(int) image->rendering_intent);
2219 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2220 " image->colorspace=%d",(int) image->colorspace);
2222 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2223 " image->gamma=%f", image->gamma);
2225 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2227 /* Set to an out-of-range color unless tRNS chunk is present */
2228 transparent_color.red=65537;
2229 transparent_color.green=65537;
2230 transparent_color.blue=65537;
2231 transparent_color.alpha=65537;
2236 num_raw_profiles = 0;
2238 ping_found_cHRM = MagickFalse;
2239 ping_found_gAMA = MagickFalse;
2240 ping_found_iCCP = MagickFalse;
2241 ping_found_sRGB = MagickFalse;
2244 Allocate the PNG structures
2246 #ifdef PNG_USER_MEM_SUPPORTED
2247 error_info.image=image;
2248 error_info.exception=exception;
2249 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2250 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2251 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2253 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2254 MagickPNGErrorHandler,MagickPNGWarningHandler);
2256 if (ping == (png_struct *) NULL)
2257 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2259 ping_info=png_create_info_struct(ping);
2261 if (ping_info == (png_info *) NULL)
2263 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2264 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2267 end_info=png_create_info_struct(ping);
2269 if (end_info == (png_info *) NULL)
2271 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2272 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2275 ping_pixels=(unsigned char *) NULL;
2277 if (setjmp(png_jmpbuf(ping)))
2280 PNG image is corrupt.
2282 png_destroy_read_struct(&ping,&ping_info,&end_info);
2284 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2285 UnlockSemaphoreInfo(ping_semaphore);
2288 if (ping_pixels != (unsigned char *) NULL)
2289 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2291 if (logging != MagickFalse)
2292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2293 " exit ReadOnePNGImage() with error.");
2295 if (image != (Image *) NULL)
2297 InheritException(exception,exception);
2301 return(GetFirstImageInList(image));
2304 /* { For navigation to end of SETJMP-protected block. Within this
2305 * block, use png_error() instead of Throwing an Exception, to ensure
2306 * that libpng is able to clean up, and that the semaphore is unlocked.
2309 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2310 LockSemaphoreInfo(ping_semaphore);
2313 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2314 /* Allow benign errors */
2315 png_set_benign_errors(ping, 1);
2319 Prepare PNG for reading.
2322 mng_info->image_found++;
2323 png_set_sig_bytes(ping,8);
2325 if (LocaleCompare(image_info->magick,"MNG") == 0)
2327 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2328 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2329 png_set_read_fn(ping,image,png_get_data);
2331 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2332 png_permit_empty_plte(ping,MagickTrue);
2333 png_set_read_fn(ping,image,png_get_data);
2335 mng_info->image=image;
2336 mng_info->bytes_in_read_buffer=0;
2337 mng_info->found_empty_plte=MagickFalse;
2338 mng_info->have_saved_bkgd_index=MagickFalse;
2339 png_set_read_fn(ping,mng_info,mng_get_data);
2345 png_set_read_fn(ping,image,png_get_data);
2347 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2348 /* Ignore unused chunks and all unknown chunks except for vpAg */
2349 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2350 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2352 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2354 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2355 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2356 (int)sizeof(unused_chunks)/5);
2357 /* Callback for other unknown chunks */
2358 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2361 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2362 # if (PNG_LIBPNG_VER >= 10400)
2363 /* Limit the size of the chunk storage cache used for sPLT, text,
2364 * and unknown chunks.
2366 png_set_chunk_cache_max(ping, 32767);
2370 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2371 /* Disable new libpng-1.5.10 feature */
2372 png_set_check_for_invalid_index (ping, 0);
2375 #if (PNG_LIBPNG_VER < 10400)
2376 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2377 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2378 /* Disable thread-unsafe features of pnggccrd */
2379 if (png_access_version_number() >= 10200)
2381 png_uint_32 mmx_disable_mask=0;
2382 png_uint_32 asm_flags;
2384 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2385 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2386 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2387 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2388 asm_flags=png_get_asm_flags(ping);
2389 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2394 png_read_info(ping,ping_info);
2396 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2397 &ping_bit_depth,&ping_color_type,
2398 &ping_interlace_method,&ping_compression_method,
2399 &ping_filter_method);
2401 ping_file_depth = ping_bit_depth;
2403 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2408 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2409 (void) SetImageProperty(image,"png:IHDR.color-type-orig ",msg,exception);
2411 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2412 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig ",msg,exception);
2415 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2418 (void) png_get_bKGD(ping, ping_info, &ping_background);
2420 if (ping_bit_depth < 8)
2422 png_set_packing(ping);
2426 image->depth=ping_bit_depth;
2427 image->depth=GetImageQuantumDepth(image,MagickFalse);
2428 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2430 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2431 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2433 image->rendering_intent=UndefinedIntent;
2434 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2436 (void) ResetMagickMemory(&image->chromaticity,0,
2437 sizeof(image->chromaticity));
2440 if (logging != MagickFalse)
2442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2443 " PNG width: %.20g, height: %.20g",
2444 (double) ping_width, (double) ping_height);
2446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2447 " PNG color_type: %d, bit_depth: %d",
2448 ping_color_type, ping_bit_depth);
2450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2451 " PNG compression_method: %d",
2452 ping_compression_method);
2454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2455 " PNG interlace_method: %d, filter_method: %d",
2456 ping_interlace_method,ping_filter_method);
2459 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2461 ping_found_gAMA=MagickTrue;
2462 if (logging != MagickFalse)
2463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2464 " Found PNG gAMA chunk.");
2467 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2469 ping_found_cHRM=MagickTrue;
2470 if (logging != MagickFalse)
2471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2472 " Found PNG cHRM chunk.");
2475 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2477 ping_found_iCCP=MagickTrue;
2478 if (logging != MagickFalse)
2479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2480 " Found PNG iCCP chunk.");
2483 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2485 ping_found_sRGB=MagickTrue;
2486 if (logging != MagickFalse)
2487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2488 " Found PNG sRGB chunk.");
2491 #ifdef PNG_READ_iCCP_SUPPORTED
2492 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2497 #if (PNG_LIBPNG_VER < 10500)
2511 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2514 if (profile_length != 0)
2519 if (logging != MagickFalse)
2520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2521 " Reading PNG iCCP chunk.");
2522 profile=BlobToStringInfo(info,profile_length);
2523 if (profile == (StringInfo *) NULL)
2525 png_warning(ping, "ICC profile is NULL");
2526 profile=DestroyStringInfo(profile);
2530 (void) SetImageProfile(image,"icc",profile,exception);
2531 profile=DestroyStringInfo(profile);
2536 #if defined(PNG_READ_sRGB_SUPPORTED)
2538 if (mng_info->have_global_srgb)
2540 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2541 (mng_info->global_srgb_intent);
2544 if (png_get_sRGB(ping,ping_info,&intent))
2546 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2549 if (logging != MagickFalse)
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2556 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2557 if (mng_info->have_global_gama)
2558 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2560 if (png_get_gAMA(ping,ping_info,&file_gamma))
2562 image->gamma=(float) file_gamma;
2563 if (logging != MagickFalse)
2564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2565 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2569 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2571 if (mng_info->have_global_chrm != MagickFalse)
2573 (void) png_set_cHRM(ping,ping_info,
2574 mng_info->global_chrm.white_point.x,
2575 mng_info->global_chrm.white_point.y,
2576 mng_info->global_chrm.red_primary.x,
2577 mng_info->global_chrm.red_primary.y,
2578 mng_info->global_chrm.green_primary.x,
2579 mng_info->global_chrm.green_primary.y,
2580 mng_info->global_chrm.blue_primary.x,
2581 mng_info->global_chrm.blue_primary.y);
2585 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2587 (void) png_get_cHRM(ping,ping_info,
2588 &image->chromaticity.white_point.x,
2589 &image->chromaticity.white_point.y,
2590 &image->chromaticity.red_primary.x,
2591 &image->chromaticity.red_primary.y,
2592 &image->chromaticity.green_primary.x,
2593 &image->chromaticity.green_primary.y,
2594 &image->chromaticity.blue_primary.x,
2595 &image->chromaticity.blue_primary.y);
2597 if (image->chromaticity.red_primary.x>0.6399f &&
2598 image->chromaticity.red_primary.x<0.6401f &&
2599 image->chromaticity.red_primary.y>0.3299f &&
2600 image->chromaticity.red_primary.y<0.3301f &&
2601 image->chromaticity.green_primary.x>0.2999f &&
2602 image->chromaticity.green_primary.x<0.3001f &&
2603 image->chromaticity.green_primary.y>0.5999f &&
2604 image->chromaticity.green_primary.y<0.6001f &&
2605 image->chromaticity.blue_primary.x>0.1499f &&
2606 image->chromaticity.blue_primary.x<0.1501f &&
2607 image->chromaticity.blue_primary.y>0.0599f &&
2608 image->chromaticity.blue_primary.y<0.0601f &&
2609 image->chromaticity.white_point.x>0.3126f &&
2610 image->chromaticity.white_point.x<0.3128f &&
2611 image->chromaticity.white_point.y>0.3289f &&
2612 image->chromaticity.white_point.y<0.3291f)
2613 ping_found_sRGB_cHRM=MagickTrue;
2615 ping_found_cHRM=MagickTrue;
2618 if (image->rendering_intent != UndefinedIntent)
2620 if (ping_found_sRGB != MagickTrue &&
2621 (ping_found_gAMA != MagickTrue ||
2622 (image->gamma > .45 && image->gamma < .46)) &&
2623 (ping_found_cHRM != MagickTrue ||
2624 ping_found_sRGB_cHRM == MagickTrue) &&
2625 ping_found_iCCP != MagickTrue)
2627 png_set_sRGB(ping,ping_info,
2628 Magick_RenderingIntent_to_PNG_RenderingIntent
2629 (image->rendering_intent));
2630 file_gamma=1.000f/2.200f;
2631 ping_found_sRGB=MagickTrue;
2632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2633 " Setting sRGB as if in input");
2637 #if defined(PNG_oFFs_SUPPORTED)
2638 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2640 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2641 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2643 if (logging != MagickFalse)
2644 if (image->page.x || image->page.y)
2645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2646 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2647 image->page.x,(double) image->page.y);
2650 #if defined(PNG_pHYs_SUPPORTED)
2651 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2653 if (mng_info->have_global_phys)
2655 png_set_pHYs(ping,ping_info,
2656 mng_info->global_x_pixels_per_unit,
2657 mng_info->global_y_pixels_per_unit,
2658 mng_info->global_phys_unit_type);
2662 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2665 Set image resolution.
2667 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2669 image->resolution.x=(double) x_resolution;
2670 image->resolution.y=(double) y_resolution;
2672 if (unit_type == PNG_RESOLUTION_METER)
2674 image->units=PixelsPerCentimeterResolution;
2675 image->resolution.x=(double) x_resolution/100.0;
2676 image->resolution.y=(double) y_resolution/100.0;
2679 if (logging != MagickFalse)
2680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2681 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2682 (double) x_resolution,(double) y_resolution,unit_type);
2686 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2691 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2693 if ((number_colors == 0) &&
2694 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2696 if (mng_info->global_plte_length)
2698 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2699 (int) mng_info->global_plte_length);
2701 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2703 if (mng_info->global_trns_length)
2706 "global tRNS has more entries than global PLTE");
2710 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2711 (int) mng_info->global_trns_length,NULL);
2714 #ifdef PNG_READ_bKGD_SUPPORTED
2716 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2717 mng_info->have_saved_bkgd_index ||
2719 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2724 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2725 if (mng_info->have_saved_bkgd_index)
2726 background.index=mng_info->saved_bkgd_index;
2728 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2729 background.index=ping_background->index;
2731 background.red=(png_uint_16)
2732 mng_info->global_plte[background.index].red;
2734 background.green=(png_uint_16)
2735 mng_info->global_plte[background.index].green;
2737 background.blue=(png_uint_16)
2738 mng_info->global_plte[background.index].blue;
2740 background.gray=(png_uint_16)
2741 mng_info->global_plte[background.index].green;
2743 png_set_bKGD(ping,ping_info,&background);
2748 png_error(ping,"No global PLTE in file");
2752 #ifdef PNG_READ_bKGD_SUPPORTED
2753 if (mng_info->have_global_bkgd &&
2754 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2755 image->background_color=mng_info->mng_global_bkgd;
2757 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2763 Set image background color.
2765 if (logging != MagickFalse)
2766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2767 " Reading PNG bKGD chunk.");
2769 /* Scale background components to 16-bit, then scale
2772 if (logging != MagickFalse)
2773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2774 " raw ping_background=(%d,%d,%d).",ping_background->red,
2775 ping_background->green,ping_background->blue);
2779 if (ping_file_depth == 1)
2782 else if (ping_file_depth == 2)
2785 else if (ping_file_depth == 4)
2788 if (ping_file_depth <= 8)
2791 ping_background->red *= bkgd_scale;
2792 ping_background->green *= bkgd_scale;
2793 ping_background->blue *= bkgd_scale;
2795 if (logging != MagickFalse)
2797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798 " bkgd_scale=%d.",bkgd_scale);
2800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2801 " ping_background=(%d,%d,%d).",ping_background->red,
2802 ping_background->green,ping_background->blue);
2805 image->background_color.red=
2806 ScaleShortToQuantum(ping_background->red);
2808 image->background_color.green=
2809 ScaleShortToQuantum(ping_background->green);
2811 image->background_color.blue=
2812 ScaleShortToQuantum(ping_background->blue);
2814 image->background_color.alpha=OpaqueAlpha;
2816 if (logging != MagickFalse)
2817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2818 " image->background_color=(%.20g,%.20g,%.20g).",
2819 (double) image->background_color.red,
2820 (double) image->background_color.green,
2821 (double) image->background_color.blue);
2823 #endif /* PNG_READ_bKGD_SUPPORTED */
2825 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2828 Image has a tRNS chunk.
2836 if (logging != MagickFalse)
2837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2838 " Reading PNG tRNS chunk.");
2840 max_sample = (int) ((one << ping_file_depth) - 1);
2842 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2843 (int)ping_trans_color->gray > max_sample) ||
2844 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2845 ((int)ping_trans_color->red > max_sample ||
2846 (int)ping_trans_color->green > max_sample ||
2847 (int)ping_trans_color->blue > max_sample)))
2849 if (logging != MagickFalse)
2850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2851 " Ignoring PNG tRNS chunk with out-of-range sample.");
2852 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2853 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2854 image->alpha_trait=UndefinedPixelTrait;
2861 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2863 /* Scale transparent_color to short */
2864 transparent_color.red= scale_to_short*ping_trans_color->red;
2865 transparent_color.green= scale_to_short*ping_trans_color->green;
2866 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2867 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2869 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2871 if (logging != MagickFalse)
2873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2874 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2877 " scaled graylevel is %.20g.",transparent_color.alpha);
2879 transparent_color.red=transparent_color.alpha;
2880 transparent_color.green=transparent_color.alpha;
2881 transparent_color.blue=transparent_color.alpha;
2885 #if defined(PNG_READ_sBIT_SUPPORTED)
2886 if (mng_info->have_global_sbit)
2888 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2889 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2892 num_passes=png_set_interlace_handling(ping);
2894 png_read_update_info(ping,ping_info);
2896 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2899 Initialize image structure.
2901 mng_info->image_box.left=0;
2902 mng_info->image_box.right=(ssize_t) ping_width;
2903 mng_info->image_box.top=0;
2904 mng_info->image_box.bottom=(ssize_t) ping_height;
2905 if (mng_info->mng_type == 0)
2907 mng_info->mng_width=ping_width;
2908 mng_info->mng_height=ping_height;
2909 mng_info->frame=mng_info->image_box;
2910 mng_info->clip=mng_info->image_box;
2915 image->page.y=mng_info->y_off[mng_info->object_id];
2918 image->compression=ZipCompression;
2919 image->columns=ping_width;
2920 image->rows=ping_height;
2922 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2923 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2925 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2926 image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
2928 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2929 * image->colorspace to GRAY, and reset image->chromaticity.
2931 SetImageColorspace(image,GRAYColorspace,exception);
2935 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2936 " image->colorspace=%d",(int) image->colorspace);
2938 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2939 ((int) ping_bit_depth < 16 &&
2940 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2945 image->storage_class=PseudoClass;
2947 image->colors=one << ping_file_depth;
2948 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2949 if (image->colors > 256)
2952 if (image->colors > 65536L)
2953 image->colors=65536L;
2955 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2960 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2961 image->colors=(size_t) number_colors;
2963 if (logging != MagickFalse)
2964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2965 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2969 if (image->storage_class == PseudoClass)
2972 Initialize image colormap.
2974 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2975 png_error(ping,"Memory allocation failed");
2977 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2982 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2984 for (i=0; i < (ssize_t) number_colors; i++)
2986 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2987 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2988 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2991 for ( ; i < (ssize_t) image->colors; i++)
2993 image->colormap[i].red=0;
2994 image->colormap[i].green=0;
2995 image->colormap[i].blue=0;
3004 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3009 for (i=0; i < (ssize_t) image->colors; i++)
3011 image->colormap[i].red=(Quantum) (i*scale);
3012 image->colormap[i].green=(Quantum) (i*scale);
3013 image->colormap[i].blue=(Quantum) (i*scale);
3018 /* Set some properties for reporting by "identify" */
3023 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3024 ping_interlace_method in value */
3026 (void) FormatLocaleString(msg,MaxTextExtent,
3027 "%d, %d",(int) ping_width, (int) ping_height);
3028 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
3030 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3031 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
3033 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3034 (int) ping_color_type,
3035 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3036 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
3038 if (ping_interlace_method == 0)
3040 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3041 (int) ping_interlace_method);
3043 else if (ping_interlace_method == 1)
3045 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3046 (int) ping_interlace_method);
3050 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3051 (int) ping_interlace_method);
3053 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3055 if (number_colors != 0)
3057 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3058 (int) number_colors);
3059 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
3065 Read image scanlines.
3067 if (image->delay != 0)
3068 mng_info->scenes_found++;
3070 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3071 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3072 (image_info->first_scene+image_info->number_scenes))))
3074 /* This happens later in non-ping decodes */
3075 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3076 image->storage_class=DirectClass;
3078 if (logging != MagickFalse)
3079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3080 " Skipping PNG image data for scene %.20g",(double)
3081 mng_info->scenes_found-1);
3082 png_destroy_read_struct(&ping,&ping_info,&end_info);
3084 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3085 UnlockSemaphoreInfo(ping_semaphore);
3088 if (logging != MagickFalse)
3089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3090 " exit ReadOnePNGImage().");
3095 if (logging != MagickFalse)
3096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3097 " Reading PNG IDAT chunk(s)");
3100 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
3101 ping_rowbytes*sizeof(*ping_pixels));
3104 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
3105 sizeof(*ping_pixels));
3107 if (ping_pixels == (unsigned char *) NULL)
3108 png_error(ping,"Memory allocation failed");
3110 if (logging != MagickFalse)
3111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3112 " Converting PNG pixels to pixel packets");
3114 Convert PNG pixels to pixel packets.
3116 quantum_info=AcquireQuantumInfo(image_info,image);
3118 if (quantum_info == (QuantumInfo *) NULL)
3119 png_error(ping,"Failed to allocate quantum_info");
3121 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3126 found_transparent_pixel;
3128 found_transparent_pixel=MagickFalse;
3130 if (image->storage_class == DirectClass)
3132 for (pass=0; pass < num_passes; pass++)
3135 Convert image to DirectClass pixel packets.
3137 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3138 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3139 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3140 BlendPixelTrait : UndefinedPixelTrait;
3142 for (y=0; y < (ssize_t) image->rows; y++)
3145 row_offset=ping_rowbytes*y;
3150 png_read_row(ping,ping_pixels+row_offset,NULL);
3152 if (pass < num_passes-1)
3155 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3157 if (q == (Quantum *) NULL)
3160 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3161 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3162 GrayQuantum,ping_pixels+row_offset,exception);
3164 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3165 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3166 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3168 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3169 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3170 RGBAQuantum,ping_pixels+row_offset,exception);
3172 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3173 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3174 IndexQuantum,ping_pixels+row_offset,exception);
3176 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3177 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3178 RGBQuantum,ping_pixels+row_offset,exception);
3180 if (found_transparent_pixel == MagickFalse)
3182 /* Is there a transparent pixel in the row? */
3183 if (y== 0 && logging != MagickFalse)
3184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3185 " Looking for cheap transparent pixel");
3187 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3189 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3190 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3191 (GetPixelAlpha(image,q) != OpaqueAlpha))
3193 if (logging != MagickFalse)
3194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3197 found_transparent_pixel = MagickTrue;
3200 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3201 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3202 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3203 transparent_color.red &&
3204 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3205 transparent_color.green &&
3206 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3207 transparent_color.blue))
3209 if (logging != MagickFalse)
3210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3212 found_transparent_pixel = MagickTrue;
3215 q+=GetPixelChannels(image);
3219 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3221 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3224 if (status == MagickFalse)
3227 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3231 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3233 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3234 if (status == MagickFalse)
3240 else /* image->storage_class != DirectClass */
3242 for (pass=0; pass < num_passes; pass++)
3251 Convert grayscale image to PseudoClass pixel packets.
3253 if (logging != MagickFalse)
3254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3255 " Converting grayscale pixels to pixel packets");
3257 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3258 BlendPixelTrait : UndefinedPixelTrait;
3260 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3261 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3262 sizeof(*quantum_scanline));
3264 if (quantum_scanline == (Quantum *) NULL)
3265 png_error(ping,"Memory allocation failed");
3267 for (y=0; y < (ssize_t) image->rows; y++)
3273 row_offset=ping_rowbytes*y;
3278 png_read_row(ping,ping_pixels+row_offset,NULL);
3280 if (pass < num_passes-1)
3283 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3285 if (q == (Quantum *) NULL)
3288 p=ping_pixels+row_offset;
3291 switch (ping_bit_depth)
3296 if (ping_color_type == 4)
3297 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3301 alpha=ScaleCharToQuantum((unsigned char)*p++);
3303 SetPixelAlpha(image,alpha,q);
3305 if (alpha != OpaqueAlpha)
3306 found_transparent_pixel = MagickTrue;
3308 q+=GetPixelChannels(image);
3312 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3320 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3322 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3326 if (image->colors > 256)
3327 quantum=((*p++) << 8);
3333 *r=ScaleShortToQuantum(quantum);
3336 if (ping_color_type == 4)
3338 if (image->colors > 256)
3339 quantum=((*p++) << 8);
3345 alpha=ScaleShortToQuantum(quantum);
3346 SetPixelAlpha(image,alpha,q);
3348 if (alpha != OpaqueAlpha)
3349 found_transparent_pixel = MagickTrue;
3351 q+=GetPixelChannels(image);
3354 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3356 p++; /* strip low byte */
3358 if (ping_color_type == 4)
3360 SetPixelAlpha(image,*p++,q);
3362 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3363 found_transparent_pixel = MagickTrue;
3366 q+=GetPixelChannels(image);
3379 Transfer image scanline.
3383 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3385 if (q == (Quantum *) NULL)
3387 for (x=0; x < (ssize_t) image->columns; x++)
3389 SetPixelIndex(image,*r++,q);
3390 q+=GetPixelChannels(image);
3393 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3396 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3398 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3401 if (status == MagickFalse)
3406 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3408 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3410 if (status == MagickFalse)
3414 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3417 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3418 UndefinedPixelTrait;
3420 if (logging != MagickFalse)
3422 if (found_transparent_pixel != MagickFalse)
3423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3424 " Found transparent pixel");
3427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3428 " No transparent pixel was found");
3430 ping_color_type&=0x03;
3435 if (quantum_info != (QuantumInfo *) NULL)
3436 quantum_info=DestroyQuantumInfo(quantum_info);
3438 if (image->storage_class == PseudoClass)
3443 alpha_trait=image->alpha_trait;
3444 image->alpha_trait=UndefinedPixelTrait;
3445 (void) SyncImage(image,exception);
3446 image->alpha_trait=alpha_trait;
3449 png_read_end(ping,end_info);
3451 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3452 (ssize_t) image_info->first_scene && image->delay != 0)
3454 png_destroy_read_struct(&ping,&ping_info,&end_info);
3455 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3457 (void) SetImageBackgroundColor(image,exception);
3458 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3459 UnlockSemaphoreInfo(ping_semaphore);
3461 if (logging != MagickFalse)
3462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3463 " exit ReadOnePNGImage() early.");
3467 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3473 Image has a transparent background.
3475 storage_class=image->storage_class;
3476 image->alpha_trait=BlendPixelTrait;
3478 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3480 if (storage_class == PseudoClass)
3482 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3484 for (x=0; x < ping_num_trans; x++)
3486 image->colormap[x].alpha_trait=BlendPixelTrait;
3487 image->colormap[x].alpha =
3488 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3492 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3494 for (x=0; x < (int) image->colors; x++)
3496 if (ScaleQuantumToShort(image->colormap[x].red) ==
3497 transparent_color.alpha)
3499 image->colormap[x].alpha_trait=BlendPixelTrait;
3500 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3504 (void) SyncImage(image,exception);
3507 #if 1 /* Should have already been done above, but glennrp problem P10
3512 for (y=0; y < (ssize_t) image->rows; y++)
3514 image->storage_class=storage_class;
3515 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3517 if (q == (Quantum *) NULL)
3521 /* Caution: on a Q8 build, this does not distinguish between
3522 * 16-bit colors that differ only in the low byte
3524 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3526 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3527 transparent_color.red &&
3528 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3529 transparent_color.green &&
3530 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3531 transparent_color.blue)
3533 SetPixelAlpha(image,TransparentAlpha,q);
3536 #if 0 /* I have not found a case where this is needed. */
3539 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3543 q+=GetPixelChannels(image);
3546 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3552 image->storage_class=DirectClass;
3555 for (j = 0; j < 2; j++)
3558 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3559 MagickTrue : MagickFalse;
3561 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3562 MagickTrue : MagickFalse;
3564 if (status != MagickFalse)
3565 for (i=0; i < (ssize_t) num_text; i++)
3567 /* Check for a profile */
3569 if (logging != MagickFalse)
3570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3571 " Reading PNG text chunk");
3573 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3575 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3585 length=text[i].text_length;
3586 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3588 if (value == (char *) NULL)
3590 png_error(ping,"Memory allocation failed");
3594 (void) ConcatenateMagickString(value,text[i].text,length+2);
3596 /* Don't save "density" or "units" property if we have a pHYs
3599 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3600 (LocaleCompare(text[i].key,"density") != 0 &&
3601 LocaleCompare(text[i].key,"units") != 0))
3602 (void) SetImageProperty(image,text[i].key,value,exception);
3604 if (logging != MagickFalse)
3606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3607 " length: %lu",(unsigned long) length);
3608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3609 " Keyword: %s",text[i].key);
3612 value=DestroyString(value);
3615 num_text_total += num_text;
3618 #ifdef MNG_OBJECT_BUFFERS
3620 Store the object if necessary.
3622 if (object_id && !mng_info->frozen[object_id])
3624 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3627 create a new object buffer.
3629 mng_info->ob[object_id]=(MngBuffer *)
3630 AcquireMagickMemory(sizeof(MngBuffer));
3632 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3634 mng_info->ob[object_id]->image=(Image *) NULL;
3635 mng_info->ob[object_id]->reference_count=1;
3639 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3640 mng_info->ob[object_id]->frozen)
3642 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3643 png_error(ping,"Memory allocation failed");
3645 if (mng_info->ob[object_id]->frozen)
3646 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3652 if (mng_info->ob[object_id]->image != (Image *) NULL)
3653 mng_info->ob[object_id]->image=DestroyImage
3654 (mng_info->ob[object_id]->image);
3656 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3659 if (mng_info->ob[object_id]->image != (Image *) NULL)
3660 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3663 png_error(ping, "Cloning image for object buffer failed");
3665 if (ping_width > 250000L || ping_height > 250000L)
3666 png_error(ping,"PNG Image dimensions are too large.");
3668 mng_info->ob[object_id]->width=ping_width;
3669 mng_info->ob[object_id]->height=ping_height;
3670 mng_info->ob[object_id]->color_type=ping_color_type;
3671 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3672 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3673 mng_info->ob[object_id]->compression_method=
3674 ping_compression_method;
3675 mng_info->ob[object_id]->filter_method=ping_filter_method;
3677 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3683 Copy the PLTE to the object buffer.
3685 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3686 mng_info->ob[object_id]->plte_length=number_colors;
3688 for (i=0; i < number_colors; i++)
3690 mng_info->ob[object_id]->plte[i]=plte[i];
3695 mng_info->ob[object_id]->plte_length=0;
3700 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3701 * alpha or if a valid tRNS chunk is present, no matter whether there
3702 * is actual transparency present.
3704 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3705 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3706 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3707 BlendPixelTrait : UndefinedPixelTrait;
3709 #if 0 /* I'm not sure what's wrong here but it does not work. */
3710 if (image->alpha_trait == BlendPixelTrait)
3712 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3713 (void) SetImageType(image,GrayscaleMatteType,exception);
3715 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3716 (void) SetImageType(image,PaletteMatteType,exception);
3719 (void) SetImageType(image,TrueColorMatteType,exception);
3724 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3725 (void) SetImageType(image,GrayscaleType,exception);
3727 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3728 (void) SetImageType(image,PaletteType,exception);
3731 (void) SetImageType(image,TrueColorType,exception);
3735 /* Set more properties for identify to retrieve */
3740 if (num_text_total != 0)
3742 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3743 (void) FormatLocaleString(msg,MaxTextExtent,
3744 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3745 (void) SetImageProperty(image,"png:text ",msg,
3749 if (num_raw_profiles != 0)
3751 (void) FormatLocaleString(msg,MaxTextExtent,
3752 "%d were found", num_raw_profiles);
3753 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3757 if (ping_found_cHRM != MagickFalse)
3759 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3760 "chunk was found (see Chromaticity, above)");
3761 (void) SetImageProperty(image,"png:cHRM ",msg,
3765 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3767 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3768 "chunk was found (see Background color, above)");
3769 (void) SetImageProperty(image,"png:bKGD ",msg,
3773 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3776 #if defined(PNG_iCCP_SUPPORTED)
3777 if (ping_found_iCCP != MagickFalse)
3778 (void) SetImageProperty(image,"png:iCCP ",msg,
3782 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3783 (void) SetImageProperty(image,"png:tRNS ",msg,
3786 #if defined(PNG_sRGB_SUPPORTED)
3787 if (ping_found_sRGB != MagickFalse)
3789 (void) FormatLocaleString(msg,MaxTextExtent,
3792 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3793 (void) SetImageProperty(image,"png:sRGB ",msg,
3798 if (ping_found_gAMA != MagickFalse)
3800 (void) FormatLocaleString(msg,MaxTextExtent,
3801 "gamma=%.8g (See Gamma, above)",
3803 (void) SetImageProperty(image,"png:gAMA ",msg,
3807 #if defined(PNG_pHYs_SUPPORTED)
3808 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3810 (void) FormatLocaleString(msg,MaxTextExtent,
3811 "x_res=%.10g, y_res=%.10g, units=%d",
3812 (double) x_resolution,(double) y_resolution, unit_type);
3813 (void) SetImageProperty(image,"png:pHYs ",msg,
3818 #if defined(PNG_oFFs_SUPPORTED)
3819 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3821 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3822 (double) image->page.x,(double) image->page.y);
3823 (void) SetImageProperty(image,"png:oFFs ",msg,
3828 if ((image->page.width != 0 && image->page.width != image->columns) ||
3829 (image->page.height != 0 && image->page.height != image->rows))
3831 (void) FormatLocaleString(msg,MaxTextExtent,
3832 "width=%.20g, height=%.20g",
3833 (double) image->page.width,(double) image->page.height);
3834 (void) SetImageProperty(image,"png:vpAg ",msg,
3840 Relinquish resources.
3842 png_destroy_read_struct(&ping,&ping_info,&end_info);
3844 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3846 if (logging != MagickFalse)
3847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3848 " exit ReadOnePNGImage()");
3850 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3851 UnlockSemaphoreInfo(ping_semaphore);
3854 /* } for navigation to beginning of SETJMP-protected block, revert to
3855 * Throwing an Exception when an error occurs.
3860 /* end of reading one PNG image */
3863 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3878 magic_number[MaxTextExtent];
3886 assert(image_info != (const ImageInfo *) NULL);
3887 assert(image_info->signature == MagickSignature);
3889 if (image_info->debug != MagickFalse)
3890 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3891 image_info->filename);
3893 assert(exception != (ExceptionInfo *) NULL);
3894 assert(exception->signature == MagickSignature);
3895 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3896 image=AcquireImage(image_info,exception);
3897 mng_info=(MngInfo *) NULL;
3898 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3900 if (status == MagickFalse)
3901 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3904 Verify PNG signature.
3906 count=ReadBlob(image,8,(unsigned char *) magic_number);
3908 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3909 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3912 Allocate a MngInfo structure.
3914 have_mng_structure=MagickFalse;
3915 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3917 if (mng_info == (MngInfo *) NULL)
3918 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3921 Initialize members of the MngInfo structure.
3923 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3924 mng_info->image=image;
3925 have_mng_structure=MagickTrue;
3928 image=ReadOnePNGImage(mng_info,image_info,exception);
3929 MngInfoFreeStruct(mng_info,&have_mng_structure);
3931 if (image == (Image *) NULL)
3933 if (previous != (Image *) NULL)
3935 if (previous->signature != MagickSignature)
3936 ThrowReaderException(CorruptImageError,"CorruptImage");
3938 (void) CloseBlob(previous);
3939 (void) DestroyImageList(previous);
3942 if (logging != MagickFalse)
3943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3944 "exit ReadPNGImage() with error");
3946 return((Image *) NULL);
3949 (void) CloseBlob(image);
3951 if ((image->columns == 0) || (image->rows == 0))
3953 if (logging != MagickFalse)
3954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3955 "exit ReadPNGImage() with error.");
3957 ThrowReaderException(CorruptImageError,"CorruptImage");
3960 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3961 ((image->gamma < .45) || (image->gamma > .46)) &&
3962 !(image->chromaticity.red_primary.x>0.6399f &&
3963 image->chromaticity.red_primary.x<0.6401f &&
3964 image->chromaticity.red_primary.y>0.3299f &&
3965 image->chromaticity.red_primary.y<0.3301f &&
3966 image->chromaticity.green_primary.x>0.2999f &&
3967 image->chromaticity.green_primary.x<0.3001f &&
3968 image->chromaticity.green_primary.y>0.5999f &&
3969 image->chromaticity.green_primary.y<0.6001f &&
3970 image->chromaticity.blue_primary.x>0.1499f &&
3971 image->chromaticity.blue_primary.x<0.1501f &&
3972 image->chromaticity.blue_primary.y>0.0599f &&
3973 image->chromaticity.blue_primary.y<0.0601f &&
3974 image->chromaticity.white_point.x>0.3126f &&
3975 image->chromaticity.white_point.x<0.3128f &&
3976 image->chromaticity.white_point.y>0.3289f &&
3977 image->chromaticity.white_point.y<0.3291f))
3978 SetImageColorspace(image,RGBColorspace,exception);
3980 if (logging != MagickFalse)
3981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3982 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3983 (double) image->page.width,(double) image->page.height,
3984 (double) image->page.x,(double) image->page.y);
3986 if (logging != MagickFalse)
3987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3994 #if defined(JNG_SUPPORTED)
3996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4000 % R e a d O n e J N G I m a g e %
4004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4006 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4007 % (minus the 8-byte signature) and returns it. It allocates the memory
4008 % necessary for the new Image structure and returns a pointer to the new
4011 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4013 % The format of the ReadOneJNGImage method is:
4015 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4016 % ExceptionInfo *exception)
4018 % A description of each parameter follows:
4020 % o mng_info: Specifies a pointer to a MngInfo structure.
4022 % o image_info: the image info.
4024 % o exception: return any errors or warnings in this structure.
4027 static Image *ReadOneJNGImage(MngInfo *mng_info,
4028 const ImageInfo *image_info, ExceptionInfo *exception)
4055 jng_image_sample_depth,
4056 jng_image_compression_method,
4057 jng_image_interlace_method,
4058 jng_alpha_sample_depth,
4059 jng_alpha_compression_method,
4060 jng_alpha_filter_method,
4061 jng_alpha_interlace_method;
4063 register const Quantum
4073 register unsigned char
4084 jng_alpha_compression_method=0;
4085 jng_alpha_sample_depth=8;
4089 alpha_image=(Image *) NULL;
4090 color_image=(Image *) NULL;
4091 alpha_image_info=(ImageInfo *) NULL;
4092 color_image_info=(ImageInfo *) NULL;
4094 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4095 " Enter ReadOneJNGImage()");
4097 image=mng_info->image;
4099 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4102 Allocate next image structure.
4104 if (logging != MagickFalse)
4105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4106 " AcquireNextImage()");
4108 AcquireNextImage(image_info,image,exception);
4110 if (GetNextImageInList(image) == (Image *) NULL)
4111 return((Image *) NULL);
4113 image=SyncNextImageInList(image);
4115 mng_info->image=image;
4118 Signature bytes have already been read.
4121 read_JSEP=MagickFalse;
4122 reading_idat=MagickFalse;
4123 skip_to_iend=MagickFalse;
4127 type[MaxTextExtent];
4136 Read a new JNG chunk.
4138 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4139 2*GetBlobSize(image));
4141 if (status == MagickFalse)
4145 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4146 length=ReadBlobMSBLong(image);
4147 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4149 if (logging != MagickFalse)
4150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4151 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4152 type[0],type[1],type[2],type[3],(double) length);
4154 if (length > PNG_UINT_31_MAX || count == 0)
4155 ThrowReaderException(CorruptImageError,"CorruptImage");
4158 chunk=(unsigned char *) NULL;
4162 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4164 if (chunk == (unsigned char *) NULL)
4165 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4167 for (i=0; i < (ssize_t) length; i++)
4168 chunk[i]=(unsigned char) ReadBlobByte(image);
4173 (void) ReadBlobMSBLong(image); /* read crc word */
4178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4183 if (memcmp(type,mng_JHDR,4) == 0)
4187 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4188 (p[2] << 8) | p[3]);
4189 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4190 (p[6] << 8) | p[7]);
4191 jng_color_type=p[8];
4192 jng_image_sample_depth=p[9];
4193 jng_image_compression_method=p[10];
4194 jng_image_interlace_method=p[11];
4196 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4199 jng_alpha_sample_depth=p[12];
4200 jng_alpha_compression_method=p[13];
4201 jng_alpha_filter_method=p[14];
4202 jng_alpha_interlace_method=p[15];
4204 if (logging != MagickFalse)
4206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4207 " jng_width: %16lu",(unsigned long) jng_width);
4209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4210 " jng_width: %16lu",(unsigned long) jng_height);
4212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4213 " jng_color_type: %16d",jng_color_type);
4215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4216 " jng_image_sample_depth: %3d",
4217 jng_image_sample_depth);
4219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4220 " jng_image_compression_method:%3d",
4221 jng_image_compression_method);
4223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4224 " jng_image_interlace_method: %3d",
4225 jng_image_interlace_method);
4227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4228 " jng_alpha_sample_depth: %3d",
4229 jng_alpha_sample_depth);
4231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4232 " jng_alpha_compression_method:%3d",
4233 jng_alpha_compression_method);
4235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4236 " jng_alpha_filter_method: %3d",
4237 jng_alpha_filter_method);
4239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4240 " jng_alpha_interlace_method: %3d",
4241 jng_alpha_interlace_method);
4246 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4252 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4253 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4254 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4257 o create color_image
4258 o open color_blob, attached to color_image
4259 o if (color type has alpha)
4260 open alpha_blob, attached to alpha_image
4263 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4265 if (color_image_info == (ImageInfo *) NULL)
4266 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4268 GetImageInfo(color_image_info);
4269 color_image=AcquireImage(color_image_info,exception);
4271 if (color_image == (Image *) NULL)
4272 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4274 if (logging != MagickFalse)
4275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4276 " Creating color_blob.");
4278 (void) AcquireUniqueFilename(color_image->filename);
4279 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4282 if (status == MagickFalse)
4283 return((Image *) NULL);
4285 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4287 alpha_image_info=(ImageInfo *)
4288 AcquireMagickMemory(sizeof(ImageInfo));
4290 if (alpha_image_info == (ImageInfo *) NULL)
4291 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4293 GetImageInfo(alpha_image_info);
4294 alpha_image=AcquireImage(alpha_image_info,exception);
4296 if (alpha_image == (Image *) NULL)
4298 alpha_image=DestroyImage(alpha_image);
4299 ThrowReaderException(ResourceLimitError,
4300 "MemoryAllocationFailed");
4303 if (logging != MagickFalse)
4304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4305 " Creating alpha_blob.");
4307 (void) AcquireUniqueFilename(alpha_image->filename);
4308 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4311 if (status == MagickFalse)
4312 return((Image *) NULL);
4314 if (jng_alpha_compression_method == 0)
4319 if (logging != MagickFalse)
4320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4321 " Writing IHDR chunk to alpha_blob.");
4323 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4324 "\211PNG\r\n\032\n");
4326 (void) WriteBlobMSBULong(alpha_image,13L);
4327 PNGType(data,mng_IHDR);
4328 LogPNGChunk(logging,mng_IHDR,13L);
4329 PNGLong(data+4,jng_width);
4330 PNGLong(data+8,jng_height);
4331 data[12]=jng_alpha_sample_depth;
4332 data[13]=0; /* color_type gray */
4333 data[14]=0; /* compression method 0 */
4334 data[15]=0; /* filter_method 0 */
4335 data[16]=0; /* interlace_method 0 */
4336 (void) WriteBlob(alpha_image,17,data);
4337 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4340 reading_idat=MagickTrue;
4343 if (memcmp(type,mng_JDAT,4) == 0)
4345 /* Copy chunk to color_image->blob */
4347 if (logging != MagickFalse)
4348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4349 " Copying JDAT chunk data to color_blob.");
4351 (void) WriteBlob(color_image,length,chunk);
4354 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4359 if (memcmp(type,mng_IDAT,4) == 0)
4364 /* Copy IDAT header and chunk data to alpha_image->blob */
4366 if (image_info->ping == MagickFalse)
4368 if (logging != MagickFalse)
4369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4370 " Copying IDAT chunk data to alpha_blob.");
4372 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4373 PNGType(data,mng_IDAT);
4374 LogPNGChunk(logging,mng_IDAT,length);
4375 (void) WriteBlob(alpha_image,4,data);
4376 (void) WriteBlob(alpha_image,length,chunk);
4377 (void) WriteBlobMSBULong(alpha_image,
4378 crc32(crc32(0,data,4),chunk,(uInt) length));
4382 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4387 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4389 /* Copy chunk data to alpha_image->blob */
4391 if (image_info->ping == MagickFalse)
4393 if (logging != MagickFalse)
4394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4395 " Copying JDAA chunk data to alpha_blob.");
4397 (void) WriteBlob(alpha_image,length,chunk);
4401 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4406 if (memcmp(type,mng_JSEP,4) == 0)
4408 read_JSEP=MagickTrue;
4411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4416 if (memcmp(type,mng_bKGD,4) == 0)
4420 image->background_color.red=ScaleCharToQuantum(p[1]);
4421 image->background_color.green=image->background_color.red;
4422 image->background_color.blue=image->background_color.red;
4427 image->background_color.red=ScaleCharToQuantum(p[1]);
4428 image->background_color.green=ScaleCharToQuantum(p[3]);
4429 image->background_color.blue=ScaleCharToQuantum(p[5]);
4432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4436 if (memcmp(type,mng_gAMA,4) == 0)
4439 image->gamma=((float) mng_get_long(p))*0.00001;
4441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4445 if (memcmp(type,mng_cHRM,4) == 0)
4449 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4450 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4451 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4452 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4453 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4454 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4455 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4456 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4459 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4463 if (memcmp(type,mng_sRGB,4) == 0)
4467 image->rendering_intent=
4468 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4469 image->gamma=1.000f/2.200f;
4470 image->chromaticity.red_primary.x=0.6400f;
4471 image->chromaticity.red_primary.y=0.3300f;
4472 image->chromaticity.green_primary.x=0.3000f;
4473 image->chromaticity.green_primary.y=0.6000f;
4474 image->chromaticity.blue_primary.x=0.1500f;
4475 image->chromaticity.blue_primary.y=0.0600f;
4476 image->chromaticity.white_point.x=0.3127f;
4477 image->chromaticity.white_point.y=0.3290f;
4480 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4484 if (memcmp(type,mng_oFFs,4) == 0)
4488 image->page.x=(ssize_t) mng_get_long(p);
4489 image->page.y=(ssize_t) mng_get_long(&p[4]);
4491 if ((int) p[8] != 0)
4493 image->page.x/=10000;
4494 image->page.y/=10000;
4499 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4504 if (memcmp(type,mng_pHYs,4) == 0)
4508 image->resolution.x=(double) mng_get_long(p);
4509 image->resolution.y=(double) mng_get_long(&p[4]);
4510 if ((int) p[8] == PNG_RESOLUTION_METER)
4512 image->units=PixelsPerCentimeterResolution;
4513 image->resolution.x=image->resolution.x/100.0f;
4514 image->resolution.y=image->resolution.y/100.0f;
4518 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4523 if (memcmp(type,mng_iCCP,4) == 0)
4527 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4534 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4536 if (memcmp(type,mng_IEND,4))
4546 Finish up reading image data:
4548 o read main image from color_blob.
4552 o if (color_type has alpha)
4553 if alpha_encoding is PNG
4554 read secondary image from alpha_blob via ReadPNG
4555 if alpha_encoding is JPEG
4556 read secondary image from alpha_blob via ReadJPEG
4560 o copy intensity of secondary image into
4561 alpha samples of main image.
4563 o destroy the secondary image.
4566 (void) CloseBlob(color_image);
4568 if (logging != MagickFalse)
4569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4570 " Reading jng_image from color_blob.");
4572 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4573 color_image->filename);
4575 color_image_info->ping=MagickFalse; /* To do: avoid this */
4576 jng_image=ReadImage(color_image_info,exception);
4578 if (jng_image == (Image *) NULL)
4579 return((Image *) NULL);
4581 (void) RelinquishUniqueFileResource(color_image->filename);
4582 color_image=DestroyImage(color_image);
4583 color_image_info=DestroyImageInfo(color_image_info);
4585 if (jng_image == (Image *) NULL)
4586 return((Image *) NULL);
4588 if (logging != MagickFalse)
4589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4590 " Copying jng_image pixels to main image.");
4592 image->rows=jng_height;
4593 image->columns=jng_width;
4595 for (y=0; y < (ssize_t) image->rows; y++)
4597 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4598 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4599 for (x=(ssize_t) image->columns; x != 0; x--)
4601 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4602 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4603 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4604 q+=GetPixelChannels(image);
4605 s+=GetPixelChannels(jng_image);
4608 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4612 jng_image=DestroyImage(jng_image);
4614 if (image_info->ping == MagickFalse)
4616 if (jng_color_type >= 12)
4618 if (jng_alpha_compression_method == 0)
4622 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4623 PNGType(data,mng_IEND);
4624 LogPNGChunk(logging,mng_IEND,0L);
4625 (void) WriteBlob(alpha_image,4,data);
4626 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4629 (void) CloseBlob(alpha_image);
4631 if (logging != MagickFalse)
4632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4633 " Reading alpha from alpha_blob.");
4635 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4636 "%s",alpha_image->filename);
4638 jng_image=ReadImage(alpha_image_info,exception);
4640 if (jng_image != (Image *) NULL)
4641 for (y=0; y < (ssize_t) image->rows; y++)
4643 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4645 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4647 if (image->alpha_trait == BlendPixelTrait)
4648 for (x=(ssize_t) image->columns; x != 0; x--)
4650 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4651 q+=GetPixelChannels(image);
4652 s+=GetPixelChannels(jng_image);
4656 for (x=(ssize_t) image->columns; x != 0; x--)
4658 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4659 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4660 image->alpha_trait=BlendPixelTrait;
4661 q+=GetPixelChannels(image);
4662 s+=GetPixelChannels(jng_image);
4665 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4668 (void) RelinquishUniqueFileResource(alpha_image->filename);
4669 alpha_image=DestroyImage(alpha_image);
4670 alpha_image_info=DestroyImageInfo(alpha_image_info);
4671 if (jng_image != (Image *) NULL)
4672 jng_image=DestroyImage(jng_image);
4676 /* Read the JNG image. */
4678 if (mng_info->mng_type == 0)
4680 mng_info->mng_width=jng_width;
4681 mng_info->mng_height=jng_height;
4684 if (image->page.width == 0 && image->page.height == 0)
4686 image->page.width=jng_width;
4687 image->page.height=jng_height;
4690 if (image->page.x == 0 && image->page.y == 0)
4692 image->page.x=mng_info->x_off[mng_info->object_id];
4693 image->page.y=mng_info->y_off[mng_info->object_id];
4698 image->page.y=mng_info->y_off[mng_info->object_id];
4701 mng_info->image_found++;
4702 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4703 2*GetBlobSize(image));
4705 if (logging != MagickFalse)
4706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4707 " exit ReadOneJNGImage()");
4713 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4717 % R e a d J N G I m a g e %
4721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4723 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4724 % (including the 8-byte signature) and returns it. It allocates the memory
4725 % necessary for the new Image structure and returns a pointer to the new
4728 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4730 % The format of the ReadJNGImage method is:
4732 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4735 % A description of each parameter follows:
4737 % o image_info: the image info.
4739 % o exception: return any errors or warnings in this structure.
4743 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4758 magic_number[MaxTextExtent];
4766 assert(image_info != (const ImageInfo *) NULL);
4767 assert(image_info->signature == MagickSignature);
4768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4769 assert(exception != (ExceptionInfo *) NULL);
4770 assert(exception->signature == MagickSignature);
4771 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4772 image=AcquireImage(image_info,exception);
4773 mng_info=(MngInfo *) NULL;
4774 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4776 if (status == MagickFalse)
4777 return((Image *) NULL);
4779 if (LocaleCompare(image_info->magick,"JNG") != 0)
4780 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4782 /* Verify JNG signature. */
4784 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4786 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4787 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4789 /* Allocate a MngInfo structure. */
4791 have_mng_structure=MagickFalse;
4792 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4794 if (mng_info == (MngInfo *) NULL)
4795 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4797 /* Initialize members of the MngInfo structure. */
4799 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4800 have_mng_structure=MagickTrue;
4802 mng_info->image=image;
4804 image=ReadOneJNGImage(mng_info,image_info,exception);
4805 MngInfoFreeStruct(mng_info,&have_mng_structure);
4807 if (image == (Image *) NULL)
4809 if (IsImageObject(previous) != MagickFalse)
4811 (void) CloseBlob(previous);
4812 (void) DestroyImageList(previous);
4815 if (logging != MagickFalse)
4816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4817 "exit ReadJNGImage() with error");
4819 return((Image *) NULL);
4821 (void) CloseBlob(image);
4823 if (image->columns == 0 || image->rows == 0)
4825 if (logging != MagickFalse)
4826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4827 "exit ReadJNGImage() with error");
4829 ThrowReaderException(CorruptImageError,"CorruptImage");
4832 if (logging != MagickFalse)
4833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4839 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4842 page_geometry[MaxTextExtent];
4875 #if defined(MNG_INSERT_LAYERS)
4877 mng_background_color;
4880 register unsigned char
4895 #if defined(MNG_INSERT_LAYERS)
4900 volatile unsigned int
4901 #ifdef MNG_OBJECT_BUFFERS
4902 mng_background_object=0,
4904 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4907 default_frame_timeout,
4909 #if defined(MNG_INSERT_LAYERS)
4915 /* These delays are all measured in image ticks_per_second,
4916 * not in MNG ticks_per_second
4919 default_frame_delay,
4923 #if defined(MNG_INSERT_LAYERS)
4932 previous_fb.bottom=0;
4934 previous_fb.right=0;
4936 default_fb.bottom=0;
4940 /* Open image file. */
4942 assert(image_info != (const ImageInfo *) NULL);
4943 assert(image_info->signature == MagickSignature);
4944 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4945 assert(exception != (ExceptionInfo *) NULL);
4946 assert(exception->signature == MagickSignature);
4947 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4948 image=AcquireImage(image_info,exception);
4949 mng_info=(MngInfo *) NULL;
4950 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4952 if (status == MagickFalse)
4953 return((Image *) NULL);
4955 first_mng_object=MagickFalse;
4957 have_mng_structure=MagickFalse;
4959 /* Allocate a MngInfo structure. */
4961 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4963 if (mng_info == (MngInfo *) NULL)
4964 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4966 /* Initialize members of the MngInfo structure. */
4968 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4969 mng_info->image=image;
4970 have_mng_structure=MagickTrue;
4972 if (LocaleCompare(image_info->magick,"MNG") == 0)
4975 magic_number[MaxTextExtent];
4977 /* Verify MNG signature. */
4978 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4979 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4980 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4982 /* Initialize some nonzero members of the MngInfo structure. */
4983 for (i=0; i < MNG_MAX_OBJECTS; i++)
4985 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4986 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4988 mng_info->exists[0]=MagickTrue;
4991 first_mng_object=MagickTrue;
4993 #if defined(MNG_INSERT_LAYERS)
4994 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4996 default_frame_delay=0;
4997 default_frame_timeout=0;
5000 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5002 skip_to_iend=MagickFalse;
5003 term_chunk_found=MagickFalse;
5004 mng_info->framing_mode=1;
5005 #if defined(MNG_INSERT_LAYERS)
5006 mandatory_back=MagickFalse;
5008 #if defined(MNG_INSERT_LAYERS)
5009 mng_background_color=image->background_color;
5011 default_fb=mng_info->frame;
5012 previous_fb=mng_info->frame;
5016 type[MaxTextExtent];
5018 if (LocaleCompare(image_info->magick,"MNG") == 0)
5027 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5028 length=ReadBlobMSBLong(image);
5029 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5031 if (logging != MagickFalse)
5032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5033 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5034 type[0],type[1],type[2],type[3],(double) length);
5036 if (length > PNG_UINT_31_MAX)
5040 ThrowReaderException(CorruptImageError,"CorruptImage");
5043 chunk=(unsigned char *) NULL;
5047 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5049 if (chunk == (unsigned char *) NULL)
5050 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5052 for (i=0; i < (ssize_t) length; i++)
5053 chunk[i]=(unsigned char) ReadBlobByte(image);
5058 (void) ReadBlobMSBLong(image); /* read crc word */
5060 #if !defined(JNG_SUPPORTED)
5061 if (memcmp(type,mng_JHDR,4) == 0)
5063 skip_to_iend=MagickTrue;
5065 if (mng_info->jhdr_warning == 0)
5066 (void) ThrowMagickException(exception,GetMagickModule(),
5067 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5069 mng_info->jhdr_warning++;
5072 if (memcmp(type,mng_DHDR,4) == 0)
5074 skip_to_iend=MagickTrue;
5076 if (mng_info->dhdr_warning == 0)
5077 (void) ThrowMagickException(exception,GetMagickModule(),
5078 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5080 mng_info->dhdr_warning++;
5082 if (memcmp(type,mng_MEND,4) == 0)
5087 if (memcmp(type,mng_IEND,4) == 0)
5088 skip_to_iend=MagickFalse;
5091 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5093 if (logging != MagickFalse)
5094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5100 if (memcmp(type,mng_MHDR,4) == 0)
5102 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5103 (p[2] << 8) | p[3]);
5105 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5106 (p[6] << 8) | p[7]);
5108 if (logging != MagickFalse)
5110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5111 " MNG width: %.20g",(double) mng_info->mng_width);
5112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5113 " MNG height: %.20g",(double) mng_info->mng_height);
5117 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5119 if (mng_info->ticks_per_second == 0)
5120 default_frame_delay=0;
5123 default_frame_delay=1UL*image->ticks_per_second/
5124 mng_info->ticks_per_second;
5126 frame_delay=default_frame_delay;
5132 simplicity=(size_t) mng_get_long(p);
5135 mng_type=1; /* Full MNG */
5137 if ((simplicity != 0) && ((simplicity | 11) == 11))
5138 mng_type=2; /* LC */
5140 if ((simplicity != 0) && ((simplicity | 9) == 9))
5141 mng_type=3; /* VLC */
5143 #if defined(MNG_INSERT_LAYERS)
5145 insert_layers=MagickTrue;
5147 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5149 /* Allocate next image structure. */
5150 AcquireNextImage(image_info,image,exception);
5152 if (GetNextImageInList(image) == (Image *) NULL)
5153 return((Image *) NULL);
5155 image=SyncNextImageInList(image);
5156 mng_info->image=image;
5159 if ((mng_info->mng_width > 65535L) ||
5160 (mng_info->mng_height > 65535L))
5161 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5163 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5164 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5165 mng_info->mng_height);
5167 mng_info->frame.left=0;
5168 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5169 mng_info->frame.top=0;
5170 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5171 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5173 for (i=0; i < MNG_MAX_OBJECTS; i++)
5174 mng_info->object_clip[i]=mng_info->frame;
5176 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5180 if (memcmp(type,mng_TERM,4) == 0)
5191 final_delay=(png_uint_32) mng_get_long(&p[2]);
5192 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5194 if (mng_iterations == PNG_UINT_31_MAX)
5197 image->iterations=mng_iterations;
5198 term_chunk_found=MagickTrue;
5201 if (logging != MagickFalse)
5203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5204 " repeat=%d",repeat);
5206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5207 " final_delay=%.20g",(double) final_delay);
5209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5210 " image->iterations=%.20g",(double) image->iterations);
5213 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5216 if (memcmp(type,mng_DEFI,4) == 0)
5219 (void) ThrowMagickException(exception,GetMagickModule(),
5220 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5223 object_id=(p[0] << 8) | p[1];
5225 if (mng_type == 2 && object_id != 0)
5226 (void) ThrowMagickException(exception,GetMagickModule(),
5227 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5230 if (object_id > MNG_MAX_OBJECTS)
5233 Instead of using a warning we should allocate a larger
5234 MngInfo structure and continue.
5236 (void) ThrowMagickException(exception,GetMagickModule(),
5237 CoderError,"object id too large","`%s'",image->filename);
5238 object_id=MNG_MAX_OBJECTS;
5241 if (mng_info->exists[object_id])
5242 if (mng_info->frozen[object_id])
5244 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5245 (void) ThrowMagickException(exception,
5246 GetMagickModule(),CoderError,
5247 "DEFI cannot redefine a frozen MNG object","`%s'",
5252 mng_info->exists[object_id]=MagickTrue;
5255 mng_info->invisible[object_id]=p[2];
5258 Extract object offset info.
5262 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5263 (p[5] << 16) | (p[6] << 8) | p[7]);
5265 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5266 (p[9] << 16) | (p[10] << 8) | p[11]);
5268 if (logging != MagickFalse)
5270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5271 " x_off[%d]: %.20g",object_id,(double)
5272 mng_info->x_off[object_id]);
5274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5275 " y_off[%d]: %.20g",object_id,(double)
5276 mng_info->y_off[object_id]);
5281 Extract object clipping info.
5284 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5287 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5290 if (memcmp(type,mng_bKGD,4) == 0)
5292 mng_info->have_global_bkgd=MagickFalse;
5296 mng_info->mng_global_bkgd.red=
5297 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5299 mng_info->mng_global_bkgd.green=
5300 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5302 mng_info->mng_global_bkgd.blue=
5303 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5305 mng_info->have_global_bkgd=MagickTrue;
5308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5311 if (memcmp(type,mng_BACK,4) == 0)
5313 #if defined(MNG_INSERT_LAYERS)
5315 mandatory_back=p[6];
5320 if (mandatory_back && length > 5)
5322 mng_background_color.red=
5323 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5325 mng_background_color.green=
5326 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5328 mng_background_color.blue=
5329 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5331 mng_background_color.alpha=OpaqueAlpha;
5334 #ifdef MNG_OBJECT_BUFFERS
5336 mng_background_object=(p[7] << 8) | p[8];
5339 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5343 if (memcmp(type,mng_PLTE,4) == 0)
5345 /* Read global PLTE. */
5347 if (length && (length < 769))
5349 if (mng_info->global_plte == (png_colorp) NULL)
5350 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5351 sizeof(*mng_info->global_plte));
5353 for (i=0; i < (ssize_t) (length/3); i++)
5355 mng_info->global_plte[i].red=p[3*i];
5356 mng_info->global_plte[i].green=p[3*i+1];
5357 mng_info->global_plte[i].blue=p[3*i+2];
5360 mng_info->global_plte_length=(unsigned int) (length/3);
5363 for ( ; i < 256; i++)
5365 mng_info->global_plte[i].red=i;
5366 mng_info->global_plte[i].green=i;
5367 mng_info->global_plte[i].blue=i;
5371 mng_info->global_plte_length=256;
5374 mng_info->global_plte_length=0;
5376 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5380 if (memcmp(type,mng_tRNS,4) == 0)
5382 /* read global tRNS */
5385 for (i=0; i < (ssize_t) length; i++)
5386 mng_info->global_trns[i]=p[i];
5389 for ( ; i < 256; i++)
5390 mng_info->global_trns[i]=255;
5392 mng_info->global_trns_length=(unsigned int) length;
5393 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5396 if (memcmp(type,mng_gAMA,4) == 0)
5403 igamma=mng_get_long(p);
5404 mng_info->global_gamma=((float) igamma)*0.00001;
5405 mng_info->have_global_gama=MagickTrue;
5409 mng_info->have_global_gama=MagickFalse;
5411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5415 if (memcmp(type,mng_cHRM,4) == 0)
5417 /* Read global cHRM */
5421 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5422 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5423 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5424 mng_info->global_chrm.red_primary.y=0.00001*
5425 mng_get_long(&p[12]);
5426 mng_info->global_chrm.green_primary.x=0.00001*
5427 mng_get_long(&p[16]);
5428 mng_info->global_chrm.green_primary.y=0.00001*
5429 mng_get_long(&p[20]);
5430 mng_info->global_chrm.blue_primary.x=0.00001*
5431 mng_get_long(&p[24]);
5432 mng_info->global_chrm.blue_primary.y=0.00001*
5433 mng_get_long(&p[28]);
5434 mng_info->have_global_chrm=MagickTrue;
5437 mng_info->have_global_chrm=MagickFalse;
5439 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5443 if (memcmp(type,mng_sRGB,4) == 0)
5450 mng_info->global_srgb_intent=
5451 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5452 mng_info->have_global_srgb=MagickTrue;
5455 mng_info->have_global_srgb=MagickFalse;
5457 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5461 if (memcmp(type,mng_iCCP,4) == 0)
5469 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5474 if (memcmp(type,mng_FRAM,4) == 0)
5477 (void) ThrowMagickException(exception,GetMagickModule(),
5478 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5481 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5482 image->delay=frame_delay;
5484 frame_delay=default_frame_delay;
5485 frame_timeout=default_frame_timeout;
5490 mng_info->framing_mode=p[0];
5492 if (logging != MagickFalse)
5493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5494 " Framing_mode=%d",mng_info->framing_mode);
5498 /* Note the delay and frame clipping boundaries. */
5500 p++; /* framing mode */
5502 while (*p && ((p-chunk) < (ssize_t) length))
5503 p++; /* frame name */
5505 p++; /* frame name terminator */
5507 if ((p-chunk) < (ssize_t) (length-4))
5514 change_delay=(*p++);
5515 change_timeout=(*p++);
5516 change_clipping=(*p++);
5517 p++; /* change_sync */
5521 frame_delay=1UL*image->ticks_per_second*
5524 if (mng_info->ticks_per_second != 0)
5525 frame_delay/=mng_info->ticks_per_second;
5528 frame_delay=PNG_UINT_31_MAX;
5530 if (change_delay == 2)
5531 default_frame_delay=frame_delay;
5535 if (logging != MagickFalse)
5536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5537 " Framing_delay=%.20g",(double) frame_delay);
5542 frame_timeout=1UL*image->ticks_per_second*
5545 if (mng_info->ticks_per_second != 0)
5546 frame_timeout/=mng_info->ticks_per_second;
5549 frame_timeout=PNG_UINT_31_MAX;
5551 if (change_delay == 2)
5552 default_frame_timeout=frame_timeout;
5556 if (logging != MagickFalse)
5557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5558 " Framing_timeout=%.20g",(double) frame_timeout);
5561 if (change_clipping)
5563 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5567 if (logging != MagickFalse)
5568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5569 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5570 (double) fb.left,(double) fb.right,(double) fb.top,
5571 (double) fb.bottom);
5573 if (change_clipping == 2)
5579 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5581 subframe_width=(size_t) (mng_info->clip.right
5582 -mng_info->clip.left);
5584 subframe_height=(size_t) (mng_info->clip.bottom
5585 -mng_info->clip.top);
5587 Insert a background layer behind the frame if framing_mode is 4.
5589 #if defined(MNG_INSERT_LAYERS)
5590 if (logging != MagickFalse)
5591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5592 " subframe_width=%.20g, subframe_height=%.20g",(double)
5593 subframe_width,(double) subframe_height);
5595 if (insert_layers && (mng_info->framing_mode == 4) &&
5596 (subframe_width) && (subframe_height))
5598 /* Allocate next image structure. */
5599 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5601 AcquireNextImage(image_info,image,exception);
5603 if (GetNextImageInList(image) == (Image *) NULL)
5605 image=DestroyImageList(image);
5606 MngInfoFreeStruct(mng_info,&have_mng_structure);
5607 return((Image *) NULL);
5610 image=SyncNextImageInList(image);
5613 mng_info->image=image;
5615 if (term_chunk_found)
5617 image->start_loop=MagickTrue;
5618 image->iterations=mng_iterations;
5619 term_chunk_found=MagickFalse;
5623 image->start_loop=MagickFalse;
5625 image->columns=subframe_width;
5626 image->rows=subframe_height;
5627 image->page.width=subframe_width;
5628 image->page.height=subframe_height;
5629 image->page.x=mng_info->clip.left;
5630 image->page.y=mng_info->clip.top;
5631 image->background_color=mng_background_color;
5632 image->alpha_trait=UndefinedPixelTrait;
5634 (void) SetImageBackgroundColor(image,exception);
5636 if (logging != MagickFalse)
5637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5638 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5639 (double) mng_info->clip.left,(double) mng_info->clip.right,
5640 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5643 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5646 if (memcmp(type,mng_CLIP,4) == 0)
5655 first_object=(p[0] << 8) | p[1];
5656 last_object=(p[2] << 8) | p[3];
5658 for (i=(int) first_object; i <= (int) last_object; i++)
5660 if (mng_info->exists[i] && !mng_info->frozen[i])
5665 box=mng_info->object_clip[i];
5666 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5673 if (memcmp(type,mng_SAVE,4) == 0)
5675 for (i=1; i < MNG_MAX_OBJECTS; i++)
5676 if (mng_info->exists[i])
5678 mng_info->frozen[i]=MagickTrue;
5679 #ifdef MNG_OBJECT_BUFFERS
5680 if (mng_info->ob[i] != (MngBuffer *) NULL)
5681 mng_info->ob[i]->frozen=MagickTrue;
5686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5691 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5693 /* Read DISC or SEEK. */
5695 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5697 for (i=1; i < MNG_MAX_OBJECTS; i++)
5698 MngInfoDiscardObject(mng_info,i);
5706 for (j=0; j < (ssize_t) length; j+=2)
5708 i=p[j] << 8 | p[j+1];
5709 MngInfoDiscardObject(mng_info,i);
5714 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5719 if (memcmp(type,mng_MOVE,4) == 0)
5727 first_object=(p[0] << 8) | p[1];
5728 last_object=(p[2] << 8) | p[3];
5729 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5731 if (mng_info->exists[i] && !mng_info->frozen[i])
5739 old_pair.a=mng_info->x_off[i];
5740 old_pair.b=mng_info->y_off[i];
5741 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5742 mng_info->x_off[i]=new_pair.a;
5743 mng_info->y_off[i]=new_pair.b;
5747 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5751 if (memcmp(type,mng_LOOP,4) == 0)
5753 ssize_t loop_iters=1;
5754 loop_level=chunk[0];
5755 mng_info->loop_active[loop_level]=1; /* mark loop active */
5757 /* Record starting point. */
5758 loop_iters=mng_get_long(&chunk[1]);
5760 if (logging != MagickFalse)
5761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5762 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5763 (double) loop_iters);
5765 if (loop_iters == 0)
5766 skipping_loop=loop_level;
5770 mng_info->loop_jump[loop_level]=TellBlob(image);
5771 mng_info->loop_count[loop_level]=loop_iters;
5774 mng_info->loop_iteration[loop_level]=0;
5775 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5779 if (memcmp(type,mng_ENDL,4) == 0)
5781 loop_level=chunk[0];
5783 if (skipping_loop > 0)
5785 if (skipping_loop == loop_level)
5788 Found end of zero-iteration loop.
5791 mng_info->loop_active[loop_level]=0;
5797 if (mng_info->loop_active[loop_level] == 1)
5799 mng_info->loop_count[loop_level]--;
5800 mng_info->loop_iteration[loop_level]++;
5802 if (logging != MagickFalse)
5803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5804 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5805 (double) loop_level,(double)
5806 mng_info->loop_count[loop_level]);
5808 if (mng_info->loop_count[loop_level] != 0)
5810 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5814 ThrowReaderException(CorruptImageError,
5815 "ImproperImageHeader");
5826 mng_info->loop_active[loop_level]=0;
5828 for (i=0; i < loop_level; i++)
5829 if (mng_info->loop_active[i] == 1)
5830 last_level=(short) i;
5831 loop_level=last_level;
5836 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5840 if (memcmp(type,mng_CLON,4) == 0)
5842 if (mng_info->clon_warning == 0)
5843 (void) ThrowMagickException(exception,GetMagickModule(),
5844 CoderError,"CLON is not implemented yet","`%s'",
5847 mng_info->clon_warning++;
5850 if (memcmp(type,mng_MAGN,4) == 0)
5865 magn_first=(p[0] << 8) | p[1];
5871 magn_last=(p[2] << 8) | p[3];
5874 magn_last=magn_first;
5875 #ifndef MNG_OBJECT_BUFFERS
5876 if (magn_first || magn_last)
5877 if (mng_info->magn_warning == 0)
5879 (void) ThrowMagickException(exception,
5880 GetMagickModule(),CoderError,
5881 "MAGN is not implemented yet for nonzero objects",
5882 "`%s'",image->filename);
5884 mng_info->magn_warning++;
5894 magn_mx=(p[5] << 8) | p[6];
5903 magn_my=(p[7] << 8) | p[8];
5912 magn_ml=(p[9] << 8) | p[10];
5921 magn_mr=(p[11] << 8) | p[12];
5930 magn_mt=(p[13] << 8) | p[14];
5939 magn_mb=(p[15] << 8) | p[16];
5951 magn_methy=magn_methx;
5954 if (magn_methx > 5 || magn_methy > 5)
5955 if (mng_info->magn_warning == 0)
5957 (void) ThrowMagickException(exception,
5958 GetMagickModule(),CoderError,
5959 "Unknown MAGN method in MNG datastream","`%s'",
5962 mng_info->magn_warning++;
5964 #ifdef MNG_OBJECT_BUFFERS
5965 /* Magnify existing objects in the range magn_first to magn_last */
5967 if (magn_first == 0 || magn_last == 0)
5969 /* Save the magnification factors for object 0 */
5970 mng_info->magn_mb=magn_mb;
5971 mng_info->magn_ml=magn_ml;
5972 mng_info->magn_mr=magn_mr;
5973 mng_info->magn_mt=magn_mt;
5974 mng_info->magn_mx=magn_mx;
5975 mng_info->magn_my=magn_my;
5976 mng_info->magn_methx=magn_methx;
5977 mng_info->magn_methy=magn_methy;
5981 if (memcmp(type,mng_PAST,4) == 0)
5983 if (mng_info->past_warning == 0)
5984 (void) ThrowMagickException(exception,GetMagickModule(),
5985 CoderError,"PAST is not implemented yet","`%s'",
5988 mng_info->past_warning++;
5991 if (memcmp(type,mng_SHOW,4) == 0)
5993 if (mng_info->show_warning == 0)
5994 (void) ThrowMagickException(exception,GetMagickModule(),
5995 CoderError,"SHOW is not implemented yet","`%s'",
5998 mng_info->show_warning++;
6001 if (memcmp(type,mng_sBIT,4) == 0)
6004 mng_info->have_global_sbit=MagickFalse;
6008 mng_info->global_sbit.gray=p[0];
6009 mng_info->global_sbit.red=p[0];
6010 mng_info->global_sbit.green=p[1];
6011 mng_info->global_sbit.blue=p[2];
6012 mng_info->global_sbit.alpha=p[3];
6013 mng_info->have_global_sbit=MagickTrue;
6016 if (memcmp(type,mng_pHYs,4) == 0)
6020 mng_info->global_x_pixels_per_unit=
6021 (size_t) mng_get_long(p);
6022 mng_info->global_y_pixels_per_unit=
6023 (size_t) mng_get_long(&p[4]);
6024 mng_info->global_phys_unit_type=p[8];
6025 mng_info->have_global_phys=MagickTrue;
6029 mng_info->have_global_phys=MagickFalse;
6031 if (memcmp(type,mng_pHYg,4) == 0)
6033 if (mng_info->phyg_warning == 0)
6034 (void) ThrowMagickException(exception,GetMagickModule(),
6035 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6037 mng_info->phyg_warning++;
6039 if (memcmp(type,mng_BASI,4) == 0)
6041 skip_to_iend=MagickTrue;
6043 if (mng_info->basi_warning == 0)
6044 (void) ThrowMagickException(exception,GetMagickModule(),
6045 CoderError,"BASI is not implemented yet","`%s'",
6048 mng_info->basi_warning++;
6049 #ifdef MNG_BASI_SUPPORTED
6050 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6051 (p[2] << 8) | p[3]);
6052 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6053 (p[6] << 8) | p[7]);
6054 basi_color_type=p[8];
6055 basi_compression_method=p[9];
6056 basi_filter_type=p[10];
6057 basi_interlace_method=p[11];
6059 basi_red=(p[12] << 8) & p[13];
6065 basi_green=(p[14] << 8) & p[15];
6071 basi_blue=(p[16] << 8) & p[17];
6077 basi_alpha=(p[18] << 8) & p[19];
6081 if (basi_sample_depth == 16)
6088 basi_viewable=p[20];
6094 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6098 if (memcmp(type,mng_IHDR,4)
6099 #if defined(JNG_SUPPORTED)
6100 && memcmp(type,mng_JHDR,4)
6104 /* Not an IHDR or JHDR chunk */
6106 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6111 if (logging != MagickFalse)
6112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6113 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6115 mng_info->exists[object_id]=MagickTrue;
6116 mng_info->viewable[object_id]=MagickTrue;
6118 if (mng_info->invisible[object_id])
6120 if (logging != MagickFalse)
6121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6122 " Skipping invisible object");
6124 skip_to_iend=MagickTrue;
6125 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6128 #if defined(MNG_INSERT_LAYERS)
6130 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6132 image_width=(size_t) mng_get_long(p);
6133 image_height=(size_t) mng_get_long(&p[4]);
6135 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6138 Insert a transparent background layer behind the entire animation
6139 if it is not full screen.
6141 #if defined(MNG_INSERT_LAYERS)
6142 if (insert_layers && mng_type && first_mng_object)
6144 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6145 (image_width < mng_info->mng_width) ||
6146 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6147 (image_height < mng_info->mng_height) ||
6148 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6150 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6153 Allocate next image structure.
6155 AcquireNextImage(image_info,image,exception);
6157 if (GetNextImageInList(image) == (Image *) NULL)
6159 image=DestroyImageList(image);
6160 MngInfoFreeStruct(mng_info,&have_mng_structure);
6161 return((Image *) NULL);
6164 image=SyncNextImageInList(image);
6166 mng_info->image=image;
6168 if (term_chunk_found)
6170 image->start_loop=MagickTrue;
6171 image->iterations=mng_iterations;
6172 term_chunk_found=MagickFalse;
6176 image->start_loop=MagickFalse;
6178 /* Make a background rectangle. */
6181 image->columns=mng_info->mng_width;
6182 image->rows=mng_info->mng_height;
6183 image->page.width=mng_info->mng_width;
6184 image->page.height=mng_info->mng_height;
6187 image->background_color=mng_background_color;
6188 (void) SetImageBackgroundColor(image,exception);
6189 if (logging != MagickFalse)
6190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6191 " Inserted transparent background layer, W=%.20g, H=%.20g",
6192 (double) mng_info->mng_width,(double) mng_info->mng_height);
6196 Insert a background layer behind the upcoming image if
6197 framing_mode is 3, and we haven't already inserted one.
6199 if (insert_layers && (mng_info->framing_mode == 3) &&
6200 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6201 (simplicity & 0x08)))
6203 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6206 Allocate next image structure.
6208 AcquireNextImage(image_info,image,exception);
6210 if (GetNextImageInList(image) == (Image *) NULL)
6212 image=DestroyImageList(image);
6213 MngInfoFreeStruct(mng_info,&have_mng_structure);
6214 return((Image *) NULL);
6217 image=SyncNextImageInList(image);
6220 mng_info->image=image;
6222 if (term_chunk_found)
6224 image->start_loop=MagickTrue;
6225 image->iterations=mng_iterations;
6226 term_chunk_found=MagickFalse;
6230 image->start_loop=MagickFalse;
6233 image->columns=subframe_width;
6234 image->rows=subframe_height;
6235 image->page.width=subframe_width;
6236 image->page.height=subframe_height;
6237 image->page.x=mng_info->clip.left;
6238 image->page.y=mng_info->clip.top;
6239 image->background_color=mng_background_color;
6240 image->alpha_trait=UndefinedPixelTrait;
6241 (void) SetImageBackgroundColor(image,exception);
6243 if (logging != MagickFalse)
6244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6245 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6246 (double) mng_info->clip.left,(double) mng_info->clip.right,
6247 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6249 #endif /* MNG_INSERT_LAYERS */
6250 first_mng_object=MagickFalse;
6252 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6255 Allocate next image structure.
6257 AcquireNextImage(image_info,image,exception);
6259 if (GetNextImageInList(image) == (Image *) NULL)
6261 image=DestroyImageList(image);
6262 MngInfoFreeStruct(mng_info,&have_mng_structure);
6263 return((Image *) NULL);
6266 image=SyncNextImageInList(image);
6268 mng_info->image=image;
6269 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6270 GetBlobSize(image));
6272 if (status == MagickFalse)
6275 if (term_chunk_found)
6277 image->start_loop=MagickTrue;
6278 term_chunk_found=MagickFalse;
6282 image->start_loop=MagickFalse;
6284 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6286 image->delay=frame_delay;
6287 frame_delay=default_frame_delay;
6293 image->page.width=mng_info->mng_width;
6294 image->page.height=mng_info->mng_height;
6295 image->page.x=mng_info->x_off[object_id];
6296 image->page.y=mng_info->y_off[object_id];
6297 image->iterations=mng_iterations;
6300 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6303 if (logging != MagickFalse)
6304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6305 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6308 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6311 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6315 mng_info->image=image;
6316 mng_info->mng_type=mng_type;
6317 mng_info->object_id=object_id;
6319 if (memcmp(type,mng_IHDR,4) == 0)
6320 image=ReadOnePNGImage(mng_info,image_info,exception);
6322 #if defined(JNG_SUPPORTED)
6324 image=ReadOneJNGImage(mng_info,image_info,exception);
6327 if (image == (Image *) NULL)
6329 if (IsImageObject(previous) != MagickFalse)
6331 (void) DestroyImageList(previous);
6332 (void) CloseBlob(previous);
6335 MngInfoFreeStruct(mng_info,&have_mng_structure);
6336 return((Image *) NULL);
6339 if (image->columns == 0 || image->rows == 0)
6341 (void) CloseBlob(image);
6342 image=DestroyImageList(image);
6343 MngInfoFreeStruct(mng_info,&have_mng_structure);
6344 return((Image *) NULL);
6347 mng_info->image=image;
6354 if (mng_info->magn_methx || mng_info->magn_methy)
6360 if (logging != MagickFalse)
6361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6362 " Processing MNG MAGN chunk");
6364 if (mng_info->magn_methx == 1)
6366 magnified_width=mng_info->magn_ml;
6368 if (image->columns > 1)
6369 magnified_width += mng_info->magn_mr;
6371 if (image->columns > 2)
6372 magnified_width += (png_uint_32)
6373 ((image->columns-2)*(mng_info->magn_mx));
6378 magnified_width=(png_uint_32) image->columns;
6380 if (image->columns > 1)
6381 magnified_width += mng_info->magn_ml-1;
6383 if (image->columns > 2)
6384 magnified_width += mng_info->magn_mr-1;
6386 if (image->columns > 3)
6387 magnified_width += (png_uint_32)
6388 ((image->columns-3)*(mng_info->magn_mx-1));
6391 if (mng_info->magn_methy == 1)
6393 magnified_height=mng_info->magn_mt;
6395 if (image->rows > 1)
6396 magnified_height += mng_info->magn_mb;
6398 if (image->rows > 2)
6399 magnified_height += (png_uint_32)
6400 ((image->rows-2)*(mng_info->magn_my));
6405 magnified_height=(png_uint_32) image->rows;
6407 if (image->rows > 1)
6408 magnified_height += mng_info->magn_mt-1;
6410 if (image->rows > 2)
6411 magnified_height += mng_info->magn_mb-1;
6413 if (image->rows > 3)
6414 magnified_height += (png_uint_32)
6415 ((image->rows-3)*(mng_info->magn_my-1));
6418 if (magnified_height > image->rows ||
6419 magnified_width > image->columns)
6446 /* Allocate next image structure. */
6448 if (logging != MagickFalse)
6449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6450 " Allocate magnified image");
6452 AcquireNextImage(image_info,image,exception);
6454 if (GetNextImageInList(image) == (Image *) NULL)
6456 image=DestroyImageList(image);
6457 MngInfoFreeStruct(mng_info,&have_mng_structure);
6458 return((Image *) NULL);
6461 large_image=SyncNextImageInList(image);
6463 large_image->columns=magnified_width;
6464 large_image->rows=magnified_height;
6466 magn_methx=mng_info->magn_methx;
6467 magn_methy=mng_info->magn_methy;
6469 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6470 #define QM unsigned short
6471 if (magn_methx != 1 || magn_methy != 1)
6474 Scale pixels to unsigned shorts to prevent
6475 overflow of intermediate values of interpolations
6477 for (y=0; y < (ssize_t) image->rows; y++)
6479 q=GetAuthenticPixels(image,0,y,image->columns,1,
6482 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6484 SetPixelRed(image,ScaleQuantumToShort(
6485 GetPixelRed(image,q)),q);
6486 SetPixelGreen(image,ScaleQuantumToShort(
6487 GetPixelGreen(image,q)),q);
6488 SetPixelBlue(image,ScaleQuantumToShort(
6489 GetPixelBlue(image,q)),q);
6490 SetPixelAlpha(image,ScaleQuantumToShort(
6491 GetPixelAlpha(image,q)),q);
6492 q+=GetPixelChannels(image);
6495 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6503 if (image->alpha_trait == BlendPixelTrait)
6504 (void) SetImageBackgroundColor(large_image,exception);
6508 large_image->background_color.alpha=OpaqueAlpha;
6509 (void) SetImageBackgroundColor(large_image,exception);
6511 if (magn_methx == 4)
6514 if (magn_methx == 5)
6517 if (magn_methy == 4)
6520 if (magn_methy == 5)
6524 /* magnify the rows into the right side of the large image */
6526 if (logging != MagickFalse)
6527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6528 " Magnify the rows to %.20g",(double) large_image->rows);
6529 m=(ssize_t) mng_info->magn_mt;
6531 length=(size_t) image->columns*GetPixelChannels(image);
6532 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6533 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6535 if ((prev == (Quantum *) NULL) ||
6536 (next == (Quantum *) NULL))
6538 image=DestroyImageList(image);
6539 MngInfoFreeStruct(mng_info,&have_mng_structure);
6540 ThrowReaderException(ResourceLimitError,
6541 "MemoryAllocationFailed");
6544 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6545 (void) CopyMagickMemory(next,n,length);
6547 for (y=0; y < (ssize_t) image->rows; y++)
6550 m=(ssize_t) mng_info->magn_mt;
6552 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6553 m=(ssize_t) mng_info->magn_mb;
6555 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6556 m=(ssize_t) mng_info->magn_mb;
6558 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6562 m=(ssize_t) mng_info->magn_my;
6568 if (y < (ssize_t) image->rows-1)
6570 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6572 (void) CopyMagickMemory(next,n,length);
6575 for (i=0; i < m; i++, yy++)
6580 assert(yy < (ssize_t) large_image->rows);
6583 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6585 q+=(large_image->columns-image->columns)*
6586 GetPixelChannels(large_image);
6588 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6590 /* To do: get color as function of indexes[x] */
6592 if (image->storage_class == PseudoClass)
6597 if (magn_methy <= 1)
6599 /* replicate previous */
6600 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6601 SetPixelGreen(large_image,GetPixelGreen(image,
6603 SetPixelBlue(large_image,GetPixelBlue(image,
6605 SetPixelAlpha(large_image,GetPixelAlpha(image,
6609 else if (magn_methy == 2 || magn_methy == 4)
6613 SetPixelRed(large_image,GetPixelRed(image,
6615 SetPixelGreen(large_image,GetPixelGreen(image,
6617 SetPixelBlue(large_image,GetPixelBlue(image,
6619 SetPixelAlpha(large_image,GetPixelAlpha(image,
6626 SetPixelRed(large_image,((QM) (((ssize_t)
6627 (2*i*(GetPixelRed(image,n)
6628 -GetPixelRed(image,pixels)+m))/
6630 +GetPixelRed(image,pixels)))),q);
6631 SetPixelGreen(large_image,((QM) (((ssize_t)
6632 (2*i*(GetPixelGreen(image,n)
6633 -GetPixelGreen(image,pixels)+m))/
6635 +GetPixelGreen(image,pixels)))),q);
6636 SetPixelBlue(large_image,((QM) (((ssize_t)
6637 (2*i*(GetPixelBlue(image,n)
6638 -GetPixelBlue(image,pixels)+m))/
6640 +GetPixelBlue(image,pixels)))),q);
6642 if (image->alpha_trait == BlendPixelTrait)
6643 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6644 (2*i*(GetPixelAlpha(image,n)
6645 -GetPixelAlpha(image,pixels)+m))
6647 GetPixelAlpha(image,pixels)))),q);
6650 if (magn_methy == 4)
6652 /* Replicate nearest */
6653 if (i <= ((m+1) << 1))
6654 SetPixelAlpha(large_image,GetPixelAlpha(image,
6657 SetPixelAlpha(large_image,GetPixelAlpha(image,
6662 else /* if (magn_methy == 3 || magn_methy == 5) */
6664 /* Replicate nearest */
6665 if (i <= ((m+1) << 1))
6667 SetPixelRed(large_image,GetPixelRed(image,
6669 SetPixelGreen(large_image,GetPixelGreen(image,
6671 SetPixelBlue(large_image,GetPixelBlue(image,
6673 SetPixelAlpha(large_image,GetPixelAlpha(image,
6679 SetPixelRed(large_image,GetPixelRed(image,n),q);
6680 SetPixelGreen(large_image,GetPixelGreen(image,n),
6682 SetPixelBlue(large_image,GetPixelBlue(image,n),
6684 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6688 if (magn_methy == 5)
6690 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6691 (GetPixelAlpha(image,n)
6692 -GetPixelAlpha(image,pixels))
6693 +m))/((ssize_t) (m*2))
6694 +GetPixelAlpha(image,pixels)),q);
6697 n+=GetPixelChannels(image);
6698 q+=GetPixelChannels(large_image);
6699 pixels+=GetPixelChannels(image);
6702 if (SyncAuthenticPixels(large_image,exception) == 0)
6708 prev=(Quantum *) RelinquishMagickMemory(prev);
6709 next=(Quantum *) RelinquishMagickMemory(next);
6711 length=image->columns;
6713 if (logging != MagickFalse)
6714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6715 " Delete original image");
6717 DeleteImageFromList(&image);
6721 mng_info->image=image;
6723 /* magnify the columns */
6724 if (logging != MagickFalse)
6725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6726 " Magnify the columns to %.20g",(double) image->columns);
6728 for (y=0; y < (ssize_t) image->rows; y++)
6733 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6734 pixels=q+(image->columns-length)*GetPixelChannels(image);
6735 n=pixels+GetPixelChannels(image);
6737 for (x=(ssize_t) (image->columns-length);
6738 x < (ssize_t) image->columns; x++)
6740 /* To do: Rewrite using Get/Set***PixelChannel() */
6742 if (x == (ssize_t) (image->columns-length))
6743 m=(ssize_t) mng_info->magn_ml;
6745 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6746 m=(ssize_t) mng_info->magn_mr;
6748 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6749 m=(ssize_t) mng_info->magn_mr;
6751 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6755 m=(ssize_t) mng_info->magn_mx;
6757 for (i=0; i < m; i++)
6759 if (magn_methx <= 1)
6761 /* replicate previous */
6762 SetPixelRed(image,GetPixelRed(image,pixels),q);
6763 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6764 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6765 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6768 else if (magn_methx == 2 || magn_methx == 4)
6772 SetPixelRed(image,GetPixelRed(image,pixels),q);
6773 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6774 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6775 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6778 /* To do: Rewrite using Get/Set***PixelChannel() */
6782 SetPixelRed(image,(QM) ((2*i*(
6783 GetPixelRed(image,n)
6784 -GetPixelRed(image,pixels))+m)
6786 GetPixelRed(image,pixels)),q);
6788 SetPixelGreen(image,(QM) ((2*i*(
6789 GetPixelGreen(image,n)
6790 -GetPixelGreen(image,pixels))+m)
6792 GetPixelGreen(image,pixels)),q);
6794 SetPixelBlue(image,(QM) ((2*i*(
6795 GetPixelBlue(image,n)
6796 -GetPixelBlue(image,pixels))+m)
6798 GetPixelBlue(image,pixels)),q);
6799 if (image->alpha_trait == BlendPixelTrait)
6800 SetPixelAlpha(image,(QM) ((2*i*(
6801 GetPixelAlpha(image,n)
6802 -GetPixelAlpha(image,pixels))+m)
6804 GetPixelAlpha(image,pixels)),q);
6807 if (magn_methx == 4)
6809 /* Replicate nearest */
6810 if (i <= ((m+1) << 1))
6812 SetPixelAlpha(image,
6813 GetPixelAlpha(image,pixels)+0,q);
6817 SetPixelAlpha(image,
6818 GetPixelAlpha(image,n)+0,q);
6823 else /* if (magn_methx == 3 || magn_methx == 5) */
6825 /* Replicate nearest */
6826 if (i <= ((m+1) << 1))
6828 SetPixelRed(image,GetPixelRed(image,pixels),q);
6829 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6830 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6831 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6836 SetPixelRed(image,GetPixelRed(image,n),q);
6837 SetPixelGreen(image,GetPixelGreen(image,n),q);
6838 SetPixelBlue(image,GetPixelBlue(image,n),q);
6839 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6842 if (magn_methx == 5)
6845 SetPixelAlpha(image,
6846 (QM) ((2*i*( GetPixelAlpha(image,n)
6847 -GetPixelAlpha(image,pixels))+m)/
6849 +GetPixelAlpha(image,pixels)),q);
6852 q+=GetPixelChannels(image);
6854 n+=GetPixelChannels(image);
6855 p+=GetPixelChannels(image);
6858 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6861 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6862 if (magn_methx != 1 || magn_methy != 1)
6865 Rescale pixels to Quantum
6867 for (y=0; y < (ssize_t) image->rows; y++)
6869 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6871 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6873 SetPixelRed(image,ScaleShortToQuantum(
6874 GetPixelRed(image,q)),q);
6875 SetPixelGreen(image,ScaleShortToQuantum(
6876 GetPixelGreen(image,q)),q);
6877 SetPixelBlue(image,ScaleShortToQuantum(
6878 GetPixelBlue(image,q)),q);
6879 SetPixelAlpha(image,ScaleShortToQuantum(
6880 GetPixelAlpha(image,q)),q);
6881 q+=GetPixelChannels(image);
6884 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6889 if (logging != MagickFalse)
6890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6891 " Finished MAGN processing");
6896 Crop_box is with respect to the upper left corner of the MNG.
6898 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6899 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6900 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6901 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6902 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6903 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6904 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6905 if ((crop_box.left != (mng_info->image_box.left
6906 +mng_info->x_off[object_id])) ||
6907 (crop_box.right != (mng_info->image_box.right
6908 +mng_info->x_off[object_id])) ||
6909 (crop_box.top != (mng_info->image_box.top
6910 +mng_info->y_off[object_id])) ||
6911 (crop_box.bottom != (mng_info->image_box.bottom
6912 +mng_info->y_off[object_id])))
6914 if (logging != MagickFalse)
6915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6916 " Crop the PNG image");
6918 if ((crop_box.left < crop_box.right) &&
6919 (crop_box.top < crop_box.bottom))
6928 Crop_info is with respect to the upper left corner of
6931 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6932 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6933 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6934 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6935 image->page.width=image->columns;
6936 image->page.height=image->rows;
6939 im=CropImage(image,&crop_info,exception);
6941 if (im != (Image *) NULL)
6943 image->columns=im->columns;
6944 image->rows=im->rows;
6945 im=DestroyImage(im);
6946 image->page.width=image->columns;
6947 image->page.height=image->rows;
6948 image->page.x=crop_box.left;
6949 image->page.y=crop_box.top;
6956 No pixels in crop area. The MNG spec still requires
6957 a layer, though, so make a single transparent pixel in
6958 the top left corner.
6963 (void) SetImageBackgroundColor(image,exception);
6964 image->page.width=1;
6965 image->page.height=1;
6970 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6971 image=mng_info->image;
6975 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6976 /* PNG does not handle depths greater than 16 so reduce it even
6979 if (image->depth > 16)
6983 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6984 if (image->depth > 8)
6986 /* To do: fill low byte properly */
6990 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6994 if (image_info->number_scenes != 0)
6996 if (mng_info->scenes_found >
6997 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7001 if (logging != MagickFalse)
7002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7003 " Finished reading image datastream.");
7005 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7007 (void) CloseBlob(image);
7009 if (logging != MagickFalse)
7010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7011 " Finished reading all image datastreams.");
7013 #if defined(MNG_INSERT_LAYERS)
7014 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7015 (mng_info->mng_height))
7018 Insert a background layer if nothing else was found.
7020 if (logging != MagickFalse)
7021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7022 " No images found. Inserting a background layer.");
7024 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7027 Allocate next image structure.
7029 AcquireNextImage(image_info,image,exception);
7030 if (GetNextImageInList(image) == (Image *) NULL)
7032 image=DestroyImageList(image);
7033 MngInfoFreeStruct(mng_info,&have_mng_structure);
7035 if (logging != MagickFalse)
7036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7037 " Allocation failed, returning NULL.");
7039 return((Image *) NULL);
7041 image=SyncNextImageInList(image);
7043 image->columns=mng_info->mng_width;
7044 image->rows=mng_info->mng_height;
7045 image->page.width=mng_info->mng_width;
7046 image->page.height=mng_info->mng_height;
7049 image->background_color=mng_background_color;
7050 image->alpha_trait=UndefinedPixelTrait;
7052 if (image_info->ping == MagickFalse)
7053 (void) SetImageBackgroundColor(image,exception);
7055 mng_info->image_found++;
7058 image->iterations=mng_iterations;
7060 if (mng_iterations == 1)
7061 image->start_loop=MagickTrue;
7063 while (GetPreviousImageInList(image) != (Image *) NULL)
7066 if (image_count > 10*mng_info->image_found)
7068 if (logging != MagickFalse)
7069 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7071 (void) ThrowMagickException(exception,GetMagickModule(),
7072 CoderError,"Linked list is corrupted, beginning of list not found",
7073 "`%s'",image_info->filename);
7075 return((Image *) NULL);
7078 image=GetPreviousImageInList(image);
7080 if (GetNextImageInList(image) == (Image *) NULL)
7082 if (logging != MagickFalse)
7083 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7085 (void) ThrowMagickException(exception,GetMagickModule(),
7086 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7087 image_info->filename);
7091 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7092 GetNextImageInList(image) ==
7095 if (logging != MagickFalse)
7096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097 " First image null");
7099 (void) ThrowMagickException(exception,GetMagickModule(),
7100 CoderError,"image->next for first image is NULL but shouldn't be.",
7101 "`%s'",image_info->filename);
7104 if (mng_info->image_found == 0)
7106 if (logging != MagickFalse)
7107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7108 " No visible images found.");
7110 (void) ThrowMagickException(exception,GetMagickModule(),
7111 CoderError,"No visible images in file","`%s'",image_info->filename);
7113 if (image != (Image *) NULL)
7114 image=DestroyImageList(image);
7116 MngInfoFreeStruct(mng_info,&have_mng_structure);
7117 return((Image *) NULL);
7120 if (mng_info->ticks_per_second)
7121 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7122 final_delay/mng_info->ticks_per_second;
7125 image->start_loop=MagickTrue;
7127 /* Find final nonzero image delay */
7128 final_image_delay=0;
7130 while (GetNextImageInList(image) != (Image *) NULL)
7133 final_image_delay=image->delay;
7135 image=GetNextImageInList(image);
7138 if (final_delay < final_image_delay)
7139 final_delay=final_image_delay;
7141 image->delay=final_delay;
7143 if (logging != MagickFalse)
7144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7145 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7146 (double) final_delay);
7148 if (logging != MagickFalse)
7154 image=GetFirstImageInList(image);
7156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7157 " Before coalesce:");
7159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7160 " scene 0 delay=%.20g",(double) image->delay);
7162 while (GetNextImageInList(image) != (Image *) NULL)
7164 image=GetNextImageInList(image);
7165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7166 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7170 image=GetFirstImageInList(image);
7171 #ifdef MNG_COALESCE_LAYERS
7181 if (logging != MagickFalse)
7182 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7185 next_image=CoalesceImages(image,exception);
7187 if (next_image == (Image *) NULL)
7188 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7190 image=DestroyImageList(image);
7193 for (next=image; next != (Image *) NULL; next=next_image)
7195 next->page.width=mng_info->mng_width;
7196 next->page.height=mng_info->mng_height;
7199 next->scene=scene++;
7200 next_image=GetNextImageInList(next);
7202 if (next_image == (Image *) NULL)
7205 if (next->delay == 0)
7208 next_image->previous=GetPreviousImageInList(next);
7209 if (GetPreviousImageInList(next) == (Image *) NULL)
7212 next->previous->next=next_image;
7213 next=DestroyImage(next);
7219 while (GetNextImageInList(image) != (Image *) NULL)
7220 image=GetNextImageInList(image);
7222 image->dispose=BackgroundDispose;
7224 if (logging != MagickFalse)
7230 image=GetFirstImageInList(image);
7232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7233 " After coalesce:");
7235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7236 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7237 (double) image->dispose);
7239 while (GetNextImageInList(image) != (Image *) NULL)
7241 image=GetNextImageInList(image);
7243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7244 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7245 (double) image->delay,(double) image->dispose);
7249 image=GetFirstImageInList(image);
7250 MngInfoFreeStruct(mng_info,&have_mng_structure);
7251 have_mng_structure=MagickFalse;
7253 if (logging != MagickFalse)
7254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7256 return(GetFirstImageInList(image));
7258 #else /* PNG_LIBPNG_VER > 10011 */
7259 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7261 printf("Your PNG library is too old: You have libpng-%s\n",
7262 PNG_LIBPNG_VER_STRING);
7264 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7265 "PNG library is too old","`%s'",image_info->filename);
7267 return(Image *) NULL;
7270 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7272 return(ReadPNGImage(image_info,exception));
7274 #endif /* PNG_LIBPNG_VER > 10011 */
7278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7282 % R e g i s t e r P N G I m a g e %
7286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7288 % RegisterPNGImage() adds properties for the PNG image format to
7289 % the list of supported formats. The properties include the image format
7290 % tag, a method to read and/or write the format, whether the format
7291 % supports the saving of more than one frame to the same file or blob,
7292 % whether the format supports native in-memory I/O, and a brief
7293 % description of the format.
7295 % The format of the RegisterPNGImage method is:
7297 % size_t RegisterPNGImage(void)
7300 ModuleExport size_t RegisterPNGImage(void)
7303 version[MaxTextExtent];
7311 "See http://www.libpng.org/ for details about the PNG format."
7316 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7322 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7328 #if defined(PNG_LIBPNG_VER_STRING)
7329 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7330 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7332 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7334 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7335 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7340 entry=SetMagickInfo("MNG");
7341 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7343 #if defined(MAGICKCORE_PNG_DELEGATE)
7344 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7345 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7348 entry->magick=(IsImageFormatHandler *) IsMNG;
7349 entry->description=ConstantString("Multiple-image Network Graphics");
7351 if (*version != '\0')
7352 entry->version=ConstantString(version);
7354 entry->module=ConstantString("PNG");
7355 entry->note=ConstantString(MNGNote);
7356 (void) RegisterMagickInfo(entry);
7358 entry=SetMagickInfo("PNG");
7360 #if defined(MAGICKCORE_PNG_DELEGATE)
7361 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7362 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7365 entry->magick=(IsImageFormatHandler *) IsPNG;
7366 entry->adjoin=MagickFalse;
7367 entry->description=ConstantString("Portable Network Graphics");
7368 entry->module=ConstantString("PNG");
7370 if (*version != '\0')
7371 entry->version=ConstantString(version);
7373 entry->note=ConstantString(PNGNote);
7374 (void) RegisterMagickInfo(entry);
7376 entry=SetMagickInfo("PNG8");
7378 #if defined(MAGICKCORE_PNG_DELEGATE)
7379 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7380 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7383 entry->magick=(IsImageFormatHandler *) IsPNG;
7384 entry->adjoin=MagickFalse;
7385 entry->description=ConstantString(
7386 "8-bit indexed with optional binary transparency");
7387 entry->module=ConstantString("PNG");
7388 (void) RegisterMagickInfo(entry);
7390 entry=SetMagickInfo("PNG24");
7393 #if defined(ZLIB_VERSION)
7394 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7395 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7397 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7399 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7400 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7404 if (*version != '\0')
7405 entry->version=ConstantString(version);
7407 #if defined(MAGICKCORE_PNG_DELEGATE)
7408 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7409 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7412 entry->magick=(IsImageFormatHandler *) IsPNG;
7413 entry->adjoin=MagickFalse;
7414 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7415 entry->module=ConstantString("PNG");
7416 (void) RegisterMagickInfo(entry);
7418 entry=SetMagickInfo("PNG32");
7420 #if defined(MAGICKCORE_PNG_DELEGATE)
7421 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7422 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7425 entry->magick=(IsImageFormatHandler *) IsPNG;
7426 entry->adjoin=MagickFalse;
7427 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7428 entry->module=ConstantString("PNG");
7429 (void) RegisterMagickInfo(entry);
7431 entry=SetMagickInfo("PNG48");
7433 #if defined(MAGICKCORE_PNG_DELEGATE)
7434 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7435 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7438 entry->magick=(IsImageFormatHandler *) IsPNG;
7439 entry->adjoin=MagickFalse;
7440 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7441 entry->module=ConstantString("PNG");
7442 (void) RegisterMagickInfo(entry);
7444 entry=SetMagickInfo("PNG64");
7446 #if defined(MAGICKCORE_PNG_DELEGATE)
7447 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7448 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7451 entry->magick=(IsImageFormatHandler *) IsPNG;
7452 entry->adjoin=MagickFalse;
7453 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7454 entry->module=ConstantString("PNG");
7455 (void) RegisterMagickInfo(entry);
7457 entry=SetMagickInfo("PNG00");
7459 #if defined(MAGICKCORE_PNG_DELEGATE)
7460 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7461 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7464 entry->magick=(IsImageFormatHandler *) IsPNG;
7465 entry->adjoin=MagickFalse;
7466 entry->description=ConstantString(
7467 "PNG inheriting bit-depth and color-type from original");
7468 entry->module=ConstantString("PNG");
7469 (void) RegisterMagickInfo(entry);
7471 entry=SetMagickInfo("JNG");
7473 #if defined(JNG_SUPPORTED)
7474 #if defined(MAGICKCORE_PNG_DELEGATE)
7475 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7476 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7480 entry->magick=(IsImageFormatHandler *) IsJNG;
7481 entry->adjoin=MagickFalse;
7482 entry->description=ConstantString("JPEG Network Graphics");
7483 entry->module=ConstantString("PNG");
7484 entry->note=ConstantString(JNGNote);
7485 (void) RegisterMagickInfo(entry);
7487 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7488 ping_semaphore=AllocateSemaphoreInfo();
7491 return(MagickImageCoderSignature);
7495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7499 % U n r e g i s t e r P N G I m a g e %
7503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7505 % UnregisterPNGImage() removes format registrations made by the
7506 % PNG module from the list of supported formats.
7508 % The format of the UnregisterPNGImage method is:
7510 % UnregisterPNGImage(void)
7513 ModuleExport void UnregisterPNGImage(void)
7515 (void) UnregisterMagickInfo("MNG");
7516 (void) UnregisterMagickInfo("PNG");
7517 (void) UnregisterMagickInfo("PNG8");
7518 (void) UnregisterMagickInfo("PNG24");
7519 (void) UnregisterMagickInfo("PNG32");
7520 (void) UnregisterMagickInfo("PNG48");
7521 (void) UnregisterMagickInfo("PNG64");
7522 (void) UnregisterMagickInfo("PNG00");
7523 (void) UnregisterMagickInfo("JNG");
7525 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7526 if (ping_semaphore != (SemaphoreInfo *) NULL)
7527 DestroySemaphoreInfo(&ping_semaphore);
7531 #if defined(MAGICKCORE_PNG_DELEGATE)
7532 #if PNG_LIBPNG_VER > 10011
7534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7538 % W r i t e M N G I m a g e %
7542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7544 % WriteMNGImage() writes an image in the Portable Network Graphics
7545 % Group's "Multiple-image Network Graphics" encoded image format.
7547 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7549 % The format of the WriteMNGImage method is:
7551 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7552 % Image *image,ExceptionInfo *exception)
7554 % A description of each parameter follows.
7556 % o image_info: the image info.
7558 % o image: The image.
7560 % o exception: return any errors or warnings in this structure.
7562 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7563 % "To do" under ReadPNGImage):
7565 % Preserve all unknown and not-yet-handled known chunks found in input
7566 % PNG file and copy them into output PNG files according to the PNG
7569 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7571 % Improve selection of color type (use indexed-colour or indexed-colour
7572 % with tRNS when 256 or fewer unique RGBA values are present).
7574 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7575 % This will be complicated if we limit ourselves to generating MNG-LC
7576 % files. For now we ignore disposal method 3 and simply overlay the next
7579 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7580 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7581 % [mostly done 15 June 1999 but still need to take care of tRNS]
7583 % Check for identical sRGB and replace with a global sRGB (and remove
7584 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7585 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7586 % local gAMA/cHRM with local sRGB if appropriate).
7588 % Check for identical sBIT chunks and write global ones.
7590 % Provide option to skip writing the signature tEXt chunks.
7592 % Use signatures to detect identical objects and reuse the first
7593 % instance of such objects instead of writing duplicate objects.
7595 % Use a smaller-than-32k value of compression window size when
7598 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7599 % ancillary text chunks and save profiles.
7601 % Provide an option to force LC files (to ensure exact framing rate)
7604 % Provide an option to force VLC files instead of LC, even when offsets
7605 % are present. This will involve expanding the embedded images with a
7606 % transparent region at the top and/or left.
7610 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7611 png_info *ping_info, unsigned char *profile_type, unsigned char
7612 *profile_description, unsigned char *profile_data, png_uint_32 length)
7631 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7633 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7636 if (image_info->verbose)
7638 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7639 (char *) profile_type, (double) length);
7642 #if PNG_LIBPNG_VER >= 14000
7643 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7645 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7647 description_length=(png_uint_32) strlen((const char *) profile_description);
7648 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7649 + description_length);
7650 #if PNG_LIBPNG_VER >= 14000
7651 text[0].text=(png_charp) png_malloc(ping,
7652 (png_alloc_size_t) allocated_length);
7653 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7655 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7656 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7658 text[0].key[0]='\0';
7659 (void) ConcatenateMagickString(text[0].key,
7660 "Raw profile type ",MaxTextExtent);
7661 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7665 (void) CopyMagickString(dp,(const char *) profile_description,
7667 dp+=description_length;
7669 (void) FormatLocaleString(dp,allocated_length-
7670 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7673 for (i=0; i < (ssize_t) length; i++)
7677 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7678 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7683 text[0].text_length=(png_size_t) (dp-text[0].text);
7684 text[0].compression=image_info->compression == NoCompression ||
7685 (image_info->compression == UndefinedCompression &&
7686 text[0].text_length < 128) ? -1 : 0;
7688 if (text[0].text_length <= allocated_length)
7689 png_set_text(ping,ping_info,text,1);
7691 png_free(ping,text[0].text);
7692 png_free(ping,text[0].key);
7693 png_free(ping,text);
7696 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7697 const char *string, MagickBooleanType logging)
7710 ResetImageProfileIterator(image);
7712 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7714 profile=GetImageProfile(image,name);
7716 if (profile != (const StringInfo *) NULL)
7721 if (LocaleNCompare(name,string,11) == 0)
7723 if (logging != MagickFalse)
7724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7725 " Found %s profile",name);
7727 ping_profile=CloneStringInfo(profile);
7728 data=GetStringInfoDatum(ping_profile),
7729 length=(png_uint_32) GetStringInfoLength(ping_profile);
7734 (void) WriteBlobMSBULong(image,length-5); /* data length */
7735 (void) WriteBlob(image,length-1,data+1);
7736 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7737 ping_profile=DestroyStringInfo(ping_profile);
7741 name=GetNextImageProfile(image);
7748 /* Write one PNG image */
7749 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7750 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7774 ping_trans_alpha[256];
7802 ping_have_cheap_transparency,
7815 /* ping_exclude_EXIF, */
7818 /* ping_exclude_iTXt, */
7823 /* ping_exclude_tRNS, */
7825 ping_exclude_zCCP, /* hex-encoded iCCP */
7828 ping_preserve_colormap,
7829 ping_need_colortype_warning,
7847 *volatile ping_pixels;
7860 ping_interlace_method,
7861 ping_compression_method,
7878 number_semitransparent,
7880 ping_pHYs_unit_type;
7883 ping_pHYs_x_resolution,
7884 ping_pHYs_y_resolution;
7886 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7887 " Enter WriteOnePNGImage()");
7889 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7890 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7891 if (image_info == (ImageInfo *) NULL)
7892 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7894 /* Define these outside of the following "if logging()" block so they will
7895 * show in debuggers.
7898 (void) ConcatenateMagickString(im_vers,
7899 MagickLibVersionText,MaxTextExtent);
7900 (void) ConcatenateMagickString(im_vers,
7901 MagickLibAddendum,MaxTextExtent);
7904 (void) ConcatenateMagickString(libpng_vers,
7905 PNG_LIBPNG_VER_STRING,32);
7907 (void) ConcatenateMagickString(libpng_runv,
7908 png_get_libpng_ver(NULL),32);
7911 (void) ConcatenateMagickString(zlib_vers,
7914 (void) ConcatenateMagickString(zlib_runv,
7919 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
7921 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
7923 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
7925 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
7928 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
7930 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
7932 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
7937 /* Initialize some stuff */
7940 ping_interlace_method=0,
7941 ping_compression_method=0,
7942 ping_filter_method=0,
7945 ping_background.red = 0;
7946 ping_background.green = 0;
7947 ping_background.blue = 0;
7948 ping_background.gray = 0;
7949 ping_background.index = 0;
7951 ping_trans_color.red=0;
7952 ping_trans_color.green=0;
7953 ping_trans_color.blue=0;
7954 ping_trans_color.gray=0;
7956 ping_pHYs_unit_type = 0;
7957 ping_pHYs_x_resolution = 0;
7958 ping_pHYs_y_resolution = 0;
7960 ping_have_blob=MagickFalse;
7961 ping_have_cheap_transparency=MagickFalse;
7962 ping_have_color=MagickTrue;
7963 ping_have_non_bw=MagickTrue;
7964 ping_have_PLTE=MagickFalse;
7965 ping_have_bKGD=MagickFalse;
7966 ping_have_iCCP=MagickFalse;
7967 ping_have_pHYs=MagickFalse;
7968 ping_have_sRGB=MagickFalse;
7969 ping_have_tRNS=MagickFalse;
7971 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7972 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7973 ping_exclude_date=mng_info->ping_exclude_date;
7974 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7975 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7976 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7977 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7978 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7979 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7980 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7981 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7982 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7983 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7984 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7985 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7987 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7988 ping_need_colortype_warning = MagickFalse;
7990 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7991 * i.e., eliminate the ICC profile and set image->rendering_intent.
7992 * Note that this will not involve any changes to the actual pixels
7993 * but merely passes information to applications that read the resulting
7996 if (ping_exclude_sRGB == MagickFalse)
8004 ResetImageProfileIterator(image);
8005 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8007 profile=GetImageProfile(image,name);
8009 if (profile != (StringInfo *) NULL)
8011 if ((LocaleCompare(name,"ICC") == 0) ||
8012 (LocaleCompare(name,"ICM") == 0))
8017 /* 0: not a known sRGB profile
8018 * 1: HP-Microsoft sRGB v2
8019 * 2: ICC sRGB v4 perceptual
8020 * 3: ICC sRGB v2 perceptual no black-compensation
8023 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
8024 check_len[4] = {0, 3144, 60960, 3052};
8033 length=(png_uint_32) GetStringInfoLength(profile);
8035 for (icheck=3; icheck > 0; icheck--)
8037 if (length == check_len[icheck])
8039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8040 " Got a %lu-byte ICC profile (potentially sRGB)",
8041 (unsigned long) length);
8043 data=GetStringInfoDatum(profile);
8044 profile_crc=crc32(0,data,length);
8046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " with crc=%8x",(unsigned int) profile_crc);
8049 if (profile_crc == check_crc[icheck])
8051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8053 if (image->rendering_intent==UndefinedIntent)
8054 image->rendering_intent=PerceptualIntent;
8060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8061 " Got a %lu-byte ICC profile",
8062 (unsigned long) length);
8065 name=GetNextImageProfile(image);
8070 number_semitransparent = 0;
8071 number_transparent = 0;
8073 if (logging != MagickFalse)
8075 if (image->storage_class == UndefinedClass)
8076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8077 " storage_class=UndefinedClass");
8078 if (image->storage_class == DirectClass)
8079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8080 " storage_class=DirectClass");
8081 if (image->storage_class == PseudoClass)
8082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8083 " storage_class=PseudoClass");
8086 if (image->storage_class == PseudoClass &&
8087 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8088 mng_info->write_png48 || mng_info->write_png64 ||
8089 (mng_info->write_png_colortype != 1 &&
8090 mng_info->write_png_colortype != 5)))
8092 (void) SyncImage(image,exception);
8093 image->storage_class = DirectClass;
8096 if (ping_preserve_colormap == MagickFalse)
8098 if (image->storage_class != PseudoClass && image->colormap != NULL)
8100 /* Free the bogus colormap; it can cause trouble later */
8101 if (logging != MagickFalse)
8102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8103 " Freeing bogus colormap");
8104 (void) RelinquishMagickMemory(image->colormap);
8105 image->colormap=NULL;
8109 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8110 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8113 Sometimes we get PseudoClass images whose RGB values don't match
8114 the colors in the colormap. This code syncs the RGB values.
8116 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8117 (void) SyncImage(image,exception);
8119 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8120 if (image->depth > 8)
8122 if (logging != MagickFalse)
8123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8124 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8130 /* Respect the -depth option */
8131 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8136 if (image->depth > 8)
8138 #if MAGICKCORE_QUANTUM_DEPTH > 16
8139 /* Scale to 16-bit */
8140 LBR16PacketRGBO(image->background_color);
8142 for (y=0; y < (ssize_t) image->rows; y++)
8144 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8146 if (r == (Quantum *) NULL)
8149 for (x=0; x < (ssize_t) image->columns; x++)
8152 r+=GetPixelChannels(image);
8155 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8159 if (image->storage_class == PseudoClass && image->colormap != NULL)
8161 for (i=0; i < (ssize_t) image->colors; i++)
8163 LBR16PacketRGBO(image->colormap[i]);
8166 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8169 else if (image->depth > 4)
8171 #if MAGICKCORE_QUANTUM_DEPTH > 8
8172 /* Scale to 8-bit */
8173 LBR08PacketRGBO(image->background_color);
8175 for (y=0; y < (ssize_t) image->rows; y++)
8177 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8179 if (r == (Quantum *) NULL)
8182 for (x=0; x < (ssize_t) image->columns; x++)
8185 r+=GetPixelChannels(image);
8188 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8192 if (image->storage_class == PseudoClass && image->colormap != NULL)
8194 for (i=0; i < (ssize_t) image->colors; i++)
8196 LBR08PacketRGBO(image->colormap[i]);
8199 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8202 if (image->depth > 2)
8204 /* Scale to 4-bit */
8205 LBR04PacketRGBO(image->background_color);
8207 for (y=0; y < (ssize_t) image->rows; y++)
8209 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8211 if (r == (Quantum *) NULL)
8214 for (x=0; x < (ssize_t) image->columns; x++)
8217 r+=GetPixelChannels(image);
8220 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8224 if (image->storage_class == PseudoClass && image->colormap != NULL)
8226 for (i=0; i < (ssize_t) image->colors; i++)
8228 LBR04PacketRGBO(image->colormap[i]);
8233 else if (image->depth > 1)
8235 /* Scale to 2-bit */
8236 LBR02PacketRGBO(image->background_color);
8238 for (y=0; y < (ssize_t) image->rows; y++)
8240 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8242 if (r == (Quantum *) NULL)
8245 for (x=0; x < (ssize_t) image->columns; x++)
8248 r+=GetPixelChannels(image);
8251 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8255 if (image->storage_class == PseudoClass && image->colormap != NULL)
8257 for (i=0; i < (ssize_t) image->colors; i++)
8259 LBR02PacketRGBO(image->colormap[i]);
8265 /* Scale to 1-bit */
8266 LBR01PacketRGBO(image->background_color);
8268 for (y=0; y < (ssize_t) image->rows; y++)
8270 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8272 if (r == (Quantum *) NULL)
8275 for (x=0; x < (ssize_t) image->columns; x++)
8278 r+=GetPixelChannels(image);
8281 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8285 if (image->storage_class == PseudoClass && image->colormap != NULL)
8287 for (i=0; i < (ssize_t) image->colors; i++)
8289 LBR01PacketRGBO(image->colormap[i]);
8295 /* To do: set to next higher multiple of 8 */
8296 if (image->depth < 8)
8299 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8300 /* PNG does not handle depths greater than 16 so reduce it even
8303 if (image->depth > 8)
8307 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8308 if (image->depth > 8)
8310 /* To do: fill low byte properly */
8314 if (image->depth == 16 && mng_info->write_png_depth != 16)
8315 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8319 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8320 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8321 mng_info->write_png_colortype < 4 &&
8322 image->alpha_trait != BlendPixelTrait)))
8324 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8325 * are not going to need the result.
8327 image_colors = (int) image->colors;
8328 number_opaque = (int) image->colors;
8329 if (mng_info->write_png_colortype == 1 ||
8330 mng_info->write_png_colortype == 5)
8331 ping_have_color=MagickFalse;
8333 ping_have_color=MagickTrue;
8334 ping_have_non_bw=MagickFalse;
8336 if (image->alpha_trait == BlendPixelTrait)
8338 number_transparent = 2;
8339 number_semitransparent = 1;
8344 number_transparent = 0;
8345 number_semitransparent = 0;
8353 * Normally we run this just once, but in the case of writing PNG8
8354 * we reduce the transparency to binary and run again, then if there
8355 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8356 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8357 * palette. Then (To do) we take care of a final reduction that is only
8358 * needed if there are still 256 colors present and one of them has both
8359 * transparent and opaque instances.
8362 tried_332 = MagickFalse;
8363 tried_333 = MagickFalse;
8364 tried_444 = MagickFalse;
8369 * Sometimes we get DirectClass images that have 256 colors or fewer.
8370 * This code will build a colormap.
8372 * Also, sometimes we get PseudoClass images with an out-of-date
8373 * colormap. This code will replace the colormap with a new one.
8374 * Sometimes we get PseudoClass images that have more than 256 colors.
8375 * This code will delete the colormap and change the image to
8378 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8379 * even though it sometimes contains left-over non-opaque values.
8381 * Also we gather some information (number of opaque, transparent,
8382 * and semitransparent pixels, and whether the image has any non-gray
8383 * pixels or only black-and-white pixels) that we might need later.
8385 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8386 * we need to check for bogus non-opaque values, at least.
8394 semitransparent[260],
8397 register const Quantum
8404 if (logging != MagickFalse)
8405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8406 " Enter BUILD_PALETTE:");
8408 if (logging != MagickFalse)
8410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8411 " image->columns=%.20g",(double) image->columns);
8412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8413 " image->rows=%.20g",(double) image->rows);
8414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8415 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8417 " image->depth=%.20g",(double) image->depth);
8419 if (image->storage_class == PseudoClass && image->colormap != NULL)
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " Original colormap:");
8423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8424 " i (red,green,blue,alpha)");
8426 for (i=0; i < 256; i++)
8428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8429 " %d (%d,%d,%d,%d)",
8431 (int) image->colormap[i].red,
8432 (int) image->colormap[i].green,
8433 (int) image->colormap[i].blue,
8434 (int) image->colormap[i].alpha);
8437 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " %d (%d,%d,%d,%d)",
8444 (int) image->colormap[i].red,
8445 (int) image->colormap[i].green,
8446 (int) image->colormap[i].blue,
8447 (int) image->colormap[i].alpha);
8452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8453 " image->colors=%d",(int) image->colors);
8455 if (image->colors == 0)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " (zero means unknown)");
8459 if (ping_preserve_colormap == MagickFalse)
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " Regenerate the colormap");
8466 number_semitransparent = 0;
8467 number_transparent = 0;
8469 for (y=0; y < (ssize_t) image->rows; y++)
8471 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8473 if (q == (Quantum *) NULL)
8476 for (x=0; x < (ssize_t) image->columns; x++)
8478 if (image->alpha_trait != BlendPixelTrait ||
8479 GetPixelAlpha(image,q) == OpaqueAlpha)
8481 if (number_opaque < 259)
8483 if (number_opaque == 0)
8485 GetPixelInfoPixel(image, q, opaque);
8486 opaque[0].alpha=OpaqueAlpha;
8490 for (i=0; i< (ssize_t) number_opaque; i++)
8492 if (IsPixelEquivalent(image,q, opaque+i))
8496 if (i == (ssize_t) number_opaque && number_opaque < 259)
8499 GetPixelInfoPixel(image, q, opaque+i);
8500 opaque[i].alpha=OpaqueAlpha;
8504 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8506 if (number_transparent < 259)
8508 if (number_transparent == 0)
8510 GetPixelInfoPixel(image, q, transparent);
8511 ping_trans_color.red=(unsigned short)
8512 GetPixelRed(image,q);
8513 ping_trans_color.green=(unsigned short)
8514 GetPixelGreen(image,q);
8515 ping_trans_color.blue=(unsigned short)
8516 GetPixelBlue(image,q);
8517 ping_trans_color.gray=(unsigned short)
8518 GetPixelGray(image,q);
8519 number_transparent = 1;
8522 for (i=0; i< (ssize_t) number_transparent; i++)
8524 if (IsPixelEquivalent(image,q, transparent+i))
8528 if (i == (ssize_t) number_transparent &&
8529 number_transparent < 259)
8531 number_transparent++;
8532 GetPixelInfoPixel(image,q,transparent+i);
8538 if (number_semitransparent < 259)
8540 if (number_semitransparent == 0)
8542 GetPixelInfoPixel(image,q,semitransparent);
8543 number_semitransparent = 1;
8546 for (i=0; i< (ssize_t) number_semitransparent; i++)
8548 if (IsPixelEquivalent(image,q, semitransparent+i)
8549 && GetPixelAlpha(image,q) ==
8550 semitransparent[i].alpha)
8554 if (i == (ssize_t) number_semitransparent &&
8555 number_semitransparent < 259)
8557 number_semitransparent++;
8558 GetPixelInfoPixel(image, q, semitransparent+i);
8562 q+=GetPixelChannels(image);
8566 if (mng_info->write_png8 == MagickFalse &&
8567 ping_exclude_bKGD == MagickFalse)
8569 /* Add the background color to the palette, if it
8570 * isn't already there.
8572 if (logging != MagickFalse)
8574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8575 " Check colormap for background (%d,%d,%d)",
8576 (int) image->background_color.red,
8577 (int) image->background_color.green,
8578 (int) image->background_color.blue);
8580 for (i=0; i<number_opaque; i++)
8582 if (opaque[i].red == image->background_color.red &&
8583 opaque[i].green == image->background_color.green &&
8584 opaque[i].blue == image->background_color.blue)
8587 if (number_opaque < 259 && i == number_opaque)
8589 opaque[i] = image->background_color;
8590 ping_background.index = i;
8592 if (logging != MagickFalse)
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595 " background_color index is %d",(int) i);
8599 else if (logging != MagickFalse)
8600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " No room in the colormap to add background color");
8604 image_colors=number_opaque+number_transparent+number_semitransparent;
8606 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8608 /* No room for the background color; remove it. */
8613 if (logging != MagickFalse)
8615 if (image_colors > 256)
8616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8617 " image has more than 256 colors");
8620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621 " image has %d colors",image_colors);
8624 if (ping_preserve_colormap != MagickFalse)
8627 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8629 ping_have_color=MagickFalse;
8630 ping_have_non_bw=MagickFalse;
8632 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8633 (IssRGBColorspace(image->colorspace) != MagickFalse))
8635 ping_have_color=MagickTrue;
8636 ping_have_non_bw=MagickTrue;
8639 if(image_colors > 256)
8641 for (y=0; y < (ssize_t) image->rows; y++)
8643 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8645 if (q == (Quantum *) NULL)
8649 for (x=0; x < (ssize_t) image->columns; x++)
8651 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8652 GetPixelRed(image,s) != GetPixelBlue(image,s))
8654 ping_have_color=MagickTrue;
8655 ping_have_non_bw=MagickTrue;
8658 s+=GetPixelChannels(image);
8661 if (ping_have_color != MagickFalse)
8664 /* Worst case is black-and-white; we are looking at every
8668 if (ping_have_non_bw == MagickFalse)
8671 for (x=0; x < (ssize_t) image->columns; x++)
8673 if (GetPixelRed(image,s) != 0 &&
8674 GetPixelRed(image,s) != QuantumRange)
8676 ping_have_non_bw=MagickTrue;
8679 s+=GetPixelChannels(image);
8686 if (image_colors < 257)
8692 * Initialize image colormap.
8695 if (logging != MagickFalse)
8696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8697 " Sort the new colormap");
8699 /* Sort palette, transparent first */;
8703 for (i=0; i<number_transparent; i++)
8704 colormap[n++] = transparent[i];
8706 for (i=0; i<number_semitransparent; i++)
8707 colormap[n++] = semitransparent[i];
8709 for (i=0; i<number_opaque; i++)
8710 colormap[n++] = opaque[i];
8712 ping_background.index +=
8713 (number_transparent + number_semitransparent);
8715 /* image_colors < 257; search the colormap instead of the pixels
8716 * to get ping_have_color and ping_have_non_bw
8720 if (ping_have_color == MagickFalse)
8722 if (colormap[i].red != colormap[i].green ||
8723 colormap[i].red != colormap[i].blue)
8725 ping_have_color=MagickTrue;
8726 ping_have_non_bw=MagickTrue;
8731 if (ping_have_non_bw == MagickFalse)
8733 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8734 ping_have_non_bw=MagickTrue;
8738 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8739 (number_transparent == 0 && number_semitransparent == 0)) &&
8740 (((mng_info->write_png_colortype-1) ==
8741 PNG_COLOR_TYPE_PALETTE) ||
8742 (mng_info->write_png_colortype == 0)))
8744 if (logging != MagickFalse)
8746 if (n != (ssize_t) image_colors)
8747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8748 " image_colors (%d) and n (%d) don't match",
8751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8752 " AcquireImageColormap");
8755 image->colors = image_colors;
8757 if (AcquireImageColormap(image,image_colors,exception) ==
8759 ThrowWriterException(ResourceLimitError,
8760 "MemoryAllocationFailed");
8762 for (i=0; i< (ssize_t) image_colors; i++)
8763 image->colormap[i] = colormap[i];
8765 if (logging != MagickFalse)
8767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8768 " image->colors=%d (%d)",
8769 (int) image->colors, image_colors);
8771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8772 " Update the pixel indexes");
8775 /* Sync the pixel indices with the new colormap */
8777 for (y=0; y < (ssize_t) image->rows; y++)
8779 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8781 if (q == (Quantum *) NULL)
8784 for (x=0; x < (ssize_t) image->columns; x++)
8786 for (i=0; i< (ssize_t) image_colors; i++)
8788 if ((image->alpha_trait != BlendPixelTrait ||
8789 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8790 image->colormap[i].red == GetPixelRed(image,q) &&
8791 image->colormap[i].green == GetPixelGreen(image,q) &&
8792 image->colormap[i].blue == GetPixelBlue(image,q))
8794 SetPixelIndex(image,i,q);
8798 q+=GetPixelChannels(image);
8801 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8807 if (logging != MagickFalse)
8809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810 " image->colors=%d", (int) image->colors);
8812 if (image->colormap != NULL)
8814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8815 " i (red,green,blue,alpha)");
8817 for (i=0; i < (ssize_t) image->colors; i++)
8819 if (i < 300 || i >= (ssize_t) image->colors - 10)
8821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8822 " %d (%d,%d,%d,%d)",
8824 (int) image->colormap[i].red,
8825 (int) image->colormap[i].green,
8826 (int) image->colormap[i].blue,
8827 (int) image->colormap[i].alpha);
8832 if (number_transparent < 257)
8833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8834 " number_transparent = %d",
8835 number_transparent);
8838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8839 " number_transparent > 256");
8841 if (number_opaque < 257)
8842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8843 " number_opaque = %d",
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " number_opaque > 256");
8850 if (number_semitransparent < 257)
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " number_semitransparent = %d",
8853 number_semitransparent);
8856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857 " number_semitransparent > 256");
8859 if (ping_have_non_bw == MagickFalse)
8860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8861 " All pixels and the background are black or white");
8863 else if (ping_have_color == MagickFalse)
8864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865 " All pixels and the background are gray");
8868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8869 " At least one pixel or the background is non-gray");
8871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8872 " Exit BUILD_PALETTE:");
8875 if (mng_info->write_png8 == MagickFalse)
8878 /* Make any reductions necessary for the PNG8 format */
8879 if (image_colors <= 256 &&
8880 image_colors != 0 && image->colormap != NULL &&
8881 number_semitransparent == 0 &&
8882 number_transparent <= 1)
8885 /* PNG8 can't have semitransparent colors so we threshold the
8886 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8887 * transparent color so if more than one is transparent we merge
8888 * them into image->background_color.
8890 if (number_semitransparent != 0 || number_transparent > 1)
8892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8893 " Thresholding the alpha channel to binary");
8895 for (y=0; y < (ssize_t) image->rows; y++)
8897 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8899 if (r == (Quantum *) NULL)
8902 for (x=0; x < (ssize_t) image->columns; x++)
8904 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8906 SetPixelInfoPixel(image,&image->background_color,r);
8907 SetPixelAlpha(image,TransparentAlpha,r);
8910 SetPixelAlpha(image,OpaqueAlpha,r);
8911 r+=GetPixelChannels(image);
8914 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8917 if (image_colors != 0 && image_colors <= 256 &&
8918 image->colormap != NULL)
8919 for (i=0; i<image_colors; i++)
8920 image->colormap[i].alpha =
8921 (image->colormap[i].alpha > TransparentAlpha/2 ?
8922 TransparentAlpha : OpaqueAlpha);
8927 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8928 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8929 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8932 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8934 if (logging != MagickFalse)
8935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8936 " Quantizing the background color to 4-4-4");
8938 tried_444 = MagickTrue;
8940 LBR04PacketRGB(image->background_color);
8942 if (logging != MagickFalse)
8943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944 " Quantizing the pixel colors to 4-4-4");
8946 if (image->colormap == NULL)
8948 for (y=0; y < (ssize_t) image->rows; y++)
8950 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8952 if (r == (Quantum *) NULL)
8955 for (x=0; x < (ssize_t) image->columns; x++)
8957 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8959 r+=GetPixelChannels(image);
8962 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8967 else /* Should not reach this; colormap already exists and
8970 if (logging != MagickFalse)
8971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8972 " Quantizing the colormap to 4-4-4");
8974 for (i=0; i<image_colors; i++)
8976 LBR04PacketRGB(image->colormap[i]);
8982 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8984 if (logging != MagickFalse)
8985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8986 " Quantizing the background color to 3-3-3");
8988 tried_333 = MagickTrue;
8990 LBR03PacketRGB(image->background_color);
8992 if (logging != MagickFalse)
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994 " Quantizing the pixel colors to 3-3-3-1");
8996 if (image->colormap == NULL)
8998 for (y=0; y < (ssize_t) image->rows; y++)
9000 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9002 if (r == (Quantum *) NULL)
9005 for (x=0; x < (ssize_t) image->columns; x++)
9007 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9009 r+=GetPixelChannels(image);
9012 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9017 else /* Should not reach this; colormap already exists and
9020 if (logging != MagickFalse)
9021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9022 " Quantizing the colormap to 3-3-3-1");
9023 for (i=0; i<image_colors; i++)
9025 LBR03PacketRGB(image->colormap[i]);
9031 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9033 if (logging != MagickFalse)
9034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035 " Quantizing the background color to 3-3-2");
9037 tried_332 = MagickTrue;
9039 /* Red and green were already done so we only quantize the blue
9043 LBR02PacketBlue(image->background_color);
9045 if (logging != MagickFalse)
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " Quantizing the pixel colors to 3-3-2-1");
9049 if (image->colormap == NULL)
9051 for (y=0; y < (ssize_t) image->rows; y++)
9053 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9055 if (r == (Quantum *) NULL)
9058 for (x=0; x < (ssize_t) image->columns; x++)
9060 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9062 r+=GetPixelChannels(image);
9065 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9070 else /* Should not reach this; colormap already exists and
9073 if (logging != MagickFalse)
9074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9075 " Quantizing the colormap to 3-3-2-1");
9076 for (i=0; i<image_colors; i++)
9078 LBR02PacketBlue(image->colormap[i]);
9085 if (image_colors == 0 || image_colors > 256)
9087 /* Take care of special case with 256 colors + 1 transparent
9088 * color. We don't need to quantize to 2-3-2-1; we only need to
9089 * eliminate one color, so we'll merge the two darkest red
9090 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9092 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9093 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9094 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9096 image->background_color.red=ScaleCharToQuantum(0x24);
9099 if (image->colormap == NULL)
9101 for (y=0; y < (ssize_t) image->rows; y++)
9103 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9105 if (r == (Quantum *) NULL)
9108 for (x=0; x < (ssize_t) image->columns; x++)
9110 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9111 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9112 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9113 GetPixelAlpha(image,r) == OpaqueAlpha)
9115 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9117 r+=GetPixelChannels(image);
9120 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9128 for (i=0; i<image_colors; i++)
9130 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9131 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9132 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9134 image->colormap[i].red=ScaleCharToQuantum(0x24);
9141 /* END OF BUILD_PALETTE */
9143 /* If we are excluding the tRNS chunk and there is transparency,
9144 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9147 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9148 (number_transparent != 0 || number_semitransparent != 0))
9150 unsigned int colortype=mng_info->write_png_colortype;
9152 if (ping_have_color == MagickFalse)
9153 mng_info->write_png_colortype = 5;
9156 mng_info->write_png_colortype = 7;
9158 if (colortype != 0 &&
9159 mng_info->write_png_colortype != colortype)
9160 ping_need_colortype_warning=MagickTrue;
9164 /* See if cheap transparency is possible. It is only possible
9165 * when there is a single transparent color, no semitransparent
9166 * color, and no opaque color that has the same RGB components
9167 * as the transparent color. We only need this information if
9168 * we are writing a PNG with colortype 0 or 2, and we have not
9169 * excluded the tRNS chunk.
9171 if (number_transparent == 1 &&
9172 mng_info->write_png_colortype < 4)
9174 ping_have_cheap_transparency = MagickTrue;
9176 if (number_semitransparent != 0)
9177 ping_have_cheap_transparency = MagickFalse;
9179 else if (image_colors == 0 || image_colors > 256 ||
9180 image->colormap == NULL)
9182 register const Quantum
9185 for (y=0; y < (ssize_t) image->rows; y++)
9187 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9189 if (q == (Quantum *) NULL)
9192 for (x=0; x < (ssize_t) image->columns; x++)
9194 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9195 (unsigned short) GetPixelRed(image,q) ==
9196 ping_trans_color.red &&
9197 (unsigned short) GetPixelGreen(image,q) ==
9198 ping_trans_color.green &&
9199 (unsigned short) GetPixelBlue(image,q) ==
9200 ping_trans_color.blue)
9202 ping_have_cheap_transparency = MagickFalse;
9206 q+=GetPixelChannels(image);
9209 if (ping_have_cheap_transparency == MagickFalse)
9215 /* Assuming that image->colormap[0] is the one transparent color
9216 * and that all others are opaque.
9218 if (image_colors > 1)
9219 for (i=1; i<image_colors; i++)
9220 if (image->colormap[i].red == image->colormap[0].red &&
9221 image->colormap[i].green == image->colormap[0].green &&
9222 image->colormap[i].blue == image->colormap[0].blue)
9224 ping_have_cheap_transparency = MagickFalse;
9229 if (logging != MagickFalse)
9231 if (ping_have_cheap_transparency == MagickFalse)
9232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9233 " Cheap transparency is not possible.");
9236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9237 " Cheap transparency is possible.");
9241 ping_have_cheap_transparency = MagickFalse;
9243 image_depth=image->depth;
9245 quantum_info = (QuantumInfo *) NULL;
9247 image_colors=(int) image->colors;
9248 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9250 mng_info->IsPalette=image->storage_class == PseudoClass &&
9251 image_colors <= 256 && image->colormap != NULL;
9253 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9254 (image->colors == 0 || image->colormap == NULL))
9256 image_info=DestroyImageInfo(image_info);
9257 image=DestroyImage(image);
9258 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9259 "Cannot write PNG8 or color-type 3; colormap is NULL",
9260 "`%s'",IMimage->filename);
9261 return(MagickFalse);
9265 Allocate the PNG structures
9267 #ifdef PNG_USER_MEM_SUPPORTED
9268 error_info.image=image;
9269 error_info.exception=exception;
9270 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9271 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9272 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9275 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9276 MagickPNGErrorHandler,MagickPNGWarningHandler);
9279 if (ping == (png_struct *) NULL)
9280 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9282 ping_info=png_create_info_struct(ping);
9284 if (ping_info == (png_info *) NULL)
9286 png_destroy_write_struct(&ping,(png_info **) NULL);
9287 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9290 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9291 ping_pixels=(unsigned char *) NULL;
9293 if (setjmp(png_jmpbuf(ping)))
9299 if (image_info->verbose)
9300 (void) printf("PNG write has failed.\n");
9302 png_destroy_write_struct(&ping,&ping_info);
9303 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9304 UnlockSemaphoreInfo(ping_semaphore);
9307 if (ping_pixels != (unsigned char *) NULL)
9308 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9310 if (quantum_info != (QuantumInfo *) NULL)
9311 quantum_info=DestroyQuantumInfo(quantum_info);
9313 if (ping_have_blob != MagickFalse)
9314 (void) CloseBlob(image);
9315 image_info=DestroyImageInfo(image_info);
9316 image=DestroyImage(image);
9317 return(MagickFalse);
9320 /* { For navigation to end of SETJMP-protected block. Within this
9321 * block, use png_error() instead of Throwing an Exception, to ensure
9322 * that libpng is able to clean up, and that the semaphore is unlocked.
9325 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9326 LockSemaphoreInfo(ping_semaphore);
9329 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9330 /* Allow benign errors */
9331 png_set_benign_errors(ping, 1);
9335 Prepare PNG for writing.
9338 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9339 if (mng_info->write_mng)
9341 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9342 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9343 /* Disable new libpng-1.5.10 feature when writing a MNG because
9344 * zero-length PLTE is OK
9346 png_set_check_for_invalid_index (ping, 0);
9351 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9352 if (mng_info->write_mng)
9353 png_permit_empty_plte(ping,MagickTrue);
9360 ping_width=(png_uint_32) image->columns;
9361 ping_height=(png_uint_32) image->rows;
9363 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9366 if (mng_info->write_png48 || mng_info->write_png64)
9369 if (mng_info->write_png_depth != 0)
9370 image_depth=mng_info->write_png_depth;
9372 /* Adjust requested depth to next higher valid depth if necessary */
9373 if (image_depth > 8)
9376 if ((image_depth > 4) && (image_depth < 8))
9379 if (image_depth == 3)
9382 if (logging != MagickFalse)
9384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9385 " width=%.20g",(double) ping_width);
9386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9387 " height=%.20g",(double) ping_height);
9388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9389 " image_matte=%.20g",(double) image->alpha_trait);
9390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9391 " image->depth=%.20g",(double) image->depth);
9392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9393 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9396 save_image_depth=image_depth;
9397 ping_bit_depth=(png_byte) save_image_depth;
9400 #if defined(PNG_pHYs_SUPPORTED)
9401 if (ping_exclude_pHYs == MagickFalse)
9403 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9404 (!mng_info->write_mng || !mng_info->equal_physs))
9406 if (logging != MagickFalse)
9407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9408 " Setting up pHYs chunk");
9410 if (image->units == PixelsPerInchResolution)
9412 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9413 ping_pHYs_x_resolution=
9414 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9415 ping_pHYs_y_resolution=
9416 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9419 else if (image->units == PixelsPerCentimeterResolution)
9421 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9422 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9423 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9428 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9429 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9430 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9433 if (logging != MagickFalse)
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9436 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9437 (int) ping_pHYs_unit_type);
9438 ping_have_pHYs = MagickTrue;
9443 if (ping_exclude_bKGD == MagickFalse)
9445 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9451 if (ping_bit_depth == 8)
9454 if (ping_bit_depth == 4)
9457 if (ping_bit_depth == 2)
9460 if (ping_bit_depth == 1)
9463 ping_background.red=(png_uint_16)
9464 (ScaleQuantumToShort(image->background_color.red) & mask);
9466 ping_background.green=(png_uint_16)
9467 (ScaleQuantumToShort(image->background_color.green) & mask);
9469 ping_background.blue=(png_uint_16)
9470 (ScaleQuantumToShort(image->background_color.blue) & mask);
9472 ping_background.gray=(png_uint_16) ping_background.green;
9475 if (logging != MagickFalse)
9477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9478 " Setting up bKGD chunk (1)");
9479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9480 " background_color index is %d",
9481 (int) ping_background.index);
9483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9484 " ping_bit_depth=%d",ping_bit_depth);
9487 ping_have_bKGD = MagickTrue;
9491 Select the color type.
9496 if (mng_info->IsPalette && mng_info->write_png8)
9499 /* To do: make this a function cause it's used twice, except
9500 for reducing the sample depth from 8. */
9502 number_colors=image_colors;
9504 ping_have_tRNS=MagickFalse;
9509 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9511 if (logging != MagickFalse)
9512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9513 " Setting up PLTE chunk with %d colors (%d)",
9514 number_colors, image_colors);
9516 for (i=0; i < (ssize_t) number_colors; i++)
9518 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9519 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9520 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9521 if (logging != MagickFalse)
9522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9523 #if MAGICKCORE_QUANTUM_DEPTH == 8
9524 " %3ld (%3d,%3d,%3d)",
9526 " %5ld (%5d,%5d,%5d)",
9528 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9532 ping_have_PLTE=MagickTrue;
9533 image_depth=ping_bit_depth;
9536 if (matte != MagickFalse)
9539 Identify which colormap entry is transparent.
9541 assert(number_colors <= 256);
9542 assert(image->colormap != NULL);
9544 for (i=0; i < (ssize_t) number_transparent; i++)
9545 ping_trans_alpha[i]=0;
9548 ping_num_trans=(unsigned short) (number_transparent +
9549 number_semitransparent);
9551 if (ping_num_trans == 0)
9552 ping_have_tRNS=MagickFalse;
9555 ping_have_tRNS=MagickTrue;
9558 if (ping_exclude_bKGD == MagickFalse)
9561 * Identify which colormap entry is the background color.
9564 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9565 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9568 ping_background.index=(png_byte) i;
9570 if (logging != MagickFalse)
9572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9573 " background_color index is %d",
9574 (int) ping_background.index);
9577 } /* end of write_png8 */
9579 else if (mng_info->write_png_colortype == 1)
9581 image_matte=MagickFalse;
9582 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9585 else if (mng_info->write_png24 || mng_info->write_png48 ||
9586 mng_info->write_png_colortype == 3)
9588 image_matte=MagickFalse;
9589 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9592 else if (mng_info->write_png32 || mng_info->write_png64 ||
9593 mng_info->write_png_colortype == 7)
9595 image_matte=MagickTrue;
9596 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9599 else /* mng_info->write_pngNN not specified */
9601 image_depth=ping_bit_depth;
9603 if (mng_info->write_png_colortype != 0)
9605 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9607 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9608 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9609 image_matte=MagickTrue;
9612 image_matte=MagickFalse;
9614 if (logging != MagickFalse)
9615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9616 " PNG colortype %d was specified:",(int) ping_color_type);
9619 else /* write_png_colortype not specified */
9621 if (logging != MagickFalse)
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Selecting PNG colortype:");
9625 ping_color_type=(png_byte) ((matte != MagickFalse)?
9626 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9628 if (image_info->type == TrueColorType)
9630 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9631 image_matte=MagickFalse;
9634 if (image_info->type == TrueColorMatteType)
9636 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9637 image_matte=MagickTrue;
9640 if (image_info->type == PaletteType ||
9641 image_info->type == PaletteMatteType)
9642 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9644 if (mng_info->write_png_colortype == 0 &&
9645 (image_info->type == UndefinedType ||
9646 image_info->type == OptimizeType))
9648 if (ping_have_color == MagickFalse)
9650 if (image_matte == MagickFalse)
9652 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9653 image_matte=MagickFalse;
9658 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9659 image_matte=MagickTrue;
9664 if (image_matte == MagickFalse)
9666 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9667 image_matte=MagickFalse;
9672 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9673 image_matte=MagickTrue;
9680 if (logging != MagickFalse)
9681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9682 " Selected PNG colortype=%d",ping_color_type);
9684 if (ping_bit_depth < 8)
9686 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9687 ping_color_type == PNG_COLOR_TYPE_RGB ||
9688 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9692 old_bit_depth=ping_bit_depth;
9694 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9696 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9700 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9705 if (image->colors == 0)
9708 png_error(ping,"image has 0 colors");
9711 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9712 ping_bit_depth <<= 1;
9715 if (logging != MagickFalse)
9717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9718 " Number of colors: %.20g",(double) image_colors);
9720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721 " Tentative PNG bit depth: %d",ping_bit_depth);
9724 if (ping_bit_depth < (int) mng_info->write_png_depth)
9725 ping_bit_depth = mng_info->write_png_depth;
9728 image_depth=ping_bit_depth;
9730 if (logging != MagickFalse)
9732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9733 " Tentative PNG color type: %s (%.20g)",
9734 PngColorTypeToString(ping_color_type),
9735 (double) ping_color_type);
9737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " image_info->type: %.20g",(double) image_info->type);
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " image_depth: %.20g",(double) image_depth);
9743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " image->depth: %.20g",(double) image->depth);
9747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9748 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9751 if (matte != MagickFalse)
9753 if (mng_info->IsPalette)
9755 if (mng_info->write_png_colortype == 0)
9757 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9759 if (ping_have_color != MagickFalse)
9760 ping_color_type=PNG_COLOR_TYPE_RGBA;
9764 * Determine if there is any transparent color.
9766 if (number_transparent + number_semitransparent == 0)
9769 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9772 image_matte=MagickFalse;
9774 if (mng_info->write_png_colortype == 0)
9775 ping_color_type&=0x03;
9785 if (ping_bit_depth == 8)
9788 if (ping_bit_depth == 4)
9791 if (ping_bit_depth == 2)
9794 if (ping_bit_depth == 1)
9797 ping_trans_color.red=(png_uint_16)
9798 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9800 ping_trans_color.green=(png_uint_16)
9801 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9803 ping_trans_color.blue=(png_uint_16)
9804 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9806 ping_trans_color.gray=(png_uint_16)
9807 (ScaleQuantumToShort(GetPixelInfoIntensity(
9808 image->colormap)) & mask);
9810 ping_trans_color.index=(png_byte) 0;
9812 ping_have_tRNS=MagickTrue;
9815 if (ping_have_tRNS != MagickFalse)
9818 * Determine if there is one and only one transparent color
9819 * and if so if it is fully transparent.
9821 if (ping_have_cheap_transparency == MagickFalse)
9822 ping_have_tRNS=MagickFalse;
9825 if (ping_have_tRNS != MagickFalse)
9827 if (mng_info->write_png_colortype == 0)
9828 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9830 if (image_depth == 8)
9832 ping_trans_color.red&=0xff;
9833 ping_trans_color.green&=0xff;
9834 ping_trans_color.blue&=0xff;
9835 ping_trans_color.gray&=0xff;
9841 if (image_depth == 8)
9843 ping_trans_color.red&=0xff;
9844 ping_trans_color.green&=0xff;
9845 ping_trans_color.blue&=0xff;
9846 ping_trans_color.gray&=0xff;
9853 if (ping_have_tRNS != MagickFalse)
9854 image_matte=MagickFalse;
9856 if ((mng_info->IsPalette) &&
9857 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9858 ping_have_color == MagickFalse &&
9859 (image_matte == MagickFalse || image_depth >= 8))
9863 if (image_matte != MagickFalse)
9864 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9866 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9868 ping_color_type=PNG_COLOR_TYPE_GRAY;
9870 if (save_image_depth == 16 && image_depth == 8)
9872 if (logging != MagickFalse)
9874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9875 " Scaling ping_trans_color (0)");
9877 ping_trans_color.gray*=0x0101;
9881 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9882 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9884 if ((image_colors == 0) ||
9885 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9886 image_colors=(int) (one << image_depth);
9888 if (image_depth > 8)
9894 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9896 if(!mng_info->write_png_depth)
9900 while ((int) (one << ping_bit_depth)
9901 < (ssize_t) image_colors)
9902 ping_bit_depth <<= 1;
9906 else if (ping_color_type ==
9907 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9908 mng_info->IsPalette)
9910 /* Check if grayscale is reducible */
9913 depth_4_ok=MagickTrue,
9914 depth_2_ok=MagickTrue,
9915 depth_1_ok=MagickTrue;
9917 for (i=0; i < (ssize_t) image_colors; i++)
9922 intensity=ScaleQuantumToChar(image->colormap[i].red);
9924 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9925 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9926 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9927 depth_2_ok=depth_1_ok=MagickFalse;
9928 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9929 depth_1_ok=MagickFalse;
9932 if (depth_1_ok && mng_info->write_png_depth <= 1)
9935 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9938 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9943 image_depth=ping_bit_depth;
9948 if (mng_info->IsPalette)
9950 number_colors=image_colors;
9952 if (image_depth <= 8)
9957 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9959 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
9961 for (i=0; i < (ssize_t) number_colors; i++)
9963 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9964 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9965 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9968 if (logging != MagickFalse)
9969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9970 " Setting up PLTE chunk with %d colors",
9973 ping_have_PLTE=MagickTrue;
9976 /* color_type is PNG_COLOR_TYPE_PALETTE */
9977 if (mng_info->write_png_depth == 0)
9985 while ((one << ping_bit_depth) < (size_t) number_colors)
9986 ping_bit_depth <<= 1;
9991 if (matte != MagickFalse)
9994 * Set up trans_colors array.
9996 assert(number_colors <= 256);
9998 ping_num_trans=(unsigned short) (number_transparent +
9999 number_semitransparent);
10001 if (ping_num_trans == 0)
10002 ping_have_tRNS=MagickFalse;
10006 if (logging != MagickFalse)
10008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10009 " Scaling ping_trans_color (1)");
10011 ping_have_tRNS=MagickTrue;
10013 for (i=0; i < ping_num_trans; i++)
10015 ping_trans_alpha[i]= (png_byte)
10016 ScaleQuantumToChar(image->colormap[i].alpha);
10026 if (image_depth < 8)
10029 if ((save_image_depth == 16) && (image_depth == 8))
10031 if (logging != MagickFalse)
10033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10034 " Scaling ping_trans_color from (%d,%d,%d)",
10035 (int) ping_trans_color.red,
10036 (int) ping_trans_color.green,
10037 (int) ping_trans_color.blue);
10040 ping_trans_color.red*=0x0101;
10041 ping_trans_color.green*=0x0101;
10042 ping_trans_color.blue*=0x0101;
10043 ping_trans_color.gray*=0x0101;
10045 if (logging != MagickFalse)
10047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10049 (int) ping_trans_color.red,
10050 (int) ping_trans_color.green,
10051 (int) ping_trans_color.blue);
10056 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10057 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10060 Adjust background and transparency samples in sub-8-bit grayscale files.
10062 if (ping_bit_depth < 8 && ping_color_type ==
10063 PNG_COLOR_TYPE_GRAY)
10071 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10073 if (ping_exclude_bKGD == MagickFalse)
10076 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10077 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10078 &image->background_color))) +.5)));
10080 if (logging != MagickFalse)
10081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10082 " Setting up bKGD chunk (2)");
10083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10084 " background_color index is %d",
10085 (int) ping_background.index);
10087 ping_have_bKGD = MagickTrue;
10090 if (logging != MagickFalse)
10091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10092 " Scaling ping_trans_color.gray from %d",
10093 (int)ping_trans_color.gray);
10095 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10096 ping_trans_color.gray)+.5);
10098 if (logging != MagickFalse)
10099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10100 " to %d", (int)ping_trans_color.gray);
10103 if (ping_exclude_bKGD == MagickFalse)
10105 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10108 Identify which colormap entry is the background color.
10111 number_colors=image_colors;
10113 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10114 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10117 ping_background.index=(png_byte) i;
10119 if (logging != MagickFalse)
10121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10122 " Setting up bKGD chunk with index=%d",(int) i);
10125 if (i < (ssize_t) number_colors)
10127 ping_have_bKGD = MagickTrue;
10129 if (logging != MagickFalse)
10131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132 " background =(%d,%d,%d)",
10133 (int) ping_background.red,
10134 (int) ping_background.green,
10135 (int) ping_background.blue);
10139 else /* Can't happen */
10141 if (logging != MagickFalse)
10142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10143 " No room in PLTE to add bKGD color");
10144 ping_have_bKGD = MagickFalse;
10149 if (logging != MagickFalse)
10150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10151 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10154 Initialize compression level and filtering.
10156 if (logging != MagickFalse)
10158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10159 " Setting up deflate compression");
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " Compression buffer size: 32768");
10165 png_set_compression_buffer_size(ping,32768L);
10167 if (logging != MagickFalse)
10168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10169 " Compression mem level: 9");
10171 png_set_compression_mem_level(ping, 9);
10173 /* Untangle the "-quality" setting:
10175 Undefined is 0; the default is used.
10180 0: Use Z_HUFFMAN_ONLY strategy with the
10181 zlib default compression level
10183 1-9: the zlib compression level
10187 0-4: the PNG filter method
10189 5: libpng adaptive filtering if compression level > 5
10190 libpng filter type "none" if compression level <= 5
10191 or if image is grayscale or palette
10193 6: libpng adaptive filtering
10195 7: "LOCO" filtering (intrapixel differing) if writing
10196 a MNG, othewise "none". Did not work in IM-6.7.0-9
10197 and earlier because of a missing "else".
10199 8: Z_RLE strategy, all filters
10200 Unused prior to IM-6.7.0-10, was same as 6
10202 9: Z_RLE strategy, no PNG filters
10203 Unused prior to IM-6.7.0-10, was same as 6
10205 Note that using the -quality option, not all combinations of
10206 PNG filter type, zlib compression level, and zlib compression
10207 strategy are possible. This will be addressed soon in a
10208 release that accomodates "-define png:compression-strategy", etc.
10212 quality=image->quality == UndefinedCompressionQuality ? 75UL :
10217 if (mng_info->write_png_compression_strategy == 0)
10218 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10221 else if (mng_info->write_png_compression_level == 0)
10226 level=(int) MagickMin((ssize_t) quality/10,9);
10228 mng_info->write_png_compression_level = level+1;
10231 if (mng_info->write_png_compression_strategy == 0)
10233 if ((quality %10) == 8 || (quality %10) == 9)
10234 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10235 mng_info->write_png_compression_strategy=Z_RLE+1;
10237 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10241 if (mng_info->write_png_compression_filter == 0)
10242 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10244 if (logging != MagickFalse)
10246 if (mng_info->write_png_compression_level)
10247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10248 " Compression level: %d",
10249 (int) mng_info->write_png_compression_level-1);
10251 if (mng_info->write_png_compression_strategy)
10252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10253 " Compression strategy: %d",
10254 (int) mng_info->write_png_compression_strategy-1);
10256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10257 " Setting up filtering");
10259 if (mng_info->write_png_compression_filter == 6)
10260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10261 " Base filter method: ADAPTIVE");
10262 else if (mng_info->write_png_compression_filter == 0 ||
10263 mng_info->write_png_compression_filter == 1)
10264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10265 " Base filter method: NONE");
10267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10268 " Base filter method: %d",
10269 (int) mng_info->write_png_compression_filter-1);
10272 if (mng_info->write_png_compression_level != 0)
10273 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10275 if (mng_info->write_png_compression_filter == 6)
10277 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10278 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10280 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10282 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10284 else if (mng_info->write_png_compression_filter == 7 ||
10285 mng_info->write_png_compression_filter == 10)
10286 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10288 else if (mng_info->write_png_compression_filter == 8)
10290 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10291 if (mng_info->write_mng)
10293 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10294 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10295 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10298 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10301 else if (mng_info->write_png_compression_filter == 9)
10302 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10304 else if (mng_info->write_png_compression_filter != 0)
10305 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10306 mng_info->write_png_compression_filter-1);
10308 if (mng_info->write_png_compression_strategy != 0)
10309 png_set_compression_strategy(ping,
10310 mng_info->write_png_compression_strategy-1);
10312 ping_interlace_method=image_info->interlace != NoInterlace;
10314 if (mng_info->write_mng)
10315 png_set_sig_bytes(ping,8);
10317 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10319 if (mng_info->write_png_colortype != 0)
10321 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10322 if (ping_have_color != MagickFalse)
10324 ping_color_type = PNG_COLOR_TYPE_RGB;
10326 if (ping_bit_depth < 8)
10330 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10331 if (ping_have_color != MagickFalse)
10332 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10335 if (ping_need_colortype_warning != MagickFalse ||
10336 ((mng_info->write_png_depth &&
10337 (int) mng_info->write_png_depth != ping_bit_depth) ||
10338 (mng_info->write_png_colortype &&
10339 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10340 mng_info->write_png_colortype != 7 &&
10341 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10343 if (logging != MagickFalse)
10345 if (ping_need_colortype_warning != MagickFalse)
10347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " Image has transparency but tRNS chunk was excluded");
10351 if (mng_info->write_png_depth)
10353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10354 " Defined png:bit-depth=%u, Computed depth=%u",
10355 mng_info->write_png_depth,
10359 if (mng_info->write_png_colortype)
10361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10362 " Defined png:color-type=%u, Computed color type=%u",
10363 mng_info->write_png_colortype-1,
10369 "Cannot write image with defined png:bit-depth or png:color-type.");
10372 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10374 /* Add an opaque matte channel */
10375 image->alpha_trait = BlendPixelTrait;
10376 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10378 if (logging != MagickFalse)
10379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10380 " Added an opaque matte channel");
10383 if (number_transparent != 0 || number_semitransparent != 0)
10385 if (ping_color_type < 4)
10387 ping_have_tRNS=MagickTrue;
10388 if (logging != MagickFalse)
10389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10390 " Setting ping_have_tRNS=MagickTrue.");
10394 if (logging != MagickFalse)
10395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396 " Writing PNG header chunks");
10398 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10399 ping_bit_depth,ping_color_type,
10400 ping_interlace_method,ping_compression_method,
10401 ping_filter_method);
10403 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10405 png_set_PLTE(ping,ping_info,palette,number_colors);
10407 if (logging != MagickFalse)
10409 for (i=0; i< (ssize_t) number_colors; i++)
10411 if (i < ping_num_trans)
10412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10413 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10415 (int) palette[i].red,
10416 (int) palette[i].green,
10417 (int) palette[i].blue,
10419 (int) ping_trans_alpha[i]);
10421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10422 " PLTE[%d] = (%d,%d,%d)",
10424 (int) palette[i].red,
10425 (int) palette[i].green,
10426 (int) palette[i].blue);
10431 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10432 if (ping_exclude_sRGB != MagickFalse ||
10433 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10435 if ((ping_exclude_tEXt == MagickFalse ||
10436 ping_exclude_zTXt == MagickFalse) &&
10437 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10439 ResetImageProfileIterator(image);
10440 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10442 profile=GetImageProfile(image,name);
10444 if (profile != (StringInfo *) NULL)
10446 #ifdef PNG_WRITE_iCCP_SUPPORTED
10447 if ((LocaleCompare(name,"ICC") == 0) ||
10448 (LocaleCompare(name,"ICM") == 0))
10451 if (ping_exclude_iCCP == MagickFalse)
10453 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10454 #if (PNG_LIBPNG_VER < 10500)
10455 (png_charp) GetStringInfoDatum(profile),
10457 (png_const_bytep) GetStringInfoDatum(profile),
10459 (png_uint_32) GetStringInfoLength(profile));
10460 ping_have_iCCP = MagickTrue;
10466 if (ping_exclude_zCCP == MagickFalse)
10468 Magick_png_write_raw_profile(image_info,ping,ping_info,
10469 (unsigned char *) name,(unsigned char *) name,
10470 GetStringInfoDatum(profile),
10471 (png_uint_32) GetStringInfoLength(profile));
10475 if (logging != MagickFalse)
10476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10477 " Setting up text chunk with %s profile",name);
10479 name=GetNextImageProfile(image);
10484 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10485 if ((mng_info->have_write_global_srgb == 0) &&
10486 (png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10488 if (ping_exclude_sRGB == MagickFalse)
10491 Note image rendering intent.
10493 if (logging != MagickFalse)
10494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10495 " Setting up sRGB chunk");
10497 (void) png_set_sRGB(ping,ping_info,(
10498 Magick_RenderingIntent_to_PNG_RenderingIntent(
10499 image->rendering_intent)));
10501 ping_have_sRGB = MagickTrue;
10505 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10508 if (ping_exclude_gAMA == MagickFalse &&
10509 ping_have_iCCP == MagickFalse &&
10510 ping_have_sRGB == MagickFalse &&
10511 (ping_exclude_sRGB == MagickFalse ||
10512 (image->gamma < .45 || image->gamma > .46)))
10514 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10518 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10520 if (logging != MagickFalse)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Setting up gAMA chunk");
10524 png_set_gAMA(ping,ping_info,image->gamma);
10528 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10530 if ((mng_info->have_write_global_chrm == 0) &&
10531 (image->chromaticity.red_primary.x != 0.0))
10534 Note image chromaticity.
10535 Note: if cHRM+gAMA == sRGB write sRGB instead.
10543 wp=image->chromaticity.white_point;
10544 rp=image->chromaticity.red_primary;
10545 gp=image->chromaticity.green_primary;
10546 bp=image->chromaticity.blue_primary;
10548 if (logging != MagickFalse)
10549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10550 " Setting up cHRM chunk");
10552 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10558 if (ping_exclude_bKGD == MagickFalse)
10560 if (ping_have_bKGD != MagickFalse)
10562 png_set_bKGD(ping,ping_info,&ping_background);
10565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " Setting up bKGD chunk");
10567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10568 " background color = (%d,%d,%d)",
10569 (int) ping_background.red,
10570 (int) ping_background.green,
10571 (int) ping_background.blue);
10572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10573 " index = %d, gray=%d",
10574 (int) ping_background.index,
10575 (int) ping_background.gray);
10580 if (ping_exclude_pHYs == MagickFalse)
10582 if (ping_have_pHYs != MagickFalse)
10584 png_set_pHYs(ping,ping_info,
10585 ping_pHYs_x_resolution,
10586 ping_pHYs_y_resolution,
10587 ping_pHYs_unit_type);
10591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592 " Setting up pHYs chunk");
10593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594 " x_resolution=%lu",
10595 (unsigned long) ping_pHYs_x_resolution);
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10597 " y_resolution=%lu",
10598 (unsigned long) ping_pHYs_y_resolution);
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 (unsigned long) ping_pHYs_unit_type);
10606 #if defined(PNG_oFFs_SUPPORTED)
10607 if (ping_exclude_oFFs == MagickFalse)
10609 if (image->page.x || image->page.y)
10611 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10612 (png_int_32) image->page.y, 0);
10614 if (logging != MagickFalse)
10615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10616 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10617 (int) image->page.x, (int) image->page.y);
10622 if (mng_info->need_blob != MagickFalse)
10624 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10626 png_error(ping,"WriteBlob Failed");
10628 ping_have_blob=MagickTrue;
10631 png_write_info_before_PLTE(ping, ping_info);
10633 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10635 if (logging != MagickFalse)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10641 if (ping_color_type == 3)
10642 (void) png_set_tRNS(ping, ping_info,
10649 (void) png_set_tRNS(ping, ping_info,
10652 &ping_trans_color);
10654 if (logging != MagickFalse)
10656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10657 " tRNS color =(%d,%d,%d)",
10658 (int) ping_trans_color.red,
10659 (int) ping_trans_color.green,
10660 (int) ping_trans_color.blue);
10665 /* write any png-chunk-b profiles */
10666 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10668 png_write_info(ping,ping_info);
10670 /* write any PNG-chunk-m profiles */
10671 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10673 if (ping_exclude_vpAg == MagickFalse)
10675 if ((image->page.width != 0 && image->page.width != image->columns) ||
10676 (image->page.height != 0 && image->page.height != image->rows))
10681 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10682 PNGType(chunk,mng_vpAg);
10683 LogPNGChunk(logging,mng_vpAg,9L);
10684 PNGLong(chunk+4,(png_uint_32) image->page.width);
10685 PNGLong(chunk+8,(png_uint_32) image->page.height);
10686 chunk[12]=0; /* unit = pixels */
10687 (void) WriteBlob(image,13,chunk);
10688 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10692 #if (PNG_LIBPNG_VER == 10206)
10693 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10694 #define PNG_HAVE_IDAT 0x04
10695 ping->mode |= PNG_HAVE_IDAT;
10696 #undef PNG_HAVE_IDAT
10699 png_set_packing(ping);
10703 rowbytes=image->columns;
10704 if (image_depth > 8)
10706 switch (ping_color_type)
10708 case PNG_COLOR_TYPE_RGB:
10712 case PNG_COLOR_TYPE_GRAY_ALPHA:
10716 case PNG_COLOR_TYPE_RGBA:
10724 if (logging != MagickFalse)
10726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10727 " Writing PNG image data");
10729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10730 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10732 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10733 sizeof(*ping_pixels));
10735 if (ping_pixels == (unsigned char *) NULL)
10736 png_error(ping,"Allocation of memory for pixels failed");
10739 Initialize image scanlines.
10741 quantum_info=AcquireQuantumInfo(image_info,image);
10742 if (quantum_info == (QuantumInfo *) NULL)
10743 png_error(ping,"Memory allocation for quantum_info failed");
10744 quantum_info->format=UndefinedQuantumFormat;
10745 quantum_info->depth=image_depth;
10746 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10747 num_passes=png_set_interlace_handling(ping);
10749 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10750 !mng_info->write_png48 && !mng_info->write_png64 &&
10751 !mng_info->write_png32) &&
10752 (mng_info->IsPalette ||
10753 (image_info->type == BilevelType)) &&
10754 image_matte == MagickFalse &&
10755 ping_have_non_bw == MagickFalse)
10757 /* Palette, Bilevel, or Opaque Monochrome */
10758 register const Quantum
10761 quantum_info->depth=8;
10762 for (pass=0; pass < num_passes; pass++)
10765 Convert PseudoClass image to a PNG monochrome image.
10767 for (y=0; y < (ssize_t) image->rows; y++)
10769 if (logging != MagickFalse && y == 0)
10770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771 " Writing row of pixels (0)");
10773 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10775 if (p == (const Quantum *) NULL)
10778 if (mng_info->IsPalette)
10780 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10781 quantum_info,GrayQuantum,ping_pixels,exception);
10782 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10783 mng_info->write_png_depth &&
10784 mng_info->write_png_depth != old_bit_depth)
10786 /* Undo pixel scaling */
10787 for (i=0; i < (ssize_t) image->columns; i++)
10788 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10789 >> (8-old_bit_depth));
10795 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10796 quantum_info,RedQuantum,ping_pixels,exception);
10799 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10800 for (i=0; i < (ssize_t) image->columns; i++)
10801 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10804 if (logging != MagickFalse && y == 0)
10805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10806 " Writing row of pixels (1)");
10808 png_write_row(ping,ping_pixels);
10810 if (image->previous == (Image *) NULL)
10812 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10813 if (status == MagickFalse)
10819 else /* Not Palette, Bilevel, or Opaque Monochrome */
10821 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10822 !mng_info->write_png48 && !mng_info->write_png64 &&
10823 !mng_info->write_png32) && (image_matte != MagickFalse ||
10824 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10825 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10827 register const Quantum
10830 for (pass=0; pass < num_passes; pass++)
10833 for (y=0; y < (ssize_t) image->rows; y++)
10835 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10837 if (p == (const Quantum *) NULL)
10840 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10842 if (mng_info->IsPalette)
10843 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10844 quantum_info,GrayQuantum,ping_pixels,exception);
10847 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10848 quantum_info,RedQuantum,ping_pixels,exception);
10850 if (logging != MagickFalse && y == 0)
10851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10852 " Writing GRAY PNG pixels (2)");
10855 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10857 if (logging != MagickFalse && y == 0)
10858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10859 " Writing GRAY_ALPHA PNG pixels (2)");
10861 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10862 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10865 if (logging != MagickFalse && y == 0)
10866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867 " Writing row of pixels (2)");
10869 png_write_row(ping,ping_pixels);
10872 if (image->previous == (Image *) NULL)
10874 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10875 if (status == MagickFalse)
10883 register const Quantum
10886 for (pass=0; pass < num_passes; pass++)
10888 if ((image_depth > 8) ||
10889 mng_info->write_png24 ||
10890 mng_info->write_png32 ||
10891 mng_info->write_png48 ||
10892 mng_info->write_png64 ||
10893 (!mng_info->write_png8 && !mng_info->IsPalette))
10895 for (y=0; y < (ssize_t) image->rows; y++)
10897 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10899 if (p == (const Quantum *) NULL)
10902 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10904 if (image->storage_class == DirectClass)
10905 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10906 quantum_info,RedQuantum,ping_pixels,exception);
10909 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10910 quantum_info,GrayQuantum,ping_pixels,exception);
10913 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10915 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10916 quantum_info,GrayAlphaQuantum,ping_pixels,
10919 if (logging != MagickFalse && y == 0)
10920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10921 " Writing GRAY_ALPHA PNG pixels (3)");
10924 else if (image_matte != MagickFalse)
10925 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10926 quantum_info,RGBAQuantum,ping_pixels,exception);
10929 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10930 quantum_info,RGBQuantum,ping_pixels,exception);
10932 if (logging != MagickFalse && y == 0)
10933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10934 " Writing row of pixels (3)");
10936 png_write_row(ping,ping_pixels);
10941 /* not ((image_depth > 8) ||
10942 mng_info->write_png24 || mng_info->write_png32 ||
10943 mng_info->write_png48 || mng_info->write_png64 ||
10944 (!mng_info->write_png8 && !mng_info->IsPalette))
10947 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10948 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10950 if (logging != MagickFalse)
10951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10952 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10954 quantum_info->depth=8;
10958 for (y=0; y < (ssize_t) image->rows; y++)
10960 if (logging != MagickFalse && y == 0)
10961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10962 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10964 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10966 if (p == (const Quantum *) NULL)
10969 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10971 quantum_info->depth=image->depth;
10973 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10974 quantum_info,GrayQuantum,ping_pixels,exception);
10977 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10979 if (logging != MagickFalse && y == 0)
10980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10981 " Writing GRAY_ALPHA PNG pixels (4)");
10983 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10984 quantum_info,GrayAlphaQuantum,ping_pixels,
10990 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10991 quantum_info,IndexQuantum,ping_pixels,exception);
10993 if (logging != MagickFalse && y <= 2)
10995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10996 " Writing row of non-gray pixels (4)");
10998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10999 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11000 (int)ping_pixels[0],(int)ping_pixels[1]);
11003 png_write_row(ping,ping_pixels);
11007 if (image->previous == (Image *) NULL)
11009 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11010 if (status == MagickFalse)
11017 if (quantum_info != (QuantumInfo *) NULL)
11018 quantum_info=DestroyQuantumInfo(quantum_info);
11020 if (logging != MagickFalse)
11022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11023 " Wrote PNG image data");
11025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11026 " Width: %.20g",(double) ping_width);
11028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11029 " Height: %.20g",(double) ping_height);
11031 if (mng_info->write_png_depth)
11033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11038 " PNG bit-depth written: %d",ping_bit_depth);
11040 if (mng_info->write_png_colortype)
11042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11043 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11047 " PNG color-type written: %d",ping_color_type);
11049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11050 " PNG Interlace method: %d",ping_interlace_method);
11053 Generate text chunks after IDAT.
11055 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11057 ResetImagePropertyIterator(image);
11058 property=GetNextImageProperty(image);
11059 while (property != (const char *) NULL)
11064 value=GetImageProperty(image,property,exception);
11066 /* Don't write any "png:" properties; those are just for "identify" */
11067 if (LocaleNCompare(property,"png:",4) != 0 &&
11069 /* Suppress density and units if we wrote a pHYs chunk */
11070 (ping_exclude_pHYs != MagickFalse ||
11071 LocaleCompare(property,"density") != 0 ||
11072 LocaleCompare(property,"units") != 0) &&
11074 /* Suppress the IM-generated Date:create and Date:modify */
11075 (ping_exclude_date == MagickFalse ||
11076 LocaleNCompare(property, "Date:",5) != 0))
11078 if (value != (const char *) NULL)
11081 #if PNG_LIBPNG_VER >= 14000
11082 text=(png_textp) png_malloc(ping,
11083 (png_alloc_size_t) sizeof(png_text));
11085 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11087 text[0].key=(char *) property;
11088 text[0].text=(char *) value;
11089 text[0].text_length=strlen(value);
11091 if (ping_exclude_tEXt != MagickFalse)
11092 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11094 else if (ping_exclude_zTXt != MagickFalse)
11095 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11099 text[0].compression=image_info->compression == NoCompression ||
11100 (image_info->compression == UndefinedCompression &&
11101 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11102 PNG_TEXT_COMPRESSION_zTXt ;
11105 if (logging != MagickFalse)
11107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11108 " Setting up text chunk");
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " keyword: '%s'",text[0].key);
11114 png_set_text(ping,ping_info,text,1);
11115 png_free(ping,text);
11118 property=GetNextImageProperty(image);
11122 /* write any PNG-chunk-e profiles */
11123 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11125 if (logging != MagickFalse)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11127 " Writing PNG end info");
11129 png_write_end(ping,ping_info);
11131 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11133 if (mng_info->page.x || mng_info->page.y ||
11134 (ping_width != mng_info->page.width) ||
11135 (ping_height != mng_info->page.height))
11141 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11143 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11144 PNGType(chunk,mng_FRAM);
11145 LogPNGChunk(logging,mng_FRAM,27L);
11147 chunk[5]=0; /* frame name separator (no name) */
11148 chunk[6]=1; /* flag for changing delay, for next frame only */
11149 chunk[7]=0; /* flag for changing frame timeout */
11150 chunk[8]=1; /* flag for changing frame clipping for next frame */
11151 chunk[9]=0; /* flag for changing frame sync_id */
11152 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11153 chunk[14]=0; /* clipping boundaries delta type */
11154 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11156 (png_uint_32) (mng_info->page.x + ping_width));
11157 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11159 (png_uint_32) (mng_info->page.y + ping_height));
11160 (void) WriteBlob(image,31,chunk);
11161 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11162 mng_info->old_framing_mode=4;
11163 mng_info->framing_mode=1;
11167 mng_info->framing_mode=3;
11169 if (mng_info->write_mng && !mng_info->need_fram &&
11170 ((int) image->dispose == 3))
11171 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11174 Free PNG resources.
11177 png_destroy_write_struct(&ping,&ping_info);
11179 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
11181 if (ping_have_blob != MagickFalse)
11182 (void) CloseBlob(image);
11184 image_info=DestroyImageInfo(image_info);
11185 image=DestroyImage(image);
11187 /* Store bit depth actually written */
11188 s[0]=(char) ping_bit_depth;
11191 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11193 if (logging != MagickFalse)
11194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11195 " exit WriteOnePNGImage()");
11197 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11198 UnlockSemaphoreInfo(ping_semaphore);
11201 /* } for navigation to beginning of SETJMP-protected block. Revert to
11202 * Throwing an Exception when an error occurs.
11205 return(MagickTrue);
11206 /* End write one PNG image */
11211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11215 % W r i t e P N G I m a g e %
11219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11221 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11222 % Multiple-image Network Graphics (MNG) image file.
11224 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11226 % The format of the WritePNGImage method is:
11228 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11229 % Image *image,ExceptionInfo *exception)
11231 % A description of each parameter follows:
11233 % o image_info: the image info.
11235 % o image: The image.
11237 % o exception: return any errors or warnings in this structure.
11239 % Returns MagickTrue on success, MagickFalse on failure.
11241 % Communicating with the PNG encoder:
11243 % While the datastream written is always in PNG format and normally would
11244 % be given the "png" file extension, this method also writes the following
11245 % pseudo-formats which are subsets of png:
11247 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11248 % a depth greater than 8, the depth is reduced. If transparency
11249 % is present, the tRNS chunk must only have values 0 and 255
11250 % (i.e., transparency is binary: fully opaque or fully
11251 % transparent). If other values are present they will be
11252 % 50%-thresholded to binary transparency. If more than 256
11253 % colors are present, they will be quantized to the 4-4-4-1,
11254 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11255 % of any resulting fully-transparent pixels is changed to
11256 % the image's background color.
11258 % If you want better quantization or dithering of the colors
11259 % or alpha than that, you need to do it before calling the
11260 % PNG encoder. The pixels contain 8-bit indices even if
11261 % they could be represented with 1, 2, or 4 bits. Grayscale
11262 % images will be written as indexed PNG files even though the
11263 % PNG grayscale type might be slightly more efficient. Please
11264 % note that writing to the PNG8 format may result in loss
11265 % of color and alpha data.
11267 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11268 % chunk can be present to convey binary transparency by naming
11269 % one of the colors as transparent. The only loss incurred
11270 % is reduction of sample depth to 8. If the image has more
11271 % than one transparent color, has semitransparent pixels, or
11272 % has an opaque pixel with the same RGB components as the
11273 % transparent color, an image is not written.
11275 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11276 % transparency is permitted, i.e., the alpha sample for
11277 % each pixel can have any value from 0 to 255. The alpha
11278 % channel is present even if the image is fully opaque.
11279 % The only loss in data is the reduction of the sample depth
11282 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11283 % chunk can be present to convey binary transparency by naming
11284 % one of the colors as transparent. If the image has more
11285 % than one transparent color, has semitransparent pixels, or
11286 % has an opaque pixel with the same RGB components as the
11287 % transparent color, an image is not written.
11289 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11290 % transparency is permitted, i.e., the alpha sample for
11291 % each pixel can have any value from 0 to 65535. The alpha
11292 % channel is present even if the image is fully opaque.
11294 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11295 % image, if the input was a PNG, is written. If these values
11296 % cannot be found, then "PNG00" falls back to the regular "PNG"
11299 % o -define: For more precise control of the PNG output, you can use the
11300 % Image options "png:bit-depth" and "png:color-type". These
11301 % can be set from the commandline with "-define" and also
11302 % from the application programming interfaces. The options
11303 % are case-independent and are converted to lowercase before
11304 % being passed to this encoder.
11306 % png:color-type can be 0, 2, 3, 4, or 6.
11308 % When png:color-type is 0 (Grayscale), png:bit-depth can
11309 % be 1, 2, 4, 8, or 16.
11311 % When png:color-type is 2 (RGB), png:bit-depth can
11314 % When png:color-type is 3 (Indexed), png:bit-depth can
11315 % be 1, 2, 4, or 8. This refers to the number of bits
11316 % used to store the index. The color samples always have
11317 % bit-depth 8 in indexed PNG files.
11319 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11320 % png:bit-depth can be 8 or 16.
11322 % If the image cannot be written without loss with the
11323 % requested bit-depth and color-type, a PNG file will not
11324 % be written, a warning will be issued, and the encoder will
11325 % return MagickFalse.
11327 % Since image encoders should not be responsible for the "heavy lifting",
11328 % the user should make sure that ImageMagick has already reduced the
11329 % image depth and number of colors and limit transparency to binary
11330 % transparency prior to attempting to write the image with depth, color,
11331 % or transparency limitations.
11333 % Note that another definition, "png:bit-depth-written" exists, but it
11334 % is not intended for external use. It is only used internally by the
11335 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11337 % It is possible to request that the PNG encoder write previously-formatted
11338 % ancillary chunks in the output PNG file, using the "-profile" commandline
11339 % option as shown below or by setting the profile via a programming
11342 % -profile PNG-chunk-x:<file>
11344 % where x is a location flag and <file> is a file containing the chunk
11345 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11346 % This encoder will compute the chunk length and CRC, so those must not
11347 % be included in the file.
11349 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11350 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11351 % of the same type, then add a short unique string after the "x" to prevent
11352 % subsequent profiles from overwriting the preceding ones, e.g.,
11354 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11356 % As of version 6.6.6 the following optimizations are always done:
11358 % o 32-bit depth is reduced to 16.
11359 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11360 % high byte and low byte are identical.
11361 % o Palette is sorted to remove unused entries and to put a
11362 % transparent color first, if BUILD_PNG_PALETTE is defined.
11363 % o Opaque matte channel is removed when writing an indexed PNG.
11364 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11365 % this can be done without loss and a larger bit depth N was not
11366 % requested via the "-define png:bit-depth=N" option.
11367 % o If matte channel is present but only one transparent color is
11368 % present, RGB+tRNS is written instead of RGBA
11369 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11370 % was requested when converting an opaque image).
11372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11374 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11375 Image *image,ExceptionInfo *exception)
11380 have_mng_structure,
11396 assert(image_info != (const ImageInfo *) NULL);
11397 assert(image_info->signature == MagickSignature);
11398 assert(image != (Image *) NULL);
11399 assert(image->signature == MagickSignature);
11400 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11401 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11403 Allocate a MngInfo structure.
11405 have_mng_structure=MagickFalse;
11406 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11408 if (mng_info == (MngInfo *) NULL)
11409 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11412 Initialize members of the MngInfo structure.
11414 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11415 mng_info->image=image;
11416 mng_info->equal_backgrounds=MagickTrue;
11417 have_mng_structure=MagickTrue;
11419 /* See if user has requested a specific PNG subformat */
11421 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11422 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11423 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11424 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11425 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11427 value=GetImageOption(image_info,"png:format");
11429 if (value != (char *) NULL)
11431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11432 " Format=%s",value);
11434 mng_info->write_png8 = MagickFalse;
11435 mng_info->write_png24 = MagickFalse;
11436 mng_info->write_png32 = MagickFalse;
11437 mng_info->write_png48 = MagickFalse;
11438 mng_info->write_png64 = MagickFalse;
11440 if (LocaleCompare(value,"png8") == 0)
11441 mng_info->write_png8 = MagickTrue;
11443 else if (LocaleCompare(value,"png24") == 0)
11444 mng_info->write_png24 = MagickTrue;
11446 else if (LocaleCompare(value,"png32") == 0)
11447 mng_info->write_png32 = MagickTrue;
11449 else if (LocaleCompare(value,"png48") == 0)
11450 mng_info->write_png48 = MagickTrue;
11452 else if (LocaleCompare(value,"png64") == 0)
11453 mng_info->write_png64 = MagickTrue;
11455 else if (LocaleCompare(value,"png00") == 0)
11457 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig
11458 Note that whitespace at the end of the property names must match
11459 that in the corresponding SetImageProperty() calls.
11461 value=GetImageProperty(image,"png:IHDR.bit-depth-orig ",exception);
11463 if (value != (char *) NULL)
11465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466 " png00 inherited bit depth=%s",value);
11468 if (LocaleCompare(value,"1") == 0)
11469 mng_info->write_png_depth = 1;
11471 else if (LocaleCompare(value,"1") == 0)
11472 mng_info->write_png_depth = 2;
11474 else if (LocaleCompare(value,"2") == 0)
11475 mng_info->write_png_depth = 4;
11477 else if (LocaleCompare(value,"8") == 0)
11478 mng_info->write_png_depth = 8;
11480 else if (LocaleCompare(value,"16") == 0)
11481 mng_info->write_png_depth = 16;
11484 value=GetImageProperty(image,"png:IHDR.color-type-orig ",exception);
11486 if (value != (char *) NULL)
11488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11489 " png00 inherited color type=%s",value);
11491 if (LocaleCompare(value,"0") == 0)
11492 mng_info->write_png_colortype = 1;
11494 else if (LocaleCompare(value,"2") == 0)
11495 mng_info->write_png_colortype = 3;
11497 else if (LocaleCompare(value,"3") == 0)
11498 mng_info->write_png_colortype = 4;
11500 else if (LocaleCompare(value,"4") == 0)
11501 mng_info->write_png_colortype = 5;
11503 else if (LocaleCompare(value,"6") == 0)
11504 mng_info->write_png_colortype = 7;
11509 if (mng_info->write_png8)
11511 mng_info->write_png_colortype = /* 3 */ 4;
11512 mng_info->write_png_depth = 8;
11516 if (mng_info->write_png24)
11518 mng_info->write_png_colortype = /* 2 */ 3;
11519 mng_info->write_png_depth = 8;
11522 if (image->alpha_trait == BlendPixelTrait)
11523 (void) SetImageType(image,TrueColorMatteType,exception);
11526 (void) SetImageType(image,TrueColorType,exception);
11528 (void) SyncImage(image,exception);
11531 if (mng_info->write_png32)
11533 mng_info->write_png_colortype = /* 6 */ 7;
11534 mng_info->write_png_depth = 8;
11537 if (image->alpha_trait == BlendPixelTrait)
11538 (void) SetImageType(image,TrueColorMatteType,exception);
11541 (void) SetImageType(image,TrueColorType,exception);
11543 (void) SyncImage(image,exception);
11546 if (mng_info->write_png48)
11548 mng_info->write_png_colortype = /* 2 */ 3;
11549 mng_info->write_png_depth = 16;
11552 if (image->alpha_trait == BlendPixelTrait)
11553 (void) SetImageType(image,TrueColorMatteType,exception);
11556 (void) SetImageType(image,TrueColorType,exception);
11558 (void) SyncImage(image,exception);
11561 if (mng_info->write_png64)
11563 mng_info->write_png_colortype = /* 6 */ 7;
11564 mng_info->write_png_depth = 16;
11567 if (image->alpha_trait == BlendPixelTrait)
11568 (void) SetImageType(image,TrueColorMatteType,exception);
11571 (void) SetImageType(image,TrueColorType,exception);
11573 (void) SyncImage(image,exception);
11576 value=GetImageOption(image_info,"png:bit-depth");
11578 if (value != (char *) NULL)
11580 if (LocaleCompare(value,"1") == 0)
11581 mng_info->write_png_depth = 1;
11583 else if (LocaleCompare(value,"2") == 0)
11584 mng_info->write_png_depth = 2;
11586 else if (LocaleCompare(value,"4") == 0)
11587 mng_info->write_png_depth = 4;
11589 else if (LocaleCompare(value,"8") == 0)
11590 mng_info->write_png_depth = 8;
11592 else if (LocaleCompare(value,"16") == 0)
11593 mng_info->write_png_depth = 16;
11596 (void) ThrowMagickException(exception,
11597 GetMagickModule(),CoderWarning,
11598 "ignoring invalid defined png:bit-depth",
11601 if (logging != MagickFalse)
11602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11603 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11606 value=GetImageOption(image_info,"png:color-type");
11608 if (value != (char *) NULL)
11610 /* We must store colortype+1 because 0 is a valid colortype */
11611 if (LocaleCompare(value,"0") == 0)
11612 mng_info->write_png_colortype = 1;
11614 else if (LocaleCompare(value,"1") == 0)
11615 mng_info->write_png_colortype = 2;
11617 else if (LocaleCompare(value,"2") == 0)
11618 mng_info->write_png_colortype = 3;
11620 else if (LocaleCompare(value,"3") == 0)
11621 mng_info->write_png_colortype = 4;
11623 else if (LocaleCompare(value,"4") == 0)
11624 mng_info->write_png_colortype = 5;
11626 else if (LocaleCompare(value,"6") == 0)
11627 mng_info->write_png_colortype = 7;
11630 (void) ThrowMagickException(exception,
11631 GetMagickModule(),CoderWarning,
11632 "ignoring invalid defined png:color-type",
11635 if (logging != MagickFalse)
11636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11637 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11640 /* Check for chunks to be excluded:
11642 * The default is to not exclude any known chunks except for any
11643 * listed in the "unused_chunks" array, above.
11645 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11646 * define (in the image properties or in the image artifacts)
11647 * or via a mng_info member. For convenience, in addition
11648 * to or instead of a comma-separated list of chunks, the
11649 * "exclude-chunk" string can be simply "all" or "none".
11651 * The exclude-chunk define takes priority over the mng_info.
11653 * A "png:include-chunk" define takes priority over both the
11654 * mng_info and the "png:exclude-chunk" define. Like the
11655 * "exclude-chunk" string, it can define "all" or "none" as
11656 * well as a comma-separated list. Chunks that are unknown to
11657 * ImageMagick are always excluded, regardless of their "copy-safe"
11658 * status according to the PNG specification, and even if they
11659 * appear in the "include-chunk" list. Such defines appearing among
11660 * the image options take priority over those found among the image
11663 * Finally, all chunks listed in the "unused_chunks" array are
11664 * automatically excluded, regardless of the other instructions
11667 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11668 * will not be written and the gAMA chunk will only be written if it
11669 * is not between .45 and .46, or approximately (1.0/2.2).
11671 * If you exclude tRNS and the image has transparency, the colortype
11672 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11674 * The -strip option causes StripImage() to set the png:include-chunk
11675 * artifact to "none,trns,gama".
11678 mng_info->ping_exclude_bKGD=MagickFalse;
11679 mng_info->ping_exclude_cHRM=MagickFalse;
11680 mng_info->ping_exclude_date=MagickFalse;
11681 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11682 mng_info->ping_exclude_gAMA=MagickFalse;
11683 mng_info->ping_exclude_iCCP=MagickFalse;
11684 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11685 mng_info->ping_exclude_oFFs=MagickFalse;
11686 mng_info->ping_exclude_pHYs=MagickFalse;
11687 mng_info->ping_exclude_sRGB=MagickFalse;
11688 mng_info->ping_exclude_tEXt=MagickFalse;
11689 mng_info->ping_exclude_tRNS=MagickFalse;
11690 mng_info->ping_exclude_vpAg=MagickFalse;
11691 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11692 mng_info->ping_exclude_zTXt=MagickFalse;
11694 mng_info->ping_preserve_colormap=MagickFalse;
11696 value=GetImageOption(image_info,"png:preserve-colormap");
11698 value=GetImageArtifact(image,"png:preserve-colormap");
11700 mng_info->ping_preserve_colormap=MagickTrue;
11702 /* Thes compression-level, compression-strategy, and compression-filter
11703 * defines take precedence over values from the -quality option.
11705 value=GetImageOption(image_info,"png:compression-level");
11707 value=GetImageArtifact(image,"png:compression-level");
11710 /* We have to add 1 to everything because 0 is a valid input,
11711 * and we want to use 0 (the default) to mean undefined.
11713 if (LocaleCompare(value,"0") == 0)
11714 mng_info->write_png_compression_level = 1;
11716 else if (LocaleCompare(value,"1") == 0)
11717 mng_info->write_png_compression_level = 2;
11719 else if (LocaleCompare(value,"2") == 0)
11720 mng_info->write_png_compression_level = 3;
11722 else if (LocaleCompare(value,"3") == 0)
11723 mng_info->write_png_compression_level = 4;
11725 else if (LocaleCompare(value,"4") == 0)
11726 mng_info->write_png_compression_level = 5;
11728 else if (LocaleCompare(value,"5") == 0)
11729 mng_info->write_png_compression_level = 6;
11731 else if (LocaleCompare(value,"6") == 0)
11732 mng_info->write_png_compression_level = 7;
11734 else if (LocaleCompare(value,"7") == 0)
11735 mng_info->write_png_compression_level = 8;
11737 else if (LocaleCompare(value,"8") == 0)
11738 mng_info->write_png_compression_level = 9;
11740 else if (LocaleCompare(value,"9") == 0)
11741 mng_info->write_png_compression_level = 10;
11744 (void) ThrowMagickException(exception,
11745 GetMagickModule(),CoderWarning,
11746 "ignoring invalid defined png:compression-level",
11750 value=GetImageOption(image_info,"png:compression-strategy");
11752 value=GetImageArtifact(image,"png:compression-strategy");
11756 if (LocaleCompare(value,"0") == 0)
11757 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11759 else if (LocaleCompare(value,"1") == 0)
11760 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11762 else if (LocaleCompare(value,"2") == 0)
11763 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11765 else if (LocaleCompare(value,"3") == 0)
11766 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11767 mng_info->write_png_compression_strategy = Z_RLE+1;
11769 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11772 else if (LocaleCompare(value,"4") == 0)
11773 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11774 mng_info->write_png_compression_strategy = Z_FIXED+1;
11776 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11780 (void) ThrowMagickException(exception,
11781 GetMagickModule(),CoderWarning,
11782 "ignoring invalid defined png:compression-strategy",
11786 value=GetImageOption(image_info,"png:compression-filter");
11788 value=GetImageArtifact(image,"png:compression-filter");
11792 /* To do: combinations of filters allowed by libpng
11793 * masks 0x08 through 0xf8
11795 * Implement this as a comma-separated list of 0,1,2,3,4,5
11796 * where 5 is a special case meaning PNG_ALL_FILTERS.
11799 if (LocaleCompare(value,"0") == 0)
11800 mng_info->write_png_compression_filter = 1;
11802 else if (LocaleCompare(value,"1") == 0)
11803 mng_info->write_png_compression_filter = 2;
11805 else if (LocaleCompare(value,"2") == 0)
11806 mng_info->write_png_compression_filter = 3;
11808 else if (LocaleCompare(value,"3") == 0)
11809 mng_info->write_png_compression_filter = 4;
11811 else if (LocaleCompare(value,"4") == 0)
11812 mng_info->write_png_compression_filter = 5;
11814 else if (LocaleCompare(value,"5") == 0)
11815 mng_info->write_png_compression_filter = 6;
11818 (void) ThrowMagickException(exception,
11819 GetMagickModule(),CoderWarning,
11820 "ignoring invalid defined png:compression-filter",
11824 excluding=MagickFalse;
11826 for (source=0; source<1; source++)
11830 value=GetImageOption(image_info,"png:exclude-chunk");
11833 value=GetImageArtifact(image,"png:exclude-chunks");
11837 value=GetImageOption(image_info,"png:exclude-chunk");
11840 value=GetImageArtifact(image,"png:exclude-chunks");
11849 excluding=MagickTrue;
11851 if (logging != MagickFalse)
11854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11855 " png:exclude-chunk=%s found in image artifacts.\n", value);
11857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11858 " png:exclude-chunk=%s found in image properties.\n", value);
11861 last=strlen(value);
11863 for (i=0; i<(int) last; i+=5)
11866 if (LocaleNCompare(value+i,"all",3) == 0)
11868 mng_info->ping_exclude_bKGD=MagickTrue;
11869 mng_info->ping_exclude_cHRM=MagickTrue;
11870 mng_info->ping_exclude_date=MagickTrue;
11871 mng_info->ping_exclude_EXIF=MagickTrue;
11872 mng_info->ping_exclude_gAMA=MagickTrue;
11873 mng_info->ping_exclude_iCCP=MagickTrue;
11874 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11875 mng_info->ping_exclude_oFFs=MagickTrue;
11876 mng_info->ping_exclude_pHYs=MagickTrue;
11877 mng_info->ping_exclude_sRGB=MagickTrue;
11878 mng_info->ping_exclude_tEXt=MagickTrue;
11879 mng_info->ping_exclude_tRNS=MagickTrue;
11880 mng_info->ping_exclude_vpAg=MagickTrue;
11881 mng_info->ping_exclude_zCCP=MagickTrue;
11882 mng_info->ping_exclude_zTXt=MagickTrue;
11886 if (LocaleNCompare(value+i,"none",4) == 0)
11888 mng_info->ping_exclude_bKGD=MagickFalse;
11889 mng_info->ping_exclude_cHRM=MagickFalse;
11890 mng_info->ping_exclude_date=MagickFalse;
11891 mng_info->ping_exclude_EXIF=MagickFalse;
11892 mng_info->ping_exclude_gAMA=MagickFalse;
11893 mng_info->ping_exclude_iCCP=MagickFalse;
11894 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11895 mng_info->ping_exclude_oFFs=MagickFalse;
11896 mng_info->ping_exclude_pHYs=MagickFalse;
11897 mng_info->ping_exclude_sRGB=MagickFalse;
11898 mng_info->ping_exclude_tEXt=MagickFalse;
11899 mng_info->ping_exclude_tRNS=MagickFalse;
11900 mng_info->ping_exclude_vpAg=MagickFalse;
11901 mng_info->ping_exclude_zCCP=MagickFalse;
11902 mng_info->ping_exclude_zTXt=MagickFalse;
11905 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11906 mng_info->ping_exclude_bKGD=MagickTrue;
11908 if (LocaleNCompare(value+i,"chrm",4) == 0)
11909 mng_info->ping_exclude_cHRM=MagickTrue;
11911 if (LocaleNCompare(value+i,"date",4) == 0)
11912 mng_info->ping_exclude_date=MagickTrue;
11914 if (LocaleNCompare(value+i,"exif",4) == 0)
11915 mng_info->ping_exclude_EXIF=MagickTrue;
11917 if (LocaleNCompare(value+i,"gama",4) == 0)
11918 mng_info->ping_exclude_gAMA=MagickTrue;
11920 if (LocaleNCompare(value+i,"iccp",4) == 0)
11921 mng_info->ping_exclude_iCCP=MagickTrue;
11924 if (LocaleNCompare(value+i,"itxt",4) == 0)
11925 mng_info->ping_exclude_iTXt=MagickTrue;
11928 if (LocaleNCompare(value+i,"gama",4) == 0)
11929 mng_info->ping_exclude_gAMA=MagickTrue;
11931 if (LocaleNCompare(value+i,"offs",4) == 0)
11932 mng_info->ping_exclude_oFFs=MagickTrue;
11934 if (LocaleNCompare(value+i,"phys",4) == 0)
11935 mng_info->ping_exclude_pHYs=MagickTrue;
11937 if (LocaleNCompare(value+i,"srgb",4) == 0)
11938 mng_info->ping_exclude_sRGB=MagickTrue;
11940 if (LocaleNCompare(value+i,"text",4) == 0)
11941 mng_info->ping_exclude_tEXt=MagickTrue;
11943 if (LocaleNCompare(value+i,"trns",4) == 0)
11944 mng_info->ping_exclude_tRNS=MagickTrue;
11946 if (LocaleNCompare(value+i,"vpag",4) == 0)
11947 mng_info->ping_exclude_vpAg=MagickTrue;
11949 if (LocaleNCompare(value+i,"zccp",4) == 0)
11950 mng_info->ping_exclude_zCCP=MagickTrue;
11952 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11953 mng_info->ping_exclude_zTXt=MagickTrue;
11959 for (source=0; source<1; source++)
11963 value=GetImageOption(image_info,"png:include-chunk");
11966 value=GetImageArtifact(image,"png:include-chunks");
11970 value=GetImageOption(image_info,"png:include-chunk");
11973 value=GetImageArtifact(image,"png:include-chunks");
11981 excluding=MagickTrue;
11983 if (logging != MagickFalse)
11986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11987 " png:include-chunk=%s found in image artifacts.\n", value);
11989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11990 " png:include-chunk=%s found in image properties.\n", value);
11993 last=strlen(value);
11995 for (i=0; i<(int) last; i+=5)
11997 if (LocaleNCompare(value+i,"all",3) == 0)
11999 mng_info->ping_exclude_bKGD=MagickFalse;
12000 mng_info->ping_exclude_cHRM=MagickFalse;
12001 mng_info->ping_exclude_date=MagickFalse;
12002 mng_info->ping_exclude_EXIF=MagickFalse;
12003 mng_info->ping_exclude_gAMA=MagickFalse;
12004 mng_info->ping_exclude_iCCP=MagickFalse;
12005 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12006 mng_info->ping_exclude_oFFs=MagickFalse;
12007 mng_info->ping_exclude_pHYs=MagickFalse;
12008 mng_info->ping_exclude_sRGB=MagickFalse;
12009 mng_info->ping_exclude_tEXt=MagickFalse;
12010 mng_info->ping_exclude_tRNS=MagickFalse;
12011 mng_info->ping_exclude_vpAg=MagickFalse;
12012 mng_info->ping_exclude_zCCP=MagickFalse;
12013 mng_info->ping_exclude_zTXt=MagickFalse;
12017 if (LocaleNCompare(value+i,"none",4) == 0)
12019 mng_info->ping_exclude_bKGD=MagickTrue;
12020 mng_info->ping_exclude_cHRM=MagickTrue;
12021 mng_info->ping_exclude_date=MagickTrue;
12022 mng_info->ping_exclude_EXIF=MagickTrue;
12023 mng_info->ping_exclude_gAMA=MagickTrue;
12024 mng_info->ping_exclude_iCCP=MagickTrue;
12025 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12026 mng_info->ping_exclude_oFFs=MagickTrue;
12027 mng_info->ping_exclude_pHYs=MagickTrue;
12028 mng_info->ping_exclude_sRGB=MagickTrue;
12029 mng_info->ping_exclude_tEXt=MagickTrue;
12030 mng_info->ping_exclude_tRNS=MagickTrue;
12031 mng_info->ping_exclude_vpAg=MagickTrue;
12032 mng_info->ping_exclude_zCCP=MagickTrue;
12033 mng_info->ping_exclude_zTXt=MagickTrue;
12036 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12037 mng_info->ping_exclude_bKGD=MagickFalse;
12039 if (LocaleNCompare(value+i,"chrm",4) == 0)
12040 mng_info->ping_exclude_cHRM=MagickFalse;
12042 if (LocaleNCompare(value+i,"date",4) == 0)
12043 mng_info->ping_exclude_date=MagickFalse;
12045 if (LocaleNCompare(value+i,"exif",4) == 0)
12046 mng_info->ping_exclude_EXIF=MagickFalse;
12048 if (LocaleNCompare(value+i,"gama",4) == 0)
12049 mng_info->ping_exclude_gAMA=MagickFalse;
12051 if (LocaleNCompare(value+i,"iccp",4) == 0)
12052 mng_info->ping_exclude_iCCP=MagickFalse;
12055 if (LocaleNCompare(value+i,"itxt",4) == 0)
12056 mng_info->ping_exclude_iTXt=MagickFalse;
12059 if (LocaleNCompare(value+i,"gama",4) == 0)
12060 mng_info->ping_exclude_gAMA=MagickFalse;
12062 if (LocaleNCompare(value+i,"offs",4) == 0)
12063 mng_info->ping_exclude_oFFs=MagickFalse;
12065 if (LocaleNCompare(value+i,"phys",4) == 0)
12066 mng_info->ping_exclude_pHYs=MagickFalse;
12068 if (LocaleNCompare(value+i,"srgb",4) == 0)
12069 mng_info->ping_exclude_sRGB=MagickFalse;
12071 if (LocaleNCompare(value+i,"text",4) == 0)
12072 mng_info->ping_exclude_tEXt=MagickFalse;
12074 if (LocaleNCompare(value+i,"trns",4) == 0)
12075 mng_info->ping_exclude_tRNS=MagickFalse;
12077 if (LocaleNCompare(value+i,"vpag",4) == 0)
12078 mng_info->ping_exclude_vpAg=MagickFalse;
12080 if (LocaleNCompare(value+i,"zccp",4) == 0)
12081 mng_info->ping_exclude_zCCP=MagickFalse;
12083 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12084 mng_info->ping_exclude_zTXt=MagickFalse;
12090 if (excluding != MagickFalse && logging != MagickFalse)
12092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12093 " Chunks to be excluded from the output png:");
12094 if (mng_info->ping_exclude_bKGD != MagickFalse)
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12097 if (mng_info->ping_exclude_cHRM != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 if (mng_info->ping_exclude_date != MagickFalse)
12101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12103 if (mng_info->ping_exclude_EXIF != MagickFalse)
12104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12106 if (mng_info->ping_exclude_gAMA != MagickFalse)
12107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12109 if (mng_info->ping_exclude_iCCP != MagickFalse)
12110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12113 if (mng_info->ping_exclude_iTXt != MagickFalse)
12114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12117 if (mng_info->ping_exclude_oFFs != MagickFalse)
12118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12120 if (mng_info->ping_exclude_pHYs != MagickFalse)
12121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12123 if (mng_info->ping_exclude_sRGB != MagickFalse)
12124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12126 if (mng_info->ping_exclude_tEXt != MagickFalse)
12127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12129 if (mng_info->ping_exclude_tRNS != MagickFalse)
12130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132 if (mng_info->ping_exclude_vpAg != MagickFalse)
12133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12135 if (mng_info->ping_exclude_zCCP != MagickFalse)
12136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138 if (mng_info->ping_exclude_zTXt != MagickFalse)
12139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12143 mng_info->need_blob = MagickTrue;
12145 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12147 MngInfoFreeStruct(mng_info,&have_mng_structure);
12149 if (logging != MagickFalse)
12150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12155 #if defined(JNG_SUPPORTED)
12157 /* Write one JNG image */
12158 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12159 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12180 jng_alpha_compression_method,
12181 jng_alpha_sample_depth,
12189 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12190 " Enter WriteOneJNGImage()");
12192 blob=(unsigned char *) NULL;
12193 jpeg_image=(Image *) NULL;
12194 jpeg_image_info=(ImageInfo *) NULL;
12197 transparent=image_info->type==GrayscaleMatteType ||
12198 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12200 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12202 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12204 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12205 image_info->quality;
12207 if (jng_alpha_quality >= 1000)
12208 jng_alpha_quality /= 1000;
12214 /* Create JPEG blob, image, and image_info */
12215 if (logging != MagickFalse)
12216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12217 " Creating jpeg_image_info for alpha.");
12219 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12221 if (jpeg_image_info == (ImageInfo *) NULL)
12222 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12224 if (logging != MagickFalse)
12225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12226 " Creating jpeg_image.");
12228 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12229 if (jpeg_image == (Image *) NULL)
12230 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12231 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12232 jpeg_image->alpha_trait=UndefinedPixelTrait;
12233 jpeg_image->quality=jng_alpha_quality;
12234 jpeg_image_info->type=GrayscaleType;
12235 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12236 (void) AcquireUniqueFilename(jpeg_image->filename);
12237 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12238 "%s",jpeg_image->filename);
12242 jng_alpha_compression_method=0;
12244 jng_alpha_sample_depth=0;
12247 /* To do: check bit depth of PNG alpha channel */
12249 /* Check if image is grayscale. */
12250 if (image_info->type != TrueColorMatteType && image_info->type !=
12251 TrueColorType && IsImageGray(image,exception))
12254 if (logging != MagickFalse)
12256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12257 " JNG Quality = %d",(int) jng_quality);
12258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12259 " JNG Color Type = %d",jng_color_type);
12262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12263 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12265 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12267 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12273 if (jng_alpha_compression_method==0)
12278 /* Encode alpha as a grayscale PNG blob */
12279 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12281 if (logging != MagickFalse)
12282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12283 " Creating PNG blob.");
12286 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12287 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12288 jpeg_image_info->interlace=NoInterlace;
12290 /* Exclude all ancillary chunks */
12291 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12293 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12296 /* Retrieve sample depth used */
12297 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12298 if (value != (char *) NULL)
12299 jng_alpha_sample_depth= (unsigned int) value[0];
12303 /* Encode alpha as a grayscale JPEG blob */
12305 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12308 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12309 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12310 jpeg_image_info->interlace=NoInterlace;
12311 if (logging != MagickFalse)
12312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12313 " Creating blob.");
12314 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12316 jng_alpha_sample_depth=8;
12318 if (logging != MagickFalse)
12319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320 " Successfully read jpeg_image into a blob, length=%.20g.",
12324 /* Destroy JPEG image and image_info */
12325 jpeg_image=DestroyImage(jpeg_image);
12326 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12327 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12330 /* Write JHDR chunk */
12331 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12332 PNGType(chunk,mng_JHDR);
12333 LogPNGChunk(logging,mng_JHDR,16L);
12334 PNGLong(chunk+4,(png_uint_32) image->columns);
12335 PNGLong(chunk+8,(png_uint_32) image->rows);
12336 chunk[12]=jng_color_type;
12337 chunk[13]=8; /* sample depth */
12338 chunk[14]=8; /*jng_image_compression_method */
12339 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12340 chunk[16]=jng_alpha_sample_depth;
12341 chunk[17]=jng_alpha_compression_method;
12342 chunk[18]=0; /*jng_alpha_filter_method */
12343 chunk[19]=0; /*jng_alpha_interlace_method */
12344 (void) WriteBlob(image,20,chunk);
12345 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12346 if (logging != MagickFalse)
12348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12349 " JNG width:%15lu",(unsigned long) image->columns);
12351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12352 " JNG height:%14lu",(unsigned long) image->rows);
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355 " JNG color type:%10d",jng_color_type);
12357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12358 " JNG sample depth:%8d",8);
12360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 " JNG compression:%9d",8);
12363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12364 " JNG interlace:%11d",0);
12366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12367 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12370 " JNG alpha compression:%3d",jng_alpha_compression_method);
12372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12373 " JNG alpha filter:%8d",0);
12375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12376 " JNG alpha interlace:%5d",0);
12379 /* Write any JNG-chunk-b profiles */
12380 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12383 Write leading ancillary chunks
12389 Write JNG bKGD chunk
12400 if (jng_color_type == 8 || jng_color_type == 12)
12404 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12405 PNGType(chunk,mng_bKGD);
12406 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12407 red=ScaleQuantumToChar(image->background_color.red);
12408 green=ScaleQuantumToChar(image->background_color.green);
12409 blue=ScaleQuantumToChar(image->background_color.blue);
12416 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12417 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12420 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12423 Write JNG sRGB chunk
12425 (void) WriteBlobMSBULong(image,1L);
12426 PNGType(chunk,mng_sRGB);
12427 LogPNGChunk(logging,mng_sRGB,1L);
12429 if (image->rendering_intent != UndefinedIntent)
12430 chunk[4]=(unsigned char)
12431 Magick_RenderingIntent_to_PNG_RenderingIntent(
12432 (image->rendering_intent));
12435 chunk[4]=(unsigned char)
12436 Magick_RenderingIntent_to_PNG_RenderingIntent(
12437 (PerceptualIntent));
12439 (void) WriteBlob(image,5,chunk);
12440 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12444 if (image->gamma != 0.0)
12447 Write JNG gAMA chunk
12449 (void) WriteBlobMSBULong(image,4L);
12450 PNGType(chunk,mng_gAMA);
12451 LogPNGChunk(logging,mng_gAMA,4L);
12452 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12453 (void) WriteBlob(image,8,chunk);
12454 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12457 if ((mng_info->equal_chrms == MagickFalse) &&
12458 (image->chromaticity.red_primary.x != 0.0))
12464 Write JNG cHRM chunk
12466 (void) WriteBlobMSBULong(image,32L);
12467 PNGType(chunk,mng_cHRM);
12468 LogPNGChunk(logging,mng_cHRM,32L);
12469 primary=image->chromaticity.white_point;
12470 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12471 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12472 primary=image->chromaticity.red_primary;
12473 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12474 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12475 primary=image->chromaticity.green_primary;
12476 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12477 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12478 primary=image->chromaticity.blue_primary;
12479 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12480 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12481 (void) WriteBlob(image,36,chunk);
12482 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12486 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12489 Write JNG pHYs chunk
12491 (void) WriteBlobMSBULong(image,9L);
12492 PNGType(chunk,mng_pHYs);
12493 LogPNGChunk(logging,mng_pHYs,9L);
12494 if (image->units == PixelsPerInchResolution)
12496 PNGLong(chunk+4,(png_uint_32)
12497 (image->resolution.x*100.0/2.54+0.5));
12499 PNGLong(chunk+8,(png_uint_32)
12500 (image->resolution.y*100.0/2.54+0.5));
12507 if (image->units == PixelsPerCentimeterResolution)
12509 PNGLong(chunk+4,(png_uint_32)
12510 (image->resolution.x*100.0+0.5));
12512 PNGLong(chunk+8,(png_uint_32)
12513 (image->resolution.y*100.0+0.5));
12520 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12521 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12525 (void) WriteBlob(image,13,chunk);
12526 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12529 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12532 Write JNG oFFs chunk
12534 (void) WriteBlobMSBULong(image,9L);
12535 PNGType(chunk,mng_oFFs);
12536 LogPNGChunk(logging,mng_oFFs,9L);
12537 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12538 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12540 (void) WriteBlob(image,13,chunk);
12541 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12543 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12545 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12546 PNGType(chunk,mng_vpAg);
12547 LogPNGChunk(logging,mng_vpAg,9L);
12548 PNGLong(chunk+4,(png_uint_32) image->page.width);
12549 PNGLong(chunk+8,(png_uint_32) image->page.height);
12550 chunk[12]=0; /* unit = pixels */
12551 (void) WriteBlob(image,13,chunk);
12552 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12558 if (jng_alpha_compression_method==0)
12566 /* Write IDAT chunk header */
12567 if (logging != MagickFalse)
12568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12569 " Write IDAT chunks from blob, length=%.20g.",(double)
12572 /* Copy IDAT chunks */
12575 for (i=8; i<(ssize_t) length; i+=len+12)
12577 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12580 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12582 /* Found an IDAT chunk. */
12583 (void) WriteBlobMSBULong(image,(size_t) len);
12584 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12585 (void) WriteBlob(image,(size_t) len+4,p);
12586 (void) WriteBlobMSBULong(image,
12587 crc32(0,p,(uInt) len+4));
12592 if (logging != MagickFalse)
12593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12594 " Skipping %c%c%c%c chunk, length=%.20g.",
12595 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12602 /* Write JDAA chunk header */
12603 if (logging != MagickFalse)
12604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12605 " Write JDAA chunk, length=%.20g.",(double) length);
12606 (void) WriteBlobMSBULong(image,(size_t) length);
12607 PNGType(chunk,mng_JDAA);
12608 LogPNGChunk(logging,mng_JDAA,length);
12609 /* Write JDAT chunk(s) data */
12610 (void) WriteBlob(image,4,chunk);
12611 (void) WriteBlob(image,length,blob);
12612 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12615 blob=(unsigned char *) RelinquishMagickMemory(blob);
12618 /* Encode image as a JPEG blob */
12619 if (logging != MagickFalse)
12620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12621 " Creating jpeg_image_info.");
12622 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12623 if (jpeg_image_info == (ImageInfo *) NULL)
12624 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12626 if (logging != MagickFalse)
12627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12628 " Creating jpeg_image.");
12630 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12631 if (jpeg_image == (Image *) NULL)
12632 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12633 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12635 (void) AcquireUniqueFilename(jpeg_image->filename);
12636 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12637 jpeg_image->filename);
12639 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12642 if (logging != MagickFalse)
12643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12644 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12645 (double) jpeg_image->rows);
12647 if (jng_color_type == 8 || jng_color_type == 12)
12648 jpeg_image_info->type=GrayscaleType;
12650 jpeg_image_info->quality=jng_quality;
12651 jpeg_image->quality=jng_quality;
12652 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12653 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12655 if (logging != MagickFalse)
12656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12657 " Creating blob.");
12659 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12661 if (logging != MagickFalse)
12663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12664 " Successfully read jpeg_image into a blob, length=%.20g.",
12667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12668 " Write JDAT chunk, length=%.20g.",(double) length);
12671 /* Write JDAT chunk(s) */
12672 (void) WriteBlobMSBULong(image,(size_t) length);
12673 PNGType(chunk,mng_JDAT);
12674 LogPNGChunk(logging,mng_JDAT,length);
12675 (void) WriteBlob(image,4,chunk);
12676 (void) WriteBlob(image,length,blob);
12677 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12679 jpeg_image=DestroyImage(jpeg_image);
12680 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12681 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12682 blob=(unsigned char *) RelinquishMagickMemory(blob);
12684 /* Write any JNG-chunk-e profiles */
12685 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12687 /* Write IEND chunk */
12688 (void) WriteBlobMSBULong(image,0L);
12689 PNGType(chunk,mng_IEND);
12690 LogPNGChunk(logging,mng_IEND,0);
12691 (void) WriteBlob(image,4,chunk);
12692 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12694 if (logging != MagickFalse)
12695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12696 " exit WriteOneJNGImage()");
12703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12707 % W r i t e J N G I m a g e %
12711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12713 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12715 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12717 % The format of the WriteJNGImage method is:
12719 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12720 % Image *image,ExceptionInfo *exception)
12722 % A description of each parameter follows:
12724 % o image_info: the image info.
12726 % o image: The image.
12728 % o exception: return any errors or warnings in this structure.
12730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12732 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12733 ExceptionInfo *exception)
12736 have_mng_structure,
12746 assert(image_info != (const ImageInfo *) NULL);
12747 assert(image_info->signature == MagickSignature);
12748 assert(image != (Image *) NULL);
12749 assert(image->signature == MagickSignature);
12750 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12751 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12752 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12753 if (status == MagickFalse)
12757 Allocate a MngInfo structure.
12759 have_mng_structure=MagickFalse;
12760 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12761 if (mng_info == (MngInfo *) NULL)
12762 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12764 Initialize members of the MngInfo structure.
12766 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12767 mng_info->image=image;
12768 have_mng_structure=MagickTrue;
12770 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12772 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12773 (void) CloseBlob(image);
12775 (void) CatchImageException(image);
12776 MngInfoFreeStruct(mng_info,&have_mng_structure);
12777 if (logging != MagickFalse)
12778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12783 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12784 ExceptionInfo *exception)
12793 have_mng_structure,
12796 volatile MagickBooleanType
12808 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12809 defined(PNG_MNG_FEATURES_SUPPORTED)
12812 all_images_are_gray,
12822 volatile unsigned int
12833 #if (PNG_LIBPNG_VER < 10200)
12834 if (image_info->verbose)
12835 printf("Your PNG library (libpng-%s) is rather old.\n",
12836 PNG_LIBPNG_VER_STRING);
12842 assert(image_info != (const ImageInfo *) NULL);
12843 assert(image_info->signature == MagickSignature);
12844 assert(image != (Image *) NULL);
12845 assert(image->signature == MagickSignature);
12846 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12847 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12848 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12849 if (status == MagickFalse)
12853 Allocate a MngInfo structure.
12855 have_mng_structure=MagickFalse;
12856 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12857 if (mng_info == (MngInfo *) NULL)
12858 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12860 Initialize members of the MngInfo structure.
12862 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12863 mng_info->image=image;
12864 have_mng_structure=MagickTrue;
12865 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12868 * See if user has requested a specific PNG subformat to be used
12869 * for all of the PNGs in the MNG being written, e.g.,
12871 * convert *.png png8:animation.mng
12873 * To do: check -define png:bit_depth and png:color_type as well,
12874 * or perhaps use mng:bit_depth and mng:color_type instead for
12878 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12879 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12880 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12882 write_jng=MagickFalse;
12883 if (image_info->compression == JPEGCompression)
12884 write_jng=MagickTrue;
12886 mng_info->adjoin=image_info->adjoin &&
12887 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12889 if (logging != MagickFalse)
12891 /* Log some info about the input */
12895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12896 " Checking input image(s)");
12898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12899 " Image_info depth: %.20g",(double) image_info->depth);
12901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12902 " Type: %d",image_info->type);
12905 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12908 " Scene: %.20g",(double) scene++);
12910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12911 " Image depth: %.20g",(double) p->depth);
12913 if (p->alpha_trait == BlendPixelTrait)
12914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12921 if (p->storage_class == PseudoClass)
12922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12923 " Storage class: PseudoClass");
12926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12927 " Storage class: DirectClass");
12930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12931 " Number of colors: %.20g",(double) p->colors);
12934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12935 " Number of colors: unspecified");
12937 if (mng_info->adjoin == MagickFalse)
12942 use_global_plte=MagickFalse;
12943 all_images_are_gray=MagickFalse;
12944 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12945 need_local_plte=MagickTrue;
12947 need_defi=MagickFalse;
12948 need_matte=MagickFalse;
12949 mng_info->framing_mode=1;
12950 mng_info->old_framing_mode=1;
12953 if (image_info->page != (char *) NULL)
12956 Determine image bounding box.
12958 SetGeometry(image,&mng_info->page);
12959 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12960 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12972 mng_info->page=image->page;
12973 need_geom=MagickTrue;
12974 if (mng_info->page.width || mng_info->page.height)
12975 need_geom=MagickFalse;
12977 Check all the scenes.
12979 initial_delay=image->delay;
12980 need_iterations=MagickFalse;
12981 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12982 mng_info->equal_physs=MagickTrue,
12983 mng_info->equal_gammas=MagickTrue;
12984 mng_info->equal_srgbs=MagickTrue;
12985 mng_info->equal_backgrounds=MagickTrue;
12987 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12988 defined(PNG_MNG_FEATURES_SUPPORTED)
12989 all_images_are_gray=MagickTrue;
12990 mng_info->equal_palettes=MagickFalse;
12991 need_local_plte=MagickFalse;
12993 for (next_image=image; next_image != (Image *) NULL; )
12997 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12998 mng_info->page.width=next_image->columns+next_image->page.x;
13000 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13001 mng_info->page.height=next_image->rows+next_image->page.y;
13004 if (next_image->page.x || next_image->page.y)
13005 need_defi=MagickTrue;
13007 if (next_image->alpha_trait == BlendPixelTrait)
13008 need_matte=MagickTrue;
13010 if ((int) next_image->dispose >= BackgroundDispose)
13011 if ((next_image->alpha_trait == BlendPixelTrait) ||
13012 next_image->page.x || next_image->page.y ||
13013 ((next_image->columns < mng_info->page.width) &&
13014 (next_image->rows < mng_info->page.height)))
13015 mng_info->need_fram=MagickTrue;
13017 if (next_image->iterations)
13018 need_iterations=MagickTrue;
13020 final_delay=next_image->delay;
13022 if (final_delay != initial_delay || final_delay > 1UL*
13023 next_image->ticks_per_second)
13024 mng_info->need_fram=1;
13026 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13027 defined(PNG_MNG_FEATURES_SUPPORTED)
13029 check for global palette possibility.
13031 if (image->alpha_trait == BlendPixelTrait)
13032 need_local_plte=MagickTrue;
13034 if (need_local_plte == 0)
13036 if (IsImageGray(image,exception) == MagickFalse)
13037 all_images_are_gray=MagickFalse;
13038 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13039 if (use_global_plte == 0)
13040 use_global_plte=mng_info->equal_palettes;
13041 need_local_plte=!mng_info->equal_palettes;
13044 if (GetNextImageInList(next_image) != (Image *) NULL)
13046 if (next_image->background_color.red !=
13047 next_image->next->background_color.red ||
13048 next_image->background_color.green !=
13049 next_image->next->background_color.green ||
13050 next_image->background_color.blue !=
13051 next_image->next->background_color.blue)
13052 mng_info->equal_backgrounds=MagickFalse;
13054 if (next_image->gamma != next_image->next->gamma)
13055 mng_info->equal_gammas=MagickFalse;
13057 if (next_image->rendering_intent !=
13058 next_image->next->rendering_intent)
13059 mng_info->equal_srgbs=MagickFalse;
13061 if ((next_image->units != next_image->next->units) ||
13062 (next_image->resolution.x != next_image->next->resolution.x) ||
13063 (next_image->resolution.y != next_image->next->resolution.y))
13064 mng_info->equal_physs=MagickFalse;
13066 if (mng_info->equal_chrms)
13068 if (next_image->chromaticity.red_primary.x !=
13069 next_image->next->chromaticity.red_primary.x ||
13070 next_image->chromaticity.red_primary.y !=
13071 next_image->next->chromaticity.red_primary.y ||
13072 next_image->chromaticity.green_primary.x !=
13073 next_image->next->chromaticity.green_primary.x ||
13074 next_image->chromaticity.green_primary.y !=
13075 next_image->next->chromaticity.green_primary.y ||
13076 next_image->chromaticity.blue_primary.x !=
13077 next_image->next->chromaticity.blue_primary.x ||
13078 next_image->chromaticity.blue_primary.y !=
13079 next_image->next->chromaticity.blue_primary.y ||
13080 next_image->chromaticity.white_point.x !=
13081 next_image->next->chromaticity.white_point.x ||
13082 next_image->chromaticity.white_point.y !=
13083 next_image->next->chromaticity.white_point.y)
13084 mng_info->equal_chrms=MagickFalse;
13088 next_image=GetNextImageInList(next_image);
13090 if (image_count < 2)
13092 mng_info->equal_backgrounds=MagickFalse;
13093 mng_info->equal_chrms=MagickFalse;
13094 mng_info->equal_gammas=MagickFalse;
13095 mng_info->equal_srgbs=MagickFalse;
13096 mng_info->equal_physs=MagickFalse;
13097 use_global_plte=MagickFalse;
13098 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13099 need_local_plte=MagickTrue;
13101 need_iterations=MagickFalse;
13104 if (mng_info->need_fram == MagickFalse)
13107 Only certain framing rates 100/n are exactly representable without
13108 the FRAM chunk but we'll allow some slop in VLC files
13110 if (final_delay == 0)
13112 if (need_iterations != MagickFalse)
13115 It's probably a GIF with loop; don't run it *too* fast.
13117 if (mng_info->adjoin)
13120 (void) ThrowMagickException(exception,GetMagickModule(),
13122 "input has zero delay between all frames; assuming",
13127 mng_info->ticks_per_second=0;
13129 if (final_delay != 0)
13130 mng_info->ticks_per_second=(png_uint_32)
13131 (image->ticks_per_second/final_delay);
13132 if (final_delay > 50)
13133 mng_info->ticks_per_second=2;
13135 if (final_delay > 75)
13136 mng_info->ticks_per_second=1;
13138 if (final_delay > 125)
13139 mng_info->need_fram=MagickTrue;
13141 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13142 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13143 (final_delay != 25) && (final_delay != 50) && (final_delay !=
13144 1UL*image->ticks_per_second))
13145 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13148 if (mng_info->need_fram != MagickFalse)
13149 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13151 If pseudocolor, we should also check to see if all the
13152 palettes are identical and write a global PLTE if they are.
13156 Write the MNG version 1.0 signature and MHDR chunk.
13158 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13159 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13160 PNGType(chunk,mng_MHDR);
13161 LogPNGChunk(logging,mng_MHDR,28L);
13162 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13163 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13164 PNGLong(chunk+12,mng_info->ticks_per_second);
13165 PNGLong(chunk+16,0L); /* layer count=unknown */
13166 PNGLong(chunk+20,0L); /* frame count=unknown */
13167 PNGLong(chunk+24,0L); /* play time=unknown */
13172 if (need_defi || mng_info->need_fram || use_global_plte)
13173 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13176 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13181 if (need_defi || mng_info->need_fram || use_global_plte)
13182 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13185 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13193 if (need_defi || mng_info->need_fram || use_global_plte)
13194 PNGLong(chunk+28,11L); /* simplicity=LC */
13197 PNGLong(chunk+28,9L); /* simplicity=VLC */
13202 if (need_defi || mng_info->need_fram || use_global_plte)
13203 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13206 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13209 (void) WriteBlob(image,32,chunk);
13210 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13211 option=GetImageOption(image_info,"mng:need-cacheoff");
13212 if (option != (const char *) NULL)
13218 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13220 PNGType(chunk,mng_nEED);
13221 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13222 (void) WriteBlobMSBULong(image,(size_t) length);
13223 LogPNGChunk(logging,mng_nEED,(size_t) length);
13225 (void) WriteBlob(image,length,chunk);
13226 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13228 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13229 (GetNextImageInList(image) != (Image *) NULL) &&
13230 (image->iterations != 1))
13233 Write MNG TERM chunk
13235 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13236 PNGType(chunk,mng_TERM);
13237 LogPNGChunk(logging,mng_TERM,10L);
13238 chunk[4]=3; /* repeat animation */
13239 chunk[5]=0; /* show last frame when done */
13240 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13241 final_delay/MagickMax(image->ticks_per_second,1)));
13243 if (image->iterations == 0)
13244 PNGLong(chunk+10,PNG_UINT_31_MAX);
13247 PNGLong(chunk+10,(png_uint_32) image->iterations);
13249 if (logging != MagickFalse)
13251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13252 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13253 final_delay/MagickMax(image->ticks_per_second,1)));
13255 if (image->iterations == 0)
13256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13257 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13261 " Image iterations: %.20g",(double) image->iterations);
13263 (void) WriteBlob(image,14,chunk);
13264 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13267 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13269 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13270 mng_info->equal_srgbs)
13273 Write MNG sRGB chunk
13275 (void) WriteBlobMSBULong(image,1L);
13276 PNGType(chunk,mng_sRGB);
13277 LogPNGChunk(logging,mng_sRGB,1L);
13279 if (image->rendering_intent != UndefinedIntent)
13280 chunk[4]=(unsigned char)
13281 Magick_RenderingIntent_to_PNG_RenderingIntent(
13282 (image->rendering_intent));
13285 chunk[4]=(unsigned char)
13286 Magick_RenderingIntent_to_PNG_RenderingIntent(
13287 (PerceptualIntent));
13289 (void) WriteBlob(image,5,chunk);
13290 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13291 mng_info->have_write_global_srgb=MagickTrue;
13296 if (image->gamma && mng_info->equal_gammas)
13299 Write MNG gAMA chunk
13301 (void) WriteBlobMSBULong(image,4L);
13302 PNGType(chunk,mng_gAMA);
13303 LogPNGChunk(logging,mng_gAMA,4L);
13304 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13305 (void) WriteBlob(image,8,chunk);
13306 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13307 mng_info->have_write_global_gama=MagickTrue;
13309 if (mng_info->equal_chrms)
13315 Write MNG cHRM chunk
13317 (void) WriteBlobMSBULong(image,32L);
13318 PNGType(chunk,mng_cHRM);
13319 LogPNGChunk(logging,mng_cHRM,32L);
13320 primary=image->chromaticity.white_point;
13321 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13322 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13323 primary=image->chromaticity.red_primary;
13324 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13325 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13326 primary=image->chromaticity.green_primary;
13327 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13328 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13329 primary=image->chromaticity.blue_primary;
13330 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13331 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13332 (void) WriteBlob(image,36,chunk);
13333 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13334 mng_info->have_write_global_chrm=MagickTrue;
13337 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13340 Write MNG pHYs chunk
13342 (void) WriteBlobMSBULong(image,9L);
13343 PNGType(chunk,mng_pHYs);
13344 LogPNGChunk(logging,mng_pHYs,9L);
13346 if (image->units == PixelsPerInchResolution)
13348 PNGLong(chunk+4,(png_uint_32)
13349 (image->resolution.x*100.0/2.54+0.5));
13351 PNGLong(chunk+8,(png_uint_32)
13352 (image->resolution.y*100.0/2.54+0.5));
13359 if (image->units == PixelsPerCentimeterResolution)
13361 PNGLong(chunk+4,(png_uint_32)
13362 (image->resolution.x*100.0+0.5));
13364 PNGLong(chunk+8,(png_uint_32)
13365 (image->resolution.y*100.0+0.5));
13372 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13373 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13377 (void) WriteBlob(image,13,chunk);
13378 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13381 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13382 or does not cover the entire frame.
13384 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13385 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13386 (image->page.width+image->page.x < mng_info->page.width))
13387 || (image->page.height && (image->page.height+image->page.y
13388 < mng_info->page.height))))
13390 (void) WriteBlobMSBULong(image,6L);
13391 PNGType(chunk,mng_BACK);
13392 LogPNGChunk(logging,mng_BACK,6L);
13393 red=ScaleQuantumToShort(image->background_color.red);
13394 green=ScaleQuantumToShort(image->background_color.green);
13395 blue=ScaleQuantumToShort(image->background_color.blue);
13396 PNGShort(chunk+4,red);
13397 PNGShort(chunk+6,green);
13398 PNGShort(chunk+8,blue);
13399 (void) WriteBlob(image,10,chunk);
13400 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13401 if (mng_info->equal_backgrounds)
13403 (void) WriteBlobMSBULong(image,6L);
13404 PNGType(chunk,mng_bKGD);
13405 LogPNGChunk(logging,mng_bKGD,6L);
13406 (void) WriteBlob(image,10,chunk);
13407 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13411 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13412 if ((need_local_plte == MagickFalse) &&
13413 (image->storage_class == PseudoClass) &&
13414 (all_images_are_gray == MagickFalse))
13420 Write MNG PLTE chunk
13422 data_length=3*image->colors;
13423 (void) WriteBlobMSBULong(image,data_length);
13424 PNGType(chunk,mng_PLTE);
13425 LogPNGChunk(logging,mng_PLTE,data_length);
13427 for (i=0; i < (ssize_t) image->colors; i++)
13429 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13430 image->colormap[i].red) & 0xff);
13431 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13432 image->colormap[i].green) & 0xff);
13433 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13434 image->colormap[i].blue) & 0xff);
13437 (void) WriteBlob(image,data_length+4,chunk);
13438 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13439 mng_info->have_write_global_plte=MagickTrue;
13445 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13446 defined(PNG_MNG_FEATURES_SUPPORTED)
13447 mng_info->equal_palettes=MagickFalse;
13451 if (mng_info->adjoin)
13453 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13454 defined(PNG_MNG_FEATURES_SUPPORTED)
13456 If we aren't using a global palette for the entire MNG, check to
13457 see if we can use one for two or more consecutive images.
13459 if (need_local_plte && use_global_plte && !all_images_are_gray)
13461 if (mng_info->IsPalette)
13464 When equal_palettes is true, this image has the same palette
13465 as the previous PseudoClass image
13467 mng_info->have_write_global_plte=mng_info->equal_palettes;
13468 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13469 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13472 Write MNG PLTE chunk
13477 data_length=3*image->colors;
13478 (void) WriteBlobMSBULong(image,data_length);
13479 PNGType(chunk,mng_PLTE);
13480 LogPNGChunk(logging,mng_PLTE,data_length);
13482 for (i=0; i < (ssize_t) image->colors; i++)
13484 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13485 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13486 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13489 (void) WriteBlob(image,data_length+4,chunk);
13490 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13491 (uInt) (data_length+4)));
13492 mng_info->have_write_global_plte=MagickTrue;
13496 mng_info->have_write_global_plte=MagickFalse;
13507 previous_x=mng_info->page.x;
13508 previous_y=mng_info->page.y;
13515 mng_info->page=image->page;
13516 if ((mng_info->page.x != previous_x) ||
13517 (mng_info->page.y != previous_y))
13519 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13520 PNGType(chunk,mng_DEFI);
13521 LogPNGChunk(logging,mng_DEFI,12L);
13522 chunk[4]=0; /* object 0 MSB */
13523 chunk[5]=0; /* object 0 LSB */
13524 chunk[6]=0; /* visible */
13525 chunk[7]=0; /* abstract */
13526 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13527 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13528 (void) WriteBlob(image,16,chunk);
13529 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13534 mng_info->write_mng=write_mng;
13536 if ((int) image->dispose >= 3)
13537 mng_info->framing_mode=3;
13539 if (mng_info->need_fram && mng_info->adjoin &&
13540 ((image->delay != mng_info->delay) ||
13541 (mng_info->framing_mode != mng_info->old_framing_mode)))
13543 if (image->delay == mng_info->delay)
13546 Write a MNG FRAM chunk with the new framing mode.
13548 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13549 PNGType(chunk,mng_FRAM);
13550 LogPNGChunk(logging,mng_FRAM,1L);
13551 chunk[4]=(unsigned char) mng_info->framing_mode;
13552 (void) WriteBlob(image,5,chunk);
13553 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13558 Write a MNG FRAM chunk with the delay.
13560 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13561 PNGType(chunk,mng_FRAM);
13562 LogPNGChunk(logging,mng_FRAM,10L);
13563 chunk[4]=(unsigned char) mng_info->framing_mode;
13564 chunk[5]=0; /* frame name separator (no name) */
13565 chunk[6]=2; /* flag for changing default delay */
13566 chunk[7]=0; /* flag for changing frame timeout */
13567 chunk[8]=0; /* flag for changing frame clipping */
13568 chunk[9]=0; /* flag for changing frame sync_id */
13569 PNGLong(chunk+10,(png_uint_32)
13570 ((mng_info->ticks_per_second*
13571 image->delay)/MagickMax(image->ticks_per_second,1)));
13572 (void) WriteBlob(image,14,chunk);
13573 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13574 mng_info->delay=(png_uint_32) image->delay;
13576 mng_info->old_framing_mode=mng_info->framing_mode;
13579 #if defined(JNG_SUPPORTED)
13580 if (image_info->compression == JPEGCompression)
13585 if (logging != MagickFalse)
13586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13587 " Writing JNG object.");
13588 /* To do: specify the desired alpha compression method. */
13589 write_info=CloneImageInfo(image_info);
13590 write_info->compression=UndefinedCompression;
13591 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13592 write_info=DestroyImageInfo(write_info);
13597 if (logging != MagickFalse)
13598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13599 " Writing PNG object.");
13601 mng_info->need_blob = MagickFalse;
13602 mng_info->ping_preserve_colormap = MagickFalse;
13604 /* We don't want any ancillary chunks written */
13605 mng_info->ping_exclude_bKGD=MagickTrue;
13606 mng_info->ping_exclude_cHRM=MagickTrue;
13607 mng_info->ping_exclude_date=MagickTrue;
13608 mng_info->ping_exclude_EXIF=MagickTrue;
13609 mng_info->ping_exclude_gAMA=MagickTrue;
13610 mng_info->ping_exclude_iCCP=MagickTrue;
13611 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13612 mng_info->ping_exclude_oFFs=MagickTrue;
13613 mng_info->ping_exclude_pHYs=MagickTrue;
13614 mng_info->ping_exclude_sRGB=MagickTrue;
13615 mng_info->ping_exclude_tEXt=MagickTrue;
13616 mng_info->ping_exclude_tRNS=MagickTrue;
13617 mng_info->ping_exclude_vpAg=MagickTrue;
13618 mng_info->ping_exclude_zCCP=MagickTrue;
13619 mng_info->ping_exclude_zTXt=MagickTrue;
13621 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13624 if (status == MagickFalse)
13626 MngInfoFreeStruct(mng_info,&have_mng_structure);
13627 (void) CloseBlob(image);
13628 return(MagickFalse);
13630 (void) CatchImageException(image);
13631 if (GetNextImageInList(image) == (Image *) NULL)
13633 image=SyncNextImageInList(image);
13634 status=SetImageProgress(image,SaveImagesTag,scene++,
13635 GetImageListLength(image));
13637 if (status == MagickFalse)
13640 } while (mng_info->adjoin);
13644 while (GetPreviousImageInList(image) != (Image *) NULL)
13645 image=GetPreviousImageInList(image);
13647 Write the MEND chunk.
13649 (void) WriteBlobMSBULong(image,0x00000000L);
13650 PNGType(chunk,mng_MEND);
13651 LogPNGChunk(logging,mng_MEND,0L);
13652 (void) WriteBlob(image,4,chunk);
13653 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13656 Relinquish resources.
13658 (void) CloseBlob(image);
13659 MngInfoFreeStruct(mng_info,&have_mng_structure);
13661 if (logging != MagickFalse)
13662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13664 return(MagickTrue);
13666 #else /* PNG_LIBPNG_VER > 10011 */
13668 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13671 printf("Your PNG library is too old: You have libpng-%s\n",
13672 PNG_LIBPNG_VER_STRING);
13674 ThrowBinaryException(CoderError,"PNG library is too old",
13675 image_info->filename);
13678 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13680 return(WritePNGImage(image_info,image));
13682 #endif /* PNG_LIBPNG_VER > 10011 */