2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2012 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 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
173 #define LBR01PixelGreen(pixel) \
174 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177 #define LBR01PixelBlue(pixel) \
178 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
181 #define LBR01PixelAlpha(pixel) \
182 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
185 #define LBR01PixelRGB(pixel) \
187 LBR01PixelRed((pixel)); \
188 LBR01PixelGreen((pixel)); \
189 LBR01PixelBlue((pixel)); \
192 #define LBR01PixelRGBA(pixel) \
194 LBR01PixelRGB((pixel)); \
195 LBR01PixelAlpha((pixel)); \
198 /* LBR02: Replicate top 2 bits */
200 #define LBR02PacketRed(pixelpacket) \
202 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
203 (pixelpacket).red=ScaleCharToQuantum( \
204 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
206 #define LBR02PacketGreen(pixelpacket) \
208 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
209 (pixelpacket).green=ScaleCharToQuantum( \
210 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
212 #define LBR02PacketBlue(pixelpacket) \
214 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
215 (pixelpacket).blue=ScaleCharToQuantum( \
216 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
218 #define LBR02PacketAlpha(pixelpacket) \
220 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
221 (pixelpacket).alpha=ScaleCharToQuantum( \
222 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
225 #define LBR02PacketRGB(pixelpacket) \
227 LBR02PacketRed((pixelpacket)); \
228 LBR02PacketGreen((pixelpacket)); \
229 LBR02PacketBlue((pixelpacket)); \
232 #define LBR02PacketRGBO(pixelpacket) \
234 LBR02PacketRGB((pixelpacket)); \
235 LBR02PacketAlpha((pixelpacket)); \
238 #define LBR02PixelRed(pixel) \
240 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
242 SetPixelRed(image, ScaleCharToQuantum( \
243 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
246 #define LBR02PixelGreen(pixel) \
248 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
250 SetPixelGreen(image, ScaleCharToQuantum( \
251 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
254 #define LBR02PixelBlue(pixel) \
256 unsigned char lbr_bits= \
257 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
258 SetPixelBlue(image, ScaleCharToQuantum( \
259 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
262 #define LBR02PixelAlpha(pixel) \
264 unsigned char lbr_bits= \
265 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
266 SetPixelAlpha(image, ScaleCharToQuantum( \
267 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
271 #define LBR02PixelRGB(pixel) \
273 LBR02PixelRed((pixel)); \
274 LBR02PixelGreen((pixel)); \
275 LBR02PixelBlue((pixel)); \
278 #define LBR02PixelRGBA(pixel) \
280 LBR02PixelRGB((pixel)); \
281 LBR02PixelAlpha((pixel)); \
284 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
285 PNG8 quantization) */
287 #define LBR03PacketRed(pixelpacket) \
289 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
290 (pixelpacket).red=ScaleCharToQuantum( \
291 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
293 #define LBR03PacketGreen(pixelpacket) \
295 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
296 (pixelpacket).green=ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
299 #define LBR03PacketBlue(pixelpacket) \
301 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
302 (pixelpacket).blue=ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
306 #define LBR03PacketRGB(pixelpacket) \
308 LBR03PacketRed((pixelpacket)); \
309 LBR03PacketGreen((pixelpacket)); \
310 LBR03PacketBlue((pixelpacket)); \
313 #define LBR03PixelRed(pixel) \
315 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
317 SetPixelRed(image, ScaleCharToQuantum( \
318 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
320 #define LBR03Green(pixel) \
322 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
324 SetPixelGreen(image, ScaleCharToQuantum( \
325 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
327 #define LBR03Blue(pixel) \
329 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
331 SetPixelBlue(image, ScaleCharToQuantum( \
332 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
335 #define LBR03RGB(pixel) \
337 LBR03PixelRed((pixel)); \
338 LBR03Green((pixel)); \
339 LBR03Blue((pixel)); \
342 /* LBR04: Replicate top 4 bits */
344 #define LBR04PacketRed(pixelpacket) \
346 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
347 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
349 #define LBR04PacketGreen(pixelpacket) \
351 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
352 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
354 #define LBR04PacketBlue(pixelpacket) \
356 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
357 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
359 #define LBR04PacketAlpha(pixelpacket) \
361 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
362 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
365 #define LBR04PacketRGB(pixelpacket) \
367 LBR04PacketRed((pixelpacket)); \
368 LBR04PacketGreen((pixelpacket)); \
369 LBR04PacketBlue((pixelpacket)); \
372 #define LBR04PacketRGBO(pixelpacket) \
374 LBR04PacketRGB((pixelpacket)); \
375 LBR04PacketAlpha((pixelpacket)); \
378 #define LBR04PixelRed(pixel) \
380 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
383 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
385 #define LBR04PixelGreen(pixel) \
387 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
389 SetPixelGreen(image,\
390 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
392 #define LBR04PixelBlue(pixel) \
394 unsigned char lbr_bits= \
395 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
397 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
399 #define LBR04PixelAlpha(pixel) \
401 unsigned char lbr_bits= \
402 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
403 SetPixelAlpha(image,\
404 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
407 #define LBR04PixelRGB(pixel) \
409 LBR04PixelRed((pixel)); \
410 LBR04PixelGreen((pixel)); \
411 LBR04PixelBlue((pixel)); \
414 #define LBR04PixelRGBA(pixel) \
416 LBR04PixelRGB((pixel)); \
417 LBR04PixelAlpha((pixel)); \
421 /* LBR08: Replicate top 8 bits */
423 #define LBR08PacketRed(pixelpacket) \
425 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
426 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
428 #define LBR08PacketGreen(pixelpacket) \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
431 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
433 #define LBR08PacketBlue(pixelpacket) \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
436 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
438 #define LBR08PacketAlpha(pixelpacket) \
440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
441 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
444 #define LBR08PacketRGB(pixelpacket) \
446 LBR08PacketRed((pixelpacket)); \
447 LBR08PacketGreen((pixelpacket)); \
448 LBR08PacketBlue((pixelpacket)); \
451 #define LBR08PacketRGBO(pixelpacket) \
453 LBR08PacketRGB((pixelpacket)); \
454 LBR08PacketAlpha((pixelpacket)); \
457 #define LBR08PixelRed(pixel) \
459 unsigned char lbr_bits= \
460 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
462 ScaleCharToQuantum((lbr_bits)), (pixel)); \
464 #define LBR08PixelGreen(pixel) \
466 unsigned char lbr_bits= \
467 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
468 SetPixelGreen(image,\
469 ScaleCharToQuantum((lbr_bits)), (pixel)); \
471 #define LBR08PixelBlue(pixel) \
473 unsigned char lbr_bits= \
474 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
476 ScaleCharToQuantum((lbr_bits)), (pixel)); \
478 #define LBR08PixelAlpha(pixel) \
480 unsigned char lbr_bits= \
481 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
482 SetPixelAlpha(image,\
483 ScaleCharToQuantum((lbr_bits)), (pixel)); \
486 #define LBR08PixelRGB(pixel) \
488 LBR08PixelRed((pixel)); \
489 LBR08PixelGreen((pixel)); \
490 LBR08PixelBlue((pixel)); \
493 #define LBR08PixelRGBA(pixel) \
495 LBR08PixelRGB((pixel)); \
496 LBR08PixelAlpha((pixel)); \
500 /* LBR16: Replicate top 16 bits */
502 #define LBR16PacketRed(pixelpacket) \
504 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
505 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
507 #define LBR16PacketGreen(pixelpacket) \
509 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
510 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
512 #define LBR16PacketBlue(pixelpacket) \
514 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
515 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
517 #define LBR16PacketAlpha(pixelpacket) \
519 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
520 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
523 #define LBR16PacketRGB(pixelpacket) \
525 LBR16PacketRed((pixelpacket)); \
526 LBR16PacketGreen((pixelpacket)); \
527 LBR16PacketBlue((pixelpacket)); \
530 #define LBR16PacketRGBO(pixelpacket) \
532 LBR16PacketRGB((pixelpacket)); \
533 LBR16PacketAlpha((pixelpacket)); \
536 #define LBR16PixelRed(pixel) \
538 unsigned short lbr_bits= \
539 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
541 ScaleShortToQuantum((lbr_bits)),(pixel)); \
543 #define LBR16PixelGreen(pixel) \
545 unsigned short lbr_bits= \
546 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
547 SetPixelGreen(image,\
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
550 #define LBR16PixelBlue(pixel) \
552 unsigned short lbr_bits= \
553 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
557 #define LBR16PixelAlpha(pixel) \
559 unsigned short lbr_bits= \
560 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
561 SetPixelAlpha(image,\
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
565 #define LBR16PixelRGB(pixel) \
567 LBR16PixelRed((pixel)); \
568 LBR16PixelGreen((pixel)); \
569 LBR16PixelBlue((pixel)); \
572 #define LBR16PixelRGBA(pixel) \
574 LBR16PixelRGB((pixel)); \
575 LBR16PixelAlpha((pixel)); \
579 Establish thread safety.
580 setjmp/longjmp is claimed to be safe on these platforms:
581 setjmp/longjmp is alleged to be unsafe on these platforms:
583 #ifndef SETJMP_IS_THREAD_SAFE
584 #define PNG_SETJMP_NOT_THREAD_SAFE
587 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
589 *ping_semaphore = (SemaphoreInfo *) NULL;
593 This temporary until I set up malloc'ed object attributes array.
594 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
597 #define MNG_MAX_OBJECTS 256
600 If this not defined, spec is interpreted strictly. If it is
601 defined, an attempt will be made to recover from some errors,
603 o global PLTE too short
608 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
609 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
610 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
611 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
612 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
613 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
614 will be enabled by default in libpng-1.2.0.
616 #ifdef PNG_MNG_FEATURES_SUPPORTED
617 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
618 # define PNG_READ_EMPTY_PLTE_SUPPORTED
620 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
621 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
626 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
627 This macro is only defined in libpng-1.0.3 and later.
628 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
630 #ifndef PNG_UINT_31_MAX
631 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
635 Constant strings for known chunk types. If you need to add a chunk,
636 add a string holding the name here. To make the code more
637 portable, we use ASCII numbers like this, not characters.
640 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
641 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
642 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
643 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
644 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
645 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
646 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
647 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
648 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
649 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
650 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
651 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
652 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
653 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
654 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
655 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
656 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
657 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
658 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
659 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
660 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
661 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
662 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
663 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
664 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
665 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
666 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
667 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
668 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
669 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
670 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
671 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
672 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
673 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
675 #if defined(JNG_SUPPORTED)
676 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
677 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
678 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
679 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
680 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
681 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
685 Other known chunks that are not yet supported by ImageMagick:
686 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
687 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
688 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
689 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
690 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
691 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
692 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
693 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
696 typedef struct _MngBox
705 typedef struct _MngPair
712 #ifdef MNG_OBJECT_BUFFERS
713 typedef struct _MngBuffer
745 typedef struct _MngInfo
748 #ifdef MNG_OBJECT_BUFFERS
750 *ob[MNG_MAX_OBJECTS];
761 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
762 bytes_in_read_buffer,
768 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
769 defined(PNG_MNG_FEATURES_SUPPORTED)
781 have_saved_bkgd_index,
782 have_write_global_chrm,
783 have_write_global_gama,
784 have_write_global_plte,
785 have_write_global_srgb,
799 x_off[MNG_MAX_OBJECTS],
800 y_off[MNG_MAX_OBJECTS];
806 object_clip[MNG_MAX_OBJECTS];
809 /* These flags could be combined into one byte */
810 exists[MNG_MAX_OBJECTS],
811 frozen[MNG_MAX_OBJECTS],
813 invisible[MNG_MAX_OBJECTS],
814 viewable[MNG_MAX_OBJECTS];
826 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
844 global_x_pixels_per_unit,
845 global_y_pixels_per_unit,
855 global_phys_unit_type,
870 write_png_compression_level,
871 write_png_compression_strategy,
872 write_png_compression_filter,
877 #ifdef MNG_BASI_SUPPORTED
885 basi_compression_method,
887 basi_interlace_method,
910 /* Added at version 6.6.6-7 */
918 /* ping_exclude_iTXt, */
925 ping_exclude_zCCP, /* hex-encoded iCCP */
927 ping_preserve_colormap;
933 Forward declarations.
935 static MagickBooleanType
936 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
938 static MagickBooleanType
939 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
941 #if defined(JNG_SUPPORTED)
942 static MagickBooleanType
943 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
946 #if PNG_LIBPNG_VER > 10011
949 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
950 static MagickBooleanType
951 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
953 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
955 * This is true if the high byte and the next highest byte of
956 * each sample of the image, the colormap, and the background color
957 * are equal to each other. We check this by seeing if the samples
958 * are unchanged when we scale them down to 8 and back up to Quantum.
960 * We don't use the method GetImageDepth() because it doesn't check
961 * background and doesn't handle PseudoClass specially.
964 #define QuantumToCharToQuantumEqQuantum(quantum) \
965 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
968 ok_to_reduce=MagickFalse;
970 if (image->depth >= 16)
977 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
979 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
980 MagickTrue : MagickFalse;
982 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
986 for (indx=0; indx < (ssize_t) image->colors; indx++)
989 QuantumToCharToQuantumEqQuantum(
990 image->colormap[indx].red) &&
991 QuantumToCharToQuantumEqQuantum(
992 image->colormap[indx].green) &&
993 QuantumToCharToQuantumEqQuantum(
994 image->colormap[indx].blue)) ?
995 MagickTrue : MagickFalse;
997 if (ok_to_reduce == MagickFalse)
1002 if ((ok_to_reduce != MagickFalse) &&
1003 (image->storage_class != PseudoClass))
1011 for (y=0; y < (ssize_t) image->rows; y++)
1013 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1015 if (p == (const Quantum *) NULL)
1017 ok_to_reduce = MagickFalse;
1021 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1024 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1026 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1027 MagickTrue : MagickFalse;
1029 if (ok_to_reduce == MagickFalse)
1032 p+=GetPixelChannels(image);
1039 if (ok_to_reduce != MagickFalse)
1041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1042 " OK to reduce PNG bit depth to 8 without loss of info");
1046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1047 " Not OK to reduce PNG bit depth to 8 without loss of info");
1051 return ok_to_reduce;
1053 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1055 static const char* PngColorTypeToString(const unsigned int color_type)
1058 *result = "Unknown";
1062 case PNG_COLOR_TYPE_GRAY:
1065 case PNG_COLOR_TYPE_GRAY_ALPHA:
1066 result = "Gray+Alpha";
1068 case PNG_COLOR_TYPE_PALETTE:
1071 case PNG_COLOR_TYPE_RGB:
1074 case PNG_COLOR_TYPE_RGB_ALPHA:
1075 result = "RGB+Alpha";
1083 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1087 case PerceptualIntent:
1090 case RelativeIntent:
1093 case SaturationIntent:
1096 case AbsoluteIntent:
1104 static RenderingIntent
1105 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1107 switch (ping_intent)
1110 return PerceptualIntent;
1113 return RelativeIntent;
1116 return SaturationIntent;
1119 return AbsoluteIntent;
1122 return UndefinedIntent;
1126 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1135 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1137 switch (ping_colortype)
1155 return "UndefinedColorType";
1160 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1174 % I m a g e I s G r a y %
1178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 % Like IsImageGray except does not change DirectClass to PseudoClass %
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
1186 register const Quantum
1194 assert(image != (Image *) NULL);
1195 assert(image->signature == MagickSignature);
1196 if (image->debug != MagickFalse)
1197 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1199 if (image->storage_class == PseudoClass)
1201 for (i=0; i < (ssize_t) image->colors; i++)
1202 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
1203 return(MagickFalse);
1206 for (y=0; y < (ssize_t) image->rows; y++)
1208 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1209 if (p == (const Quantum *) NULL)
1210 return(MagickFalse);
1211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1213 if (IsPixelGray(image,p) == MagickFalse)
1214 return(MagickFalse);
1215 p+=GetPixelChannels(image);
1220 #endif /* PNG_LIBPNG_VER > 10011 */
1221 #endif /* MAGICKCORE_PNG_DELEGATE */
1224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234 % IsMNG() returns MagickTrue if the image format type, identified by the
1235 % magick string, is MNG.
1237 % The format of the IsMNG method is:
1239 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1241 % A description of each parameter follows:
1243 % o magick: compare image format pattern against these bytes.
1245 % o length: Specifies the length of the magick string.
1249 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1252 return(MagickFalse);
1254 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1257 return(MagickFalse);
1261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1271 % IsJNG() returns MagickTrue if the image format type, identified by the
1272 % magick string, is JNG.
1274 % The format of the IsJNG method is:
1276 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1278 % A description of each parameter follows:
1280 % o magick: compare image format pattern against these bytes.
1282 % o length: Specifies the length of the magick string.
1286 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1289 return(MagickFalse);
1291 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1294 return(MagickFalse);
1298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1308 % IsPNG() returns MagickTrue if the image format type, identified by the
1309 % magick string, is PNG.
1311 % The format of the IsPNG method is:
1313 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1315 % A description of each parameter follows:
1317 % o magick: compare image format pattern against these bytes.
1319 % o length: Specifies the length of the magick string.
1322 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1325 return(MagickFalse);
1327 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1330 return(MagickFalse);
1333 #if defined(MAGICKCORE_PNG_DELEGATE)
1334 #if defined(__cplusplus) || defined(c_plusplus)
1338 #if (PNG_LIBPNG_VER > 10011)
1339 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1344 assert(image != (Image *) NULL);
1345 assert(image->signature == MagickSignature);
1346 buffer[0]=(unsigned char) (value >> 24);
1347 buffer[1]=(unsigned char) (value >> 16);
1348 buffer[2]=(unsigned char) (value >> 8);
1349 buffer[3]=(unsigned char) value;
1350 return((size_t) WriteBlob(image,4,buffer));
1353 static void PNGLong(png_bytep p,png_uint_32 value)
1355 *p++=(png_byte) ((value >> 24) & 0xff);
1356 *p++=(png_byte) ((value >> 16) & 0xff);
1357 *p++=(png_byte) ((value >> 8) & 0xff);
1358 *p++=(png_byte) (value & 0xff);
1361 #if defined(JNG_SUPPORTED)
1362 static void PNGsLong(png_bytep p,png_int_32 value)
1364 *p++=(png_byte) ((value >> 24) & 0xff);
1365 *p++=(png_byte) ((value >> 16) & 0xff);
1366 *p++=(png_byte) ((value >> 8) & 0xff);
1367 *p++=(png_byte) (value & 0xff);
1371 static void PNGShort(png_bytep p,png_uint_16 value)
1373 *p++=(png_byte) ((value >> 8) & 0xff);
1374 *p++=(png_byte) (value & 0xff);
1377 static void PNGType(png_bytep p,png_bytep type)
1379 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1382 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1385 if (logging != MagickFalse)
1386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1387 " Writing %c%c%c%c chunk, length: %.20g",
1388 type[0],type[1],type[2],type[3],(double) length);
1390 #endif /* PNG_LIBPNG_VER > 10011 */
1392 #if defined(__cplusplus) || defined(c_plusplus)
1396 #if PNG_LIBPNG_VER > 10011
1398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1402 % R e a d P N G I m a g e %
1406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1408 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1409 % Multiple-image Network Graphics (MNG) image file and returns it. It
1410 % allocates the memory necessary for the new Image structure and returns a
1411 % pointer to the new image or set of images.
1413 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1415 % The format of the ReadPNGImage method is:
1417 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1419 % A description of each parameter follows:
1421 % o image_info: the image info.
1423 % o exception: return any errors or warnings in this structure.
1425 % To do, more or less in chronological order (as of version 5.5.2,
1426 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1428 % Get 16-bit cheap transparency working.
1430 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1432 % Preserve all unknown and not-yet-handled known chunks found in input
1433 % PNG file and copy them into output PNG files according to the PNG
1436 % (At this point, PNG encoding should be in full MNG compliance)
1438 % Provide options for choice of background to use when the MNG BACK
1439 % chunk is not present or is not mandatory (i.e., leave transparent,
1440 % user specified, MNG BACK, PNG bKGD)
1442 % Implement LOOP/ENDL [done, but could do discretionary loops more
1443 % efficiently by linking in the duplicate frames.].
1445 % Decode and act on the MHDR simplicity profile (offer option to reject
1446 % files or attempt to process them anyway when the profile isn't LC or VLC).
1448 % Upgrade to full MNG without Delta-PNG.
1450 % o BACK [done a while ago except for background image ID]
1451 % o MOVE [done 15 May 1999]
1452 % o CLIP [done 15 May 1999]
1453 % o DISC [done 19 May 1999]
1454 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1455 % o SEEK [partially done 19 May 1999 (discard function only)]
1459 % o MNG-level tEXt/iTXt/zTXt
1464 % o iTXt (wait for libpng implementation).
1466 % Use the scene signature to discover when an identical scene is
1467 % being reused, and just point to the original image->exception instead
1468 % of storing another set of pixels. This not specific to MNG
1469 % but could be applied generally.
1471 % Upgrade to full MNG with Delta-PNG.
1473 % JNG tEXt/iTXt/zTXt
1475 % We will not attempt to read files containing the CgBI chunk.
1476 % They are really Xcode files meant for display on the iPhone.
1477 % These are not valid PNG files and it is impossible to recover
1478 % the original PNG from files that have been converted to Xcode-PNG,
1479 % since irretrievable loss of color data has occurred due to the
1480 % use of premultiplied alpha.
1483 #if defined(__cplusplus) || defined(c_plusplus)
1488 This the function that does the actual reading of data. It is
1489 the same as the one supplied in libpng, except that it receives the
1490 datastream from the ReadBlob() function instead of standard input.
1492 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1497 image=(Image *) png_get_io_ptr(png_ptr);
1503 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1504 if (check != length)
1509 (void) FormatLocaleString(msg,MaxTextExtent,
1510 "Expected %.20g bytes; found %.20g bytes",(double) length,
1512 png_warning(png_ptr,msg);
1513 png_error(png_ptr,"Read Exception");
1518 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1519 !defined(PNG_MNG_FEATURES_SUPPORTED)
1520 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1521 * older than libpng-1.0.3a, which was the first to allow the empty
1522 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1523 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1524 * encountered after an empty PLTE, so we have to look ahead for bKGD
1525 * chunks and remove them from the datastream that is passed to libpng,
1526 * and store their contents for later use.
1528 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1543 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1544 image=(Image *) mng_info->image;
1545 while (mng_info->bytes_in_read_buffer && length)
1547 data[i]=mng_info->read_buffer[i];
1548 mng_info->bytes_in_read_buffer--;
1554 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1556 if (check != length)
1557 png_error(png_ptr,"Read Exception");
1561 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1564 check=(png_size_t) ReadBlob(image,(size_t) length,
1565 (char *) mng_info->read_buffer);
1566 mng_info->read_buffer[4]=0;
1567 mng_info->bytes_in_read_buffer=4;
1568 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1569 mng_info->found_empty_plte=MagickTrue;
1570 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1572 mng_info->found_empty_plte=MagickFalse;
1573 mng_info->have_saved_bkgd_index=MagickFalse;
1577 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1580 check=(png_size_t) ReadBlob(image,(size_t) length,
1581 (char *) mng_info->read_buffer);
1582 mng_info->read_buffer[4]=0;
1583 mng_info->bytes_in_read_buffer=4;
1584 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1585 if (mng_info->found_empty_plte)
1588 Skip the bKGD data byte and CRC.
1591 ReadBlob(image,5,(char *) mng_info->read_buffer);
1592 check=(png_size_t) ReadBlob(image,(size_t) length,
1593 (char *) mng_info->read_buffer);
1594 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1595 mng_info->have_saved_bkgd_index=MagickTrue;
1596 mng_info->bytes_in_read_buffer=0;
1604 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1609 image=(Image *) png_get_io_ptr(png_ptr);
1615 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1617 if (check != length)
1618 png_error(png_ptr,"WriteBlob Failed");
1622 static void png_flush_data(png_structp png_ptr)
1627 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1628 static int PalettesAreEqual(Image *a,Image *b)
1633 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1634 return((int) MagickFalse);
1636 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1637 return((int) MagickFalse);
1639 if (a->colors != b->colors)
1640 return((int) MagickFalse);
1642 for (i=0; i < (ssize_t) a->colors; i++)
1644 if ((a->colormap[i].red != b->colormap[i].red) ||
1645 (a->colormap[i].green != b->colormap[i].green) ||
1646 (a->colormap[i].blue != b->colormap[i].blue))
1647 return((int) MagickFalse);
1650 return((int) MagickTrue);
1654 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1656 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1657 mng_info->exists[i] && !mng_info->frozen[i])
1659 #ifdef MNG_OBJECT_BUFFERS
1660 if (mng_info->ob[i] != (MngBuffer *) NULL)
1662 if (mng_info->ob[i]->reference_count > 0)
1663 mng_info->ob[i]->reference_count--;
1665 if (mng_info->ob[i]->reference_count == 0)
1667 if (mng_info->ob[i]->image != (Image *) NULL)
1668 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1670 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1673 mng_info->ob[i]=(MngBuffer *) NULL;
1675 mng_info->exists[i]=MagickFalse;
1676 mng_info->invisible[i]=MagickFalse;
1677 mng_info->viewable[i]=MagickFalse;
1678 mng_info->frozen[i]=MagickFalse;
1679 mng_info->x_off[i]=0;
1680 mng_info->y_off[i]=0;
1681 mng_info->object_clip[i].left=0;
1682 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1683 mng_info->object_clip[i].top=0;
1684 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1688 static void MngInfoFreeStruct(MngInfo *mng_info,
1689 MagickBooleanType *have_mng_structure)
1691 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1696 for (i=1; i < MNG_MAX_OBJECTS; i++)
1697 MngInfoDiscardObject(mng_info,i);
1699 if (mng_info->global_plte != (png_colorp) NULL)
1700 mng_info->global_plte=(png_colorp)
1701 RelinquishMagickMemory(mng_info->global_plte);
1703 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1704 *have_mng_structure=MagickFalse;
1708 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1714 if (box.left < box2.left)
1717 if (box.top < box2.top)
1720 if (box.right > box2.right)
1721 box.right=box2.right;
1723 if (box.bottom > box2.bottom)
1724 box.bottom=box2.bottom;
1729 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1735 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1737 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1738 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1739 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1740 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1741 if (delta_type != 0)
1743 box.left+=previous_box.left;
1744 box.right+=previous_box.right;
1745 box.top+=previous_box.top;
1746 box.bottom+=previous_box.bottom;
1752 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1758 Read two ssize_ts from CLON, MOVE or PAST chunk
1760 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1761 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1763 if (delta_type != 0)
1765 pair.a+=previous_pair.a;
1766 pair.b+=previous_pair.b;
1772 static long mng_get_long(unsigned char *p)
1774 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1777 typedef struct _PNGErrorInfo
1786 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1797 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1798 image=error_info->image;
1799 exception=error_info->exception;
1801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1802 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1804 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1805 "`%s'",image->filename);
1807 #if (PNG_LIBPNG_VER < 10500)
1808 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1809 * are building with libpng-1.4.x and can be ignored.
1811 longjmp(ping->jmpbuf,1);
1813 png_longjmp(ping,1);
1817 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1828 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1829 png_error(ping, message);
1831 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1832 image=error_info->image;
1833 exception=error_info->exception;
1834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1835 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1837 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1838 message,"`%s'",image->filename);
1841 #ifdef PNG_USER_MEM_SUPPORTED
1842 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1845 return((png_voidp) AcquireMagickMemory((size_t) size));
1849 Free a pointer. It is removed from the list at the same time.
1851 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1854 ptr=RelinquishMagickMemory(ptr);
1855 return((png_free_ptr) NULL);
1859 #if defined(__cplusplus) || defined(c_plusplus)
1864 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1865 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1870 register unsigned char
1884 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1885 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1886 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1887 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1888 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1892 /* look for newline */
1896 /* look for length */
1897 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1900 length=(png_uint_32) StringToLong(sp);
1902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1903 " length: %lu",(unsigned long) length);
1905 while (*sp != ' ' && *sp != '\n')
1908 /* allocate space */
1911 png_warning(ping,"invalid profile length");
1912 return(MagickFalse);
1915 profile=BlobToStringInfo((const void *) NULL,length);
1917 if (profile == (StringInfo *) NULL)
1919 png_warning(ping, "unable to copy profile");
1920 return(MagickFalse);
1923 /* copy profile, skipping white space and column 1 "=" signs */
1924 dp=GetStringInfoDatum(profile);
1927 for (i=0; i < (ssize_t) nibbles; i++)
1929 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1933 png_warning(ping, "ran out of profile data");
1934 profile=DestroyStringInfo(profile);
1935 return(MagickFalse);
1941 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1944 (*dp++)+=unhex[(int) *sp++];
1947 We have already read "Raw profile type.
1949 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1950 profile=DestroyStringInfo(profile);
1952 if (image_info->verbose)
1953 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1958 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1959 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1965 /* The unknown chunk structure contains the chunk data:
1970 Note that libpng has already taken care of the CRC handling.
1974 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1975 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1976 return(0); /* Did not recognize */
1978 /* recognized vpAg */
1980 if (chunk->size != 9)
1981 return(-1); /* Error return */
1983 if (chunk->data[8] != 0)
1984 return(0); /* ImageMagick requires pixel units */
1986 image=(Image *) png_get_user_chunk_ptr(ping);
1988 image->page.width=(size_t) ((chunk->data[0] << 24) |
1989 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1991 image->page.height=(size_t) ((chunk->data[4] << 24) |
1992 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1994 /* Return one of the following: */
1995 /* return(-n); chunk had an error */
1996 /* return(0); did not recognize */
1997 /* return(n); success */
2005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2009 % R e a d O n e P N G I m a g e %
2013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2015 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2016 % (minus the 8-byte signature) and returns it. It allocates the memory
2017 % necessary for the new Image structure and returns a pointer to the new
2020 % The format of the ReadOnePNGImage method is:
2022 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2023 % ExceptionInfo *exception)
2025 % A description of each parameter follows:
2027 % o mng_info: Specifies a pointer to a MngInfo structure.
2029 % o image_info: the image info.
2031 % o exception: return any errors or warnings in this structure.
2034 static Image *ReadOnePNGImage(MngInfo *mng_info,
2035 const ImageInfo *image_info, ExceptionInfo *exception)
2037 /* Read one PNG image */
2039 /* To do: Read the tIME chunk into the date:modify property */
2040 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2054 ping_interlace_method,
2055 ping_compression_method,
2106 register unsigned char
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 #if (PNG_LIBPNG_VER < 10200)
2145 if (image_info->verbose)
2146 printf("Your PNG library (libpng-%s) is rather old.\n",
2147 PNG_LIBPNG_VER_STRING);
2150 #if (PNG_LIBPNG_VER >= 10400)
2151 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2152 if (image_info->verbose)
2154 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2155 PNG_LIBPNG_VER_STRING);
2156 printf("Please update it.\n");
2162 quantum_info = (QuantumInfo *) NULL;
2163 image=mng_info->image;
2165 if (logging != MagickFalse)
2166 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2167 " image->matte=%d",(int) image->matte);
2169 /* Set to an out-of-range color unless tRNS chunk is present */
2170 transparent_color.red=65537;
2171 transparent_color.green=65537;
2172 transparent_color.blue=65537;
2173 transparent_color.alpha=65537;
2177 num_raw_profiles = 0;
2180 Allocate the PNG structures
2182 #ifdef PNG_USER_MEM_SUPPORTED
2183 error_info.image=image;
2184 error_info.exception=exception;
2185 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2186 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2187 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2189 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2190 MagickPNGErrorHandler,MagickPNGWarningHandler);
2192 if (ping == (png_struct *) NULL)
2193 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2195 ping_info=png_create_info_struct(ping);
2197 if (ping_info == (png_info *) NULL)
2199 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2200 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2203 end_info=png_create_info_struct(ping);
2205 if (end_info == (png_info *) NULL)
2207 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2208 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2211 ping_pixels=(unsigned char *) NULL;
2213 if (setjmp(png_jmpbuf(ping)))
2216 PNG image is corrupt.
2218 png_destroy_read_struct(&ping,&ping_info,&end_info);
2220 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2221 UnlockSemaphoreInfo(ping_semaphore);
2224 if (ping_pixels != (unsigned char *) NULL)
2225 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2227 if (logging != MagickFalse)
2228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2229 " exit ReadOnePNGImage() with error.");
2231 if (image != (Image *) NULL)
2233 InheritException(exception,exception);
2237 return(GetFirstImageInList(image));
2240 /* { For navigation to end of SETJMP-protected block. Within this
2241 * block, use png_error() instead of Throwing an Exception, to ensure
2242 * that libpng is able to clean up, and that the semaphore is unlocked.
2245 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2246 LockSemaphoreInfo(ping_semaphore);
2250 Prepare PNG for reading.
2253 mng_info->image_found++;
2254 png_set_sig_bytes(ping,8);
2256 if (LocaleCompare(image_info->magick,"MNG") == 0)
2258 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2259 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2260 png_set_read_fn(ping,image,png_get_data);
2262 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2263 png_permit_empty_plte(ping,MagickTrue);
2264 png_set_read_fn(ping,image,png_get_data);
2266 mng_info->image=image;
2267 mng_info->bytes_in_read_buffer=0;
2268 mng_info->found_empty_plte=MagickFalse;
2269 mng_info->have_saved_bkgd_index=MagickFalse;
2270 png_set_read_fn(ping,mng_info,mng_get_data);
2276 png_set_read_fn(ping,image,png_get_data);
2278 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2279 /* Ignore unused chunks and all unknown chunks except for vpAg */
2280 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2281 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2282 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2283 (int)sizeof(unused_chunks)/5);
2284 /* Callback for other unknown chunks */
2285 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2288 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2289 /* Disable new libpng-1.5.10 feature */
2290 png_set_check_for_invalid_index (ping, 0);
2293 #if (PNG_LIBPNG_VER < 10400)
2294 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2295 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2296 /* Disable thread-unsafe features of pnggccrd */
2297 if (png_access_version_number() >= 10200)
2299 png_uint_32 mmx_disable_mask=0;
2300 png_uint_32 asm_flags;
2302 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2303 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2304 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2305 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2306 asm_flags=png_get_asm_flags(ping);
2307 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2312 png_read_info(ping,ping_info);
2314 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2315 &ping_bit_depth,&ping_color_type,
2316 &ping_interlace_method,&ping_compression_method,
2317 &ping_filter_method);
2319 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2322 (void) png_get_bKGD(ping, ping_info, &ping_background);
2324 if (ping_bit_depth < 8)
2326 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2328 png_set_packing(ping);
2333 image->depth=ping_bit_depth;
2334 image->depth=GetImageQuantumDepth(image,MagickFalse);
2335 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2336 if (logging != MagickFalse)
2338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2339 " PNG width: %.20g, height: %.20g",
2340 (double) ping_width, (double) ping_height);
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " PNG color_type: %d, bit_depth: %d",
2344 ping_color_type, ping_bit_depth);
2346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2347 " PNG compression_method: %d",
2348 ping_compression_method);
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " PNG interlace_method: %d, filter_method: %d",
2352 ping_interlace_method,ping_filter_method);
2355 #ifdef PNG_READ_iCCP_SUPPORTED
2356 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2361 #if (PNG_LIBPNG_VER < 10500)
2375 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2378 if (profile_length != 0)
2383 if (logging != MagickFalse)
2384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2385 " Reading PNG iCCP chunk.");
2386 profile=BlobToStringInfo(info,profile_length);
2387 if (profile == (StringInfo *) NULL)
2389 png_warning(ping, "ICC profile is NULL");
2390 profile=DestroyStringInfo(profile);
2394 (void) SetImageProfile(image,"icc",profile,exception);
2395 profile=DestroyStringInfo(profile);
2400 #if defined(PNG_READ_sRGB_SUPPORTED)
2402 if (mng_info->have_global_srgb)
2404 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2405 (mng_info->global_srgb_intent);
2408 if (png_get_sRGB(ping,ping_info,&intent))
2410 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2413 if (logging != MagickFalse)
2414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2415 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2420 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2421 if (mng_info->have_global_gama)
2422 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2424 if (png_get_gAMA(ping,ping_info,&file_gamma))
2426 image->gamma=(float) file_gamma;
2427 if (logging != MagickFalse)
2428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2429 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2432 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2434 if (mng_info->have_global_chrm != MagickFalse)
2436 (void) png_set_cHRM(ping,ping_info,
2437 mng_info->global_chrm.white_point.x,
2438 mng_info->global_chrm.white_point.y,
2439 mng_info->global_chrm.red_primary.x,
2440 mng_info->global_chrm.red_primary.y,
2441 mng_info->global_chrm.green_primary.x,
2442 mng_info->global_chrm.green_primary.y,
2443 mng_info->global_chrm.blue_primary.x,
2444 mng_info->global_chrm.blue_primary.y);
2448 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2450 (void) png_get_cHRM(ping,ping_info,
2451 &image->chromaticity.white_point.x,
2452 &image->chromaticity.white_point.y,
2453 &image->chromaticity.red_primary.x,
2454 &image->chromaticity.red_primary.y,
2455 &image->chromaticity.green_primary.x,
2456 &image->chromaticity.green_primary.y,
2457 &image->chromaticity.blue_primary.x,
2458 &image->chromaticity.blue_primary.y);
2460 if (logging != MagickFalse)
2461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2462 " Reading PNG cHRM chunk.");
2465 if (image->rendering_intent != UndefinedIntent)
2467 png_set_sRGB(ping,ping_info,
2468 Magick_RenderingIntent_to_PNG_RenderingIntent
2469 (image->rendering_intent));
2470 png_set_gAMA(ping,ping_info,1.000f/2.200f);
2471 png_set_cHRM(ping,ping_info,
2472 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2473 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2475 #if defined(PNG_oFFs_SUPPORTED)
2476 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2478 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2479 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2481 if (logging != MagickFalse)
2482 if (image->page.x || image->page.y)
2483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2484 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2485 image->page.x,(double) image->page.y);
2488 #if defined(PNG_pHYs_SUPPORTED)
2489 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2491 if (mng_info->have_global_phys)
2493 png_set_pHYs(ping,ping_info,
2494 mng_info->global_x_pixels_per_unit,
2495 mng_info->global_y_pixels_per_unit,
2496 mng_info->global_phys_unit_type);
2500 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2503 Set image resolution.
2505 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2507 image->resolution.x=(double) x_resolution;
2508 image->resolution.y=(double) y_resolution;
2510 if (unit_type == PNG_RESOLUTION_METER)
2512 image->units=PixelsPerCentimeterResolution;
2513 image->resolution.x=(double) x_resolution/100.0;
2514 image->resolution.y=(double) y_resolution/100.0;
2517 if (logging != MagickFalse)
2518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2519 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2520 (double) x_resolution,(double) y_resolution,unit_type);
2524 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2532 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2534 if ((number_colors == 0) &&
2535 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2537 if (mng_info->global_plte_length)
2539 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2540 (int) mng_info->global_plte_length);
2542 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2544 if (mng_info->global_trns_length)
2547 "global tRNS has more entries than global PLTE");
2551 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2552 (int) mng_info->global_trns_length,NULL);
2555 #ifdef PNG_READ_bKGD_SUPPORTED
2557 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2558 mng_info->have_saved_bkgd_index ||
2560 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2565 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2566 if (mng_info->have_saved_bkgd_index)
2567 background.index=mng_info->saved_bkgd_index;
2569 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2570 background.index=ping_background->index;
2572 background.red=(png_uint_16)
2573 mng_info->global_plte[background.index].red;
2575 background.green=(png_uint_16)
2576 mng_info->global_plte[background.index].green;
2578 background.blue=(png_uint_16)
2579 mng_info->global_plte[background.index].blue;
2581 background.gray=(png_uint_16)
2582 mng_info->global_plte[background.index].green;
2584 png_set_bKGD(ping,ping_info,&background);
2589 png_error(ping,"No global PLTE in file");
2593 #ifdef PNG_READ_bKGD_SUPPORTED
2594 if (mng_info->have_global_bkgd &&
2595 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2596 image->background_color=mng_info->mng_global_bkgd;
2598 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2604 Set image background color.
2606 if (logging != MagickFalse)
2607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2608 " Reading PNG bKGD chunk.");
2610 /* Scale background components to 16-bit, then scale
2613 if (logging != MagickFalse)
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 " raw ping_background=(%d,%d,%d).",ping_background->red,
2616 ping_background->green,ping_background->blue);
2620 if (ping_bit_depth == 1)
2623 else if (ping_bit_depth == 2)
2626 else if (ping_bit_depth == 4)
2629 if (ping_bit_depth <= 8)
2632 ping_background->red *= bkgd_scale;
2633 ping_background->green *= bkgd_scale;
2634 ping_background->blue *= bkgd_scale;
2636 if (logging != MagickFalse)
2638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2639 " bkgd_scale=%d.",bkgd_scale);
2641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2642 " ping_background=(%d,%d,%d).",ping_background->red,
2643 ping_background->green,ping_background->blue);
2646 image->background_color.red=
2647 ScaleShortToQuantum(ping_background->red);
2649 image->background_color.green=
2650 ScaleShortToQuantum(ping_background->green);
2652 image->background_color.blue=
2653 ScaleShortToQuantum(ping_background->blue);
2655 image->background_color.alpha=OpaqueAlpha;
2657 if (logging != MagickFalse)
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2659 " image->background_color=(%.20g,%.20g,%.20g).",
2660 (double) image->background_color.red,
2661 (double) image->background_color.green,
2662 (double) image->background_color.blue);
2664 #endif /* PNG_READ_bKGD_SUPPORTED */
2666 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2669 Image has a tRNS chunk.
2677 if (logging != MagickFalse)
2678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2679 " Reading PNG tRNS chunk.");
2681 max_sample = (int) ((one << ping_bit_depth) - 1);
2683 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2684 (int)ping_trans_color->gray > max_sample) ||
2685 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2686 ((int)ping_trans_color->red > max_sample ||
2687 (int)ping_trans_color->green > max_sample ||
2688 (int)ping_trans_color->blue > max_sample)))
2690 if (logging != MagickFalse)
2691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2692 " Ignoring PNG tRNS chunk with out-of-range sample.");
2693 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2694 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2695 image->matte=MagickFalse;
2702 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2704 /* Scale transparent_color to short */
2705 transparent_color.red= scale_to_short*ping_trans_color->red;
2706 transparent_color.green= scale_to_short*ping_trans_color->green;
2707 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2708 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2710 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2712 if (logging != MagickFalse)
2714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2715 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2718 " scaled graylevel is %.20g.",transparent_color.alpha);
2720 transparent_color.red=transparent_color.alpha;
2721 transparent_color.green=transparent_color.alpha;
2722 transparent_color.blue=transparent_color.alpha;
2726 #if defined(PNG_READ_sBIT_SUPPORTED)
2727 if (mng_info->have_global_sbit)
2729 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2730 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2733 num_passes=png_set_interlace_handling(ping);
2735 png_read_update_info(ping,ping_info);
2737 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2740 Initialize image structure.
2742 mng_info->image_box.left=0;
2743 mng_info->image_box.right=(ssize_t) ping_width;
2744 mng_info->image_box.top=0;
2745 mng_info->image_box.bottom=(ssize_t) ping_height;
2746 if (mng_info->mng_type == 0)
2748 mng_info->mng_width=ping_width;
2749 mng_info->mng_height=ping_height;
2750 mng_info->frame=mng_info->image_box;
2751 mng_info->clip=mng_info->image_box;
2756 image->page.y=mng_info->y_off[mng_info->object_id];
2759 image->compression=ZipCompression;
2760 image->columns=ping_width;
2761 image->rows=ping_height;
2763 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2764 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2766 if (!png_get_valid(ping,ping_info,PNG_INFO_gAMA) &&
2767 !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
2768 !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2770 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2771 * and reset image->chromaticity.
2773 SetImageColorspace(image,GRAYColorspace,exception);
2778 /* Use colorspace data from PNG ancillary chunks */
2779 image->colorspace=GRAYColorspace;
2783 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2784 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2789 image->storage_class=PseudoClass;
2791 image->colors=one << ping_bit_depth;
2792 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2793 if (image->colors > 256)
2796 if (image->colors > 65536L)
2797 image->colors=65536L;
2799 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2807 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2808 image->colors=(size_t) number_colors;
2810 if (logging != MagickFalse)
2811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2812 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2816 if (image->storage_class == PseudoClass)
2819 Initialize image colormap.
2821 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2822 png_error(ping,"Memory allocation failed");
2824 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2832 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2834 for (i=0; i < (ssize_t) number_colors; i++)
2836 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2837 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2838 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2841 for ( ; i < (ssize_t) image->colors; i++)
2843 image->colormap[i].red=0;
2844 image->colormap[i].green=0;
2845 image->colormap[i].blue=0;
2854 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2859 for (i=0; i < (ssize_t) image->colors; i++)
2861 image->colormap[i].red=(Quantum) (i*scale);
2862 image->colormap[i].green=(Quantum) (i*scale);
2863 image->colormap[i].blue=(Quantum) (i*scale);
2868 /* Set some properties for reporting by "identify" */
2873 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2874 ping_interlace_method in value */
2876 (void) FormatLocaleString(msg,MaxTextExtent,
2877 "%d, %d",(int) ping_width, (int) ping_height);
2878 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2880 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2881 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2883 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2884 (int) ping_color_type,
2885 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
2886 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2888 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2889 (int) ping_interlace_method);
2890 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2894 Read image scanlines.
2896 if (image->delay != 0)
2897 mng_info->scenes_found++;
2899 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2900 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2901 (image_info->first_scene+image_info->number_scenes))))
2903 /* This happens later in non-ping decodes */
2904 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2905 image->storage_class=DirectClass;
2907 if (logging != MagickFalse)
2908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2909 " Skipping PNG image data for scene %.20g",(double)
2910 mng_info->scenes_found-1);
2911 png_destroy_read_struct(&ping,&ping_info,&end_info);
2913 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2914 UnlockSemaphoreInfo(ping_semaphore);
2917 if (logging != MagickFalse)
2918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2919 " exit ReadOnePNGImage().");
2924 if (logging != MagickFalse)
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926 " Reading PNG IDAT chunk(s)");
2929 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2930 ping_rowbytes*sizeof(*ping_pixels));
2933 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2934 sizeof(*ping_pixels));
2936 if (ping_pixels == (unsigned char *) NULL)
2937 png_error(ping,"Memory allocation failed");
2939 if (logging != MagickFalse)
2940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2941 " Converting PNG pixels to pixel packets");
2943 Convert PNG pixels to pixel packets.
2945 quantum_info=AcquireQuantumInfo(image_info,image);
2947 if (quantum_info == (QuantumInfo *) NULL)
2948 png_error(ping,"Failed to allocate quantum_info");
2953 found_transparent_pixel;
2955 found_transparent_pixel=MagickFalse;
2957 if (image->storage_class == DirectClass)
2959 for (pass=0; pass < num_passes; pass++)
2962 Convert image to DirectClass pixel packets.
2964 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2968 depth=(ssize_t) ping_bit_depth;
2970 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2971 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2972 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2973 MagickTrue : MagickFalse;
2975 for (y=0; y < (ssize_t) image->rows; y++)
2978 row_offset=ping_rowbytes*y;
2983 png_read_row(ping,ping_pixels+row_offset,NULL);
2984 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2986 if (q == (Quantum *) NULL)
2989 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2990 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2991 GrayQuantum,ping_pixels+row_offset,exception);
2993 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2994 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2995 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2997 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2998 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2999 RGBAQuantum,ping_pixels+row_offset,exception);
3001 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3002 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3003 IndexQuantum,ping_pixels+row_offset,exception);
3005 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3006 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3007 RGBQuantum,ping_pixels+row_offset,exception);
3009 if (found_transparent_pixel == MagickFalse)
3011 /* Is there a transparent pixel in the row? */
3012 if (y== 0 && logging != MagickFalse)
3013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3014 " Looking for cheap transparent pixel");
3016 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3018 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3019 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3020 (GetPixelAlpha(image,q) != OpaqueAlpha))
3022 if (logging != MagickFalse)
3023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3026 found_transparent_pixel = MagickTrue;
3029 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3030 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3031 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3032 transparent_color.red &&
3033 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3034 transparent_color.green &&
3035 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3036 transparent_color.blue))
3038 if (logging != MagickFalse)
3039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3041 found_transparent_pixel = MagickTrue;
3044 q+=GetPixelChannels(image);
3048 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3050 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3053 if (status == MagickFalse)
3056 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3060 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3062 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3063 if (status == MagickFalse)
3069 else /* image->storage_class != DirectClass */
3071 for (pass=0; pass < num_passes; pass++)
3080 Convert grayscale image to PseudoClass pixel packets.
3082 if (logging != MagickFalse)
3083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3084 " Converting grayscale pixels to pixel packets");
3086 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3087 MagickTrue : MagickFalse;
3089 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3090 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
3092 if (quantum_scanline == (Quantum *) NULL)
3093 png_error(ping,"Memory allocation failed");
3095 for (y=0; y < (ssize_t) image->rows; y++)
3098 row_offset=ping_rowbytes*y;
3103 png_read_row(ping,ping_pixels+row_offset,NULL);
3104 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3106 if (q == (Quantum *) NULL)
3109 p=ping_pixels+row_offset;
3112 switch (ping_bit_depth)
3119 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3121 for (bit=7; bit >= 0; bit--)
3122 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3126 if ((image->columns % 8) != 0)
3128 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3129 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3137 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3139 *r++=(*p >> 6) & 0x03;
3140 *r++=(*p >> 4) & 0x03;
3141 *r++=(*p >> 2) & 0x03;
3145 if ((image->columns % 4) != 0)
3147 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3148 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3156 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3158 *r++=(*p >> 4) & 0x0f;
3162 if ((image->columns % 2) != 0)
3163 *r++=(*p++ >> 4) & 0x0f;
3170 if (ping_color_type == 4)
3171 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3174 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3175 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3176 found_transparent_pixel = MagickTrue;
3177 q+=GetPixelChannels(image);
3181 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3189 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3191 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3195 if (image->colors > 256)
3196 quantum=((*p++) << 8);
3202 *r=ScaleShortToQuantum(quantum);
3205 if (ping_color_type == 4)
3207 if (image->colors > 256)
3208 quantum=((*p++) << 8);
3213 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3214 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3215 found_transparent_pixel = MagickTrue;
3216 q+=GetPixelChannels(image);
3219 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3221 p++; /* strip low byte */
3223 if (ping_color_type == 4)
3225 SetPixelAlpha(image,*p++,q);
3226 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3227 found_transparent_pixel = MagickTrue;
3229 q+=GetPixelChannels(image);
3242 Transfer image scanline.
3246 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3248 if (q == (Quantum *) NULL)
3250 for (x=0; x < (ssize_t) image->columns; x++)
3252 SetPixelIndex(image,*r++,q);
3253 q+=GetPixelChannels(image);
3256 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3259 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3261 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3264 if (status == MagickFalse)
3269 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3271 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3273 if (status == MagickFalse)
3277 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3280 image->matte=found_transparent_pixel;
3282 if (logging != MagickFalse)
3284 if (found_transparent_pixel != MagickFalse)
3285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3286 " Found transparent pixel");
3289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3290 " No transparent pixel was found");
3292 ping_color_type&=0x03;
3297 if (quantum_info != (QuantumInfo *) NULL)
3298 quantum_info=DestroyQuantumInfo(quantum_info);
3300 if (image->storage_class == PseudoClass)
3306 image->matte=MagickFalse;
3307 (void) SyncImage(image,exception);
3311 png_read_end(ping,end_info);
3313 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3314 (ssize_t) image_info->first_scene && image->delay != 0)
3316 png_destroy_read_struct(&ping,&ping_info,&end_info);
3317 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3319 (void) SetImageBackgroundColor(image,exception);
3320 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3321 UnlockSemaphoreInfo(ping_semaphore);
3323 if (logging != MagickFalse)
3324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3325 " exit ReadOnePNGImage() early.");
3329 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3335 Image has a transparent background.
3337 storage_class=image->storage_class;
3338 image->matte=MagickTrue;
3340 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3342 if (storage_class == PseudoClass)
3344 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3346 for (x=0; x < ping_num_trans; x++)
3348 image->colormap[x].matte=MagickTrue;
3349 image->colormap[x].alpha =
3350 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3354 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3356 for (x=0; x < (int) image->colors; x++)
3358 if (ScaleQuantumToShort(image->colormap[x].red) ==
3359 transparent_color.alpha)
3361 image->colormap[x].matte=MagickTrue;
3362 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3366 (void) SyncImage(image,exception);
3369 #if 1 /* Should have already been done above, but glennrp problem P10
3374 for (y=0; y < (ssize_t) image->rows; y++)
3376 image->storage_class=storage_class;
3377 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3379 if (q == (Quantum *) NULL)
3383 /* Caution: on a Q8 build, this does not distinguish between
3384 * 16-bit colors that differ only in the low byte
3386 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3388 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3389 transparent_color.red &&
3390 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3391 transparent_color.green &&
3392 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3393 transparent_color.blue)
3395 SetPixelAlpha(image,TransparentAlpha,q);
3398 #if 0 /* I have not found a case where this is needed. */
3401 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3405 q+=GetPixelChannels(image);
3408 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3414 image->storage_class=DirectClass;
3417 for (j = 0; j < 2; j++)
3420 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3421 MagickTrue : MagickFalse;
3423 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3424 MagickTrue : MagickFalse;
3426 if (status != MagickFalse)
3427 for (i=0; i < (ssize_t) num_text; i++)
3429 /* Check for a profile */
3431 if (logging != MagickFalse)
3432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3433 " Reading PNG text chunk");
3435 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3437 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3447 length=text[i].text_length;
3448 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3450 if (value == (char *) NULL)
3452 png_error(ping,"Memory allocation failed");
3456 (void) ConcatenateMagickString(value,text[i].text,length+2);
3458 /* Don't save "density" or "units" property if we have a pHYs
3461 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3462 (LocaleCompare(text[i].key,"density") != 0 &&
3463 LocaleCompare(text[i].key,"units") != 0))
3464 (void) SetImageProperty(image,text[i].key,value,exception);
3466 if (logging != MagickFalse)
3468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3469 " length: %lu",(unsigned long) length);
3470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3471 " Keyword: %s",text[i].key);
3474 value=DestroyString(value);
3477 num_text_total += num_text;
3480 #ifdef MNG_OBJECT_BUFFERS
3482 Store the object if necessary.
3484 if (object_id && !mng_info->frozen[object_id])
3486 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3489 create a new object buffer.
3491 mng_info->ob[object_id]=(MngBuffer *)
3492 AcquireMagickMemory(sizeof(MngBuffer));
3494 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3496 mng_info->ob[object_id]->image=(Image *) NULL;
3497 mng_info->ob[object_id]->reference_count=1;
3501 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3502 mng_info->ob[object_id]->frozen)
3504 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3505 png_error(ping,"Memory allocation failed");
3507 if (mng_info->ob[object_id]->frozen)
3508 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3514 if (mng_info->ob[object_id]->image != (Image *) NULL)
3515 mng_info->ob[object_id]->image=DestroyImage
3516 (mng_info->ob[object_id]->image);
3518 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3521 if (mng_info->ob[object_id]->image != (Image *) NULL)
3522 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3525 png_error(ping, "Cloning image for object buffer failed");
3527 if (ping_width > 250000L || ping_height > 250000L)
3528 png_error(ping,"PNG Image dimensions are too large.");
3530 mng_info->ob[object_id]->width=ping_width;
3531 mng_info->ob[object_id]->height=ping_height;
3532 mng_info->ob[object_id]->color_type=ping_color_type;
3533 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3534 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3535 mng_info->ob[object_id]->compression_method=
3536 ping_compression_method;
3537 mng_info->ob[object_id]->filter_method=ping_filter_method;
3539 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3548 Copy the PLTE to the object buffer.
3550 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3551 mng_info->ob[object_id]->plte_length=number_colors;
3553 for (i=0; i < number_colors; i++)
3555 mng_info->ob[object_id]->plte[i]=plte[i];
3560 mng_info->ob[object_id]->plte_length=0;
3565 /* Set image->matte to MagickTrue if the input colortype supports
3566 * alpha or if a valid tRNS chunk is present, no matter whether there
3567 * is actual transparency present.
3569 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3570 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3571 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3572 MagickTrue : MagickFalse;
3574 /* Set more properties for identify to retrieve */
3579 if (num_text_total != 0)
3581 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3582 (void) FormatLocaleString(msg,MaxTextExtent,
3583 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3584 (void) SetImageProperty(image,"png:text ",msg,
3588 if (num_raw_profiles != 0)
3590 (void) FormatLocaleString(msg,MaxTextExtent,
3591 "%d were found", num_raw_profiles);
3592 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3596 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3598 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3599 "chunk was found (see Chromaticity, above)");
3600 (void) SetImageProperty(image,"png:cHRM ",msg,
3604 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3606 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3607 "chunk was found (see Background color, above)");
3608 (void) SetImageProperty(image,"png:bKGD ",msg,
3612 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3615 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3616 (void) SetImageProperty(image,"png:iCCP ",msg,
3619 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3620 (void) SetImageProperty(image,"png:tRNS ",msg,
3623 #if defined(PNG_sRGB_SUPPORTED)
3624 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3626 (void) FormatLocaleString(msg,MaxTextExtent,
3627 "intent=%d (See Rendering intent)", (int) intent);
3628 (void) SetImageProperty(image,"png:sRGB ",msg,
3633 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3635 (void) FormatLocaleString(msg,MaxTextExtent,
3636 "gamma=%.8g (See Gamma, above)",
3638 (void) SetImageProperty(image,"png:gAMA ",msg,
3642 #if defined(PNG_pHYs_SUPPORTED)
3643 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3645 (void) FormatLocaleString(msg,MaxTextExtent,
3646 "x_res=%.10g, y_res=%.10g, units=%d",
3647 (double) x_resolution,(double) y_resolution, unit_type);
3648 (void) SetImageProperty(image,"png:pHYs ",msg,
3653 #if defined(PNG_oFFs_SUPPORTED)
3654 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3656 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3657 (double) image->page.x,(double) image->page.y);
3658 (void) SetImageProperty(image,"png:oFFs ",msg,
3663 if ((image->page.width != 0 && image->page.width != image->columns) ||
3664 (image->page.height != 0 && image->page.height != image->rows))
3666 (void) FormatLocaleString(msg,MaxTextExtent,
3667 "width=%.20g, height=%.20g",
3668 (double) image->page.width,(double) image->page.height);
3669 (void) SetImageProperty(image,"png:vpAg ",msg,
3675 Relinquish resources.
3677 png_destroy_read_struct(&ping,&ping_info,&end_info);
3679 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3681 if (logging != MagickFalse)
3682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3683 " exit ReadOnePNGImage()");
3685 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3686 UnlockSemaphoreInfo(ping_semaphore);
3689 /* } for navigation to beginning of SETJMP-protected block, revert to
3690 * Throwing an Exception when an error occurs.
3695 /* end of reading one PNG image */
3698 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3713 magic_number[MaxTextExtent];
3721 assert(image_info != (const ImageInfo *) NULL);
3722 assert(image_info->signature == MagickSignature);
3724 if (image_info->debug != MagickFalse)
3725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3726 image_info->filename);
3728 assert(exception != (ExceptionInfo *) NULL);
3729 assert(exception->signature == MagickSignature);
3730 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3731 image=AcquireImage(image_info,exception);
3732 mng_info=(MngInfo *) NULL;
3733 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3735 if (status == MagickFalse)
3736 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3739 Verify PNG signature.
3741 count=ReadBlob(image,8,(unsigned char *) magic_number);
3743 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3744 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3747 Allocate a MngInfo structure.
3749 have_mng_structure=MagickFalse;
3750 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3752 if (mng_info == (MngInfo *) NULL)
3753 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3756 Initialize members of the MngInfo structure.
3758 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3759 mng_info->image=image;
3760 have_mng_structure=MagickTrue;
3763 image=ReadOnePNGImage(mng_info,image_info,exception);
3764 MngInfoFreeStruct(mng_info,&have_mng_structure);
3766 if (image == (Image *) NULL)
3768 if (previous != (Image *) NULL)
3770 if (previous->signature != MagickSignature)
3771 ThrowReaderException(CorruptImageError,"CorruptImage");
3773 (void) CloseBlob(previous);
3774 (void) DestroyImageList(previous);
3777 if (logging != MagickFalse)
3778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3779 "exit ReadPNGImage() with error");
3781 return((Image *) NULL);
3784 (void) CloseBlob(image);
3786 if ((image->columns == 0) || (image->rows == 0))
3788 if (logging != MagickFalse)
3789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3790 "exit ReadPNGImage() with error.");
3792 ThrowReaderException(CorruptImageError,"CorruptImage");
3795 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3797 (void) SetImageType(image,TrueColorType,exception);
3798 image->matte=MagickFalse;
3801 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3802 (void) SetImageType(image,TrueColorMatteType,exception);
3804 if (logging != MagickFalse)
3805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3806 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3807 (double) image->page.width,(double) image->page.height,
3808 (double) image->page.x,(double) image->page.y);
3810 if (logging != MagickFalse)
3811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3818 #if defined(JNG_SUPPORTED)
3820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3824 % R e a d O n e J N G I m a g e %
3828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3830 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3831 % (minus the 8-byte signature) and returns it. It allocates the memory
3832 % necessary for the new Image structure and returns a pointer to the new
3835 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3837 % The format of the ReadOneJNGImage method is:
3839 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3840 % ExceptionInfo *exception)
3842 % A description of each parameter follows:
3844 % o mng_info: Specifies a pointer to a MngInfo structure.
3846 % o image_info: the image info.
3848 % o exception: return any errors or warnings in this structure.
3851 static Image *ReadOneJNGImage(MngInfo *mng_info,
3852 const ImageInfo *image_info, ExceptionInfo *exception)
3879 jng_image_sample_depth,
3880 jng_image_compression_method,
3881 jng_image_interlace_method,
3882 jng_alpha_sample_depth,
3883 jng_alpha_compression_method,
3884 jng_alpha_filter_method,
3885 jng_alpha_interlace_method;
3887 register const Quantum
3897 register unsigned char
3908 jng_alpha_compression_method=0;
3909 jng_alpha_sample_depth=8;
3913 alpha_image=(Image *) NULL;
3914 color_image=(Image *) NULL;
3915 alpha_image_info=(ImageInfo *) NULL;
3916 color_image_info=(ImageInfo *) NULL;
3918 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3919 " Enter ReadOneJNGImage()");
3921 image=mng_info->image;
3923 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3926 Allocate next image structure.
3928 if (logging != MagickFalse)
3929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3930 " AcquireNextImage()");
3932 AcquireNextImage(image_info,image,exception);
3934 if (GetNextImageInList(image) == (Image *) NULL)
3935 return((Image *) NULL);
3937 image=SyncNextImageInList(image);
3939 mng_info->image=image;
3942 Signature bytes have already been read.
3945 read_JSEP=MagickFalse;
3946 reading_idat=MagickFalse;
3947 skip_to_iend=MagickFalse;
3951 type[MaxTextExtent];
3960 Read a new JNG chunk.
3962 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3963 2*GetBlobSize(image));
3965 if (status == MagickFalse)
3969 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3970 length=ReadBlobMSBLong(image);
3971 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3973 if (logging != MagickFalse)
3974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3975 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3976 type[0],type[1],type[2],type[3],(double) length);
3978 if (length > PNG_UINT_31_MAX || count == 0)
3979 ThrowReaderException(CorruptImageError,"CorruptImage");
3982 chunk=(unsigned char *) NULL;
3986 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3988 if (chunk == (unsigned char *) NULL)
3989 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3991 for (i=0; i < (ssize_t) length; i++)
3992 chunk[i]=(unsigned char) ReadBlobByte(image);
3997 (void) ReadBlobMSBLong(image); /* read crc word */
4002 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4007 if (memcmp(type,mng_JHDR,4) == 0)
4011 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4012 (p[2] << 8) | p[3]);
4013 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4014 (p[6] << 8) | p[7]);
4015 jng_color_type=p[8];
4016 jng_image_sample_depth=p[9];
4017 jng_image_compression_method=p[10];
4018 jng_image_interlace_method=p[11];
4020 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4023 jng_alpha_sample_depth=p[12];
4024 jng_alpha_compression_method=p[13];
4025 jng_alpha_filter_method=p[14];
4026 jng_alpha_interlace_method=p[15];
4028 if (logging != MagickFalse)
4030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4031 " jng_width: %16lu",(unsigned long) jng_width);
4033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4034 " jng_width: %16lu",(unsigned long) jng_height);
4036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4037 " jng_color_type: %16d",jng_color_type);
4039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4040 " jng_image_sample_depth: %3d",
4041 jng_image_sample_depth);
4043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4044 " jng_image_compression_method:%3d",
4045 jng_image_compression_method);
4047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4048 " jng_image_interlace_method: %3d",
4049 jng_image_interlace_method);
4051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4052 " jng_alpha_sample_depth: %3d",
4053 jng_alpha_sample_depth);
4055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4056 " jng_alpha_compression_method:%3d",
4057 jng_alpha_compression_method);
4059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4060 " jng_alpha_filter_method: %3d",
4061 jng_alpha_filter_method);
4063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4064 " jng_alpha_interlace_method: %3d",
4065 jng_alpha_interlace_method);
4070 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4076 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4077 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4078 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4081 o create color_image
4082 o open color_blob, attached to color_image
4083 o if (color type has alpha)
4084 open alpha_blob, attached to alpha_image
4087 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4089 if (color_image_info == (ImageInfo *) NULL)
4090 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4092 GetImageInfo(color_image_info);
4093 color_image=AcquireImage(color_image_info,exception);
4095 if (color_image == (Image *) NULL)
4096 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4098 if (logging != MagickFalse)
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4100 " Creating color_blob.");
4102 (void) AcquireUniqueFilename(color_image->filename);
4103 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4106 if (status == MagickFalse)
4107 return((Image *) NULL);
4109 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4111 alpha_image_info=(ImageInfo *)
4112 AcquireMagickMemory(sizeof(ImageInfo));
4114 if (alpha_image_info == (ImageInfo *) NULL)
4115 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4117 GetImageInfo(alpha_image_info);
4118 alpha_image=AcquireImage(alpha_image_info,exception);
4120 if (alpha_image == (Image *) NULL)
4122 alpha_image=DestroyImage(alpha_image);
4123 ThrowReaderException(ResourceLimitError,
4124 "MemoryAllocationFailed");
4127 if (logging != MagickFalse)
4128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4129 " Creating alpha_blob.");
4131 (void) AcquireUniqueFilename(alpha_image->filename);
4132 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4135 if (status == MagickFalse)
4136 return((Image *) NULL);
4138 if (jng_alpha_compression_method == 0)
4143 if (logging != MagickFalse)
4144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4145 " Writing IHDR chunk to alpha_blob.");
4147 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4148 "\211PNG\r\n\032\n");
4150 (void) WriteBlobMSBULong(alpha_image,13L);
4151 PNGType(data,mng_IHDR);
4152 LogPNGChunk(logging,mng_IHDR,13L);
4153 PNGLong(data+4,jng_width);
4154 PNGLong(data+8,jng_height);
4155 data[12]=jng_alpha_sample_depth;
4156 data[13]=0; /* color_type gray */
4157 data[14]=0; /* compression method 0 */
4158 data[15]=0; /* filter_method 0 */
4159 data[16]=0; /* interlace_method 0 */
4160 (void) WriteBlob(alpha_image,17,data);
4161 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4164 reading_idat=MagickTrue;
4167 if (memcmp(type,mng_JDAT,4) == 0)
4169 /* Copy chunk to color_image->blob */
4171 if (logging != MagickFalse)
4172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4173 " Copying JDAT chunk data to color_blob.");
4175 (void) WriteBlob(color_image,length,chunk);
4178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4183 if (memcmp(type,mng_IDAT,4) == 0)
4188 /* Copy IDAT header and chunk data to alpha_image->blob */
4190 if (image_info->ping == MagickFalse)
4192 if (logging != MagickFalse)
4193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4194 " Copying IDAT chunk data to alpha_blob.");
4196 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4197 PNGType(data,mng_IDAT);
4198 LogPNGChunk(logging,mng_IDAT,length);
4199 (void) WriteBlob(alpha_image,4,data);
4200 (void) WriteBlob(alpha_image,length,chunk);
4201 (void) WriteBlobMSBULong(alpha_image,
4202 crc32(crc32(0,data,4),chunk,(uInt) length));
4206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4213 /* Copy chunk data to alpha_image->blob */
4215 if (image_info->ping == MagickFalse)
4217 if (logging != MagickFalse)
4218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4219 " Copying JDAA chunk data to alpha_blob.");
4221 (void) WriteBlob(alpha_image,length,chunk);
4225 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4230 if (memcmp(type,mng_JSEP,4) == 0)
4232 read_JSEP=MagickTrue;
4235 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4240 if (memcmp(type,mng_bKGD,4) == 0)
4244 image->background_color.red=ScaleCharToQuantum(p[1]);
4245 image->background_color.green=image->background_color.red;
4246 image->background_color.blue=image->background_color.red;
4251 image->background_color.red=ScaleCharToQuantum(p[1]);
4252 image->background_color.green=ScaleCharToQuantum(p[3]);
4253 image->background_color.blue=ScaleCharToQuantum(p[5]);
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4260 if (memcmp(type,mng_gAMA,4) == 0)
4263 image->gamma=((float) mng_get_long(p))*0.00001;
4265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4269 if (memcmp(type,mng_cHRM,4) == 0)
4273 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4274 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4275 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4276 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4277 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4278 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4279 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4280 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4283 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4287 if (memcmp(type,mng_sRGB,4) == 0)
4291 image->rendering_intent=
4292 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4293 image->gamma=1.000f/2.200f;
4294 image->chromaticity.red_primary.x=0.6400f;
4295 image->chromaticity.red_primary.y=0.3300f;
4296 image->chromaticity.green_primary.x=0.3000f;
4297 image->chromaticity.green_primary.y=0.6000f;
4298 image->chromaticity.blue_primary.x=0.1500f;
4299 image->chromaticity.blue_primary.y=0.0600f;
4300 image->chromaticity.white_point.x=0.3127f;
4301 image->chromaticity.white_point.y=0.3290f;
4304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4308 if (memcmp(type,mng_oFFs,4) == 0)
4312 image->page.x=(ssize_t) mng_get_long(p);
4313 image->page.y=(ssize_t) mng_get_long(&p[4]);
4315 if ((int) p[8] != 0)
4317 image->page.x/=10000;
4318 image->page.y/=10000;
4323 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4328 if (memcmp(type,mng_pHYs,4) == 0)
4332 image->resolution.x=(double) mng_get_long(p);
4333 image->resolution.y=(double) mng_get_long(&p[4]);
4334 if ((int) p[8] == PNG_RESOLUTION_METER)
4336 image->units=PixelsPerCentimeterResolution;
4337 image->resolution.x=image->resolution.x/100.0f;
4338 image->resolution.y=image->resolution.y/100.0f;
4342 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4347 if (memcmp(type,mng_iCCP,4) == 0)
4351 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4358 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4360 if (memcmp(type,mng_IEND,4))
4370 Finish up reading image data:
4372 o read main image from color_blob.
4376 o if (color_type has alpha)
4377 if alpha_encoding is PNG
4378 read secondary image from alpha_blob via ReadPNG
4379 if alpha_encoding is JPEG
4380 read secondary image from alpha_blob via ReadJPEG
4384 o copy intensity of secondary image into
4385 alpha samples of main image.
4387 o destroy the secondary image.
4390 (void) CloseBlob(color_image);
4392 if (logging != MagickFalse)
4393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4394 " Reading jng_image from color_blob.");
4396 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4397 color_image->filename);
4399 color_image_info->ping=MagickFalse; /* To do: avoid this */
4400 jng_image=ReadImage(color_image_info,exception);
4402 if (jng_image == (Image *) NULL)
4403 return((Image *) NULL);
4405 (void) RelinquishUniqueFileResource(color_image->filename);
4406 color_image=DestroyImage(color_image);
4407 color_image_info=DestroyImageInfo(color_image_info);
4409 if (jng_image == (Image *) NULL)
4410 return((Image *) NULL);
4412 if (logging != MagickFalse)
4413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4414 " Copying jng_image pixels to main image.");
4416 image->rows=jng_height;
4417 image->columns=jng_width;
4419 for (y=0; y < (ssize_t) image->rows; y++)
4421 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4422 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4423 for (x=(ssize_t) image->columns; x != 0; x--)
4425 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4426 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4427 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4428 q+=GetPixelChannels(image);
4429 s+=GetPixelChannels(jng_image);
4432 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4436 jng_image=DestroyImage(jng_image);
4438 if (image_info->ping == MagickFalse)
4440 if (jng_color_type >= 12)
4442 if (jng_alpha_compression_method == 0)
4446 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4447 PNGType(data,mng_IEND);
4448 LogPNGChunk(logging,mng_IEND,0L);
4449 (void) WriteBlob(alpha_image,4,data);
4450 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4453 (void) CloseBlob(alpha_image);
4455 if (logging != MagickFalse)
4456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4457 " Reading alpha from alpha_blob.");
4459 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4460 "%s",alpha_image->filename);
4462 jng_image=ReadImage(alpha_image_info,exception);
4464 if (jng_image != (Image *) NULL)
4465 for (y=0; y < (ssize_t) image->rows; y++)
4467 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4469 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4471 if (image->matte != MagickFalse)
4472 for (x=(ssize_t) image->columns; x != 0; x--)
4474 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4475 q+=GetPixelChannels(image);
4476 s+=GetPixelChannels(jng_image);
4480 for (x=(ssize_t) image->columns; x != 0; x--)
4482 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4483 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4484 image->matte=MagickTrue;
4485 q+=GetPixelChannels(image);
4486 s+=GetPixelChannels(jng_image);
4489 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4492 (void) RelinquishUniqueFileResource(alpha_image->filename);
4493 alpha_image=DestroyImage(alpha_image);
4494 alpha_image_info=DestroyImageInfo(alpha_image_info);
4495 if (jng_image != (Image *) NULL)
4496 jng_image=DestroyImage(jng_image);
4500 /* Read the JNG image. */
4502 if (mng_info->mng_type == 0)
4504 mng_info->mng_width=jng_width;
4505 mng_info->mng_height=jng_height;
4508 if (image->page.width == 0 && image->page.height == 0)
4510 image->page.width=jng_width;
4511 image->page.height=jng_height;
4514 if (image->page.x == 0 && image->page.y == 0)
4516 image->page.x=mng_info->x_off[mng_info->object_id];
4517 image->page.y=mng_info->y_off[mng_info->object_id];
4522 image->page.y=mng_info->y_off[mng_info->object_id];
4525 mng_info->image_found++;
4526 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4527 2*GetBlobSize(image));
4529 if (logging != MagickFalse)
4530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4531 " exit ReadOneJNGImage()");
4537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4541 % R e a d J N G I m a g e %
4545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4548 % (including the 8-byte signature) and returns it. It allocates the memory
4549 % necessary for the new Image structure and returns a pointer to the new
4552 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4554 % The format of the ReadJNGImage method is:
4556 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4559 % A description of each parameter follows:
4561 % o image_info: the image info.
4563 % o exception: return any errors or warnings in this structure.
4567 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4582 magic_number[MaxTextExtent];
4590 assert(image_info != (const ImageInfo *) NULL);
4591 assert(image_info->signature == MagickSignature);
4592 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4593 assert(exception != (ExceptionInfo *) NULL);
4594 assert(exception->signature == MagickSignature);
4595 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4596 image=AcquireImage(image_info,exception);
4597 mng_info=(MngInfo *) NULL;
4598 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4600 if (status == MagickFalse)
4601 return((Image *) NULL);
4603 if (LocaleCompare(image_info->magick,"JNG") != 0)
4604 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4606 /* Verify JNG signature. */
4608 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4610 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4611 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4613 /* Allocate a MngInfo structure. */
4615 have_mng_structure=MagickFalse;
4616 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4618 if (mng_info == (MngInfo *) NULL)
4619 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4621 /* Initialize members of the MngInfo structure. */
4623 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4624 have_mng_structure=MagickTrue;
4626 mng_info->image=image;
4628 image=ReadOneJNGImage(mng_info,image_info,exception);
4629 MngInfoFreeStruct(mng_info,&have_mng_structure);
4631 if (image == (Image *) NULL)
4633 if (IsImageObject(previous) != MagickFalse)
4635 (void) CloseBlob(previous);
4636 (void) DestroyImageList(previous);
4639 if (logging != MagickFalse)
4640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4641 "exit ReadJNGImage() with error");
4643 return((Image *) NULL);
4645 (void) CloseBlob(image);
4647 if (image->columns == 0 || image->rows == 0)
4649 if (logging != MagickFalse)
4650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4651 "exit ReadJNGImage() with error");
4653 ThrowReaderException(CorruptImageError,"CorruptImage");
4656 if (logging != MagickFalse)
4657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4663 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4666 page_geometry[MaxTextExtent];
4699 #if defined(MNG_INSERT_LAYERS)
4701 mng_background_color;
4704 register unsigned char
4719 #if defined(MNG_INSERT_LAYERS)
4724 volatile unsigned int
4725 #ifdef MNG_OBJECT_BUFFERS
4726 mng_background_object=0,
4728 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4731 default_frame_timeout,
4733 #if defined(MNG_INSERT_LAYERS)
4739 /* These delays are all measured in image ticks_per_second,
4740 * not in MNG ticks_per_second
4743 default_frame_delay,
4747 #if defined(MNG_INSERT_LAYERS)
4756 previous_fb.bottom=0;
4758 previous_fb.right=0;
4760 default_fb.bottom=0;
4764 /* Open image file. */
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 ReadMNGImage()");
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 first_mng_object=MagickFalse;
4781 have_mng_structure=MagickFalse;
4783 /* Allocate a MngInfo structure. */
4785 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4787 if (mng_info == (MngInfo *) NULL)
4788 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4790 /* Initialize members of the MngInfo structure. */
4792 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4793 mng_info->image=image;
4794 have_mng_structure=MagickTrue;
4796 if (LocaleCompare(image_info->magick,"MNG") == 0)
4799 magic_number[MaxTextExtent];
4801 /* Verify MNG signature. */
4802 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4803 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4804 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4806 /* Initialize some nonzero members of the MngInfo structure. */
4807 for (i=0; i < MNG_MAX_OBJECTS; i++)
4809 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4810 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4812 mng_info->exists[0]=MagickTrue;
4815 first_mng_object=MagickTrue;
4817 #if defined(MNG_INSERT_LAYERS)
4818 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4820 default_frame_delay=0;
4821 default_frame_timeout=0;
4824 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4826 skip_to_iend=MagickFalse;
4827 term_chunk_found=MagickFalse;
4828 mng_info->framing_mode=1;
4829 #if defined(MNG_INSERT_LAYERS)
4830 mandatory_back=MagickFalse;
4832 #if defined(MNG_INSERT_LAYERS)
4833 mng_background_color=image->background_color;
4835 default_fb=mng_info->frame;
4836 previous_fb=mng_info->frame;
4840 type[MaxTextExtent];
4842 if (LocaleCompare(image_info->magick,"MNG") == 0)
4851 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4852 length=ReadBlobMSBLong(image);
4853 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4855 if (logging != MagickFalse)
4856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4857 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4858 type[0],type[1],type[2],type[3],(double) length);
4860 if (length > PNG_UINT_31_MAX)
4864 ThrowReaderException(CorruptImageError,"CorruptImage");
4867 chunk=(unsigned char *) NULL;
4871 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4873 if (chunk == (unsigned char *) NULL)
4874 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4876 for (i=0; i < (ssize_t) length; i++)
4877 chunk[i]=(unsigned char) ReadBlobByte(image);
4882 (void) ReadBlobMSBLong(image); /* read crc word */
4884 #if !defined(JNG_SUPPORTED)
4885 if (memcmp(type,mng_JHDR,4) == 0)
4887 skip_to_iend=MagickTrue;
4889 if (mng_info->jhdr_warning == 0)
4890 (void) ThrowMagickException(exception,GetMagickModule(),
4891 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4893 mng_info->jhdr_warning++;
4896 if (memcmp(type,mng_DHDR,4) == 0)
4898 skip_to_iend=MagickTrue;
4900 if (mng_info->dhdr_warning == 0)
4901 (void) ThrowMagickException(exception,GetMagickModule(),
4902 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4904 mng_info->dhdr_warning++;
4906 if (memcmp(type,mng_MEND,4) == 0)
4911 if (memcmp(type,mng_IEND,4) == 0)
4912 skip_to_iend=MagickFalse;
4915 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4917 if (logging != MagickFalse)
4918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4924 if (memcmp(type,mng_MHDR,4) == 0)
4926 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4927 (p[2] << 8) | p[3]);
4929 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4930 (p[6] << 8) | p[7]);
4932 if (logging != MagickFalse)
4934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4935 " MNG width: %.20g",(double) mng_info->mng_width);
4936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4937 " MNG height: %.20g",(double) mng_info->mng_height);
4941 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4943 if (mng_info->ticks_per_second == 0)
4944 default_frame_delay=0;
4947 default_frame_delay=1UL*image->ticks_per_second/
4948 mng_info->ticks_per_second;
4950 frame_delay=default_frame_delay;
4956 simplicity=(size_t) mng_get_long(p);
4959 mng_type=1; /* Full MNG */
4961 if ((simplicity != 0) && ((simplicity | 11) == 11))
4962 mng_type=2; /* LC */
4964 if ((simplicity != 0) && ((simplicity | 9) == 9))
4965 mng_type=3; /* VLC */
4967 #if defined(MNG_INSERT_LAYERS)
4969 insert_layers=MagickTrue;
4971 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4973 /* Allocate next image structure. */
4974 AcquireNextImage(image_info,image,exception);
4976 if (GetNextImageInList(image) == (Image *) NULL)
4977 return((Image *) NULL);
4979 image=SyncNextImageInList(image);
4980 mng_info->image=image;
4983 if ((mng_info->mng_width > 65535L) ||
4984 (mng_info->mng_height > 65535L))
4985 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4987 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4988 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4989 mng_info->mng_height);
4991 mng_info->frame.left=0;
4992 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4993 mng_info->frame.top=0;
4994 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4995 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4997 for (i=0; i < MNG_MAX_OBJECTS; i++)
4998 mng_info->object_clip[i]=mng_info->frame;
5000 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5004 if (memcmp(type,mng_TERM,4) == 0)
5015 final_delay=(png_uint_32) mng_get_long(&p[2]);
5016 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5018 if (mng_iterations == PNG_UINT_31_MAX)
5021 image->iterations=mng_iterations;
5022 term_chunk_found=MagickTrue;
5025 if (logging != MagickFalse)
5027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5028 " repeat=%d",repeat);
5030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5031 " final_delay=%.20g",(double) final_delay);
5033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5034 " image->iterations=%.20g",(double) image->iterations);
5037 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5040 if (memcmp(type,mng_DEFI,4) == 0)
5043 (void) ThrowMagickException(exception,GetMagickModule(),
5044 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5047 object_id=(p[0] << 8) | p[1];
5049 if (mng_type == 2 && object_id != 0)
5050 (void) ThrowMagickException(exception,GetMagickModule(),
5051 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5054 if (object_id > MNG_MAX_OBJECTS)
5057 Instead of using a warning we should allocate a larger
5058 MngInfo structure and continue.
5060 (void) ThrowMagickException(exception,GetMagickModule(),
5061 CoderError,"object id too large","`%s'",image->filename);
5062 object_id=MNG_MAX_OBJECTS;
5065 if (mng_info->exists[object_id])
5066 if (mng_info->frozen[object_id])
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5069 (void) ThrowMagickException(exception,
5070 GetMagickModule(),CoderError,
5071 "DEFI cannot redefine a frozen MNG object","`%s'",
5076 mng_info->exists[object_id]=MagickTrue;
5079 mng_info->invisible[object_id]=p[2];
5082 Extract object offset info.
5086 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5087 (p[5] << 16) | (p[6] << 8) | p[7]);
5089 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5090 (p[9] << 16) | (p[10] << 8) | p[11]);
5092 if (logging != MagickFalse)
5094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5095 " x_off[%d]: %.20g",object_id,(double)
5096 mng_info->x_off[object_id]);
5098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5099 " y_off[%d]: %.20g",object_id,(double)
5100 mng_info->y_off[object_id]);
5105 Extract object clipping info.
5108 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5111 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5114 if (memcmp(type,mng_bKGD,4) == 0)
5116 mng_info->have_global_bkgd=MagickFalse;
5120 mng_info->mng_global_bkgd.red=
5121 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5123 mng_info->mng_global_bkgd.green=
5124 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5126 mng_info->mng_global_bkgd.blue=
5127 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5129 mng_info->have_global_bkgd=MagickTrue;
5132 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5135 if (memcmp(type,mng_BACK,4) == 0)
5137 #if defined(MNG_INSERT_LAYERS)
5139 mandatory_back=p[6];
5144 if (mandatory_back && length > 5)
5146 mng_background_color.red=
5147 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5149 mng_background_color.green=
5150 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5152 mng_background_color.blue=
5153 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5155 mng_background_color.alpha=OpaqueAlpha;
5158 #ifdef MNG_OBJECT_BUFFERS
5160 mng_background_object=(p[7] << 8) | p[8];
5163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5167 if (memcmp(type,mng_PLTE,4) == 0)
5169 /* Read global PLTE. */
5171 if (length && (length < 769))
5173 if (mng_info->global_plte == (png_colorp) NULL)
5174 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5175 sizeof(*mng_info->global_plte));
5177 for (i=0; i < (ssize_t) (length/3); i++)
5179 mng_info->global_plte[i].red=p[3*i];
5180 mng_info->global_plte[i].green=p[3*i+1];
5181 mng_info->global_plte[i].blue=p[3*i+2];
5184 mng_info->global_plte_length=(unsigned int) (length/3);
5187 for ( ; i < 256; i++)
5189 mng_info->global_plte[i].red=i;
5190 mng_info->global_plte[i].green=i;
5191 mng_info->global_plte[i].blue=i;
5195 mng_info->global_plte_length=256;
5198 mng_info->global_plte_length=0;
5200 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5204 if (memcmp(type,mng_tRNS,4) == 0)
5206 /* read global tRNS */
5209 for (i=0; i < (ssize_t) length; i++)
5210 mng_info->global_trns[i]=p[i];
5213 for ( ; i < 256; i++)
5214 mng_info->global_trns[i]=255;
5216 mng_info->global_trns_length=(unsigned int) length;
5217 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5220 if (memcmp(type,mng_gAMA,4) == 0)
5227 igamma=mng_get_long(p);
5228 mng_info->global_gamma=((float) igamma)*0.00001;
5229 mng_info->have_global_gama=MagickTrue;
5233 mng_info->have_global_gama=MagickFalse;
5235 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5239 if (memcmp(type,mng_cHRM,4) == 0)
5241 /* Read global cHRM */
5245 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5246 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5247 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5248 mng_info->global_chrm.red_primary.y=0.00001*
5249 mng_get_long(&p[12]);
5250 mng_info->global_chrm.green_primary.x=0.00001*
5251 mng_get_long(&p[16]);
5252 mng_info->global_chrm.green_primary.y=0.00001*
5253 mng_get_long(&p[20]);
5254 mng_info->global_chrm.blue_primary.x=0.00001*
5255 mng_get_long(&p[24]);
5256 mng_info->global_chrm.blue_primary.y=0.00001*
5257 mng_get_long(&p[28]);
5258 mng_info->have_global_chrm=MagickTrue;
5261 mng_info->have_global_chrm=MagickFalse;
5263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5267 if (memcmp(type,mng_sRGB,4) == 0)
5274 mng_info->global_srgb_intent=
5275 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5276 mng_info->have_global_srgb=MagickTrue;
5279 mng_info->have_global_srgb=MagickFalse;
5281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5285 if (memcmp(type,mng_iCCP,4) == 0)
5293 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5298 if (memcmp(type,mng_FRAM,4) == 0)
5301 (void) ThrowMagickException(exception,GetMagickModule(),
5302 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5305 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5306 image->delay=frame_delay;
5308 frame_delay=default_frame_delay;
5309 frame_timeout=default_frame_timeout;
5314 mng_info->framing_mode=p[0];
5316 if (logging != MagickFalse)
5317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5318 " Framing_mode=%d",mng_info->framing_mode);
5322 /* Note the delay and frame clipping boundaries. */
5324 p++; /* framing mode */
5326 while (*p && ((p-chunk) < (ssize_t) length))
5327 p++; /* frame name */
5329 p++; /* frame name terminator */
5331 if ((p-chunk) < (ssize_t) (length-4))
5338 change_delay=(*p++);
5339 change_timeout=(*p++);
5340 change_clipping=(*p++);
5341 p++; /* change_sync */
5345 frame_delay=1UL*image->ticks_per_second*
5348 if (mng_info->ticks_per_second != 0)
5349 frame_delay/=mng_info->ticks_per_second;
5352 frame_delay=PNG_UINT_31_MAX;
5354 if (change_delay == 2)
5355 default_frame_delay=frame_delay;
5359 if (logging != MagickFalse)
5360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5361 " Framing_delay=%.20g",(double) frame_delay);
5366 frame_timeout=1UL*image->ticks_per_second*
5369 if (mng_info->ticks_per_second != 0)
5370 frame_timeout/=mng_info->ticks_per_second;
5373 frame_timeout=PNG_UINT_31_MAX;
5375 if (change_delay == 2)
5376 default_frame_timeout=frame_timeout;
5380 if (logging != MagickFalse)
5381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5382 " Framing_timeout=%.20g",(double) frame_timeout);
5385 if (change_clipping)
5387 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5391 if (logging != MagickFalse)
5392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5393 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5394 (double) fb.left,(double) fb.right,(double) fb.top,
5395 (double) fb.bottom);
5397 if (change_clipping == 2)
5403 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5405 subframe_width=(size_t) (mng_info->clip.right
5406 -mng_info->clip.left);
5408 subframe_height=(size_t) (mng_info->clip.bottom
5409 -mng_info->clip.top);
5411 Insert a background layer behind the frame if framing_mode is 4.
5413 #if defined(MNG_INSERT_LAYERS)
5414 if (logging != MagickFalse)
5415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5416 " subframe_width=%.20g, subframe_height=%.20g",(double)
5417 subframe_width,(double) subframe_height);
5419 if (insert_layers && (mng_info->framing_mode == 4) &&
5420 (subframe_width) && (subframe_height))
5422 /* Allocate next image structure. */
5423 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5425 AcquireNextImage(image_info,image,exception);
5427 if (GetNextImageInList(image) == (Image *) NULL)
5429 image=DestroyImageList(image);
5430 MngInfoFreeStruct(mng_info,&have_mng_structure);
5431 return((Image *) NULL);
5434 image=SyncNextImageInList(image);
5437 mng_info->image=image;
5439 if (term_chunk_found)
5441 image->start_loop=MagickTrue;
5442 image->iterations=mng_iterations;
5443 term_chunk_found=MagickFalse;
5447 image->start_loop=MagickFalse;
5449 image->columns=subframe_width;
5450 image->rows=subframe_height;
5451 image->page.width=subframe_width;
5452 image->page.height=subframe_height;
5453 image->page.x=mng_info->clip.left;
5454 image->page.y=mng_info->clip.top;
5455 image->background_color=mng_background_color;
5456 image->matte=MagickFalse;
5458 (void) SetImageBackgroundColor(image,exception);
5460 if (logging != MagickFalse)
5461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5462 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5463 (double) mng_info->clip.left,(double) mng_info->clip.right,
5464 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5467 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5470 if (memcmp(type,mng_CLIP,4) == 0)
5479 first_object=(p[0] << 8) | p[1];
5480 last_object=(p[2] << 8) | p[3];
5482 for (i=(int) first_object; i <= (int) last_object; i++)
5484 if (mng_info->exists[i] && !mng_info->frozen[i])
5489 box=mng_info->object_clip[i];
5490 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5494 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5497 if (memcmp(type,mng_SAVE,4) == 0)
5499 for (i=1; i < MNG_MAX_OBJECTS; i++)
5500 if (mng_info->exists[i])
5502 mng_info->frozen[i]=MagickTrue;
5503 #ifdef MNG_OBJECT_BUFFERS
5504 if (mng_info->ob[i] != (MngBuffer *) NULL)
5505 mng_info->ob[i]->frozen=MagickTrue;
5510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5515 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5517 /* Read DISC or SEEK. */
5519 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5521 for (i=1; i < MNG_MAX_OBJECTS; i++)
5522 MngInfoDiscardObject(mng_info,i);
5530 for (j=0; j < (ssize_t) length; j+=2)
5532 i=p[j] << 8 | p[j+1];
5533 MngInfoDiscardObject(mng_info,i);
5538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5543 if (memcmp(type,mng_MOVE,4) == 0)
5551 first_object=(p[0] << 8) | p[1];
5552 last_object=(p[2] << 8) | p[3];
5553 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5555 if (mng_info->exists[i] && !mng_info->frozen[i])
5563 old_pair.a=mng_info->x_off[i];
5564 old_pair.b=mng_info->y_off[i];
5565 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5566 mng_info->x_off[i]=new_pair.a;
5567 mng_info->y_off[i]=new_pair.b;
5571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5575 if (memcmp(type,mng_LOOP,4) == 0)
5577 ssize_t loop_iters=1;
5578 loop_level=chunk[0];
5579 mng_info->loop_active[loop_level]=1; /* mark loop active */
5581 /* Record starting point. */
5582 loop_iters=mng_get_long(&chunk[1]);
5584 if (logging != MagickFalse)
5585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5586 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5587 (double) loop_iters);
5589 if (loop_iters == 0)
5590 skipping_loop=loop_level;
5594 mng_info->loop_jump[loop_level]=TellBlob(image);
5595 mng_info->loop_count[loop_level]=loop_iters;
5598 mng_info->loop_iteration[loop_level]=0;
5599 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5603 if (memcmp(type,mng_ENDL,4) == 0)
5605 loop_level=chunk[0];
5607 if (skipping_loop > 0)
5609 if (skipping_loop == loop_level)
5612 Found end of zero-iteration loop.
5615 mng_info->loop_active[loop_level]=0;
5621 if (mng_info->loop_active[loop_level] == 1)
5623 mng_info->loop_count[loop_level]--;
5624 mng_info->loop_iteration[loop_level]++;
5626 if (logging != MagickFalse)
5627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5628 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5629 (double) loop_level,(double)
5630 mng_info->loop_count[loop_level]);
5632 if (mng_info->loop_count[loop_level] != 0)
5634 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5638 ThrowReaderException(CorruptImageError,
5639 "ImproperImageHeader");
5650 mng_info->loop_active[loop_level]=0;
5652 for (i=0; i < loop_level; i++)
5653 if (mng_info->loop_active[i] == 1)
5654 last_level=(short) i;
5655 loop_level=last_level;
5660 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5664 if (memcmp(type,mng_CLON,4) == 0)
5666 if (mng_info->clon_warning == 0)
5667 (void) ThrowMagickException(exception,GetMagickModule(),
5668 CoderError,"CLON is not implemented yet","`%s'",
5671 mng_info->clon_warning++;
5674 if (memcmp(type,mng_MAGN,4) == 0)
5689 magn_first=(p[0] << 8) | p[1];
5695 magn_last=(p[2] << 8) | p[3];
5698 magn_last=magn_first;
5699 #ifndef MNG_OBJECT_BUFFERS
5700 if (magn_first || magn_last)
5701 if (mng_info->magn_warning == 0)
5703 (void) ThrowMagickException(exception,
5704 GetMagickModule(),CoderError,
5705 "MAGN is not implemented yet for nonzero objects",
5706 "`%s'",image->filename);
5708 mng_info->magn_warning++;
5718 magn_mx=(p[5] << 8) | p[6];
5727 magn_my=(p[7] << 8) | p[8];
5736 magn_ml=(p[9] << 8) | p[10];
5745 magn_mr=(p[11] << 8) | p[12];
5754 magn_mt=(p[13] << 8) | p[14];
5763 magn_mb=(p[15] << 8) | p[16];
5775 magn_methy=magn_methx;
5778 if (magn_methx > 5 || magn_methy > 5)
5779 if (mng_info->magn_warning == 0)
5781 (void) ThrowMagickException(exception,
5782 GetMagickModule(),CoderError,
5783 "Unknown MAGN method in MNG datastream","`%s'",
5786 mng_info->magn_warning++;
5788 #ifdef MNG_OBJECT_BUFFERS
5789 /* Magnify existing objects in the range magn_first to magn_last */
5791 if (magn_first == 0 || magn_last == 0)
5793 /* Save the magnification factors for object 0 */
5794 mng_info->magn_mb=magn_mb;
5795 mng_info->magn_ml=magn_ml;
5796 mng_info->magn_mr=magn_mr;
5797 mng_info->magn_mt=magn_mt;
5798 mng_info->magn_mx=magn_mx;
5799 mng_info->magn_my=magn_my;
5800 mng_info->magn_methx=magn_methx;
5801 mng_info->magn_methy=magn_methy;
5805 if (memcmp(type,mng_PAST,4) == 0)
5807 if (mng_info->past_warning == 0)
5808 (void) ThrowMagickException(exception,GetMagickModule(),
5809 CoderError,"PAST is not implemented yet","`%s'",
5812 mng_info->past_warning++;
5815 if (memcmp(type,mng_SHOW,4) == 0)
5817 if (mng_info->show_warning == 0)
5818 (void) ThrowMagickException(exception,GetMagickModule(),
5819 CoderError,"SHOW is not implemented yet","`%s'",
5822 mng_info->show_warning++;
5825 if (memcmp(type,mng_sBIT,4) == 0)
5828 mng_info->have_global_sbit=MagickFalse;
5832 mng_info->global_sbit.gray=p[0];
5833 mng_info->global_sbit.red=p[0];
5834 mng_info->global_sbit.green=p[1];
5835 mng_info->global_sbit.blue=p[2];
5836 mng_info->global_sbit.alpha=p[3];
5837 mng_info->have_global_sbit=MagickTrue;
5840 if (memcmp(type,mng_pHYs,4) == 0)
5844 mng_info->global_x_pixels_per_unit=
5845 (size_t) mng_get_long(p);
5846 mng_info->global_y_pixels_per_unit=
5847 (size_t) mng_get_long(&p[4]);
5848 mng_info->global_phys_unit_type=p[8];
5849 mng_info->have_global_phys=MagickTrue;
5853 mng_info->have_global_phys=MagickFalse;
5855 if (memcmp(type,mng_pHYg,4) == 0)
5857 if (mng_info->phyg_warning == 0)
5858 (void) ThrowMagickException(exception,GetMagickModule(),
5859 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5861 mng_info->phyg_warning++;
5863 if (memcmp(type,mng_BASI,4) == 0)
5865 skip_to_iend=MagickTrue;
5867 if (mng_info->basi_warning == 0)
5868 (void) ThrowMagickException(exception,GetMagickModule(),
5869 CoderError,"BASI is not implemented yet","`%s'",
5872 mng_info->basi_warning++;
5873 #ifdef MNG_BASI_SUPPORTED
5874 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5875 (p[2] << 8) | p[3]);
5876 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5877 (p[6] << 8) | p[7]);
5878 basi_color_type=p[8];
5879 basi_compression_method=p[9];
5880 basi_filter_type=p[10];
5881 basi_interlace_method=p[11];
5883 basi_red=(p[12] << 8) & p[13];
5889 basi_green=(p[14] << 8) & p[15];
5895 basi_blue=(p[16] << 8) & p[17];
5901 basi_alpha=(p[18] << 8) & p[19];
5905 if (basi_sample_depth == 16)
5912 basi_viewable=p[20];
5918 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5922 if (memcmp(type,mng_IHDR,4)
5923 #if defined(JNG_SUPPORTED)
5924 && memcmp(type,mng_JHDR,4)
5928 /* Not an IHDR or JHDR chunk */
5930 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5935 if (logging != MagickFalse)
5936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5937 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5939 mng_info->exists[object_id]=MagickTrue;
5940 mng_info->viewable[object_id]=MagickTrue;
5942 if (mng_info->invisible[object_id])
5944 if (logging != MagickFalse)
5945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5946 " Skipping invisible object");
5948 skip_to_iend=MagickTrue;
5949 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5952 #if defined(MNG_INSERT_LAYERS)
5954 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5956 image_width=(size_t) mng_get_long(p);
5957 image_height=(size_t) mng_get_long(&p[4]);
5959 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5962 Insert a transparent background layer behind the entire animation
5963 if it is not full screen.
5965 #if defined(MNG_INSERT_LAYERS)
5966 if (insert_layers && mng_type && first_mng_object)
5968 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5969 (image_width < mng_info->mng_width) ||
5970 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5971 (image_height < mng_info->mng_height) ||
5972 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5974 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5977 Allocate next image structure.
5979 AcquireNextImage(image_info,image,exception);
5981 if (GetNextImageInList(image) == (Image *) NULL)
5983 image=DestroyImageList(image);
5984 MngInfoFreeStruct(mng_info,&have_mng_structure);
5985 return((Image *) NULL);
5988 image=SyncNextImageInList(image);
5990 mng_info->image=image;
5992 if (term_chunk_found)
5994 image->start_loop=MagickTrue;
5995 image->iterations=mng_iterations;
5996 term_chunk_found=MagickFalse;
6000 image->start_loop=MagickFalse;
6002 /* Make a background rectangle. */
6005 image->columns=mng_info->mng_width;
6006 image->rows=mng_info->mng_height;
6007 image->page.width=mng_info->mng_width;
6008 image->page.height=mng_info->mng_height;
6011 image->background_color=mng_background_color;
6012 (void) SetImageBackgroundColor(image,exception);
6013 if (logging != MagickFalse)
6014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6015 " Inserted transparent background layer, W=%.20g, H=%.20g",
6016 (double) mng_info->mng_width,(double) mng_info->mng_height);
6020 Insert a background layer behind the upcoming image if
6021 framing_mode is 3, and we haven't already inserted one.
6023 if (insert_layers && (mng_info->framing_mode == 3) &&
6024 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6025 (simplicity & 0x08)))
6027 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6030 Allocate next image structure.
6032 AcquireNextImage(image_info,image,exception);
6034 if (GetNextImageInList(image) == (Image *) NULL)
6036 image=DestroyImageList(image);
6037 MngInfoFreeStruct(mng_info,&have_mng_structure);
6038 return((Image *) NULL);
6041 image=SyncNextImageInList(image);
6044 mng_info->image=image;
6046 if (term_chunk_found)
6048 image->start_loop=MagickTrue;
6049 image->iterations=mng_iterations;
6050 term_chunk_found=MagickFalse;
6054 image->start_loop=MagickFalse;
6057 image->columns=subframe_width;
6058 image->rows=subframe_height;
6059 image->page.width=subframe_width;
6060 image->page.height=subframe_height;
6061 image->page.x=mng_info->clip.left;
6062 image->page.y=mng_info->clip.top;
6063 image->background_color=mng_background_color;
6064 image->matte=MagickFalse;
6065 (void) SetImageBackgroundColor(image,exception);
6067 if (logging != MagickFalse)
6068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6069 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6070 (double) mng_info->clip.left,(double) mng_info->clip.right,
6071 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6073 #endif /* MNG_INSERT_LAYERS */
6074 first_mng_object=MagickFalse;
6076 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6079 Allocate next image structure.
6081 AcquireNextImage(image_info,image,exception);
6083 if (GetNextImageInList(image) == (Image *) NULL)
6085 image=DestroyImageList(image);
6086 MngInfoFreeStruct(mng_info,&have_mng_structure);
6087 return((Image *) NULL);
6090 image=SyncNextImageInList(image);
6092 mng_info->image=image;
6093 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6094 GetBlobSize(image));
6096 if (status == MagickFalse)
6099 if (term_chunk_found)
6101 image->start_loop=MagickTrue;
6102 term_chunk_found=MagickFalse;
6106 image->start_loop=MagickFalse;
6108 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6110 image->delay=frame_delay;
6111 frame_delay=default_frame_delay;
6117 image->page.width=mng_info->mng_width;
6118 image->page.height=mng_info->mng_height;
6119 image->page.x=mng_info->x_off[object_id];
6120 image->page.y=mng_info->y_off[object_id];
6121 image->iterations=mng_iterations;
6124 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6127 if (logging != MagickFalse)
6128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6129 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6132 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6135 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6139 mng_info->image=image;
6140 mng_info->mng_type=mng_type;
6141 mng_info->object_id=object_id;
6143 if (memcmp(type,mng_IHDR,4) == 0)
6144 image=ReadOnePNGImage(mng_info,image_info,exception);
6146 #if defined(JNG_SUPPORTED)
6148 image=ReadOneJNGImage(mng_info,image_info,exception);
6151 if (image == (Image *) NULL)
6153 if (IsImageObject(previous) != MagickFalse)
6155 (void) DestroyImageList(previous);
6156 (void) CloseBlob(previous);
6159 MngInfoFreeStruct(mng_info,&have_mng_structure);
6160 return((Image *) NULL);
6163 if (image->columns == 0 || image->rows == 0)
6165 (void) CloseBlob(image);
6166 image=DestroyImageList(image);
6167 MngInfoFreeStruct(mng_info,&have_mng_structure);
6168 return((Image *) NULL);
6171 mng_info->image=image;
6178 if (mng_info->magn_methx || mng_info->magn_methy)
6184 if (logging != MagickFalse)
6185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6186 " Processing MNG MAGN chunk");
6188 if (mng_info->magn_methx == 1)
6190 magnified_width=mng_info->magn_ml;
6192 if (image->columns > 1)
6193 magnified_width += mng_info->magn_mr;
6195 if (image->columns > 2)
6196 magnified_width += (png_uint_32)
6197 ((image->columns-2)*(mng_info->magn_mx));
6202 magnified_width=(png_uint_32) image->columns;
6204 if (image->columns > 1)
6205 magnified_width += mng_info->magn_ml-1;
6207 if (image->columns > 2)
6208 magnified_width += mng_info->magn_mr-1;
6210 if (image->columns > 3)
6211 magnified_width += (png_uint_32)
6212 ((image->columns-3)*(mng_info->magn_mx-1));
6215 if (mng_info->magn_methy == 1)
6217 magnified_height=mng_info->magn_mt;
6219 if (image->rows > 1)
6220 magnified_height += mng_info->magn_mb;
6222 if (image->rows > 2)
6223 magnified_height += (png_uint_32)
6224 ((image->rows-2)*(mng_info->magn_my));
6229 magnified_height=(png_uint_32) image->rows;
6231 if (image->rows > 1)
6232 magnified_height += mng_info->magn_mt-1;
6234 if (image->rows > 2)
6235 magnified_height += mng_info->magn_mb-1;
6237 if (image->rows > 3)
6238 magnified_height += (png_uint_32)
6239 ((image->rows-3)*(mng_info->magn_my-1));
6242 if (magnified_height > image->rows ||
6243 magnified_width > image->columns)
6270 /* Allocate next image structure. */
6272 if (logging != MagickFalse)
6273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6274 " Allocate magnified image");
6276 AcquireNextImage(image_info,image,exception);
6278 if (GetNextImageInList(image) == (Image *) NULL)
6280 image=DestroyImageList(image);
6281 MngInfoFreeStruct(mng_info,&have_mng_structure);
6282 return((Image *) NULL);
6285 large_image=SyncNextImageInList(image);
6287 large_image->columns=magnified_width;
6288 large_image->rows=magnified_height;
6290 magn_methx=mng_info->magn_methx;
6291 magn_methy=mng_info->magn_methy;
6293 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6294 #define QM unsigned short
6295 if (magn_methx != 1 || magn_methy != 1)
6298 Scale pixels to unsigned shorts to prevent
6299 overflow of intermediate values of interpolations
6301 for (y=0; y < (ssize_t) image->rows; y++)
6303 q=GetAuthenticPixels(image,0,y,image->columns,1,
6306 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6308 SetPixelRed(image,ScaleQuantumToShort(
6309 GetPixelRed(image,q)),q);
6310 SetPixelGreen(image,ScaleQuantumToShort(
6311 GetPixelGreen(image,q)),q);
6312 SetPixelBlue(image,ScaleQuantumToShort(
6313 GetPixelBlue(image,q)),q);
6314 SetPixelAlpha(image,ScaleQuantumToShort(
6315 GetPixelAlpha(image,q)),q);
6316 q+=GetPixelChannels(image);
6319 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6327 if (image->matte != MagickFalse)
6328 (void) SetImageBackgroundColor(large_image,exception);
6332 large_image->background_color.alpha=OpaqueAlpha;
6333 (void) SetImageBackgroundColor(large_image,exception);
6335 if (magn_methx == 4)
6338 if (magn_methx == 5)
6341 if (magn_methy == 4)
6344 if (magn_methy == 5)
6348 /* magnify the rows into the right side of the large image */
6350 if (logging != MagickFalse)
6351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6352 " Magnify the rows to %.20g",(double) large_image->rows);
6353 m=(ssize_t) mng_info->magn_mt;
6355 length=(size_t) image->columns*GetPixelChannels(image);
6356 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6357 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6359 if ((prev == (Quantum *) NULL) ||
6360 (next == (Quantum *) NULL))
6362 image=DestroyImageList(image);
6363 MngInfoFreeStruct(mng_info,&have_mng_structure);
6364 ThrowReaderException(ResourceLimitError,
6365 "MemoryAllocationFailed");
6368 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6369 (void) CopyMagickMemory(next,n,length);
6371 for (y=0; y < (ssize_t) image->rows; y++)
6374 m=(ssize_t) mng_info->magn_mt;
6376 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6377 m=(ssize_t) mng_info->magn_mb;
6379 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6380 m=(ssize_t) mng_info->magn_mb;
6382 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6386 m=(ssize_t) mng_info->magn_my;
6392 if (y < (ssize_t) image->rows-1)
6394 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6396 (void) CopyMagickMemory(next,n,length);
6399 for (i=0; i < m; i++, yy++)
6404 assert(yy < (ssize_t) large_image->rows);
6407 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6409 q+=(large_image->columns-image->columns)*
6410 GetPixelChannels(large_image);
6412 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6414 /* To do: get color as function of indexes[x] */
6416 if (image->storage_class == PseudoClass)
6421 if (magn_methy <= 1)
6423 /* replicate previous */
6424 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6425 SetPixelGreen(large_image,GetPixelGreen(image,
6427 SetPixelBlue(large_image,GetPixelBlue(image,
6429 SetPixelAlpha(large_image,GetPixelAlpha(image,
6433 else if (magn_methy == 2 || magn_methy == 4)
6437 SetPixelRed(large_image,GetPixelRed(image,
6439 SetPixelGreen(large_image,GetPixelGreen(image,
6441 SetPixelBlue(large_image,GetPixelBlue(image,
6443 SetPixelAlpha(large_image,GetPixelAlpha(image,
6450 SetPixelRed(large_image,((QM) (((ssize_t)
6451 (2*i*(GetPixelRed(image,n)
6452 -GetPixelRed(image,pixels)+m))/
6454 +GetPixelRed(image,pixels)))),q);
6455 SetPixelGreen(large_image,((QM) (((ssize_t)
6456 (2*i*(GetPixelGreen(image,n)
6457 -GetPixelGreen(image,pixels)+m))/
6459 +GetPixelGreen(image,pixels)))),q);
6460 SetPixelBlue(large_image,((QM) (((ssize_t)
6461 (2*i*(GetPixelBlue(image,n)
6462 -GetPixelBlue(image,pixels)+m))/
6464 +GetPixelBlue(image,pixels)))),q);
6466 if (image->matte != MagickFalse)
6467 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6468 (2*i*(GetPixelAlpha(image,n)
6469 -GetPixelAlpha(image,pixels)+m))
6471 GetPixelAlpha(image,pixels)))),q);
6474 if (magn_methy == 4)
6476 /* Replicate nearest */
6477 if (i <= ((m+1) << 1))
6478 SetPixelAlpha(large_image,GetPixelAlpha(image,
6481 SetPixelAlpha(large_image,GetPixelAlpha(image,
6486 else /* if (magn_methy == 3 || magn_methy == 5) */
6488 /* Replicate nearest */
6489 if (i <= ((m+1) << 1))
6491 SetPixelRed(large_image,GetPixelRed(image,
6493 SetPixelGreen(large_image,GetPixelGreen(image,
6495 SetPixelBlue(large_image,GetPixelBlue(image,
6497 SetPixelAlpha(large_image,GetPixelAlpha(image,
6503 SetPixelRed(large_image,GetPixelRed(image,n),q);
6504 SetPixelGreen(large_image,GetPixelGreen(image,n),
6506 SetPixelBlue(large_image,GetPixelBlue(image,n),
6508 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6512 if (magn_methy == 5)
6514 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6515 (GetPixelAlpha(image,n)
6516 -GetPixelAlpha(image,pixels))
6517 +m))/((ssize_t) (m*2))
6518 +GetPixelAlpha(image,pixels)),q);
6521 n+=GetPixelChannels(image);
6522 q+=GetPixelChannels(large_image);
6523 pixels+=GetPixelChannels(image);
6526 if (SyncAuthenticPixels(large_image,exception) == 0)
6532 prev=(Quantum *) RelinquishMagickMemory(prev);
6533 next=(Quantum *) RelinquishMagickMemory(next);
6535 length=image->columns;
6537 if (logging != MagickFalse)
6538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6539 " Delete original image");
6541 DeleteImageFromList(&image);
6545 mng_info->image=image;
6547 /* magnify the columns */
6548 if (logging != MagickFalse)
6549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6550 " Magnify the columns to %.20g",(double) image->columns);
6552 for (y=0; y < (ssize_t) image->rows; y++)
6557 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6558 pixels=q+(image->columns-length)*GetPixelChannels(image);
6559 n=pixels+GetPixelChannels(image);
6561 for (x=(ssize_t) (image->columns-length);
6562 x < (ssize_t) image->columns; x++)
6564 /* To do: Rewrite using Get/Set***PixelChannel() */
6566 if (x == (ssize_t) (image->columns-length))
6567 m=(ssize_t) mng_info->magn_ml;
6569 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6570 m=(ssize_t) mng_info->magn_mr;
6572 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6573 m=(ssize_t) mng_info->magn_mr;
6575 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6579 m=(ssize_t) mng_info->magn_mx;
6581 for (i=0; i < m; i++)
6583 if (magn_methx <= 1)
6585 /* replicate previous */
6586 SetPixelRed(image,GetPixelRed(image,pixels),q);
6587 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6588 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6589 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6592 else if (magn_methx == 2 || magn_methx == 4)
6596 SetPixelRed(image,GetPixelRed(image,pixels),q);
6597 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6598 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6599 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6602 /* To do: Rewrite using Get/Set***PixelChannel() */
6606 SetPixelRed(image,(QM) ((2*i*(
6607 GetPixelRed(image,n)
6608 -GetPixelRed(image,pixels))+m)
6610 GetPixelRed(image,pixels)),q);
6612 SetPixelGreen(image,(QM) ((2*i*(
6613 GetPixelGreen(image,n)
6614 -GetPixelGreen(image,pixels))+m)
6616 GetPixelGreen(image,pixels)),q);
6618 SetPixelBlue(image,(QM) ((2*i*(
6619 GetPixelBlue(image,n)
6620 -GetPixelBlue(image,pixels))+m)
6622 GetPixelBlue(image,pixels)),q);
6623 if (image->matte != MagickFalse)
6624 SetPixelAlpha(image,(QM) ((2*i*(
6625 GetPixelAlpha(image,n)
6626 -GetPixelAlpha(image,pixels))+m)
6628 GetPixelAlpha(image,pixels)),q);
6631 if (magn_methx == 4)
6633 /* Replicate nearest */
6634 if (i <= ((m+1) << 1))
6636 SetPixelAlpha(image,
6637 GetPixelAlpha(image,pixels)+0,q);
6641 SetPixelAlpha(image,
6642 GetPixelAlpha(image,n)+0,q);
6647 else /* if (magn_methx == 3 || magn_methx == 5) */
6649 /* Replicate nearest */
6650 if (i <= ((m+1) << 1))
6652 SetPixelRed(image,GetPixelRed(image,pixels),q);
6653 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6654 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6655 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6660 SetPixelRed(image,GetPixelRed(image,n),q);
6661 SetPixelGreen(image,GetPixelGreen(image,n),q);
6662 SetPixelBlue(image,GetPixelBlue(image,n),q);
6663 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6666 if (magn_methx == 5)
6669 SetPixelAlpha(image,
6670 (QM) ((2*i*( GetPixelAlpha(image,n)
6671 -GetPixelAlpha(image,pixels))+m)/
6673 +GetPixelAlpha(image,pixels)),q);
6676 q+=GetPixelChannels(image);
6678 n+=GetPixelChannels(image);
6679 p+=GetPixelChannels(image);
6682 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6685 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6686 if (magn_methx != 1 || magn_methy != 1)
6689 Rescale pixels to Quantum
6691 for (y=0; y < (ssize_t) image->rows; y++)
6693 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6695 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6697 SetPixelRed(image,ScaleShortToQuantum(
6698 GetPixelRed(image,q)),q);
6699 SetPixelGreen(image,ScaleShortToQuantum(
6700 GetPixelGreen(image,q)),q);
6701 SetPixelBlue(image,ScaleShortToQuantum(
6702 GetPixelBlue(image,q)),q);
6703 SetPixelAlpha(image,ScaleShortToQuantum(
6704 GetPixelAlpha(image,q)),q);
6705 q+=GetPixelChannels(image);
6708 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6713 if (logging != MagickFalse)
6714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6715 " Finished MAGN processing");
6720 Crop_box is with respect to the upper left corner of the MNG.
6722 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6723 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6724 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6725 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6726 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6727 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6728 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6729 if ((crop_box.left != (mng_info->image_box.left
6730 +mng_info->x_off[object_id])) ||
6731 (crop_box.right != (mng_info->image_box.right
6732 +mng_info->x_off[object_id])) ||
6733 (crop_box.top != (mng_info->image_box.top
6734 +mng_info->y_off[object_id])) ||
6735 (crop_box.bottom != (mng_info->image_box.bottom
6736 +mng_info->y_off[object_id])))
6738 if (logging != MagickFalse)
6739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6740 " Crop the PNG image");
6742 if ((crop_box.left < crop_box.right) &&
6743 (crop_box.top < crop_box.bottom))
6752 Crop_info is with respect to the upper left corner of
6755 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6756 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6757 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6758 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6759 image->page.width=image->columns;
6760 image->page.height=image->rows;
6763 im=CropImage(image,&crop_info,exception);
6765 if (im != (Image *) NULL)
6767 image->columns=im->columns;
6768 image->rows=im->rows;
6769 im=DestroyImage(im);
6770 image->page.width=image->columns;
6771 image->page.height=image->rows;
6772 image->page.x=crop_box.left;
6773 image->page.y=crop_box.top;
6780 No pixels in crop area. The MNG spec still requires
6781 a layer, though, so make a single transparent pixel in
6782 the top left corner.
6787 (void) SetImageBackgroundColor(image,exception);
6788 image->page.width=1;
6789 image->page.height=1;
6794 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6795 image=mng_info->image;
6799 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6800 /* PNG does not handle depths greater than 16 so reduce it even
6803 if (image->depth > 16)
6807 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6808 if (image->depth > 8)
6810 /* To do: fill low byte properly */
6814 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6818 if (image_info->number_scenes != 0)
6820 if (mng_info->scenes_found >
6821 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6825 if (logging != MagickFalse)
6826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6827 " Finished reading image datastream.");
6829 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6831 (void) CloseBlob(image);
6833 if (logging != MagickFalse)
6834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6835 " Finished reading all image datastreams.");
6837 #if defined(MNG_INSERT_LAYERS)
6838 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6839 (mng_info->mng_height))
6842 Insert a background layer if nothing else was found.
6844 if (logging != MagickFalse)
6845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6846 " No images found. Inserting a background layer.");
6848 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6851 Allocate next image structure.
6853 AcquireNextImage(image_info,image,exception);
6854 if (GetNextImageInList(image) == (Image *) NULL)
6856 image=DestroyImageList(image);
6857 MngInfoFreeStruct(mng_info,&have_mng_structure);
6859 if (logging != MagickFalse)
6860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6861 " Allocation failed, returning NULL.");
6863 return((Image *) NULL);
6865 image=SyncNextImageInList(image);
6867 image->columns=mng_info->mng_width;
6868 image->rows=mng_info->mng_height;
6869 image->page.width=mng_info->mng_width;
6870 image->page.height=mng_info->mng_height;
6873 image->background_color=mng_background_color;
6874 image->matte=MagickFalse;
6876 if (image_info->ping == MagickFalse)
6877 (void) SetImageBackgroundColor(image,exception);
6879 mng_info->image_found++;
6882 image->iterations=mng_iterations;
6884 if (mng_iterations == 1)
6885 image->start_loop=MagickTrue;
6887 while (GetPreviousImageInList(image) != (Image *) NULL)
6890 if (image_count > 10*mng_info->image_found)
6892 if (logging != MagickFalse)
6893 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6895 (void) ThrowMagickException(exception,GetMagickModule(),
6896 CoderError,"Linked list is corrupted, beginning of list not found",
6897 "`%s'",image_info->filename);
6899 return((Image *) NULL);
6902 image=GetPreviousImageInList(image);
6904 if (GetNextImageInList(image) == (Image *) NULL)
6906 if (logging != MagickFalse)
6907 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6909 (void) ThrowMagickException(exception,GetMagickModule(),
6910 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6911 image_info->filename);
6915 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6916 GetNextImageInList(image) ==
6919 if (logging != MagickFalse)
6920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6921 " First image null");
6923 (void) ThrowMagickException(exception,GetMagickModule(),
6924 CoderError,"image->next for first image is NULL but shouldn't be.",
6925 "`%s'",image_info->filename);
6928 if (mng_info->image_found == 0)
6930 if (logging != MagickFalse)
6931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932 " No visible images found.");
6934 (void) ThrowMagickException(exception,GetMagickModule(),
6935 CoderError,"No visible images in file","`%s'",image_info->filename);
6937 if (image != (Image *) NULL)
6938 image=DestroyImageList(image);
6940 MngInfoFreeStruct(mng_info,&have_mng_structure);
6941 return((Image *) NULL);
6944 if (mng_info->ticks_per_second)
6945 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6946 final_delay/mng_info->ticks_per_second;
6949 image->start_loop=MagickTrue;
6951 /* Find final nonzero image delay */
6952 final_image_delay=0;
6954 while (GetNextImageInList(image) != (Image *) NULL)
6957 final_image_delay=image->delay;
6959 image=GetNextImageInList(image);
6962 if (final_delay < final_image_delay)
6963 final_delay=final_image_delay;
6965 image->delay=final_delay;
6967 if (logging != MagickFalse)
6968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6969 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6970 (double) final_delay);
6972 if (logging != MagickFalse)
6978 image=GetFirstImageInList(image);
6980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6981 " Before coalesce:");
6983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6984 " scene 0 delay=%.20g",(double) image->delay);
6986 while (GetNextImageInList(image) != (Image *) NULL)
6988 image=GetNextImageInList(image);
6989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6990 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6994 image=GetFirstImageInList(image);
6995 #ifdef MNG_COALESCE_LAYERS
7005 if (logging != MagickFalse)
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7009 next_image=CoalesceImages(image,exception);
7011 if (next_image == (Image *) NULL)
7012 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7014 image=DestroyImageList(image);
7017 for (next=image; next != (Image *) NULL; next=next_image)
7019 next->page.width=mng_info->mng_width;
7020 next->page.height=mng_info->mng_height;
7023 next->scene=scene++;
7024 next_image=GetNextImageInList(next);
7026 if (next_image == (Image *) NULL)
7029 if (next->delay == 0)
7032 next_image->previous=GetPreviousImageInList(next);
7033 if (GetPreviousImageInList(next) == (Image *) NULL)
7036 next->previous->next=next_image;
7037 next=DestroyImage(next);
7043 while (GetNextImageInList(image) != (Image *) NULL)
7044 image=GetNextImageInList(image);
7046 image->dispose=BackgroundDispose;
7048 if (logging != MagickFalse)
7054 image=GetFirstImageInList(image);
7056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7057 " After coalesce:");
7059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7060 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7061 (double) image->dispose);
7063 while (GetNextImageInList(image) != (Image *) NULL)
7065 image=GetNextImageInList(image);
7067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7068 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7069 (double) image->delay,(double) image->dispose);
7073 image=GetFirstImageInList(image);
7074 MngInfoFreeStruct(mng_info,&have_mng_structure);
7075 have_mng_structure=MagickFalse;
7077 if (logging != MagickFalse)
7078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7080 return(GetFirstImageInList(image));
7082 #else /* PNG_LIBPNG_VER > 10011 */
7083 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7085 printf("Your PNG library is too old: You have libpng-%s\n",
7086 PNG_LIBPNG_VER_STRING);
7088 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7089 "PNG library is too old","`%s'",image_info->filename);
7091 return(Image *) NULL;
7094 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7096 return(ReadPNGImage(image_info,exception));
7098 #endif /* PNG_LIBPNG_VER > 10011 */
7102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7106 % R e g i s t e r P N G I m a g e %
7110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7112 % RegisterPNGImage() adds properties for the PNG image format to
7113 % the list of supported formats. The properties include the image format
7114 % tag, a method to read and/or write the format, whether the format
7115 % supports the saving of more than one frame to the same file or blob,
7116 % whether the format supports native in-memory I/O, and a brief
7117 % description of the format.
7119 % The format of the RegisterPNGImage method is:
7121 % size_t RegisterPNGImage(void)
7124 ModuleExport size_t RegisterPNGImage(void)
7127 version[MaxTextExtent];
7135 "See http://www.libpng.org/ for details about the PNG format."
7140 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7146 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7152 #if defined(PNG_LIBPNG_VER_STRING)
7153 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7154 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7156 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7158 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7159 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7164 entry=SetMagickInfo("MNG");
7165 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7167 #if defined(MAGICKCORE_PNG_DELEGATE)
7168 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7169 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7172 entry->magick=(IsImageFormatHandler *) IsMNG;
7173 entry->description=ConstantString("Multiple-image Network Graphics");
7175 if (*version != '\0')
7176 entry->version=ConstantString(version);
7178 entry->module=ConstantString("PNG");
7179 entry->note=ConstantString(MNGNote);
7180 (void) RegisterMagickInfo(entry);
7182 entry=SetMagickInfo("PNG");
7184 #if defined(MAGICKCORE_PNG_DELEGATE)
7185 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7186 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7189 entry->magick=(IsImageFormatHandler *) IsPNG;
7190 entry->adjoin=MagickFalse;
7191 entry->description=ConstantString("Portable Network Graphics");
7192 entry->module=ConstantString("PNG");
7194 if (*version != '\0')
7195 entry->version=ConstantString(version);
7197 entry->note=ConstantString(PNGNote);
7198 (void) RegisterMagickInfo(entry);
7200 entry=SetMagickInfo("PNG8");
7202 #if defined(MAGICKCORE_PNG_DELEGATE)
7203 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7204 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7207 entry->magick=(IsImageFormatHandler *) IsPNG;
7208 entry->adjoin=MagickFalse;
7209 entry->description=ConstantString(
7210 "8-bit indexed with optional binary transparency");
7211 entry->module=ConstantString("PNG");
7212 (void) RegisterMagickInfo(entry);
7214 entry=SetMagickInfo("PNG24");
7217 #if defined(ZLIB_VERSION)
7218 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7219 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7221 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7223 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7224 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7228 if (*version != '\0')
7229 entry->version=ConstantString(version);
7231 #if defined(MAGICKCORE_PNG_DELEGATE)
7232 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7233 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7236 entry->magick=(IsImageFormatHandler *) IsPNG;
7237 entry->adjoin=MagickFalse;
7238 entry->description=ConstantString("opaque 24-bit RGB");
7239 entry->module=ConstantString("PNG");
7240 (void) RegisterMagickInfo(entry);
7242 entry=SetMagickInfo("PNG32");
7244 #if defined(MAGICKCORE_PNG_DELEGATE)
7245 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7246 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7249 entry->magick=(IsImageFormatHandler *) IsPNG;
7250 entry->adjoin=MagickFalse;
7251 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7252 entry->module=ConstantString("PNG");
7253 (void) RegisterMagickInfo(entry);
7255 entry=SetMagickInfo("JNG");
7257 #if defined(JNG_SUPPORTED)
7258 #if defined(MAGICKCORE_PNG_DELEGATE)
7259 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7260 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7264 entry->magick=(IsImageFormatHandler *) IsJNG;
7265 entry->adjoin=MagickFalse;
7266 entry->description=ConstantString("JPEG Network Graphics");
7267 entry->module=ConstantString("PNG");
7268 entry->note=ConstantString(JNGNote);
7269 (void) RegisterMagickInfo(entry);
7271 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7272 ping_semaphore=AllocateSemaphoreInfo();
7275 return(MagickImageCoderSignature);
7279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7283 % U n r e g i s t e r P N G I m a g e %
7287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7289 % UnregisterPNGImage() removes format registrations made by the
7290 % PNG module from the list of supported formats.
7292 % The format of the UnregisterPNGImage method is:
7294 % UnregisterPNGImage(void)
7297 ModuleExport void UnregisterPNGImage(void)
7299 (void) UnregisterMagickInfo("MNG");
7300 (void) UnregisterMagickInfo("PNG");
7301 (void) UnregisterMagickInfo("PNG8");
7302 (void) UnregisterMagickInfo("PNG24");
7303 (void) UnregisterMagickInfo("PNG32");
7304 (void) UnregisterMagickInfo("JNG");
7306 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7307 if (ping_semaphore != (SemaphoreInfo *) NULL)
7308 DestroySemaphoreInfo(&ping_semaphore);
7312 #if defined(MAGICKCORE_PNG_DELEGATE)
7313 #if PNG_LIBPNG_VER > 10011
7315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7319 % W r i t e M N G I m a g e %
7323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7325 % WriteMNGImage() writes an image in the Portable Network Graphics
7326 % Group's "Multiple-image Network Graphics" encoded image format.
7328 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7330 % The format of the WriteMNGImage method is:
7332 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7333 % Image *image,ExceptionInfo *exception)
7335 % A description of each parameter follows.
7337 % o image_info: the image info.
7339 % o image: The image.
7341 % o exception: return any errors or warnings in this structure.
7343 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7344 % "To do" under ReadPNGImage):
7346 % Preserve all unknown and not-yet-handled known chunks found in input
7347 % PNG file and copy them into output PNG files according to the PNG
7350 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7352 % Improve selection of color type (use indexed-colour or indexed-colour
7353 % with tRNS when 256 or fewer unique RGBA values are present).
7355 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7356 % This will be complicated if we limit ourselves to generating MNG-LC
7357 % files. For now we ignore disposal method 3 and simply overlay the next
7360 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7361 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7362 % [mostly done 15 June 1999 but still need to take care of tRNS]
7364 % Check for identical sRGB and replace with a global sRGB (and remove
7365 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7366 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7367 % local gAMA/cHRM with local sRGB if appropriate).
7369 % Check for identical sBIT chunks and write global ones.
7371 % Provide option to skip writing the signature tEXt chunks.
7373 % Use signatures to detect identical objects and reuse the first
7374 % instance of such objects instead of writing duplicate objects.
7376 % Use a smaller-than-32k value of compression window size when
7379 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7380 % ancillary text chunks and save profiles.
7382 % Provide an option to force LC files (to ensure exact framing rate)
7385 % Provide an option to force VLC files instead of LC, even when offsets
7386 % are present. This will involve expanding the embedded images with a
7387 % transparent region at the top and/or left.
7391 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7392 png_info *ping_info, unsigned char *profile_type, unsigned char
7393 *profile_description, unsigned char *profile_data, png_uint_32 length)
7412 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7414 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7417 if (image_info->verbose)
7419 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7420 (char *) profile_type, (double) length);
7423 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7424 description_length=(png_uint_32) strlen((const char *) profile_description);
7425 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7426 + description_length);
7427 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7428 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7429 text[0].key[0]='\0';
7430 (void) ConcatenateMagickString(text[0].key,
7431 "Raw profile type ",MaxTextExtent);
7432 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7436 (void) CopyMagickString(dp,(const char *) profile_description,
7438 dp+=description_length;
7440 (void) FormatLocaleString(dp,allocated_length-
7441 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7444 for (i=0; i < (ssize_t) length; i++)
7448 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7449 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7454 text[0].text_length=(png_size_t) (dp-text[0].text);
7455 text[0].compression=image_info->compression == NoCompression ||
7456 (image_info->compression == UndefinedCompression &&
7457 text[0].text_length < 128) ? -1 : 0;
7459 if (text[0].text_length <= allocated_length)
7460 png_set_text(ping,ping_info,text,1);
7462 png_free(ping,text[0].text);
7463 png_free(ping,text[0].key);
7464 png_free(ping,text);
7467 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7468 const char *string, MagickBooleanType logging)
7481 ResetImageProfileIterator(image);
7483 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7485 profile=GetImageProfile(image,name);
7487 if (profile != (const StringInfo *) NULL)
7492 if (LocaleNCompare(name,string,11) == 0)
7494 if (logging != MagickFalse)
7495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7496 " Found %s profile",name);
7498 ping_profile=CloneStringInfo(profile);
7499 data=GetStringInfoDatum(ping_profile),
7500 length=(png_uint_32) GetStringInfoLength(ping_profile);
7505 (void) WriteBlobMSBULong(image,length-5); /* data length */
7506 (void) WriteBlob(image,length-1,data+1);
7507 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7508 ping_profile=DestroyStringInfo(ping_profile);
7512 name=GetNextImageProfile(image);
7519 /* Write one PNG image */
7520 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7521 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7545 ping_trans_alpha[256];
7573 ping_have_cheap_transparency,
7584 /* ping_exclude_EXIF, */
7587 /* ping_exclude_iTXt, */
7592 /* ping_exclude_tRNS, */
7594 ping_exclude_zCCP, /* hex-encoded iCCP */
7597 ping_preserve_colormap,
7598 ping_need_colortype_warning,
7622 ping_interlace_method,
7623 ping_compression_method,
7640 number_semitransparent,
7642 ping_pHYs_unit_type;
7645 ping_pHYs_x_resolution,
7646 ping_pHYs_y_resolution;
7648 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7649 " Enter WriteOnePNGImage()");
7651 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7652 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7653 if (image_info == (ImageInfo *) NULL)
7654 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7656 /* Initialize some stuff */
7659 ping_interlace_method=0,
7660 ping_compression_method=0,
7661 ping_filter_method=0,
7664 ping_background.red = 0;
7665 ping_background.green = 0;
7666 ping_background.blue = 0;
7667 ping_background.gray = 0;
7668 ping_background.index = 0;
7670 ping_trans_color.red=0;
7671 ping_trans_color.green=0;
7672 ping_trans_color.blue=0;
7673 ping_trans_color.gray=0;
7675 ping_pHYs_unit_type = 0;
7676 ping_pHYs_x_resolution = 0;
7677 ping_pHYs_y_resolution = 0;
7679 ping_have_blob=MagickFalse;
7680 ping_have_color=MagickTrue;
7681 ping_have_non_bw=MagickTrue;
7682 ping_have_PLTE=MagickFalse;
7683 ping_have_bKGD=MagickFalse;
7684 ping_have_pHYs=MagickFalse;
7685 ping_have_tRNS=MagickFalse;
7687 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7688 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7689 ping_exclude_date=mng_info->ping_exclude_date;
7690 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7691 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7692 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7693 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7694 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7695 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7696 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7697 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7698 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7699 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7700 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7701 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7703 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7704 ping_need_colortype_warning = MagickFalse;
7706 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7707 * i.e., eliminate the ICC profile and set image->rendering_intent.
7708 * Note that this will not involve any changes to the actual pixels
7709 * but merely passes information to applications that read the resulting
7712 if (ping_exclude_sRGB == MagickFalse)
7720 ResetImageProfileIterator(image);
7721 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7723 profile=GetImageProfile(image,name);
7725 if (profile != (StringInfo *) NULL)
7727 if ((LocaleCompare(name,"ICC") == 0) ||
7728 (LocaleCompare(name,"ICM") == 0))
7733 /* 0: not a known sRGB profile
7734 * 1: HP-Microsoft sRGB v2
7735 * 2: ICC sRGB v4 perceptual
7736 * 3: ICC sRGB v2 perceptual no black-compensation
7739 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7740 check_len[4] = {0, 3144, 60960, 3052};
7749 length=(png_uint_32) GetStringInfoLength(profile);
7751 for (icheck=3; icheck > 0; icheck--)
7753 if (length == check_len[icheck])
7755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7756 " Got a %lu-byte ICC profile (potentially sRGB)",
7757 (unsigned long) length);
7759 data=GetStringInfoDatum(profile);
7760 profile_crc=crc32(0,data,length);
7762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7763 " with crc=%8x",(unsigned int) profile_crc);
7765 if (profile_crc == check_crc[icheck])
7767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7769 if (image->rendering_intent==UndefinedIntent)
7770 image->rendering_intent=PerceptualIntent;
7776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7777 " Got a %lu-byte ICC profile",
7778 (unsigned long) length);
7781 name=GetNextImageProfile(image);
7786 number_semitransparent = 0;
7787 number_transparent = 0;
7789 if (logging != MagickFalse)
7791 if (image->storage_class == UndefinedClass)
7792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7793 " storage_class=UndefinedClass");
7794 if (image->storage_class == DirectClass)
7795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7796 " storage_class=DirectClass");
7797 if (image->storage_class == PseudoClass)
7798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7799 " storage_class=PseudoClass");
7802 if (image->storage_class == PseudoClass &&
7803 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7804 (mng_info->write_png_colortype != 0 &&
7805 mng_info->write_png_colortype != 4)))
7807 (void) SyncImage(image,exception);
7808 image->storage_class = DirectClass;
7811 if (ping_preserve_colormap == MagickFalse)
7813 if (image->storage_class != PseudoClass && image->colormap != NULL)
7815 /* Free the bogus colormap; it can cause trouble later */
7816 if (logging != MagickFalse)
7817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7818 " Freeing bogus colormap");
7819 (void) RelinquishMagickMemory(image->colormap);
7820 image->colormap=NULL;
7824 if ((IssRGBColorspace(image->colorspace) == MagickFalse) &&
7825 (IsImageGray(image,exception) == MagickFalse))
7826 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7829 Sometimes we get PseudoClass images whose RGB values don't match
7830 the colors in the colormap. This code syncs the RGB values.
7832 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7833 (void) SyncImage(image,exception);
7835 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7836 if (image->depth > 8)
7838 if (logging != MagickFalse)
7839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7840 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7846 /* Respect the -depth option */
7847 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7852 if (image->depth > 8)
7854 #if MAGICKCORE_QUANTUM_DEPTH > 16
7855 /* Scale to 16-bit */
7856 LBR16PacketRGBO(image->background_color);
7858 for (y=0; y < (ssize_t) image->rows; y++)
7860 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7862 if (r == (Quantum *) NULL)
7865 for (x=0; x < (ssize_t) image->columns; x++)
7868 r+=GetPixelChannels(image);
7871 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7875 if (image->storage_class == PseudoClass && image->colormap != NULL)
7877 for (i=0; i < (ssize_t) image->colors; i++)
7879 LBR16PacketRGBO(image->colormap[i]);
7882 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7885 else if (image->depth > 4)
7887 #if MAGICKCORE_QUANTUM_DEPTH > 8
7888 /* Scale to 8-bit */
7889 LBR08PacketRGBO(image->background_color);
7891 for (y=0; y < (ssize_t) image->rows; y++)
7893 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7895 if (r == (Quantum *) NULL)
7898 for (x=0; x < (ssize_t) image->columns; x++)
7901 r+=GetPixelChannels(image);
7904 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7908 if (image->storage_class == PseudoClass && image->colormap != NULL)
7910 for (i=0; i < (ssize_t) image->colors; i++)
7912 LBR08PacketRGBO(image->colormap[i]);
7915 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7918 if (image->depth > 2)
7920 /* Scale to 4-bit */
7921 LBR04PacketRGBO(image->background_color);
7923 for (y=0; y < (ssize_t) image->rows; y++)
7925 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7927 if (r == (Quantum *) NULL)
7930 for (x=0; x < (ssize_t) image->columns; x++)
7933 r+=GetPixelChannels(image);
7936 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7940 if (image->storage_class == PseudoClass && image->colormap != NULL)
7942 for (i=0; i < (ssize_t) image->colors; i++)
7944 LBR04PacketRGBO(image->colormap[i]);
7949 else if (image->depth > 1)
7951 /* Scale to 2-bit */
7952 LBR02PacketRGBO(image->background_color);
7954 for (y=0; y < (ssize_t) image->rows; y++)
7956 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7958 if (r == (Quantum *) NULL)
7961 for (x=0; x < (ssize_t) image->columns; x++)
7964 r+=GetPixelChannels(image);
7967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7971 if (image->storage_class == PseudoClass && image->colormap != NULL)
7973 for (i=0; i < (ssize_t) image->colors; i++)
7975 LBR02PacketRGBO(image->colormap[i]);
7981 /* Scale to 1-bit */
7982 LBR01PacketRGBO(image->background_color);
7984 for (y=0; y < (ssize_t) image->rows; y++)
7986 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7988 if (r == (Quantum *) NULL)
7991 for (x=0; x < (ssize_t) image->columns; x++)
7994 r+=GetPixelChannels(image);
7997 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8001 if (image->storage_class == PseudoClass && image->colormap != NULL)
8003 for (i=0; i < (ssize_t) image->colors; i++)
8005 LBR01PacketRGBO(image->colormap[i]);
8011 /* To do: set to next higher multiple of 8 */
8012 if (image->depth < 8)
8015 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8016 /* PNG does not handle depths greater than 16 so reduce it even
8019 if (image->depth > 8)
8023 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8024 if (image->depth > 8)
8026 /* To do: fill low byte properly */
8030 if (image->depth == 16 && mng_info->write_png_depth != 16)
8031 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8035 /* Normally we run this just once, but in the case of writing PNG8
8036 * we reduce the transparency to binary and run again, then if there
8037 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8038 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8039 * palette. Then (To do) we take care of a final reduction that is only
8040 * needed if there are still 256 colors present and one of them has both
8041 * transparent and opaque instances.
8044 tried_332 = MagickFalse;
8045 tried_333 = MagickFalse;
8046 tried_444 = MagickFalse;
8052 * Sometimes we get DirectClass images that have 256 colors or fewer.
8053 * This code will build a colormap.
8055 * Also, sometimes we get PseudoClass images with an out-of-date
8056 * colormap. This code will replace the colormap with a new one.
8057 * Sometimes we get PseudoClass images that have more than 256 colors.
8058 * This code will delete the colormap and change the image to
8061 * If image->matte is MagickFalse, we ignore the alpha channel
8062 * even though it sometimes contains left-over non-opaque values.
8064 * Also we gather some information (number of opaque, transparent,
8065 * and semitransparent pixels, and whether the image has any non-gray
8066 * pixels or only black-and-white pixels) that we might need later.
8068 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8069 * we need to check for bogus non-opaque values, at least.
8077 semitransparent[260],
8080 register const Quantum
8087 if (logging != MagickFalse)
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " Enter BUILD_PALETTE:");
8091 if (logging != MagickFalse)
8093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " image->columns=%.20g",(double) image->columns);
8095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8096 " image->rows=%.20g",(double) image->rows);
8097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8098 " image->matte=%.20g",(double) image->matte);
8099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8100 " image->depth=%.20g",(double) image->depth);
8102 if (image->storage_class == PseudoClass && image->colormap != NULL)
8104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8105 " Original colormap:");
8106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8107 " i (red,green,blue,alpha)");
8109 for (i=0; i < 256; i++)
8111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8112 " %d (%d,%d,%d,%d)",
8114 (int) image->colormap[i].red,
8115 (int) image->colormap[i].green,
8116 (int) image->colormap[i].blue,
8117 (int) image->colormap[i].alpha);
8120 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8125 " %d (%d,%d,%d,%d)",
8127 (int) image->colormap[i].red,
8128 (int) image->colormap[i].green,
8129 (int) image->colormap[i].blue,
8130 (int) image->colormap[i].alpha);
8135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8136 " image->colors=%d",(int) image->colors);
8138 if (image->colors == 0)
8139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8140 " (zero means unknown)");
8142 if (ping_preserve_colormap == MagickFalse)
8143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8144 " Regenerate the colormap");
8149 number_semitransparent = 0;
8150 number_transparent = 0;
8152 for (y=0; y < (ssize_t) image->rows; y++)
8154 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8156 if (q == (Quantum *) NULL)
8159 for (x=0; x < (ssize_t) image->columns; x++)
8161 if (image->matte == MagickFalse ||
8162 GetPixelAlpha(image,q) == OpaqueAlpha)
8164 if (number_opaque < 259)
8166 if (number_opaque == 0)
8168 GetPixelInfoPixel(image, q, opaque);
8169 opaque[0].alpha=OpaqueAlpha;
8173 for (i=0; i< (ssize_t) number_opaque; i++)
8175 if (IsPixelEquivalent(image,q, opaque+i))
8179 if (i == (ssize_t) number_opaque && number_opaque < 259)
8182 GetPixelInfoPixel(image, q, opaque+i);
8183 opaque[i].alpha=OpaqueAlpha;
8187 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8189 if (number_transparent < 259)
8191 if (number_transparent == 0)
8193 GetPixelInfoPixel(image, q, transparent);
8194 ping_trans_color.red=(unsigned short)
8195 GetPixelRed(image,q);
8196 ping_trans_color.green=(unsigned short)
8197 GetPixelGreen(image,q);
8198 ping_trans_color.blue=(unsigned short)
8199 GetPixelBlue(image,q);
8200 ping_trans_color.gray=(unsigned short)
8201 GetPixelRed(image,q);
8202 number_transparent = 1;
8205 for (i=0; i< (ssize_t) number_transparent; i++)
8207 if (IsPixelEquivalent(image,q, transparent+i))
8211 if (i == (ssize_t) number_transparent &&
8212 number_transparent < 259)
8214 number_transparent++;
8215 GetPixelInfoPixel(image,q,transparent+i);
8221 if (number_semitransparent < 259)
8223 if (number_semitransparent == 0)
8225 GetPixelInfoPixel(image,q,semitransparent);
8226 number_semitransparent = 1;
8229 for (i=0; i< (ssize_t) number_semitransparent; i++)
8231 if (IsPixelEquivalent(image,q, semitransparent+i)
8232 && GetPixelAlpha(image,q) ==
8233 semitransparent[i].alpha)
8237 if (i == (ssize_t) number_semitransparent &&
8238 number_semitransparent < 259)
8240 number_semitransparent++;
8241 GetPixelInfoPixel(image, q, semitransparent+i);
8245 q+=GetPixelChannels(image);
8249 if (mng_info->write_png8 == MagickFalse &&
8250 ping_exclude_bKGD == MagickFalse)
8252 /* Add the background color to the palette, if it
8253 * isn't already there.
8255 if (logging != MagickFalse)
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " Check colormap for background (%d,%d,%d)",
8259 (int) image->background_color.red,
8260 (int) image->background_color.green,
8261 (int) image->background_color.blue);
8263 for (i=0; i<number_opaque; i++)
8265 if (opaque[i].red == image->background_color.red &&
8266 opaque[i].green == image->background_color.green &&
8267 opaque[i].blue == image->background_color.blue)
8270 if (number_opaque < 259 && i == number_opaque)
8272 opaque[i] = image->background_color;
8273 ping_background.index = i;
8274 if (logging != MagickFalse)
8276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277 " background_color index is %d",(int) i);
8281 else if (logging != MagickFalse)
8282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8283 " No room in the colormap to add background color");
8286 image_colors=number_opaque+number_transparent+number_semitransparent;
8288 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8290 /* No room for the background color; remove it. */
8295 if (logging != MagickFalse)
8297 if (image_colors > 256)
8298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8299 " image has more than 256 colors");
8302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8303 " image has %d colors",image_colors);
8306 if (ping_preserve_colormap != MagickFalse)
8309 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8311 ping_have_color=MagickFalse;
8312 ping_have_non_bw=MagickFalse;
8314 if(image_colors > 256)
8316 for (y=0; y < (ssize_t) image->rows; y++)
8318 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8320 if (q == (Quantum *) NULL)
8324 for (x=0; x < (ssize_t) image->columns; x++)
8326 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8327 GetPixelRed(image,s) != GetPixelBlue(image,s))
8329 ping_have_color=MagickTrue;
8330 ping_have_non_bw=MagickTrue;
8333 s+=GetPixelChannels(image);
8336 if (ping_have_color != MagickFalse)
8339 /* Worst case is black-and-white; we are looking at every
8343 if (ping_have_non_bw == MagickFalse)
8346 for (x=0; x < (ssize_t) image->columns; x++)
8348 if (GetPixelRed(image,s) != 0 &&
8349 GetPixelRed(image,s) != QuantumRange)
8351 ping_have_non_bw=MagickTrue;
8354 s+=GetPixelChannels(image);
8361 if (image_colors < 257)
8367 * Initialize image colormap.
8370 if (logging != MagickFalse)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " Sort the new colormap");
8374 /* Sort palette, transparent first */;
8378 for (i=0; i<number_transparent; i++)
8379 colormap[n++] = transparent[i];
8381 for (i=0; i<number_semitransparent; i++)
8382 colormap[n++] = semitransparent[i];
8384 for (i=0; i<number_opaque; i++)
8385 colormap[n++] = opaque[i];
8387 ping_background.index +=
8388 (number_transparent + number_semitransparent);
8390 /* image_colors < 257; search the colormap instead of the pixels
8391 * to get ping_have_color and ping_have_non_bw
8395 if (ping_have_color == MagickFalse)
8397 if (colormap[i].red != colormap[i].green ||
8398 colormap[i].red != colormap[i].blue)
8400 ping_have_color=MagickTrue;
8401 ping_have_non_bw=MagickTrue;
8406 if (ping_have_non_bw == MagickFalse)
8408 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8409 ping_have_non_bw=MagickTrue;
8413 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8414 (number_transparent == 0 && number_semitransparent == 0)) &&
8415 (((mng_info->write_png_colortype-1) ==
8416 PNG_COLOR_TYPE_PALETTE) ||
8417 (mng_info->write_png_colortype == 0)))
8419 if (logging != MagickFalse)
8421 if (n != (ssize_t) image_colors)
8422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8423 " image_colors (%d) and n (%d) don't match",
8426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " AcquireImageColormap");
8430 image->colors = image_colors;
8432 if (AcquireImageColormap(image,image_colors,exception) ==
8434 ThrowWriterException(ResourceLimitError,
8435 "MemoryAllocationFailed");
8437 for (i=0; i< (ssize_t) image_colors; i++)
8438 image->colormap[i] = colormap[i];
8440 if (logging != MagickFalse)
8442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443 " image->colors=%d (%d)",
8444 (int) image->colors, image_colors);
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " Update the pixel indexes");
8450 /* Sync the pixel indices with the new colormap */
8452 for (y=0; y < (ssize_t) image->rows; y++)
8454 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8456 if (q == (Quantum *) NULL)
8459 for (x=0; x < (ssize_t) image->columns; x++)
8461 for (i=0; i< (ssize_t) image_colors; i++)
8463 if ((image->matte == MagickFalse ||
8464 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8465 image->colormap[i].red == GetPixelRed(image,q) &&
8466 image->colormap[i].green == GetPixelGreen(image,q) &&
8467 image->colormap[i].blue == GetPixelBlue(image,q))
8469 SetPixelIndex(image,i,q);
8473 q+=GetPixelChannels(image);
8476 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8482 if (logging != MagickFalse)
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " image->colors=%d", (int) image->colors);
8487 if (image->colormap != NULL)
8489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8490 " i (red,green,blue,alpha)");
8492 for (i=0; i < (ssize_t) image->colors; i++)
8494 if (i < 300 || i >= (ssize_t) image->colors - 10)
8496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " %d (%d,%d,%d,%d)",
8499 (int) image->colormap[i].red,
8500 (int) image->colormap[i].green,
8501 (int) image->colormap[i].blue,
8502 (int) image->colormap[i].alpha);
8507 if (number_transparent < 257)
8508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8509 " number_transparent = %d",
8510 number_transparent);
8513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8514 " number_transparent > 256");
8516 if (number_opaque < 257)
8517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " number_opaque = %d",
8522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8523 " number_opaque > 256");
8525 if (number_semitransparent < 257)
8526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8527 " number_semitransparent = %d",
8528 number_semitransparent);
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532 " number_semitransparent > 256");
8534 if (ping_have_non_bw == MagickFalse)
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " All pixels and the background are black or white");
8538 else if (ping_have_color == MagickFalse)
8539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8540 " All pixels and the background are gray");
8543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8544 " At least one pixel or the background is non-gray");
8546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8547 " Exit BUILD_PALETTE:");
8550 if (mng_info->write_png8 == MagickFalse)
8553 /* Make any reductions necessary for the PNG8 format */
8554 if (image_colors <= 256 &&
8555 image_colors != 0 && image->colormap != NULL &&
8556 number_semitransparent == 0 &&
8557 number_transparent <= 1)
8560 /* PNG8 can't have semitransparent colors so we threshold the
8561 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8562 * transparent color so if more than one is transparent we merge
8563 * them into image->background_color.
8565 if (number_semitransparent != 0 || number_transparent > 1)
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Thresholding the alpha channel to binary");
8570 for (y=0; y < (ssize_t) image->rows; y++)
8572 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8574 if (r == (Quantum *) NULL)
8577 for (x=0; x < (ssize_t) image->columns; x++)
8579 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8581 SetPixelInfoPixel(image,&image->background_color,r);
8582 SetPixelAlpha(image,TransparentAlpha,r);
8585 SetPixelAlpha(image,OpaqueAlpha,r);
8586 r+=GetPixelChannels(image);
8589 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8592 if (image_colors != 0 && image_colors <= 256 &&
8593 image->colormap != NULL)
8594 for (i=0; i<image_colors; i++)
8595 image->colormap[i].alpha =
8596 (image->colormap[i].alpha > TransparentAlpha/2 ?
8597 TransparentAlpha : OpaqueAlpha);
8602 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8603 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8604 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8607 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Quantizing the background color to 4-4-4");
8613 tried_444 = MagickTrue;
8615 LBR04PacketRGB(image->background_color);
8617 if (logging != MagickFalse)
8618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8619 " Quantizing the pixel colors to 4-4-4");
8621 if (image->colormap == NULL)
8623 for (y=0; y < (ssize_t) image->rows; y++)
8625 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8627 if (r == (Quantum *) NULL)
8630 for (x=0; x < (ssize_t) image->columns; x++)
8632 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8634 r+=GetPixelChannels(image);
8637 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8642 else /* Should not reach this; colormap already exists and
8645 if (logging != MagickFalse)
8646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8647 " Quantizing the colormap to 4-4-4");
8649 for (i=0; i<image_colors; i++)
8651 LBR04PacketRGB(image->colormap[i]);
8657 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8659 if (logging != MagickFalse)
8660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8661 " Quantizing the background color to 3-3-3");
8663 tried_333 = MagickTrue;
8665 LBR03PacketRGB(image->background_color);
8667 if (logging != MagickFalse)
8668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8669 " Quantizing the pixel colors to 3-3-3-1");
8671 if (image->colormap == NULL)
8673 for (y=0; y < (ssize_t) image->rows; y++)
8675 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8677 if (r == (Quantum *) NULL)
8680 for (x=0; x < (ssize_t) image->columns; x++)
8682 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8684 r+=GetPixelChannels(image);
8687 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8692 else /* Should not reach this; colormap already exists and
8695 if (logging != MagickFalse)
8696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8697 " Quantizing the colormap to 3-3-3-1");
8698 for (i=0; i<image_colors; i++)
8700 LBR03PacketRGB(image->colormap[i]);
8706 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8708 if (logging != MagickFalse)
8709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710 " Quantizing the background color to 3-3-2");
8712 tried_332 = MagickTrue;
8714 /* Red and green were already done so we only quantize the blue
8718 LBR02PacketBlue(image->background_color);
8720 if (logging != MagickFalse)
8721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8722 " Quantizing the pixel colors to 3-3-2-1");
8724 if (image->colormap == NULL)
8726 for (y=0; y < (ssize_t) image->rows; y++)
8728 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8730 if (r == (Quantum *) NULL)
8733 for (x=0; x < (ssize_t) image->columns; x++)
8735 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8737 r+=GetPixelChannels(image);
8740 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8745 else /* Should not reach this; colormap already exists and
8748 if (logging != MagickFalse)
8749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8750 " Quantizing the colormap to 3-3-2-1");
8751 for (i=0; i<image_colors; i++)
8753 LBR02PacketBlue(image->colormap[i]);
8760 if (image_colors == 0 || image_colors > 256)
8762 /* Take care of special case with 256 colors + 1 transparent
8763 * color. We don't need to quantize to 2-3-2-1; we only need to
8764 * eliminate one color, so we'll merge the two darkest red
8765 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8767 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8768 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8769 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8771 image->background_color.red=ScaleCharToQuantum(0x24);
8774 if (image->colormap == NULL)
8776 for (y=0; y < (ssize_t) image->rows; y++)
8778 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8780 if (r == (Quantum *) NULL)
8783 for (x=0; x < (ssize_t) image->columns; x++)
8785 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8786 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8787 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8788 GetPixelAlpha(image,r) == OpaqueAlpha)
8790 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8792 r+=GetPixelChannels(image);
8795 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8803 for (i=0; i<image_colors; i++)
8805 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8806 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8807 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8809 image->colormap[i].red=ScaleCharToQuantum(0x24);
8815 /* END OF BUILD_PALETTE */
8817 /* If we are excluding the tRNS chunk and there is transparency,
8818 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8821 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8822 (number_transparent != 0 || number_semitransparent != 0))
8824 unsigned int colortype=mng_info->write_png_colortype;
8826 if (ping_have_color == MagickFalse)
8827 mng_info->write_png_colortype = 5;
8830 mng_info->write_png_colortype = 7;
8832 if (colortype != 0 &&
8833 mng_info->write_png_colortype != colortype)
8834 ping_need_colortype_warning=MagickTrue;
8838 /* See if cheap transparency is possible. It is only possible
8839 * when there is a single transparent color, no semitransparent
8840 * color, and no opaque color that has the same RGB components
8841 * as the transparent color. We only need this information if
8842 * we are writing a PNG with colortype 0 or 2, and we have not
8843 * excluded the tRNS chunk.
8845 if (number_transparent == 1 &&
8846 mng_info->write_png_colortype < 4)
8848 ping_have_cheap_transparency = MagickTrue;
8850 if (number_semitransparent != 0)
8851 ping_have_cheap_transparency = MagickFalse;
8853 else if (image_colors == 0 || image_colors > 256 ||
8854 image->colormap == NULL)
8856 register const Quantum
8859 for (y=0; y < (ssize_t) image->rows; y++)
8861 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8863 if (q == (Quantum *) NULL)
8866 for (x=0; x < (ssize_t) image->columns; x++)
8868 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8869 (unsigned short) GetPixelRed(image,q) ==
8870 ping_trans_color.red &&
8871 (unsigned short) GetPixelGreen(image,q) ==
8872 ping_trans_color.green &&
8873 (unsigned short) GetPixelBlue(image,q) ==
8874 ping_trans_color.blue)
8876 ping_have_cheap_transparency = MagickFalse;
8880 q+=GetPixelChannels(image);
8883 if (ping_have_cheap_transparency == MagickFalse)
8889 /* Assuming that image->colormap[0] is the one transparent color
8890 * and that all others are opaque.
8892 if (image_colors > 1)
8893 for (i=1; i<image_colors; i++)
8894 if (image->colormap[i].red == image->colormap[0].red &&
8895 image->colormap[i].green == image->colormap[0].green &&
8896 image->colormap[i].blue == image->colormap[0].blue)
8898 ping_have_cheap_transparency = MagickFalse;
8903 if (logging != MagickFalse)
8905 if (ping_have_cheap_transparency == MagickFalse)
8906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907 " Cheap transparency is not possible.");
8910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911 " Cheap transparency is possible.");
8915 ping_have_cheap_transparency = MagickFalse;
8917 image_depth=image->depth;
8919 quantum_info = (QuantumInfo *) NULL;
8921 image_colors=(int) image->colors;
8922 image_matte=image->matte;
8924 mng_info->IsPalette=image->storage_class == PseudoClass &&
8925 image_colors <= 256 && image->colormap != NULL;
8927 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8928 (image->colors == 0 || image->colormap == NULL))
8930 image_info=DestroyImageInfo(image_info);
8931 image=DestroyImage(image);
8932 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8933 "Cannot write PNG8 or color-type 3; colormap is NULL",
8934 "`%s'",IMimage->filename);
8935 return(MagickFalse);
8939 Allocate the PNG structures
8941 #ifdef PNG_USER_MEM_SUPPORTED
8942 error_info.image=image;
8943 error_info.exception=exception;
8944 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
8945 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8946 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8949 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
8950 MagickPNGErrorHandler,MagickPNGWarningHandler);
8953 if (ping == (png_struct *) NULL)
8954 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8956 ping_info=png_create_info_struct(ping);
8958 if (ping_info == (png_info *) NULL)
8960 png_destroy_write_struct(&ping,(png_info **) NULL);
8961 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8964 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8965 ping_pixels=(unsigned char *) NULL;
8967 if (setjmp(png_jmpbuf(ping)))
8973 if (image_info->verbose)
8974 (void) printf("PNG write has failed.\n");
8976 png_destroy_write_struct(&ping,&ping_info);
8977 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
8978 UnlockSemaphoreInfo(ping_semaphore);
8981 if (ping_pixels != (unsigned char *) NULL)
8982 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8984 if (quantum_info != (QuantumInfo *) NULL)
8985 quantum_info=DestroyQuantumInfo(quantum_info);
8987 if (ping_have_blob != MagickFalse)
8988 (void) CloseBlob(image);
8989 image_info=DestroyImageInfo(image_info);
8990 image=DestroyImage(image);
8991 return(MagickFalse);
8994 /* { For navigation to end of SETJMP-protected block. Within this
8995 * block, use png_error() instead of Throwing an Exception, to ensure
8996 * that libpng is able to clean up, and that the semaphore is unlocked.
8999 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9000 LockSemaphoreInfo(ping_semaphore);
9004 Prepare PNG for writing.
9007 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9008 if (mng_info->write_mng)
9010 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9011 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9012 /* Disable new libpng-1.5.10 feature when writing a MNG because
9013 * zero-length PLTE is OK
9015 png_set_check_for_invalid_index (ping, 0);
9020 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9021 if (mng_info->write_mng)
9022 png_permit_empty_plte(ping,MagickTrue);
9029 ping_width=(png_uint_32) image->columns;
9030 ping_height=(png_uint_32) image->rows;
9032 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9035 if (mng_info->write_png_depth != 0)
9036 image_depth=mng_info->write_png_depth;
9038 /* Adjust requested depth to next higher valid depth if necessary */
9039 if (image_depth > 8)
9042 if ((image_depth > 4) && (image_depth < 8))
9045 if (image_depth == 3)
9048 if (logging != MagickFalse)
9050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9051 " width=%.20g",(double) ping_width);
9052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053 " height=%.20g",(double) ping_height);
9054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055 " image_matte=%.20g",(double) image->matte);
9056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057 " image->depth=%.20g",(double) image->depth);
9058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9059 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9062 save_image_depth=image_depth;
9063 ping_bit_depth=(png_byte) save_image_depth;
9066 #if defined(PNG_pHYs_SUPPORTED)
9067 if (ping_exclude_pHYs == MagickFalse)
9069 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9070 (!mng_info->write_mng || !mng_info->equal_physs))
9072 if (logging != MagickFalse)
9073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " Setting up pHYs chunk");
9076 if (image->units == PixelsPerInchResolution)
9078 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9079 ping_pHYs_x_resolution=
9080 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9081 ping_pHYs_y_resolution=
9082 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9085 else if (image->units == PixelsPerCentimeterResolution)
9087 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9088 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9089 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9094 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9095 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9096 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9099 if (logging != MagickFalse)
9100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9101 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9102 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9103 (int) ping_pHYs_unit_type);
9104 ping_have_pHYs = MagickTrue;
9109 if (ping_exclude_bKGD == MagickFalse)
9111 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9117 if (ping_bit_depth == 8)
9120 if (ping_bit_depth == 4)
9123 if (ping_bit_depth == 2)
9126 if (ping_bit_depth == 1)
9129 ping_background.red=(png_uint_16)
9130 (ScaleQuantumToShort(image->background_color.red) & mask);
9132 ping_background.green=(png_uint_16)
9133 (ScaleQuantumToShort(image->background_color.green) & mask);
9135 ping_background.blue=(png_uint_16)
9136 (ScaleQuantumToShort(image->background_color.blue) & mask);
9138 ping_background.gray=(png_uint_16) ping_background.green;
9141 if (logging != MagickFalse)
9143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9144 " Setting up bKGD chunk (1)");
9145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9146 " background_color index is %d",
9147 (int) ping_background.index);
9149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9150 " ping_bit_depth=%d",ping_bit_depth);
9153 ping_have_bKGD = MagickTrue;
9157 Select the color type.
9162 if (mng_info->IsPalette && mng_info->write_png8)
9165 /* To do: make this a function cause it's used twice, except
9166 for reducing the sample depth from 8. */
9168 number_colors=image_colors;
9170 ping_have_tRNS=MagickFalse;
9175 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Setting up PLTE chunk with %d colors (%d)",
9180 number_colors, image_colors);
9182 for (i=0; i < (ssize_t) number_colors; i++)
9184 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9185 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9186 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9187 if (logging != MagickFalse)
9188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9189 #if MAGICKCORE_QUANTUM_DEPTH == 8
9190 " %3ld (%3d,%3d,%3d)",
9192 " %5ld (%5d,%5d,%5d)",
9194 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9198 ping_have_PLTE=MagickTrue;
9199 image_depth=ping_bit_depth;
9202 if (matte != MagickFalse)
9205 Identify which colormap entry is transparent.
9207 assert(number_colors <= 256);
9208 assert(image->colormap != NULL);
9210 for (i=0; i < (ssize_t) number_transparent; i++)
9211 ping_trans_alpha[i]=0;
9214 ping_num_trans=(unsigned short) (number_transparent +
9215 number_semitransparent);
9217 if (ping_num_trans == 0)
9218 ping_have_tRNS=MagickFalse;
9221 ping_have_tRNS=MagickTrue;
9224 if (ping_exclude_bKGD == MagickFalse)
9227 * Identify which colormap entry is the background color.
9230 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9231 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9234 ping_background.index=(png_byte) i;
9236 if (logging != MagickFalse)
9238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9239 " background_color index is %d",
9240 (int) ping_background.index);
9243 } /* end of write_png8 */
9245 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9247 image_matte=MagickFalse;
9248 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9251 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9253 image_matte=MagickTrue;
9254 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9257 else /* mng_info->write_pngNN not specified */
9259 image_depth=ping_bit_depth;
9261 if (mng_info->write_png_colortype != 0)
9263 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9265 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9266 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9267 image_matte=MagickTrue;
9270 image_matte=MagickFalse;
9272 if (logging != MagickFalse)
9273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9274 " PNG colortype %d was specified:",(int) ping_color_type);
9277 else /* write_png_colortype not specified */
9279 if (logging != MagickFalse)
9280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9281 " Selecting PNG colortype:");
9283 ping_color_type=(png_byte) ((matte != MagickFalse)?
9284 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9286 if (image_info->type == TrueColorType)
9288 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9289 image_matte=MagickFalse;
9292 if (image_info->type == TrueColorMatteType)
9294 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9295 image_matte=MagickTrue;
9298 if (image_info->type == PaletteType ||
9299 image_info->type == PaletteMatteType)
9300 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9302 if (mng_info->write_png_colortype == 0 &&
9303 (image_info->type == UndefinedType ||
9304 image_info->type == OptimizeType))
9306 if (ping_have_color == MagickFalse)
9308 if (image_matte == MagickFalse)
9310 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9311 image_matte=MagickFalse;
9316 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9317 image_matte=MagickTrue;
9322 if (image_matte == MagickFalse)
9324 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9325 image_matte=MagickFalse;
9330 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9331 image_matte=MagickTrue;
9338 if (logging != MagickFalse)
9339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9340 " Selected PNG colortype=%d",ping_color_type);
9342 if (ping_bit_depth < 8)
9344 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9345 ping_color_type == PNG_COLOR_TYPE_RGB ||
9346 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9350 old_bit_depth=ping_bit_depth;
9352 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9354 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9358 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9363 if (image->colors == 0)
9366 png_error(ping,"image has 0 colors");
9369 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9370 ping_bit_depth <<= 1;
9373 if (logging != MagickFalse)
9375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9376 " Number of colors: %.20g",(double) image_colors);
9378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9379 " Tentative PNG bit depth: %d",ping_bit_depth);
9382 if (ping_bit_depth < (int) mng_info->write_png_depth)
9383 ping_bit_depth = mng_info->write_png_depth;
9386 image_depth=ping_bit_depth;
9388 if (logging != MagickFalse)
9390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9391 " Tentative PNG color type: %s (%.20g)",
9392 PngColorTypeToString(ping_color_type),
9393 (double) ping_color_type);
9395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9396 " image_info->type: %.20g",(double) image_info->type);
9398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9399 " image_depth: %.20g",(double) image_depth);
9401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " image->depth: %.20g",(double) image->depth);
9405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9409 if (matte != MagickFalse)
9411 if (mng_info->IsPalette)
9413 if (mng_info->write_png_colortype == 0)
9415 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9417 if (ping_have_color != MagickFalse)
9418 ping_color_type=PNG_COLOR_TYPE_RGBA;
9422 * Determine if there is any transparent color.
9424 if (number_transparent + number_semitransparent == 0)
9427 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9430 image_matte=MagickFalse;
9432 if (mng_info->write_png_colortype == 0)
9433 ping_color_type&=0x03;
9443 if (ping_bit_depth == 8)
9446 if (ping_bit_depth == 4)
9449 if (ping_bit_depth == 2)
9452 if (ping_bit_depth == 1)
9455 ping_trans_color.red=(png_uint_16)
9456 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9458 ping_trans_color.green=(png_uint_16)
9459 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9461 ping_trans_color.blue=(png_uint_16)
9462 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9464 ping_trans_color.gray=(png_uint_16)
9465 (ScaleQuantumToShort(GetPixelInfoIntensity(
9466 image->colormap)) & mask);
9468 ping_trans_color.index=(png_byte) 0;
9470 ping_have_tRNS=MagickTrue;
9473 if (ping_have_tRNS != MagickFalse)
9476 * Determine if there is one and only one transparent color
9477 * and if so if it is fully transparent.
9479 if (ping_have_cheap_transparency == MagickFalse)
9480 ping_have_tRNS=MagickFalse;
9483 if (ping_have_tRNS != MagickFalse)
9485 if (mng_info->write_png_colortype == 0)
9486 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9488 if (image_depth == 8)
9490 ping_trans_color.red&=0xff;
9491 ping_trans_color.green&=0xff;
9492 ping_trans_color.blue&=0xff;
9493 ping_trans_color.gray&=0xff;
9499 if (image_depth == 8)
9501 ping_trans_color.red&=0xff;
9502 ping_trans_color.green&=0xff;
9503 ping_trans_color.blue&=0xff;
9504 ping_trans_color.gray&=0xff;
9511 if (ping_have_tRNS != MagickFalse)
9512 image_matte=MagickFalse;
9514 if ((mng_info->IsPalette) &&
9515 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9516 ping_have_color == MagickFalse &&
9517 (image_matte == MagickFalse || image_depth >= 8))
9521 if (image_matte != MagickFalse)
9522 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9524 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9526 ping_color_type=PNG_COLOR_TYPE_GRAY;
9528 if (save_image_depth == 16 && image_depth == 8)
9530 if (logging != MagickFalse)
9532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9533 " Scaling ping_trans_color (0)");
9535 ping_trans_color.gray*=0x0101;
9539 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9540 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9542 if ((image_colors == 0) ||
9543 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9544 image_colors=(int) (one << image_depth);
9546 if (image_depth > 8)
9552 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9554 if(!mng_info->write_png_depth)
9558 while ((int) (one << ping_bit_depth)
9559 < (ssize_t) image_colors)
9560 ping_bit_depth <<= 1;
9564 else if (ping_color_type ==
9565 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9566 mng_info->IsPalette)
9568 /* Check if grayscale is reducible */
9571 depth_4_ok=MagickTrue,
9572 depth_2_ok=MagickTrue,
9573 depth_1_ok=MagickTrue;
9575 for (i=0; i < (ssize_t) image_colors; i++)
9580 intensity=ScaleQuantumToChar(image->colormap[i].red);
9582 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9583 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9584 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9585 depth_2_ok=depth_1_ok=MagickFalse;
9586 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9587 depth_1_ok=MagickFalse;
9590 if (depth_1_ok && mng_info->write_png_depth <= 1)
9593 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9596 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9601 image_depth=ping_bit_depth;
9606 if (mng_info->IsPalette)
9608 number_colors=image_colors;
9610 if (image_depth <= 8)
9615 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9617 if (mng_info->have_write_global_plte && matte == MagickFalse)
9619 png_set_PLTE(ping,ping_info,NULL,0);
9621 if (logging != MagickFalse)
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Setting up empty PLTE chunk");
9628 for (i=0; i < (ssize_t) number_colors; i++)
9630 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9631 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9632 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9635 if (logging != MagickFalse)
9636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9637 " Setting up PLTE chunk with %d colors",
9640 ping_have_PLTE=MagickTrue;
9643 /* color_type is PNG_COLOR_TYPE_PALETTE */
9644 if (mng_info->write_png_depth == 0)
9652 while ((one << ping_bit_depth) < (size_t) number_colors)
9653 ping_bit_depth <<= 1;
9658 if (matte != MagickFalse)
9661 * Set up trans_colors array.
9663 assert(number_colors <= 256);
9665 ping_num_trans=(unsigned short) (number_transparent +
9666 number_semitransparent);
9668 if (ping_num_trans == 0)
9669 ping_have_tRNS=MagickFalse;
9673 if (logging != MagickFalse)
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676 " Scaling ping_trans_color (1)");
9678 ping_have_tRNS=MagickTrue;
9680 for (i=0; i < ping_num_trans; i++)
9682 ping_trans_alpha[i]= (png_byte)
9683 ScaleQuantumToChar(image->colormap[i].alpha);
9693 if (image_depth < 8)
9696 if ((save_image_depth == 16) && (image_depth == 8))
9698 if (logging != MagickFalse)
9700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9701 " Scaling ping_trans_color from (%d,%d,%d)",
9702 (int) ping_trans_color.red,
9703 (int) ping_trans_color.green,
9704 (int) ping_trans_color.blue);
9707 ping_trans_color.red*=0x0101;
9708 ping_trans_color.green*=0x0101;
9709 ping_trans_color.blue*=0x0101;
9710 ping_trans_color.gray*=0x0101;
9712 if (logging != MagickFalse)
9714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 (int) ping_trans_color.red,
9717 (int) ping_trans_color.green,
9718 (int) ping_trans_color.blue);
9723 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9724 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9727 Adjust background and transparency samples in sub-8-bit grayscale files.
9729 if (ping_bit_depth < 8 && ping_color_type ==
9730 PNG_COLOR_TYPE_GRAY)
9738 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9740 if (ping_exclude_bKGD == MagickFalse)
9743 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9744 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9745 &image->background_color))) +.5)));
9747 if (logging != MagickFalse)
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9749 " Setting up bKGD chunk (2)");
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " background_color index is %d",
9752 (int) ping_background.index);
9754 ping_have_bKGD = MagickTrue;
9757 if (logging != MagickFalse)
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " Scaling ping_trans_color.gray from %d",
9760 (int)ping_trans_color.gray);
9762 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9763 ping_trans_color.gray)+.5);
9765 if (logging != MagickFalse)
9766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9767 " to %d", (int)ping_trans_color.gray);
9770 if (ping_exclude_bKGD == MagickFalse)
9772 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9775 Identify which colormap entry is the background color.
9778 number_colors=image_colors;
9780 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9781 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9784 ping_background.index=(png_byte) i;
9786 if (logging != MagickFalse)
9788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9789 " Setting up bKGD chunk with index=%d",(int) i);
9792 if (i < (ssize_t) number_colors)
9794 ping_have_bKGD = MagickTrue;
9796 if (logging != MagickFalse)
9798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9799 " background =(%d,%d,%d)",
9800 (int) ping_background.red,
9801 (int) ping_background.green,
9802 (int) ping_background.blue);
9806 else /* Can't happen */
9808 if (logging != MagickFalse)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9810 " No room in PLTE to add bKGD color");
9811 ping_have_bKGD = MagickFalse;
9816 if (logging != MagickFalse)
9817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9818 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9821 Initialize compression level and filtering.
9823 if (logging != MagickFalse)
9825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 " Setting up deflate compression");
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Compression buffer size: 32768");
9832 png_set_compression_buffer_size(ping,32768L);
9834 if (logging != MagickFalse)
9835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9836 " Compression mem level: 9");
9838 png_set_compression_mem_level(ping, 9);
9840 /* Untangle the "-quality" setting:
9842 Undefined is 0; the default is used.
9847 0: Use Z_HUFFMAN_ONLY strategy with the
9848 zlib default compression level
9850 1-9: the zlib compression level
9854 0-4: the PNG filter method
9856 5: libpng adaptive filtering if compression level > 5
9857 libpng filter type "none" if compression level <= 5
9858 or if image is grayscale or palette
9860 6: libpng adaptive filtering
9862 7: "LOCO" filtering (intrapixel differing) if writing
9863 a MNG, othewise "none". Did not work in IM-6.7.0-9
9864 and earlier because of a missing "else".
9866 8: Z_RLE strategy, all filters
9867 Unused prior to IM-6.7.0-10, was same as 6
9869 9: Z_RLE strategy, no PNG filters
9870 Unused prior to IM-6.7.0-10, was same as 6
9872 Note that using the -quality option, not all combinations of
9873 PNG filter type, zlib compression level, and zlib compression
9874 strategy are possible. This will be addressed soon in a
9875 release that accomodates "-define png:compression-strategy", etc.
9879 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9884 if (mng_info->write_png_compression_strategy == 0)
9885 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9888 else if (mng_info->write_png_compression_level == 0)
9893 level=(int) MagickMin((ssize_t) quality/10,9);
9895 mng_info->write_png_compression_level = level+1;
9898 if (mng_info->write_png_compression_strategy == 0)
9900 if ((quality %10) == 8 || (quality %10) == 9)
9901 mng_info->write_png_compression_strategy=Z_RLE;
9904 if (mng_info->write_png_compression_filter == 0)
9905 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9907 if (logging != MagickFalse)
9909 if (mng_info->write_png_compression_level)
9910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9911 " Compression level: %d",
9912 (int) mng_info->write_png_compression_level-1);
9914 if (mng_info->write_png_compression_strategy)
9915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916 " Compression strategy: %d",
9917 (int) mng_info->write_png_compression_strategy-1);
9919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9920 " Setting up filtering");
9922 if (mng_info->write_png_compression_filter == 6)
9923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9924 " Base filter method: ADAPTIVE");
9925 else if (mng_info->write_png_compression_filter == 0 ||
9926 mng_info->write_png_compression_filter == 1)
9927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928 " Base filter method: NONE");
9930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9931 " Base filter method: %d",
9932 (int) mng_info->write_png_compression_filter-1);
9935 if (mng_info->write_png_compression_level != 0)
9936 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9938 if (mng_info->write_png_compression_filter == 6)
9940 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9941 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9943 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9945 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9947 else if (mng_info->write_png_compression_filter == 7 ||
9948 mng_info->write_png_compression_filter == 10)
9949 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9951 else if (mng_info->write_png_compression_filter == 8)
9953 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9954 if (mng_info->write_mng)
9956 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9957 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9958 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9961 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9964 else if (mng_info->write_png_compression_filter == 9)
9965 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9967 else if (mng_info->write_png_compression_filter != 0)
9968 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9969 mng_info->write_png_compression_filter-1);
9971 if (mng_info->write_png_compression_strategy != 0)
9972 png_set_compression_strategy(ping,
9973 mng_info->write_png_compression_strategy-1);
9975 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9976 if (ping_exclude_sRGB != MagickFalse ||
9977 (image->rendering_intent == UndefinedIntent))
9979 if ((ping_exclude_tEXt == MagickFalse ||
9980 ping_exclude_zTXt == MagickFalse) &&
9981 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9983 ResetImageProfileIterator(image);
9984 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9986 profile=GetImageProfile(image,name);
9988 if (profile != (StringInfo *) NULL)
9990 #ifdef PNG_WRITE_iCCP_SUPPORTED
9991 if ((LocaleCompare(name,"ICC") == 0) ||
9992 (LocaleCompare(name,"ICM") == 0))
9995 if (ping_exclude_iCCP == MagickFalse)
9997 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9998 #if (PNG_LIBPNG_VER < 10500)
9999 (png_charp) GetStringInfoDatum(profile),
10001 (png_const_bytep) GetStringInfoDatum(profile),
10003 (png_uint_32) GetStringInfoLength(profile));
10009 if (ping_exclude_zCCP == MagickFalse)
10011 Magick_png_write_raw_profile(image_info,ping,ping_info,
10012 (unsigned char *) name,(unsigned char *) name,
10013 GetStringInfoDatum(profile),
10014 (png_uint_32) GetStringInfoLength(profile));
10018 if (logging != MagickFalse)
10019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10020 " Setting up text chunk with %s profile",name);
10022 name=GetNextImageProfile(image);
10027 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10028 if ((mng_info->have_write_global_srgb == 0) &&
10029 (image->rendering_intent != UndefinedIntent))
10031 if (ping_exclude_sRGB == MagickFalse)
10034 Note image rendering intent.
10036 if (logging != MagickFalse)
10037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10038 " Setting up sRGB chunk");
10040 (void) png_set_sRGB(ping,ping_info,(
10041 Magick_RenderingIntent_to_PNG_RenderingIntent(
10042 image->rendering_intent)));
10046 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10049 if (ping_exclude_gAMA == MagickFalse &&
10050 (ping_exclude_sRGB == MagickFalse ||
10051 (image->gamma < .45 || image->gamma > .46)))
10053 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10057 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10059 if (logging != MagickFalse)
10060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10061 " Setting up gAMA chunk");
10063 png_set_gAMA(ping,ping_info,image->gamma);
10067 if (ping_exclude_cHRM == MagickFalse)
10069 if ((mng_info->have_write_global_chrm == 0) &&
10070 (image->chromaticity.red_primary.x != 0.0))
10073 Note image chromaticity.
10074 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10082 wp=image->chromaticity.white_point;
10083 rp=image->chromaticity.red_primary;
10084 gp=image->chromaticity.green_primary;
10085 bp=image->chromaticity.blue_primary;
10087 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Setting up cHRM chunk");
10091 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10097 ping_interlace_method=image_info->interlace != NoInterlace;
10099 if (mng_info->write_mng)
10100 png_set_sig_bytes(ping,8);
10102 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10104 if (mng_info->write_png_colortype != 0)
10106 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10107 if (ping_have_color != MagickFalse)
10109 ping_color_type = PNG_COLOR_TYPE_RGB;
10111 if (ping_bit_depth < 8)
10115 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10116 if (ping_have_color != MagickFalse)
10117 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10120 if (ping_need_colortype_warning != MagickFalse ||
10121 ((mng_info->write_png_depth &&
10122 (int) mng_info->write_png_depth != ping_bit_depth) ||
10123 (mng_info->write_png_colortype &&
10124 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10125 mng_info->write_png_colortype != 7 &&
10126 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10128 if (logging != MagickFalse)
10130 if (ping_need_colortype_warning != MagickFalse)
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133 " Image has transparency but tRNS chunk was excluded");
10136 if (mng_info->write_png_depth)
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10139 " Defined png:bit-depth=%u, Computed depth=%u",
10140 mng_info->write_png_depth,
10144 if (mng_info->write_png_colortype)
10146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10147 " Defined png:color-type=%u, Computed color type=%u",
10148 mng_info->write_png_colortype-1,
10154 "Cannot write image with defined png:bit-depth or png:color-type.");
10157 if (image_matte != MagickFalse && image->matte == MagickFalse)
10159 /* Add an opaque matte channel */
10160 image->matte = MagickTrue;
10161 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10163 if (logging != MagickFalse)
10164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10165 " Added an opaque matte channel");
10168 if (number_transparent != 0 || number_semitransparent != 0)
10170 if (ping_color_type < 4)
10172 ping_have_tRNS=MagickTrue;
10173 if (logging != MagickFalse)
10174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10175 " Setting ping_have_tRNS=MagickTrue.");
10179 if (logging != MagickFalse)
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " Writing PNG header chunks");
10183 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10184 ping_bit_depth,ping_color_type,
10185 ping_interlace_method,ping_compression_method,
10186 ping_filter_method);
10188 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10190 png_set_PLTE(ping,ping_info,palette,number_colors);
10192 if (logging != MagickFalse)
10194 for (i=0; i< (ssize_t) number_colors; i++)
10196 if (i < ping_num_trans)
10197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10200 (int) palette[i].red,
10201 (int) palette[i].green,
10202 (int) palette[i].blue,
10204 (int) ping_trans_alpha[i]);
10206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10207 " PLTE[%d] = (%d,%d,%d)",
10209 (int) palette[i].red,
10210 (int) palette[i].green,
10211 (int) palette[i].blue);
10216 if (ping_exclude_bKGD == MagickFalse)
10218 if (ping_have_bKGD != MagickFalse)
10220 png_set_bKGD(ping,ping_info,&ping_background);
10223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10224 " Setting up bKGD chunk");
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " background color = (%d,%d,%d)",
10227 (int) ping_background.red,
10228 (int) ping_background.green,
10229 (int) ping_background.blue);
10230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10231 " index = %d, gray=%d",
10232 (int) ping_background.index,
10233 (int) ping_background.gray);
10238 if (ping_exclude_pHYs == MagickFalse)
10240 if (ping_have_pHYs != MagickFalse)
10242 png_set_pHYs(ping,ping_info,
10243 ping_pHYs_x_resolution,
10244 ping_pHYs_y_resolution,
10245 ping_pHYs_unit_type);
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Setting up pHYs chunk");
10251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10252 " x_resolution=%lu",
10253 (unsigned long) ping_pHYs_x_resolution);
10254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10255 " y_resolution=%lu",
10256 (unsigned long) ping_pHYs_y_resolution);
10257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10259 (unsigned long) ping_pHYs_unit_type);
10264 #if defined(PNG_oFFs_SUPPORTED)
10265 if (ping_exclude_oFFs == MagickFalse)
10267 if (image->page.x || image->page.y)
10269 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10270 (png_int_32) image->page.y, 0);
10272 if (logging != MagickFalse)
10273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10274 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10275 (int) image->page.x, (int) image->page.y);
10280 if (mng_info->need_blob != MagickFalse)
10282 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10284 png_error(ping,"WriteBlob Failed");
10286 ping_have_blob=MagickTrue;
10289 png_write_info_before_PLTE(ping, ping_info);
10291 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10293 if (logging != MagickFalse)
10295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10296 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10299 if (ping_color_type == 3)
10300 (void) png_set_tRNS(ping, ping_info,
10307 (void) png_set_tRNS(ping, ping_info,
10310 &ping_trans_color);
10312 if (logging != MagickFalse)
10314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10315 " tRNS color =(%d,%d,%d)",
10316 (int) ping_trans_color.red,
10317 (int) ping_trans_color.green,
10318 (int) ping_trans_color.blue);
10323 /* write any png-chunk-b profiles */
10324 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10326 png_write_info(ping,ping_info);
10328 /* write any PNG-chunk-m profiles */
10329 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10331 if (ping_exclude_vpAg == MagickFalse)
10333 if ((image->page.width != 0 && image->page.width != image->columns) ||
10334 (image->page.height != 0 && image->page.height != image->rows))
10339 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10340 PNGType(chunk,mng_vpAg);
10341 LogPNGChunk(logging,mng_vpAg,9L);
10342 PNGLong(chunk+4,(png_uint_32) image->page.width);
10343 PNGLong(chunk+8,(png_uint_32) image->page.height);
10344 chunk[12]=0; /* unit = pixels */
10345 (void) WriteBlob(image,13,chunk);
10346 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10350 #if (PNG_LIBPNG_VER == 10206)
10351 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10352 #define PNG_HAVE_IDAT 0x04
10353 ping->mode |= PNG_HAVE_IDAT;
10354 #undef PNG_HAVE_IDAT
10357 png_set_packing(ping);
10361 rowbytes=image->columns;
10362 if (image_depth > 8)
10364 switch (ping_color_type)
10366 case PNG_COLOR_TYPE_RGB:
10370 case PNG_COLOR_TYPE_GRAY_ALPHA:
10374 case PNG_COLOR_TYPE_RGBA:
10382 if (logging != MagickFalse)
10384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10385 " Writing PNG image data");
10387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10390 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10391 sizeof(*ping_pixels));
10393 if (ping_pixels == (unsigned char *) NULL)
10394 png_error(ping,"Allocation of memory for pixels failed");
10397 Initialize image scanlines.
10399 quantum_info=AcquireQuantumInfo(image_info,image);
10400 if (quantum_info == (QuantumInfo *) NULL)
10401 png_error(ping,"Memory allocation for quantum_info failed");
10402 quantum_info->format=UndefinedQuantumFormat;
10403 quantum_info->depth=image_depth;
10404 num_passes=png_set_interlace_handling(ping);
10406 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10407 !mng_info->write_png32) &&
10408 (mng_info->IsPalette ||
10409 (image_info->type == BilevelType)) &&
10410 image_matte == MagickFalse &&
10411 ping_have_non_bw == MagickFalse)
10413 /* Palette, Bilevel, or Opaque Monochrome */
10414 register const Quantum
10417 quantum_info->depth=8;
10418 for (pass=0; pass < num_passes; pass++)
10421 Convert PseudoClass image to a PNG monochrome image.
10423 for (y=0; y < (ssize_t) image->rows; y++)
10425 if (logging != MagickFalse && y == 0)
10426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10427 " Writing row of pixels (0)");
10429 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10431 if (p == (const Quantum *) NULL)
10434 if (mng_info->IsPalette)
10436 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10437 quantum_info,GrayQuantum,ping_pixels,exception);
10438 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10439 mng_info->write_png_depth &&
10440 mng_info->write_png_depth != old_bit_depth)
10442 /* Undo pixel scaling */
10443 for (i=0; i < (ssize_t) image->columns; i++)
10444 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10445 >> (8-old_bit_depth));
10451 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10452 quantum_info,RedQuantum,ping_pixels,exception);
10455 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10456 for (i=0; i < (ssize_t) image->columns; i++)
10457 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10460 if (logging != MagickFalse && y == 0)
10461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462 " Writing row of pixels (1)");
10464 png_write_row(ping,ping_pixels);
10466 if (image->previous == (Image *) NULL)
10468 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10469 if (status == MagickFalse)
10475 else /* Not Palette, Bilevel, or Opaque Monochrome */
10477 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10478 !mng_info->write_png32) &&
10479 (image_matte != MagickFalse ||
10480 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10481 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10483 register const Quantum
10486 for (pass=0; pass < num_passes; pass++)
10489 for (y=0; y < (ssize_t) image->rows; y++)
10491 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10493 if (p == (const Quantum *) NULL)
10496 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10498 if (mng_info->IsPalette)
10499 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10500 quantum_info,GrayQuantum,ping_pixels,exception);
10503 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10504 quantum_info,RedQuantum,ping_pixels,exception);
10506 if (logging != MagickFalse && y == 0)
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " Writing GRAY PNG pixels (2)");
10511 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10513 if (logging != MagickFalse && y == 0)
10514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10515 " Writing GRAY_ALPHA PNG pixels (2)");
10517 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10518 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10521 if (logging != MagickFalse && y == 0)
10522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10523 " Writing row of pixels (2)");
10525 png_write_row(ping,ping_pixels);
10528 if (image->previous == (Image *) NULL)
10530 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10531 if (status == MagickFalse)
10539 register const Quantum
10542 for (pass=0; pass < num_passes; pass++)
10544 if ((image_depth > 8) || (mng_info->write_png24 ||
10545 mng_info->write_png32 ||
10546 (!mng_info->write_png8 && !mng_info->IsPalette)))
10548 for (y=0; y < (ssize_t) image->rows; y++)
10550 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10552 if (p == (const Quantum *) NULL)
10555 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10557 if (image->storage_class == DirectClass)
10558 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10559 quantum_info,RedQuantum,ping_pixels,exception);
10562 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10563 quantum_info,GrayQuantum,ping_pixels,exception);
10566 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10568 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10569 quantum_info,GrayAlphaQuantum,ping_pixels,
10572 if (logging != MagickFalse && y == 0)
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10574 " Writing GRAY_ALPHA PNG pixels (3)");
10577 else if (image_matte != MagickFalse)
10578 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10579 quantum_info,RGBAQuantum,ping_pixels,exception);
10582 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10583 quantum_info,RGBQuantum,ping_pixels,exception);
10585 if (logging != MagickFalse && y == 0)
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " Writing row of pixels (3)");
10589 png_write_row(ping,ping_pixels);
10594 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10595 mng_info->write_png32 ||
10596 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10598 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10599 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10601 if (logging != MagickFalse)
10602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10603 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10605 quantum_info->depth=8;
10609 for (y=0; y < (ssize_t) image->rows; y++)
10611 if (logging != MagickFalse && y == 0)
10612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10613 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10615 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10617 if (p == (const Quantum *) NULL)
10620 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10622 quantum_info->depth=image->depth;
10624 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10625 quantum_info,GrayQuantum,ping_pixels,exception);
10628 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10630 if (logging != MagickFalse && y == 0)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " Writing GRAY_ALPHA PNG pixels (4)");
10634 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10635 quantum_info,GrayAlphaQuantum,ping_pixels,
10641 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10642 quantum_info,IndexQuantum,ping_pixels,exception);
10644 if (logging != MagickFalse && y <= 2)
10646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10647 " Writing row of non-gray pixels (4)");
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10651 (int)ping_pixels[0],(int)ping_pixels[1]);
10654 png_write_row(ping,ping_pixels);
10658 if (image->previous == (Image *) NULL)
10660 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10661 if (status == MagickFalse)
10668 if (quantum_info != (QuantumInfo *) NULL)
10669 quantum_info=DestroyQuantumInfo(quantum_info);
10671 if (logging != MagickFalse)
10673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10674 " Wrote PNG image data");
10676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677 " Width: %.20g",(double) ping_width);
10679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10680 " Height: %.20g",(double) ping_height);
10682 if (mng_info->write_png_depth)
10684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10685 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10689 " PNG bit-depth written: %d",ping_bit_depth);
10691 if (mng_info->write_png_colortype)
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698 " PNG color-type written: %d",ping_color_type);
10700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10701 " PNG Interlace method: %d",ping_interlace_method);
10704 Generate text chunks after IDAT.
10706 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10708 ResetImagePropertyIterator(image);
10709 property=GetNextImageProperty(image);
10710 while (property != (const char *) NULL)
10715 value=GetImageProperty(image,property,exception);
10717 /* Don't write any "png:" properties; those are just for "identify" */
10718 if (LocaleNCompare(property,"png:",4) != 0 &&
10720 /* Suppress density and units if we wrote a pHYs chunk */
10721 (ping_exclude_pHYs != MagickFalse ||
10722 LocaleCompare(property,"density") != 0 ||
10723 LocaleCompare(property,"units") != 0) &&
10725 /* Suppress the IM-generated Date:create and Date:modify */
10726 (ping_exclude_date == MagickFalse ||
10727 LocaleNCompare(property, "Date:",5) != 0))
10729 if (value != (const char *) NULL)
10731 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10732 text[0].key=(char *) property;
10733 text[0].text=(char *) value;
10734 text[0].text_length=strlen(value);
10736 if (ping_exclude_tEXt != MagickFalse)
10737 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10739 else if (ping_exclude_zTXt != MagickFalse)
10740 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10744 text[0].compression=image_info->compression == NoCompression ||
10745 (image_info->compression == UndefinedCompression &&
10746 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10747 PNG_TEXT_COMPRESSION_zTXt ;
10750 if (logging != MagickFalse)
10752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10753 " Setting up text chunk");
10755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756 " keyword: %s",text[0].key);
10759 png_set_text(ping,ping_info,text,1);
10760 png_free(ping,text);
10763 property=GetNextImageProperty(image);
10767 /* write any PNG-chunk-e profiles */
10768 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10770 if (logging != MagickFalse)
10771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10772 " Writing PNG end info");
10774 png_write_end(ping,ping_info);
10776 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10778 if (mng_info->page.x || mng_info->page.y ||
10779 (ping_width != mng_info->page.width) ||
10780 (ping_height != mng_info->page.height))
10786 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10788 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10789 PNGType(chunk,mng_FRAM);
10790 LogPNGChunk(logging,mng_FRAM,27L);
10792 chunk[5]=0; /* frame name separator (no name) */
10793 chunk[6]=1; /* flag for changing delay, for next frame only */
10794 chunk[7]=0; /* flag for changing frame timeout */
10795 chunk[8]=1; /* flag for changing frame clipping for next frame */
10796 chunk[9]=0; /* flag for changing frame sync_id */
10797 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10798 chunk[14]=0; /* clipping boundaries delta type */
10799 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10801 (png_uint_32) (mng_info->page.x + ping_width));
10802 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10804 (png_uint_32) (mng_info->page.y + ping_height));
10805 (void) WriteBlob(image,31,chunk);
10806 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10807 mng_info->old_framing_mode=4;
10808 mng_info->framing_mode=1;
10812 mng_info->framing_mode=3;
10814 if (mng_info->write_mng && !mng_info->need_fram &&
10815 ((int) image->dispose == 3))
10816 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10819 Free PNG resources.
10822 png_destroy_write_struct(&ping,&ping_info);
10824 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10826 if (ping_have_blob != MagickFalse)
10827 (void) CloseBlob(image);
10829 image_info=DestroyImageInfo(image_info);
10830 image=DestroyImage(image);
10832 /* Store bit depth actually written */
10833 s[0]=(char) ping_bit_depth;
10836 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10838 if (logging != MagickFalse)
10839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10840 " exit WriteOnePNGImage()");
10842 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10843 UnlockSemaphoreInfo(ping_semaphore);
10846 /* } for navigation to beginning of SETJMP-protected block. Revert to
10847 * Throwing an Exception when an error occurs.
10850 return(MagickTrue);
10851 /* End write one PNG image */
10856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10860 % W r i t e P N G I m a g e %
10864 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10866 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10867 % Multiple-image Network Graphics (MNG) image file.
10869 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10871 % The format of the WritePNGImage method is:
10873 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10874 % Image *image,ExceptionInfo *exception)
10876 % A description of each parameter follows:
10878 % o image_info: the image info.
10880 % o image: The image.
10882 % o exception: return any errors or warnings in this structure.
10884 % Returns MagickTrue on success, MagickFalse on failure.
10886 % Communicating with the PNG encoder:
10888 % While the datastream written is always in PNG format and normally would
10889 % be given the "png" file extension, this method also writes the following
10890 % pseudo-formats which are subsets of png:
10892 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10893 % a depth greater than 8, the depth is reduced. If transparency
10894 % is present, the tRNS chunk must only have values 0 and 255
10895 % (i.e., transparency is binary: fully opaque or fully
10896 % transparent). If other values are present they will be
10897 % 50%-thresholded to binary transparency. If more than 256
10898 % colors are present, they will be quantized to the 4-4-4-1,
10899 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10900 % of any resulting fully-transparent pixels is changed to
10901 % the image's background color.
10903 % If you want better quantization or dithering of the colors
10904 % or alpha than that, you need to do it before calling the
10905 % PNG encoder. The pixels contain 8-bit indices even if
10906 % they could be represented with 1, 2, or 4 bits. Grayscale
10907 % images will be written as indexed PNG files even though the
10908 % PNG grayscale type might be slightly more efficient. Please
10909 % note that writing to the PNG8 format may result in loss
10910 % of color and alpha data.
10912 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10913 % chunk can be present to convey binary transparency by naming
10914 % one of the colors as transparent. The only loss incurred
10915 % is reduction of sample depth to 8. If the image has more
10916 % than one transparent color, has semitransparent pixels, or
10917 % has an opaque pixel with the same RGB components as the
10918 % transparent color, an image is not written.
10920 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10921 % transparency is permitted, i.e., the alpha sample for
10922 % each pixel can have any value from 0 to 255. The alpha
10923 % channel is present even if the image is fully opaque.
10924 % The only loss in data is the reduction of the sample depth
10927 % o -define: For more precise control of the PNG output, you can use the
10928 % Image options "png:bit-depth" and "png:color-type". These
10929 % can be set from the commandline with "-define" and also
10930 % from the application programming interfaces. The options
10931 % are case-independent and are converted to lowercase before
10932 % being passed to this encoder.
10934 % png:color-type can be 0, 2, 3, 4, or 6.
10936 % When png:color-type is 0 (Grayscale), png:bit-depth can
10937 % be 1, 2, 4, 8, or 16.
10939 % When png:color-type is 2 (RGB), png:bit-depth can
10942 % When png:color-type is 3 (Indexed), png:bit-depth can
10943 % be 1, 2, 4, or 8. This refers to the number of bits
10944 % used to store the index. The color samples always have
10945 % bit-depth 8 in indexed PNG files.
10947 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10948 % png:bit-depth can be 8 or 16.
10950 % If the image cannot be written without loss with the requested bit-depth
10951 % and color-type, a PNG file will not be written, and the encoder will
10952 % return MagickFalse.
10954 % Since image encoders should not be responsible for the "heavy lifting",
10955 % the user should make sure that ImageMagick has already reduced the
10956 % image depth and number of colors and limit transparency to binary
10957 % transparency prior to attempting to write the image with depth, color,
10958 % or transparency limitations.
10960 % Note that another definition, "png:bit-depth-written" exists, but it
10961 % is not intended for external use. It is only used internally by the
10962 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10964 % It is possible to request that the PNG encoder write previously-formatted
10965 % ancillary chunks in the output PNG file, using the "-profile" commandline
10966 % option as shown below or by setting the profile via a programming
10969 % -profile PNG-chunk-x:<file>
10971 % where x is a location flag and <file> is a file containing the chunk
10972 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10973 % This encoder will compute the chunk length and CRC, so those must not
10974 % be included in the file.
10976 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10977 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10978 % of the same type, then add a short unique string after the "x" to prevent
10979 % subsequent profiles from overwriting the preceding ones, e.g.,
10981 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10983 % As of version 6.6.6 the following optimizations are always done:
10985 % o 32-bit depth is reduced to 16.
10986 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10987 % high byte and low byte are identical.
10988 % o Palette is sorted to remove unused entries and to put a
10989 % transparent color first, if BUILD_PNG_PALETTE is defined.
10990 % o Opaque matte channel is removed when writing an indexed PNG.
10991 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10992 % this can be done without loss and a larger bit depth N was not
10993 % requested via the "-define png:bit-depth=N" option.
10994 % o If matte channel is present but only one transparent color is
10995 % present, RGB+tRNS is written instead of RGBA
10996 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10997 % was requested when converting an opaque image).
10999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11001 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11002 Image *image,ExceptionInfo *exception)
11007 have_mng_structure,
11023 assert(image_info != (const ImageInfo *) NULL);
11024 assert(image_info->signature == MagickSignature);
11025 assert(image != (Image *) NULL);
11026 assert(image->signature == MagickSignature);
11027 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11028 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11030 Allocate a MngInfo structure.
11032 have_mng_structure=MagickFalse;
11033 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11035 if (mng_info == (MngInfo *) NULL)
11036 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11039 Initialize members of the MngInfo structure.
11041 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11042 mng_info->image=image;
11043 mng_info->equal_backgrounds=MagickTrue;
11044 have_mng_structure=MagickTrue;
11046 /* See if user has requested a specific PNG subformat */
11048 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11049 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11050 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11052 value=GetImageOption(image_info,"png:format");
11054 if (value != (char *) NULL)
11056 if (LocaleCompare(value,"png8") == 0)
11058 mng_info->write_png8 = MagickTrue;
11059 mng_info->write_png24 = MagickFalse;
11060 mng_info->write_png32 = MagickFalse;
11063 else if (LocaleCompare(value,"png24") == 0)
11065 mng_info->write_png8 = MagickFalse;
11066 mng_info->write_png24 = MagickTrue;
11067 mng_info->write_png32 = MagickFalse;
11070 else if (LocaleCompare(value,"png32") == 0)
11072 mng_info->write_png8 = MagickFalse;
11073 mng_info->write_png24 = MagickFalse;
11074 mng_info->write_png32 = MagickTrue;
11077 if (mng_info->write_png8)
11079 mng_info->write_png_colortype = /* 3 */ 4;
11080 mng_info->write_png_depth = 8;
11084 if (mng_info->write_png24)
11086 mng_info->write_png_colortype = /* 2 */ 3;
11087 mng_info->write_png_depth = 8;
11090 if (image->matte == MagickTrue)
11091 (void) SetImageType(image,TrueColorMatteType,exception);
11094 (void) SetImageType(image,TrueColorType,exception);
11096 (void) SyncImage(image,exception);
11099 if (mng_info->write_png32)
11101 mng_info->write_png_colortype = /* 6 */ 7;
11102 mng_info->write_png_depth = 8;
11105 if (image->matte == MagickTrue)
11106 (void) SetImageType(image,TrueColorMatteType,exception);
11109 (void) SetImageType(image,TrueColorType,exception);
11111 (void) SyncImage(image,exception);
11114 value=GetImageOption(image_info,"png:bit-depth");
11116 if (value != (char *) NULL)
11118 if (LocaleCompare(value,"1") == 0)
11119 mng_info->write_png_depth = 1;
11121 else if (LocaleCompare(value,"2") == 0)
11122 mng_info->write_png_depth = 2;
11124 else if (LocaleCompare(value,"4") == 0)
11125 mng_info->write_png_depth = 4;
11127 else if (LocaleCompare(value,"8") == 0)
11128 mng_info->write_png_depth = 8;
11130 else if (LocaleCompare(value,"16") == 0)
11131 mng_info->write_png_depth = 16;
11134 (void) ThrowMagickException(exception,
11135 GetMagickModule(),CoderWarning,
11136 "ignoring invalid defined png:bit-depth",
11139 if (logging != MagickFalse)
11140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11141 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11144 value=GetImageOption(image_info,"png:color-type");
11146 if (value != (char *) NULL)
11148 /* We must store colortype+1 because 0 is a valid colortype */
11149 if (LocaleCompare(value,"0") == 0)
11150 mng_info->write_png_colortype = 1;
11152 else if (LocaleCompare(value,"1") == 0)
11153 mng_info->write_png_colortype = 2;
11155 else if (LocaleCompare(value,"2") == 0)
11156 mng_info->write_png_colortype = 3;
11158 else if (LocaleCompare(value,"3") == 0)
11159 mng_info->write_png_colortype = 4;
11161 else if (LocaleCompare(value,"4") == 0)
11162 mng_info->write_png_colortype = 5;
11164 else if (LocaleCompare(value,"6") == 0)
11165 mng_info->write_png_colortype = 7;
11168 (void) ThrowMagickException(exception,
11169 GetMagickModule(),CoderWarning,
11170 "ignoring invalid defined png:color-type",
11173 if (logging != MagickFalse)
11174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11178 /* Check for chunks to be excluded:
11180 * The default is to not exclude any known chunks except for any
11181 * listed in the "unused_chunks" array, above.
11183 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11184 * define (in the image properties or in the image artifacts)
11185 * or via a mng_info member. For convenience, in addition
11186 * to or instead of a comma-separated list of chunks, the
11187 * "exclude-chunk" string can be simply "all" or "none".
11189 * The exclude-chunk define takes priority over the mng_info.
11191 * A "png:include-chunk" define takes priority over both the
11192 * mng_info and the "png:exclude-chunk" define. Like the
11193 * "exclude-chunk" string, it can define "all" or "none" as
11194 * well as a comma-separated list. Chunks that are unknown to
11195 * ImageMagick are always excluded, regardless of their "copy-safe"
11196 * status according to the PNG specification, and even if they
11197 * appear in the "include-chunk" list. Such defines appearing among
11198 * the image options take priority over those found among the image
11201 * Finally, all chunks listed in the "unused_chunks" array are
11202 * automatically excluded, regardless of the other instructions
11205 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11206 * will not be written and the gAMA chunk will only be written if it
11207 * is not between .45 and .46, or approximately (1.0/2.2).
11209 * If you exclude tRNS and the image has transparency, the colortype
11210 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11212 * The -strip option causes StripImage() to set the png:include-chunk
11213 * artifact to "none,trns,gama".
11216 mng_info->ping_exclude_bKGD=MagickFalse;
11217 mng_info->ping_exclude_cHRM=MagickFalse;
11218 mng_info->ping_exclude_date=MagickFalse;
11219 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11220 mng_info->ping_exclude_gAMA=MagickFalse;
11221 mng_info->ping_exclude_iCCP=MagickFalse;
11222 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11223 mng_info->ping_exclude_oFFs=MagickFalse;
11224 mng_info->ping_exclude_pHYs=MagickFalse;
11225 mng_info->ping_exclude_sRGB=MagickFalse;
11226 mng_info->ping_exclude_tEXt=MagickFalse;
11227 mng_info->ping_exclude_tRNS=MagickFalse;
11228 mng_info->ping_exclude_vpAg=MagickFalse;
11229 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11230 mng_info->ping_exclude_zTXt=MagickFalse;
11232 mng_info->ping_preserve_colormap=MagickFalse;
11234 value=GetImageArtifact(image,"png:preserve-colormap");
11236 value=GetImageOption(image_info,"png:preserve-colormap");
11238 mng_info->ping_preserve_colormap=MagickTrue;
11240 /* Thes compression-level, compression-strategy, and compression-filter
11241 * defines take precedence over values from the -quality option.
11243 value=GetImageArtifact(image,"png:compression-level");
11245 value=GetImageOption(image_info,"png:compression-level");
11248 /* We have to add 1 to everything because 0 is a valid input,
11249 * and we want to use 0 (the default) to mean undefined.
11251 if (LocaleCompare(value,"0") == 0)
11252 mng_info->write_png_compression_level = 1;
11254 else if (LocaleCompare(value,"1") == 0)
11255 mng_info->write_png_compression_level = 2;
11257 else if (LocaleCompare(value,"2") == 0)
11258 mng_info->write_png_compression_level = 3;
11260 else if (LocaleCompare(value,"3") == 0)
11261 mng_info->write_png_compression_level = 4;
11263 else if (LocaleCompare(value,"4") == 0)
11264 mng_info->write_png_compression_level = 5;
11266 else if (LocaleCompare(value,"5") == 0)
11267 mng_info->write_png_compression_level = 6;
11269 else if (LocaleCompare(value,"6") == 0)
11270 mng_info->write_png_compression_level = 7;
11272 else if (LocaleCompare(value,"7") == 0)
11273 mng_info->write_png_compression_level = 8;
11275 else if (LocaleCompare(value,"8") == 0)
11276 mng_info->write_png_compression_level = 9;
11278 else if (LocaleCompare(value,"9") == 0)
11279 mng_info->write_png_compression_level = 10;
11282 (void) ThrowMagickException(exception,
11283 GetMagickModule(),CoderWarning,
11284 "ignoring invalid defined png:compression-level",
11288 value=GetImageArtifact(image,"png:compression-strategy");
11290 value=GetImageOption(image_info,"png:compression-strategy");
11294 if (LocaleCompare(value,"0") == 0)
11295 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11297 else if (LocaleCompare(value,"1") == 0)
11298 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11300 else if (LocaleCompare(value,"2") == 0)
11301 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11303 else if (LocaleCompare(value,"3") == 0)
11304 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11305 mng_info->write_png_compression_strategy = Z_RLE+1;
11307 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11310 else if (LocaleCompare(value,"4") == 0)
11311 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11312 mng_info->write_png_compression_strategy = Z_FIXED+1;
11314 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11318 (void) ThrowMagickException(exception,
11319 GetMagickModule(),CoderWarning,
11320 "ignoring invalid defined png:compression-strategy",
11324 value=GetImageArtifact(image,"png:compression-filter");
11326 value=GetImageOption(image_info,"png:compression-filter");
11330 /* To do: combinations of filters allowed by libpng
11331 * masks 0x08 through 0xf8
11333 * Implement this as a comma-separated list of 0,1,2,3,4,5
11334 * where 5 is a special case meaning PNG_ALL_FILTERS.
11337 if (LocaleCompare(value,"0") == 0)
11338 mng_info->write_png_compression_filter = 1;
11340 if (LocaleCompare(value,"1") == 0)
11341 mng_info->write_png_compression_filter = 2;
11343 else if (LocaleCompare(value,"2") == 0)
11344 mng_info->write_png_compression_filter = 3;
11346 else if (LocaleCompare(value,"3") == 0)
11347 mng_info->write_png_compression_filter = 4;
11349 else if (LocaleCompare(value,"4") == 0)
11350 mng_info->write_png_compression_filter = 5;
11352 else if (LocaleCompare(value,"5") == 0)
11353 mng_info->write_png_compression_filter = 6;
11356 (void) ThrowMagickException(exception,
11357 GetMagickModule(),CoderWarning,
11358 "ignoring invalid defined png:compression-filter",
11362 excluding=MagickFalse;
11364 for (source=0; source<1; source++)
11368 value=GetImageArtifact(image,"png:exclude-chunk");
11371 value=GetImageArtifact(image,"png:exclude-chunks");
11375 value=GetImageOption(image_info,"png:exclude-chunk");
11378 value=GetImageOption(image_info,"png:exclude-chunks");
11387 excluding=MagickTrue;
11389 if (logging != MagickFalse)
11392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11393 " png:exclude-chunk=%s found in image artifacts.\n", value);
11395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11396 " png:exclude-chunk=%s found in image properties.\n", value);
11399 last=strlen(value);
11401 for (i=0; i<(int) last; i+=5)
11404 if (LocaleNCompare(value+i,"all",3) == 0)
11406 mng_info->ping_exclude_bKGD=MagickTrue;
11407 mng_info->ping_exclude_cHRM=MagickTrue;
11408 mng_info->ping_exclude_date=MagickTrue;
11409 mng_info->ping_exclude_EXIF=MagickTrue;
11410 mng_info->ping_exclude_gAMA=MagickTrue;
11411 mng_info->ping_exclude_iCCP=MagickTrue;
11412 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11413 mng_info->ping_exclude_oFFs=MagickTrue;
11414 mng_info->ping_exclude_pHYs=MagickTrue;
11415 mng_info->ping_exclude_sRGB=MagickTrue;
11416 mng_info->ping_exclude_tEXt=MagickTrue;
11417 mng_info->ping_exclude_tRNS=MagickTrue;
11418 mng_info->ping_exclude_vpAg=MagickTrue;
11419 mng_info->ping_exclude_zCCP=MagickTrue;
11420 mng_info->ping_exclude_zTXt=MagickTrue;
11424 if (LocaleNCompare(value+i,"none",4) == 0)
11426 mng_info->ping_exclude_bKGD=MagickFalse;
11427 mng_info->ping_exclude_cHRM=MagickFalse;
11428 mng_info->ping_exclude_date=MagickFalse;
11429 mng_info->ping_exclude_EXIF=MagickFalse;
11430 mng_info->ping_exclude_gAMA=MagickFalse;
11431 mng_info->ping_exclude_iCCP=MagickFalse;
11432 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11433 mng_info->ping_exclude_oFFs=MagickFalse;
11434 mng_info->ping_exclude_pHYs=MagickFalse;
11435 mng_info->ping_exclude_sRGB=MagickFalse;
11436 mng_info->ping_exclude_tEXt=MagickFalse;
11437 mng_info->ping_exclude_tRNS=MagickFalse;
11438 mng_info->ping_exclude_vpAg=MagickFalse;
11439 mng_info->ping_exclude_zCCP=MagickFalse;
11440 mng_info->ping_exclude_zTXt=MagickFalse;
11443 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11444 mng_info->ping_exclude_bKGD=MagickTrue;
11446 if (LocaleNCompare(value+i,"chrm",4) == 0)
11447 mng_info->ping_exclude_cHRM=MagickTrue;
11449 if (LocaleNCompare(value+i,"date",4) == 0)
11450 mng_info->ping_exclude_date=MagickTrue;
11452 if (LocaleNCompare(value+i,"exif",4) == 0)
11453 mng_info->ping_exclude_EXIF=MagickTrue;
11455 if (LocaleNCompare(value+i,"gama",4) == 0)
11456 mng_info->ping_exclude_gAMA=MagickTrue;
11458 if (LocaleNCompare(value+i,"iccp",4) == 0)
11459 mng_info->ping_exclude_iCCP=MagickTrue;
11462 if (LocaleNCompare(value+i,"itxt",4) == 0)
11463 mng_info->ping_exclude_iTXt=MagickTrue;
11466 if (LocaleNCompare(value+i,"gama",4) == 0)
11467 mng_info->ping_exclude_gAMA=MagickTrue;
11469 if (LocaleNCompare(value+i,"offs",4) == 0)
11470 mng_info->ping_exclude_oFFs=MagickTrue;
11472 if (LocaleNCompare(value+i,"phys",4) == 0)
11473 mng_info->ping_exclude_pHYs=MagickTrue;
11475 if (LocaleNCompare(value+i,"srgb",4) == 0)
11476 mng_info->ping_exclude_sRGB=MagickTrue;
11478 if (LocaleNCompare(value+i,"text",4) == 0)
11479 mng_info->ping_exclude_tEXt=MagickTrue;
11481 if (LocaleNCompare(value+i,"trns",4) == 0)
11482 mng_info->ping_exclude_tRNS=MagickTrue;
11484 if (LocaleNCompare(value+i,"vpag",4) == 0)
11485 mng_info->ping_exclude_vpAg=MagickTrue;
11487 if (LocaleNCompare(value+i,"zccp",4) == 0)
11488 mng_info->ping_exclude_zCCP=MagickTrue;
11490 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11491 mng_info->ping_exclude_zTXt=MagickTrue;
11497 for (source=0; source<1; source++)
11501 value=GetImageArtifact(image,"png:include-chunk");
11504 value=GetImageArtifact(image,"png:include-chunks");
11508 value=GetImageOption(image_info,"png:include-chunk");
11511 value=GetImageOption(image_info,"png:include-chunks");
11519 excluding=MagickTrue;
11521 if (logging != MagickFalse)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " png:include-chunk=%s found in image artifacts.\n", value);
11527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528 " png:include-chunk=%s found in image properties.\n", value);
11531 last=strlen(value);
11533 for (i=0; i<(int) last; i+=5)
11535 if (LocaleNCompare(value+i,"all",3) == 0)
11537 mng_info->ping_exclude_bKGD=MagickFalse;
11538 mng_info->ping_exclude_cHRM=MagickFalse;
11539 mng_info->ping_exclude_date=MagickFalse;
11540 mng_info->ping_exclude_EXIF=MagickFalse;
11541 mng_info->ping_exclude_gAMA=MagickFalse;
11542 mng_info->ping_exclude_iCCP=MagickFalse;
11543 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11544 mng_info->ping_exclude_oFFs=MagickFalse;
11545 mng_info->ping_exclude_pHYs=MagickFalse;
11546 mng_info->ping_exclude_sRGB=MagickFalse;
11547 mng_info->ping_exclude_tEXt=MagickFalse;
11548 mng_info->ping_exclude_tRNS=MagickFalse;
11549 mng_info->ping_exclude_vpAg=MagickFalse;
11550 mng_info->ping_exclude_zCCP=MagickFalse;
11551 mng_info->ping_exclude_zTXt=MagickFalse;
11555 if (LocaleNCompare(value+i,"none",4) == 0)
11557 mng_info->ping_exclude_bKGD=MagickTrue;
11558 mng_info->ping_exclude_cHRM=MagickTrue;
11559 mng_info->ping_exclude_date=MagickTrue;
11560 mng_info->ping_exclude_EXIF=MagickTrue;
11561 mng_info->ping_exclude_gAMA=MagickTrue;
11562 mng_info->ping_exclude_iCCP=MagickTrue;
11563 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11564 mng_info->ping_exclude_oFFs=MagickTrue;
11565 mng_info->ping_exclude_pHYs=MagickTrue;
11566 mng_info->ping_exclude_sRGB=MagickTrue;
11567 mng_info->ping_exclude_tEXt=MagickTrue;
11568 mng_info->ping_exclude_tRNS=MagickTrue;
11569 mng_info->ping_exclude_vpAg=MagickTrue;
11570 mng_info->ping_exclude_zCCP=MagickTrue;
11571 mng_info->ping_exclude_zTXt=MagickTrue;
11574 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11575 mng_info->ping_exclude_bKGD=MagickFalse;
11577 if (LocaleNCompare(value+i,"chrm",4) == 0)
11578 mng_info->ping_exclude_cHRM=MagickFalse;
11580 if (LocaleNCompare(value+i,"date",4) == 0)
11581 mng_info->ping_exclude_date=MagickFalse;
11583 if (LocaleNCompare(value+i,"exif",4) == 0)
11584 mng_info->ping_exclude_EXIF=MagickFalse;
11586 if (LocaleNCompare(value+i,"gama",4) == 0)
11587 mng_info->ping_exclude_gAMA=MagickFalse;
11589 if (LocaleNCompare(value+i,"iccp",4) == 0)
11590 mng_info->ping_exclude_iCCP=MagickFalse;
11593 if (LocaleNCompare(value+i,"itxt",4) == 0)
11594 mng_info->ping_exclude_iTXt=MagickFalse;
11597 if (LocaleNCompare(value+i,"gama",4) == 0)
11598 mng_info->ping_exclude_gAMA=MagickFalse;
11600 if (LocaleNCompare(value+i,"offs",4) == 0)
11601 mng_info->ping_exclude_oFFs=MagickFalse;
11603 if (LocaleNCompare(value+i,"phys",4) == 0)
11604 mng_info->ping_exclude_pHYs=MagickFalse;
11606 if (LocaleNCompare(value+i,"srgb",4) == 0)
11607 mng_info->ping_exclude_sRGB=MagickFalse;
11609 if (LocaleNCompare(value+i,"text",4) == 0)
11610 mng_info->ping_exclude_tEXt=MagickFalse;
11612 if (LocaleNCompare(value+i,"trns",4) == 0)
11613 mng_info->ping_exclude_tRNS=MagickFalse;
11615 if (LocaleNCompare(value+i,"vpag",4) == 0)
11616 mng_info->ping_exclude_vpAg=MagickFalse;
11618 if (LocaleNCompare(value+i,"zccp",4) == 0)
11619 mng_info->ping_exclude_zCCP=MagickFalse;
11621 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11622 mng_info->ping_exclude_zTXt=MagickFalse;
11628 if (excluding != MagickFalse && logging != MagickFalse)
11630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631 " Chunks to be excluded from the output png:");
11632 if (mng_info->ping_exclude_bKGD != MagickFalse)
11633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11635 if (mng_info->ping_exclude_cHRM != MagickFalse)
11636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11638 if (mng_info->ping_exclude_date != MagickFalse)
11639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11641 if (mng_info->ping_exclude_EXIF != MagickFalse)
11642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11644 if (mng_info->ping_exclude_gAMA != MagickFalse)
11645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11647 if (mng_info->ping_exclude_iCCP != MagickFalse)
11648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11651 if (mng_info->ping_exclude_iTXt != MagickFalse)
11652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11655 if (mng_info->ping_exclude_oFFs != MagickFalse)
11656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11658 if (mng_info->ping_exclude_pHYs != MagickFalse)
11659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11661 if (mng_info->ping_exclude_sRGB != MagickFalse)
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11664 if (mng_info->ping_exclude_tEXt != MagickFalse)
11665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11667 if (mng_info->ping_exclude_tRNS != MagickFalse)
11668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670 if (mng_info->ping_exclude_vpAg != MagickFalse)
11671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673 if (mng_info->ping_exclude_zCCP != MagickFalse)
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11676 if (mng_info->ping_exclude_zTXt != MagickFalse)
11677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11681 mng_info->need_blob = MagickTrue;
11683 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11685 MngInfoFreeStruct(mng_info,&have_mng_structure);
11687 if (logging != MagickFalse)
11688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11693 #if defined(JNG_SUPPORTED)
11695 /* Write one JNG image */
11696 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11697 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11718 jng_alpha_compression_method,
11719 jng_alpha_sample_depth,
11727 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11728 " Enter WriteOneJNGImage()");
11730 blob=(unsigned char *) NULL;
11731 jpeg_image=(Image *) NULL;
11732 jpeg_image_info=(ImageInfo *) NULL;
11735 transparent=image_info->type==GrayscaleMatteType ||
11736 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
11738 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11740 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11742 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11743 image_info->quality;
11745 if (jng_alpha_quality >= 1000)
11746 jng_alpha_quality /= 1000;
11752 /* Create JPEG blob, image, and image_info */
11753 if (logging != MagickFalse)
11754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11755 " Creating jpeg_image_info for alpha.");
11757 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11759 if (jpeg_image_info == (ImageInfo *) NULL)
11760 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11762 if (logging != MagickFalse)
11763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11764 " Creating jpeg_image.");
11766 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11767 if (jpeg_image == (Image *) NULL)
11768 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11769 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11770 jpeg_image->matte=MagickFalse;
11771 jpeg_image->quality=jng_alpha_quality;
11772 jpeg_image_info->type=GrayscaleType;
11773 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11774 (void) AcquireUniqueFilename(jpeg_image->filename);
11775 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11776 "%s",jpeg_image->filename);
11780 jng_alpha_compression_method=0;
11782 jng_alpha_sample_depth=0;
11785 /* To do: check bit depth of PNG alpha channel */
11787 /* Check if image is grayscale. */
11788 if (image_info->type != TrueColorMatteType && image_info->type !=
11789 TrueColorType && ImageIsGray(image,exception))
11792 if (logging != MagickFalse)
11794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG Quality = %d",(int) jng_quality);
11796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11797 " JNG Color Type = %d",jng_color_type);
11800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11803 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11805 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11811 if (jng_alpha_compression_method==0)
11816 /* Encode alpha as a grayscale PNG blob */
11817 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11819 if (logging != MagickFalse)
11820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11821 " Creating PNG blob.");
11824 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11825 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11826 jpeg_image_info->interlace=NoInterlace;
11828 /* Exclude all ancillary chunks */
11829 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11831 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11834 /* Retrieve sample depth used */
11835 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11836 if (value != (char *) NULL)
11837 jng_alpha_sample_depth= (unsigned int) value[0];
11841 /* Encode alpha as a grayscale JPEG blob */
11843 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11846 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11847 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11848 jpeg_image_info->interlace=NoInterlace;
11849 if (logging != MagickFalse)
11850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11851 " Creating blob.");
11852 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11854 jng_alpha_sample_depth=8;
11856 if (logging != MagickFalse)
11857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11858 " Successfully read jpeg_image into a blob, length=%.20g.",
11862 /* Destroy JPEG image and image_info */
11863 jpeg_image=DestroyImage(jpeg_image);
11864 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11865 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11868 /* Write JHDR chunk */
11869 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11870 PNGType(chunk,mng_JHDR);
11871 LogPNGChunk(logging,mng_JHDR,16L);
11872 PNGLong(chunk+4,(png_uint_32) image->columns);
11873 PNGLong(chunk+8,(png_uint_32) image->rows);
11874 chunk[12]=jng_color_type;
11875 chunk[13]=8; /* sample depth */
11876 chunk[14]=8; /*jng_image_compression_method */
11877 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11878 chunk[16]=jng_alpha_sample_depth;
11879 chunk[17]=jng_alpha_compression_method;
11880 chunk[18]=0; /*jng_alpha_filter_method */
11881 chunk[19]=0; /*jng_alpha_interlace_method */
11882 (void) WriteBlob(image,20,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11884 if (logging != MagickFalse)
11886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11887 " JNG width:%15lu",(unsigned long) image->columns);
11889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11890 " JNG height:%14lu",(unsigned long) image->rows);
11892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11893 " JNG color type:%10d",jng_color_type);
11895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11896 " JNG sample depth:%8d",8);
11898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11899 " JNG compression:%9d",8);
11901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11902 " JNG interlace:%11d",0);
11904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11905 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11908 " JNG alpha compression:%3d",jng_alpha_compression_method);
11910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11911 " JNG alpha filter:%8d",0);
11913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11914 " JNG alpha interlace:%5d",0);
11917 /* Write any JNG-chunk-b profiles */
11918 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11921 Write leading ancillary chunks
11927 Write JNG bKGD chunk
11938 if (jng_color_type == 8 || jng_color_type == 12)
11942 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11943 PNGType(chunk,mng_bKGD);
11944 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11945 red=ScaleQuantumToChar(image->background_color.red);
11946 green=ScaleQuantumToChar(image->background_color.green);
11947 blue=ScaleQuantumToChar(image->background_color.blue);
11954 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11955 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11958 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11961 Write JNG sRGB chunk
11963 (void) WriteBlobMSBULong(image,1L);
11964 PNGType(chunk,mng_sRGB);
11965 LogPNGChunk(logging,mng_sRGB,1L);
11967 if (image->rendering_intent != UndefinedIntent)
11968 chunk[4]=(unsigned char)
11969 Magick_RenderingIntent_to_PNG_RenderingIntent(
11970 (image->rendering_intent));
11973 chunk[4]=(unsigned char)
11974 Magick_RenderingIntent_to_PNG_RenderingIntent(
11975 (PerceptualIntent));
11977 (void) WriteBlob(image,5,chunk);
11978 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11982 if (image->gamma != 0.0)
11985 Write JNG gAMA chunk
11987 (void) WriteBlobMSBULong(image,4L);
11988 PNGType(chunk,mng_gAMA);
11989 LogPNGChunk(logging,mng_gAMA,4L);
11990 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11991 (void) WriteBlob(image,8,chunk);
11992 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11995 if ((mng_info->equal_chrms == MagickFalse) &&
11996 (image->chromaticity.red_primary.x != 0.0))
12002 Write JNG cHRM chunk
12004 (void) WriteBlobMSBULong(image,32L);
12005 PNGType(chunk,mng_cHRM);
12006 LogPNGChunk(logging,mng_cHRM,32L);
12007 primary=image->chromaticity.white_point;
12008 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12009 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12010 primary=image->chromaticity.red_primary;
12011 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12012 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12013 primary=image->chromaticity.green_primary;
12014 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12015 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12016 primary=image->chromaticity.blue_primary;
12017 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12018 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12019 (void) WriteBlob(image,36,chunk);
12020 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12024 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12027 Write JNG pHYs chunk
12029 (void) WriteBlobMSBULong(image,9L);
12030 PNGType(chunk,mng_pHYs);
12031 LogPNGChunk(logging,mng_pHYs,9L);
12032 if (image->units == PixelsPerInchResolution)
12034 PNGLong(chunk+4,(png_uint_32)
12035 (image->resolution.x*100.0/2.54+0.5));
12037 PNGLong(chunk+8,(png_uint_32)
12038 (image->resolution.y*100.0/2.54+0.5));
12045 if (image->units == PixelsPerCentimeterResolution)
12047 PNGLong(chunk+4,(png_uint_32)
12048 (image->resolution.x*100.0+0.5));
12050 PNGLong(chunk+8,(png_uint_32)
12051 (image->resolution.y*100.0+0.5));
12058 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12059 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12063 (void) WriteBlob(image,13,chunk);
12064 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12067 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12070 Write JNG oFFs chunk
12072 (void) WriteBlobMSBULong(image,9L);
12073 PNGType(chunk,mng_oFFs);
12074 LogPNGChunk(logging,mng_oFFs,9L);
12075 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12076 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12078 (void) WriteBlob(image,13,chunk);
12079 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12081 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12083 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12084 PNGType(chunk,mng_vpAg);
12085 LogPNGChunk(logging,mng_vpAg,9L);
12086 PNGLong(chunk+4,(png_uint_32) image->page.width);
12087 PNGLong(chunk+8,(png_uint_32) image->page.height);
12088 chunk[12]=0; /* unit = pixels */
12089 (void) WriteBlob(image,13,chunk);
12090 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12096 if (jng_alpha_compression_method==0)
12104 /* Write IDAT chunk header */
12105 if (logging != MagickFalse)
12106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12107 " Write IDAT chunks from blob, length=%.20g.",(double)
12110 /* Copy IDAT chunks */
12113 for (i=8; i<(ssize_t) length; i+=len+12)
12115 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12118 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12120 /* Found an IDAT chunk. */
12121 (void) WriteBlobMSBULong(image,(size_t) len);
12122 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12123 (void) WriteBlob(image,(size_t) len+4,p);
12124 (void) WriteBlobMSBULong(image,
12125 crc32(0,p,(uInt) len+4));
12130 if (logging != MagickFalse)
12131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132 " Skipping %c%c%c%c chunk, length=%.20g.",
12133 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12140 /* Write JDAA chunk header */
12141 if (logging != MagickFalse)
12142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12143 " Write JDAA chunk, length=%.20g.",(double) length);
12144 (void) WriteBlobMSBULong(image,(size_t) length);
12145 PNGType(chunk,mng_JDAA);
12146 LogPNGChunk(logging,mng_JDAA,length);
12147 /* Write JDAT chunk(s) data */
12148 (void) WriteBlob(image,4,chunk);
12149 (void) WriteBlob(image,length,blob);
12150 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12153 blob=(unsigned char *) RelinquishMagickMemory(blob);
12156 /* Encode image as a JPEG blob */
12157 if (logging != MagickFalse)
12158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12159 " Creating jpeg_image_info.");
12160 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12161 if (jpeg_image_info == (ImageInfo *) NULL)
12162 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12164 if (logging != MagickFalse)
12165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12166 " Creating jpeg_image.");
12168 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12169 if (jpeg_image == (Image *) NULL)
12170 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12171 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12173 (void) AcquireUniqueFilename(jpeg_image->filename);
12174 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12175 jpeg_image->filename);
12177 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12180 if (logging != MagickFalse)
12181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12182 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12183 (double) jpeg_image->rows);
12185 if (jng_color_type == 8 || jng_color_type == 12)
12186 jpeg_image_info->type=GrayscaleType;
12188 jpeg_image_info->quality=jng_quality;
12189 jpeg_image->quality=jng_quality;
12190 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12191 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12193 if (logging != MagickFalse)
12194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195 " Creating blob.");
12197 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12199 if (logging != MagickFalse)
12201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202 " Successfully read jpeg_image into a blob, length=%.20g.",
12205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12206 " Write JDAT chunk, length=%.20g.",(double) length);
12209 /* Write JDAT chunk(s) */
12210 (void) WriteBlobMSBULong(image,(size_t) length);
12211 PNGType(chunk,mng_JDAT);
12212 LogPNGChunk(logging,mng_JDAT,length);
12213 (void) WriteBlob(image,4,chunk);
12214 (void) WriteBlob(image,length,blob);
12215 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12217 jpeg_image=DestroyImage(jpeg_image);
12218 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12219 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12220 blob=(unsigned char *) RelinquishMagickMemory(blob);
12222 /* Write any JNG-chunk-e profiles */
12223 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12225 /* Write IEND chunk */
12226 (void) WriteBlobMSBULong(image,0L);
12227 PNGType(chunk,mng_IEND);
12228 LogPNGChunk(logging,mng_IEND,0);
12229 (void) WriteBlob(image,4,chunk);
12230 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12232 if (logging != MagickFalse)
12233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12234 " exit WriteOneJNGImage()");
12241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12245 % W r i t e J N G I m a g e %
12249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12251 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12253 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12255 % The format of the WriteJNGImage method is:
12257 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12258 % Image *image,ExceptionInfo *exception)
12260 % A description of each parameter follows:
12262 % o image_info: the image info.
12264 % o image: The image.
12266 % o exception: return any errors or warnings in this structure.
12268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12270 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12271 ExceptionInfo *exception)
12274 have_mng_structure,
12284 assert(image_info != (const ImageInfo *) NULL);
12285 assert(image_info->signature == MagickSignature);
12286 assert(image != (Image *) NULL);
12287 assert(image->signature == MagickSignature);
12288 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12289 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12290 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12291 if (status == MagickFalse)
12295 Allocate a MngInfo structure.
12297 have_mng_structure=MagickFalse;
12298 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12299 if (mng_info == (MngInfo *) NULL)
12300 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12302 Initialize members of the MngInfo structure.
12304 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12305 mng_info->image=image;
12306 have_mng_structure=MagickTrue;
12308 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12310 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12311 (void) CloseBlob(image);
12313 (void) CatchImageException(image);
12314 MngInfoFreeStruct(mng_info,&have_mng_structure);
12315 if (logging != MagickFalse)
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12321 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12322 ExceptionInfo *exception)
12331 have_mng_structure,
12334 volatile MagickBooleanType
12346 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12347 defined(PNG_MNG_FEATURES_SUPPORTED)
12350 all_images_are_gray,
12360 volatile unsigned int
12371 #if (PNG_LIBPNG_VER < 10200)
12372 if (image_info->verbose)
12373 printf("Your PNG library (libpng-%s) is rather old.\n",
12374 PNG_LIBPNG_VER_STRING);
12380 assert(image_info != (const ImageInfo *) NULL);
12381 assert(image_info->signature == MagickSignature);
12382 assert(image != (Image *) NULL);
12383 assert(image->signature == MagickSignature);
12384 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12385 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12386 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12387 if (status == MagickFalse)
12391 Allocate a MngInfo structure.
12393 have_mng_structure=MagickFalse;
12394 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12395 if (mng_info == (MngInfo *) NULL)
12396 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12398 Initialize members of the MngInfo structure.
12400 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12401 mng_info->image=image;
12402 have_mng_structure=MagickTrue;
12403 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12406 * See if user has requested a specific PNG subformat to be used
12407 * for all of the PNGs in the MNG being written, e.g.,
12409 * convert *.png png8:animation.mng
12411 * To do: check -define png:bit_depth and png:color_type as well,
12412 * or perhaps use mng:bit_depth and mng:color_type instead for
12416 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12417 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12418 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12420 write_jng=MagickFalse;
12421 if (image_info->compression == JPEGCompression)
12422 write_jng=MagickTrue;
12424 mng_info->adjoin=image_info->adjoin &&
12425 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12427 if (logging != MagickFalse)
12429 /* Log some info about the input */
12433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12434 " Checking input image(s)");
12436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12437 " Image_info depth: %.20g",(double) image_info->depth);
12439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12440 " Type: %d",image_info->type);
12443 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12446 " Scene: %.20g",(double) scene++);
12448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12449 " Image depth: %.20g",(double) p->depth);
12452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12459 if (p->storage_class == PseudoClass)
12460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12461 " Storage class: PseudoClass");
12464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12465 " Storage class: DirectClass");
12468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12469 " Number of colors: %.20g",(double) p->colors);
12472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12473 " Number of colors: unspecified");
12475 if (mng_info->adjoin == MagickFalse)
12480 use_global_plte=MagickFalse;
12481 all_images_are_gray=MagickFalse;
12482 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12483 need_local_plte=MagickTrue;
12485 need_defi=MagickFalse;
12486 need_matte=MagickFalse;
12487 mng_info->framing_mode=1;
12488 mng_info->old_framing_mode=1;
12491 if (image_info->page != (char *) NULL)
12494 Determine image bounding box.
12496 SetGeometry(image,&mng_info->page);
12497 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12498 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12510 mng_info->page=image->page;
12511 need_geom=MagickTrue;
12512 if (mng_info->page.width || mng_info->page.height)
12513 need_geom=MagickFalse;
12515 Check all the scenes.
12517 initial_delay=image->delay;
12518 need_iterations=MagickFalse;
12519 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12520 mng_info->equal_physs=MagickTrue,
12521 mng_info->equal_gammas=MagickTrue;
12522 mng_info->equal_srgbs=MagickTrue;
12523 mng_info->equal_backgrounds=MagickTrue;
12525 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12526 defined(PNG_MNG_FEATURES_SUPPORTED)
12527 all_images_are_gray=MagickTrue;
12528 mng_info->equal_palettes=MagickFalse;
12529 need_local_plte=MagickFalse;
12531 for (next_image=image; next_image != (Image *) NULL; )
12535 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12536 mng_info->page.width=next_image->columns+next_image->page.x;
12538 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12539 mng_info->page.height=next_image->rows+next_image->page.y;
12542 if (next_image->page.x || next_image->page.y)
12543 need_defi=MagickTrue;
12545 if (next_image->matte)
12546 need_matte=MagickTrue;
12548 if ((int) next_image->dispose >= BackgroundDispose)
12549 if (next_image->matte || next_image->page.x || next_image->page.y ||
12550 ((next_image->columns < mng_info->page.width) &&
12551 (next_image->rows < mng_info->page.height)))
12552 mng_info->need_fram=MagickTrue;
12554 if (next_image->iterations)
12555 need_iterations=MagickTrue;
12557 final_delay=next_image->delay;
12559 if (final_delay != initial_delay || final_delay > 1UL*
12560 next_image->ticks_per_second)
12561 mng_info->need_fram=1;
12563 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12564 defined(PNG_MNG_FEATURES_SUPPORTED)
12566 check for global palette possibility.
12568 if (image->matte != MagickFalse)
12569 need_local_plte=MagickTrue;
12571 if (need_local_plte == 0)
12573 if (ImageIsGray(image,exception) == MagickFalse)
12574 all_images_are_gray=MagickFalse;
12575 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12576 if (use_global_plte == 0)
12577 use_global_plte=mng_info->equal_palettes;
12578 need_local_plte=!mng_info->equal_palettes;
12581 if (GetNextImageInList(next_image) != (Image *) NULL)
12583 if (next_image->background_color.red !=
12584 next_image->next->background_color.red ||
12585 next_image->background_color.green !=
12586 next_image->next->background_color.green ||
12587 next_image->background_color.blue !=
12588 next_image->next->background_color.blue)
12589 mng_info->equal_backgrounds=MagickFalse;
12591 if (next_image->gamma != next_image->next->gamma)
12592 mng_info->equal_gammas=MagickFalse;
12594 if (next_image->rendering_intent !=
12595 next_image->next->rendering_intent)
12596 mng_info->equal_srgbs=MagickFalse;
12598 if ((next_image->units != next_image->next->units) ||
12599 (next_image->resolution.x != next_image->next->resolution.x) ||
12600 (next_image->resolution.y != next_image->next->resolution.y))
12601 mng_info->equal_physs=MagickFalse;
12603 if (mng_info->equal_chrms)
12605 if (next_image->chromaticity.red_primary.x !=
12606 next_image->next->chromaticity.red_primary.x ||
12607 next_image->chromaticity.red_primary.y !=
12608 next_image->next->chromaticity.red_primary.y ||
12609 next_image->chromaticity.green_primary.x !=
12610 next_image->next->chromaticity.green_primary.x ||
12611 next_image->chromaticity.green_primary.y !=
12612 next_image->next->chromaticity.green_primary.y ||
12613 next_image->chromaticity.blue_primary.x !=
12614 next_image->next->chromaticity.blue_primary.x ||
12615 next_image->chromaticity.blue_primary.y !=
12616 next_image->next->chromaticity.blue_primary.y ||
12617 next_image->chromaticity.white_point.x !=
12618 next_image->next->chromaticity.white_point.x ||
12619 next_image->chromaticity.white_point.y !=
12620 next_image->next->chromaticity.white_point.y)
12621 mng_info->equal_chrms=MagickFalse;
12625 next_image=GetNextImageInList(next_image);
12627 if (image_count < 2)
12629 mng_info->equal_backgrounds=MagickFalse;
12630 mng_info->equal_chrms=MagickFalse;
12631 mng_info->equal_gammas=MagickFalse;
12632 mng_info->equal_srgbs=MagickFalse;
12633 mng_info->equal_physs=MagickFalse;
12634 use_global_plte=MagickFalse;
12635 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12636 need_local_plte=MagickTrue;
12638 need_iterations=MagickFalse;
12641 if (mng_info->need_fram == MagickFalse)
12644 Only certain framing rates 100/n are exactly representable without
12645 the FRAM chunk but we'll allow some slop in VLC files
12647 if (final_delay == 0)
12649 if (need_iterations != MagickFalse)
12652 It's probably a GIF with loop; don't run it *too* fast.
12654 if (mng_info->adjoin)
12657 (void) ThrowMagickException(exception,GetMagickModule(),
12659 "input has zero delay between all frames; assuming",
12664 mng_info->ticks_per_second=0;
12666 if (final_delay != 0)
12667 mng_info->ticks_per_second=(png_uint_32)
12668 (image->ticks_per_second/final_delay);
12669 if (final_delay > 50)
12670 mng_info->ticks_per_second=2;
12672 if (final_delay > 75)
12673 mng_info->ticks_per_second=1;
12675 if (final_delay > 125)
12676 mng_info->need_fram=MagickTrue;
12678 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12679 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12680 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12681 1UL*image->ticks_per_second))
12682 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12685 if (mng_info->need_fram != MagickFalse)
12686 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12688 If pseudocolor, we should also check to see if all the
12689 palettes are identical and write a global PLTE if they are.
12693 Write the MNG version 1.0 signature and MHDR chunk.
12695 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12696 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12697 PNGType(chunk,mng_MHDR);
12698 LogPNGChunk(logging,mng_MHDR,28L);
12699 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12700 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12701 PNGLong(chunk+12,mng_info->ticks_per_second);
12702 PNGLong(chunk+16,0L); /* layer count=unknown */
12703 PNGLong(chunk+20,0L); /* frame count=unknown */
12704 PNGLong(chunk+24,0L); /* play time=unknown */
12709 if (need_defi || mng_info->need_fram || use_global_plte)
12710 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12713 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12718 if (need_defi || mng_info->need_fram || use_global_plte)
12719 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12722 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12730 if (need_defi || mng_info->need_fram || use_global_plte)
12731 PNGLong(chunk+28,11L); /* simplicity=LC */
12734 PNGLong(chunk+28,9L); /* simplicity=VLC */
12739 if (need_defi || mng_info->need_fram || use_global_plte)
12740 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12743 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12746 (void) WriteBlob(image,32,chunk);
12747 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12748 option=GetImageOption(image_info,"mng:need-cacheoff");
12749 if (option != (const char *) NULL)
12755 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12757 PNGType(chunk,mng_nEED);
12758 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12759 (void) WriteBlobMSBULong(image,(size_t) length);
12760 LogPNGChunk(logging,mng_nEED,(size_t) length);
12762 (void) WriteBlob(image,length,chunk);
12763 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12765 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12766 (GetNextImageInList(image) != (Image *) NULL) &&
12767 (image->iterations != 1))
12770 Write MNG TERM chunk
12772 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12773 PNGType(chunk,mng_TERM);
12774 LogPNGChunk(logging,mng_TERM,10L);
12775 chunk[4]=3; /* repeat animation */
12776 chunk[5]=0; /* show last frame when done */
12777 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12778 final_delay/MagickMax(image->ticks_per_second,1)));
12780 if (image->iterations == 0)
12781 PNGLong(chunk+10,PNG_UINT_31_MAX);
12784 PNGLong(chunk+10,(png_uint_32) image->iterations);
12786 if (logging != MagickFalse)
12788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12789 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12790 final_delay/MagickMax(image->ticks_per_second,1)));
12792 if (image->iterations == 0)
12793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12794 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12798 " Image iterations: %.20g",(double) image->iterations);
12800 (void) WriteBlob(image,14,chunk);
12801 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12804 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12806 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12807 mng_info->equal_srgbs)
12810 Write MNG sRGB chunk
12812 (void) WriteBlobMSBULong(image,1L);
12813 PNGType(chunk,mng_sRGB);
12814 LogPNGChunk(logging,mng_sRGB,1L);
12816 if (image->rendering_intent != UndefinedIntent)
12817 chunk[4]=(unsigned char)
12818 Magick_RenderingIntent_to_PNG_RenderingIntent(
12819 (image->rendering_intent));
12822 chunk[4]=(unsigned char)
12823 Magick_RenderingIntent_to_PNG_RenderingIntent(
12824 (PerceptualIntent));
12826 (void) WriteBlob(image,5,chunk);
12827 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12828 mng_info->have_write_global_srgb=MagickTrue;
12833 if (image->gamma && mng_info->equal_gammas)
12836 Write MNG gAMA chunk
12838 (void) WriteBlobMSBULong(image,4L);
12839 PNGType(chunk,mng_gAMA);
12840 LogPNGChunk(logging,mng_gAMA,4L);
12841 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12842 (void) WriteBlob(image,8,chunk);
12843 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12844 mng_info->have_write_global_gama=MagickTrue;
12846 if (mng_info->equal_chrms)
12852 Write MNG cHRM chunk
12854 (void) WriteBlobMSBULong(image,32L);
12855 PNGType(chunk,mng_cHRM);
12856 LogPNGChunk(logging,mng_cHRM,32L);
12857 primary=image->chromaticity.white_point;
12858 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12859 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12860 primary=image->chromaticity.red_primary;
12861 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12862 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12863 primary=image->chromaticity.green_primary;
12864 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12865 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12866 primary=image->chromaticity.blue_primary;
12867 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12868 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12869 (void) WriteBlob(image,36,chunk);
12870 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12871 mng_info->have_write_global_chrm=MagickTrue;
12874 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12877 Write MNG pHYs chunk
12879 (void) WriteBlobMSBULong(image,9L);
12880 PNGType(chunk,mng_pHYs);
12881 LogPNGChunk(logging,mng_pHYs,9L);
12883 if (image->units == PixelsPerInchResolution)
12885 PNGLong(chunk+4,(png_uint_32)
12886 (image->resolution.x*100.0/2.54+0.5));
12888 PNGLong(chunk+8,(png_uint_32)
12889 (image->resolution.y*100.0/2.54+0.5));
12896 if (image->units == PixelsPerCentimeterResolution)
12898 PNGLong(chunk+4,(png_uint_32)
12899 (image->resolution.x*100.0+0.5));
12901 PNGLong(chunk+8,(png_uint_32)
12902 (image->resolution.y*100.0+0.5));
12909 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12910 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12914 (void) WriteBlob(image,13,chunk);
12915 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12918 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12919 or does not cover the entire frame.
12921 if (write_mng && (image->matte || image->page.x > 0 ||
12922 image->page.y > 0 || (image->page.width &&
12923 (image->page.width+image->page.x < mng_info->page.width))
12924 || (image->page.height && (image->page.height+image->page.y
12925 < mng_info->page.height))))
12927 (void) WriteBlobMSBULong(image,6L);
12928 PNGType(chunk,mng_BACK);
12929 LogPNGChunk(logging,mng_BACK,6L);
12930 red=ScaleQuantumToShort(image->background_color.red);
12931 green=ScaleQuantumToShort(image->background_color.green);
12932 blue=ScaleQuantumToShort(image->background_color.blue);
12933 PNGShort(chunk+4,red);
12934 PNGShort(chunk+6,green);
12935 PNGShort(chunk+8,blue);
12936 (void) WriteBlob(image,10,chunk);
12937 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12938 if (mng_info->equal_backgrounds)
12940 (void) WriteBlobMSBULong(image,6L);
12941 PNGType(chunk,mng_bKGD);
12942 LogPNGChunk(logging,mng_bKGD,6L);
12943 (void) WriteBlob(image,10,chunk);
12944 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12948 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12949 if ((need_local_plte == MagickFalse) &&
12950 (image->storage_class == PseudoClass) &&
12951 (all_images_are_gray == MagickFalse))
12957 Write MNG PLTE chunk
12959 data_length=3*image->colors;
12960 (void) WriteBlobMSBULong(image,data_length);
12961 PNGType(chunk,mng_PLTE);
12962 LogPNGChunk(logging,mng_PLTE,data_length);
12964 for (i=0; i < (ssize_t) image->colors; i++)
12966 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12967 image->colormap[i].red) & 0xff);
12968 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12969 image->colormap[i].green) & 0xff);
12970 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12971 image->colormap[i].blue) & 0xff);
12974 (void) WriteBlob(image,data_length+4,chunk);
12975 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12976 mng_info->have_write_global_plte=MagickTrue;
12982 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12983 defined(PNG_MNG_FEATURES_SUPPORTED)
12984 mng_info->equal_palettes=MagickFalse;
12988 if (mng_info->adjoin)
12990 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12991 defined(PNG_MNG_FEATURES_SUPPORTED)
12993 If we aren't using a global palette for the entire MNG, check to
12994 see if we can use one for two or more consecutive images.
12996 if (need_local_plte && use_global_plte && !all_images_are_gray)
12998 if (mng_info->IsPalette)
13001 When equal_palettes is true, this image has the same palette
13002 as the previous PseudoClass image
13004 mng_info->have_write_global_plte=mng_info->equal_palettes;
13005 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13006 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13009 Write MNG PLTE chunk
13014 data_length=3*image->colors;
13015 (void) WriteBlobMSBULong(image,data_length);
13016 PNGType(chunk,mng_PLTE);
13017 LogPNGChunk(logging,mng_PLTE,data_length);
13019 for (i=0; i < (ssize_t) image->colors; i++)
13021 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13022 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13023 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13026 (void) WriteBlob(image,data_length+4,chunk);
13027 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13028 (uInt) (data_length+4)));
13029 mng_info->have_write_global_plte=MagickTrue;
13033 mng_info->have_write_global_plte=MagickFalse;
13044 previous_x=mng_info->page.x;
13045 previous_y=mng_info->page.y;
13052 mng_info->page=image->page;
13053 if ((mng_info->page.x != previous_x) ||
13054 (mng_info->page.y != previous_y))
13056 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13057 PNGType(chunk,mng_DEFI);
13058 LogPNGChunk(logging,mng_DEFI,12L);
13059 chunk[4]=0; /* object 0 MSB */
13060 chunk[5]=0; /* object 0 LSB */
13061 chunk[6]=0; /* visible */
13062 chunk[7]=0; /* abstract */
13063 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13064 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13065 (void) WriteBlob(image,16,chunk);
13066 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13071 mng_info->write_mng=write_mng;
13073 if ((int) image->dispose >= 3)
13074 mng_info->framing_mode=3;
13076 if (mng_info->need_fram && mng_info->adjoin &&
13077 ((image->delay != mng_info->delay) ||
13078 (mng_info->framing_mode != mng_info->old_framing_mode)))
13080 if (image->delay == mng_info->delay)
13083 Write a MNG FRAM chunk with the new framing mode.
13085 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13086 PNGType(chunk,mng_FRAM);
13087 LogPNGChunk(logging,mng_FRAM,1L);
13088 chunk[4]=(unsigned char) mng_info->framing_mode;
13089 (void) WriteBlob(image,5,chunk);
13090 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13095 Write a MNG FRAM chunk with the delay.
13097 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13098 PNGType(chunk,mng_FRAM);
13099 LogPNGChunk(logging,mng_FRAM,10L);
13100 chunk[4]=(unsigned char) mng_info->framing_mode;
13101 chunk[5]=0; /* frame name separator (no name) */
13102 chunk[6]=2; /* flag for changing default delay */
13103 chunk[7]=0; /* flag for changing frame timeout */
13104 chunk[8]=0; /* flag for changing frame clipping */
13105 chunk[9]=0; /* flag for changing frame sync_id */
13106 PNGLong(chunk+10,(png_uint_32)
13107 ((mng_info->ticks_per_second*
13108 image->delay)/MagickMax(image->ticks_per_second,1)));
13109 (void) WriteBlob(image,14,chunk);
13110 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13111 mng_info->delay=(png_uint_32) image->delay;
13113 mng_info->old_framing_mode=mng_info->framing_mode;
13116 #if defined(JNG_SUPPORTED)
13117 if (image_info->compression == JPEGCompression)
13122 if (logging != MagickFalse)
13123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13124 " Writing JNG object.");
13125 /* To do: specify the desired alpha compression method. */
13126 write_info=CloneImageInfo(image_info);
13127 write_info->compression=UndefinedCompression;
13128 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13129 write_info=DestroyImageInfo(write_info);
13134 if (logging != MagickFalse)
13135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13136 " Writing PNG object.");
13138 mng_info->need_blob = MagickFalse;
13139 mng_info->ping_preserve_colormap = MagickFalse;
13141 /* We don't want any ancillary chunks written */
13142 mng_info->ping_exclude_bKGD=MagickTrue;
13143 mng_info->ping_exclude_cHRM=MagickTrue;
13144 mng_info->ping_exclude_date=MagickTrue;
13145 mng_info->ping_exclude_EXIF=MagickTrue;
13146 mng_info->ping_exclude_gAMA=MagickTrue;
13147 mng_info->ping_exclude_iCCP=MagickTrue;
13148 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13149 mng_info->ping_exclude_oFFs=MagickTrue;
13150 mng_info->ping_exclude_pHYs=MagickTrue;
13151 mng_info->ping_exclude_sRGB=MagickTrue;
13152 mng_info->ping_exclude_tEXt=MagickTrue;
13153 mng_info->ping_exclude_tRNS=MagickTrue;
13154 mng_info->ping_exclude_vpAg=MagickTrue;
13155 mng_info->ping_exclude_zCCP=MagickTrue;
13156 mng_info->ping_exclude_zTXt=MagickTrue;
13158 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13161 if (status == MagickFalse)
13163 MngInfoFreeStruct(mng_info,&have_mng_structure);
13164 (void) CloseBlob(image);
13165 return(MagickFalse);
13167 (void) CatchImageException(image);
13168 if (GetNextImageInList(image) == (Image *) NULL)
13170 image=SyncNextImageInList(image);
13171 status=SetImageProgress(image,SaveImagesTag,scene++,
13172 GetImageListLength(image));
13174 if (status == MagickFalse)
13177 } while (mng_info->adjoin);
13181 while (GetPreviousImageInList(image) != (Image *) NULL)
13182 image=GetPreviousImageInList(image);
13184 Write the MEND chunk.
13186 (void) WriteBlobMSBULong(image,0x00000000L);
13187 PNGType(chunk,mng_MEND);
13188 LogPNGChunk(logging,mng_MEND,0L);
13189 (void) WriteBlob(image,4,chunk);
13190 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13193 Relinquish resources.
13195 (void) CloseBlob(image);
13196 MngInfoFreeStruct(mng_info,&have_mng_structure);
13198 if (logging != MagickFalse)
13199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13201 return(MagickTrue);
13203 #else /* PNG_LIBPNG_VER > 10011 */
13205 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13208 printf("Your PNG library is too old: You have libpng-%s\n",
13209 PNG_LIBPNG_VER_STRING);
13211 ThrowBinaryException(CoderError,"PNG library is too old",
13212 image_info->filename);
13215 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13217 return(WritePNGImage(image_info,image));
13219 #endif /* PNG_LIBPNG_VER > 10011 */