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 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7828 Sometimes we get PseudoClass images whose RGB values don't match
7829 the colors in the colormap. This code syncs the RGB values.
7831 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7832 (void) SyncImage(image,exception);
7834 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7835 if (image->depth > 8)
7837 if (logging != MagickFalse)
7838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7839 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7845 /* Respect the -depth option */
7846 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7851 if (image->depth > 8)
7853 #if MAGICKCORE_QUANTUM_DEPTH > 16
7854 /* Scale to 16-bit */
7855 LBR16PacketRGBO(image->background_color);
7857 for (y=0; y < (ssize_t) image->rows; y++)
7859 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7861 if (r == (Quantum *) NULL)
7864 for (x=0; x < (ssize_t) image->columns; x++)
7867 r+=GetPixelChannels(image);
7870 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7874 if (image->storage_class == PseudoClass && image->colormap != NULL)
7876 for (i=0; i < (ssize_t) image->colors; i++)
7878 LBR16PacketRGBO(image->colormap[i]);
7881 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7884 else if (image->depth > 4)
7886 #if MAGICKCORE_QUANTUM_DEPTH > 8
7887 /* Scale to 8-bit */
7888 LBR08PacketRGBO(image->background_color);
7890 for (y=0; y < (ssize_t) image->rows; y++)
7892 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7894 if (r == (Quantum *) NULL)
7897 for (x=0; x < (ssize_t) image->columns; x++)
7900 r+=GetPixelChannels(image);
7903 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7907 if (image->storage_class == PseudoClass && image->colormap != NULL)
7909 for (i=0; i < (ssize_t) image->colors; i++)
7911 LBR08PacketRGBO(image->colormap[i]);
7914 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7917 if (image->depth > 2)
7919 /* Scale to 4-bit */
7920 LBR04PacketRGBO(image->background_color);
7922 for (y=0; y < (ssize_t) image->rows; y++)
7924 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7926 if (r == (Quantum *) NULL)
7929 for (x=0; x < (ssize_t) image->columns; x++)
7932 r+=GetPixelChannels(image);
7935 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7939 if (image->storage_class == PseudoClass && image->colormap != NULL)
7941 for (i=0; i < (ssize_t) image->colors; i++)
7943 LBR04PacketRGBO(image->colormap[i]);
7948 else if (image->depth > 1)
7950 /* Scale to 2-bit */
7951 LBR02PacketRGBO(image->background_color);
7953 for (y=0; y < (ssize_t) image->rows; y++)
7955 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7957 if (r == (Quantum *) NULL)
7960 for (x=0; x < (ssize_t) image->columns; x++)
7963 r+=GetPixelChannels(image);
7966 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7970 if (image->storage_class == PseudoClass && image->colormap != NULL)
7972 for (i=0; i < (ssize_t) image->colors; i++)
7974 LBR02PacketRGBO(image->colormap[i]);
7980 /* Scale to 1-bit */
7981 LBR01PacketRGBO(image->background_color);
7983 for (y=0; y < (ssize_t) image->rows; y++)
7985 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7987 if (r == (Quantum *) NULL)
7990 for (x=0; x < (ssize_t) image->columns; x++)
7993 r+=GetPixelChannels(image);
7996 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8000 if (image->storage_class == PseudoClass && image->colormap != NULL)
8002 for (i=0; i < (ssize_t) image->colors; i++)
8004 LBR01PacketRGBO(image->colormap[i]);
8010 /* To do: set to next higher multiple of 8 */
8011 if (image->depth < 8)
8014 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8015 /* PNG does not handle depths greater than 16 so reduce it even
8018 if (image->depth > 8)
8022 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8023 if (image->depth > 8)
8025 /* To do: fill low byte properly */
8029 if (image->depth == 16 && mng_info->write_png_depth != 16)
8030 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8034 /* Normally we run this just once, but in the case of writing PNG8
8035 * we reduce the transparency to binary and run again, then if there
8036 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8037 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8038 * palette. Then (To do) we take care of a final reduction that is only
8039 * needed if there are still 256 colors present and one of them has both
8040 * transparent and opaque instances.
8043 tried_332 = MagickFalse;
8044 tried_333 = MagickFalse;
8045 tried_444 = MagickFalse;
8051 * Sometimes we get DirectClass images that have 256 colors or fewer.
8052 * This code will build a colormap.
8054 * Also, sometimes we get PseudoClass images with an out-of-date
8055 * colormap. This code will replace the colormap with a new one.
8056 * Sometimes we get PseudoClass images that have more than 256 colors.
8057 * This code will delete the colormap and change the image to
8060 * If image->matte is MagickFalse, we ignore the alpha channel
8061 * even though it sometimes contains left-over non-opaque values.
8063 * Also we gather some information (number of opaque, transparent,
8064 * and semitransparent pixels, and whether the image has any non-gray
8065 * pixels or only black-and-white pixels) that we might need later.
8067 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8068 * we need to check for bogus non-opaque values, at least.
8076 semitransparent[260],
8079 register const Quantum
8086 if (logging != MagickFalse)
8087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8088 " Enter BUILD_PALETTE:");
8090 if (logging != MagickFalse)
8092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8093 " image->columns=%.20g",(double) image->columns);
8094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8095 " image->rows=%.20g",(double) image->rows);
8096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8097 " image->matte=%.20g",(double) image->matte);
8098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8099 " image->depth=%.20g",(double) image->depth);
8101 if (image->storage_class == PseudoClass && image->colormap != NULL)
8103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8104 " Original colormap:");
8105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8106 " i (red,green,blue,alpha)");
8108 for (i=0; i < 256; i++)
8110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8111 " %d (%d,%d,%d,%d)",
8113 (int) image->colormap[i].red,
8114 (int) image->colormap[i].green,
8115 (int) image->colormap[i].blue,
8116 (int) image->colormap[i].alpha);
8119 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8124 " %d (%d,%d,%d,%d)",
8126 (int) image->colormap[i].red,
8127 (int) image->colormap[i].green,
8128 (int) image->colormap[i].blue,
8129 (int) image->colormap[i].alpha);
8134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8135 " image->colors=%d",(int) image->colors);
8137 if (image->colors == 0)
8138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8139 " (zero means unknown)");
8141 if (ping_preserve_colormap == MagickFalse)
8142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8143 " Regenerate the colormap");
8148 number_semitransparent = 0;
8149 number_transparent = 0;
8151 for (y=0; y < (ssize_t) image->rows; y++)
8153 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8155 if (q == (Quantum *) NULL)
8158 for (x=0; x < (ssize_t) image->columns; x++)
8160 if (image->matte == MagickFalse ||
8161 GetPixelAlpha(image,q) == OpaqueAlpha)
8163 if (number_opaque < 259)
8165 if (number_opaque == 0)
8167 GetPixelInfoPixel(image, q, opaque);
8168 opaque[0].alpha=OpaqueAlpha;
8172 for (i=0; i< (ssize_t) number_opaque; i++)
8174 if (IsPixelEquivalent(image,q, opaque+i))
8178 if (i == (ssize_t) number_opaque && number_opaque < 259)
8181 GetPixelInfoPixel(image, q, opaque+i);
8182 opaque[i].alpha=OpaqueAlpha;
8186 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8188 if (number_transparent < 259)
8190 if (number_transparent == 0)
8192 GetPixelInfoPixel(image, q, transparent);
8193 ping_trans_color.red=(unsigned short)
8194 GetPixelRed(image,q);
8195 ping_trans_color.green=(unsigned short)
8196 GetPixelGreen(image,q);
8197 ping_trans_color.blue=(unsigned short)
8198 GetPixelBlue(image,q);
8199 ping_trans_color.gray=(unsigned short)
8200 GetPixelRed(image,q);
8201 number_transparent = 1;
8204 for (i=0; i< (ssize_t) number_transparent; i++)
8206 if (IsPixelEquivalent(image,q, transparent+i))
8210 if (i == (ssize_t) number_transparent &&
8211 number_transparent < 259)
8213 number_transparent++;
8214 GetPixelInfoPixel(image,q,transparent+i);
8220 if (number_semitransparent < 259)
8222 if (number_semitransparent == 0)
8224 GetPixelInfoPixel(image,q,semitransparent);
8225 number_semitransparent = 1;
8228 for (i=0; i< (ssize_t) number_semitransparent; i++)
8230 if (IsPixelEquivalent(image,q, semitransparent+i)
8231 && GetPixelAlpha(image,q) ==
8232 semitransparent[i].alpha)
8236 if (i == (ssize_t) number_semitransparent &&
8237 number_semitransparent < 259)
8239 number_semitransparent++;
8240 GetPixelInfoPixel(image, q, semitransparent+i);
8244 q+=GetPixelChannels(image);
8248 if (mng_info->write_png8 == MagickFalse &&
8249 ping_exclude_bKGD == MagickFalse)
8251 /* Add the background color to the palette, if it
8252 * isn't already there.
8254 if (logging != MagickFalse)
8256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8257 " Check colormap for background (%d,%d,%d)",
8258 (int) image->background_color.red,
8259 (int) image->background_color.green,
8260 (int) image->background_color.blue);
8262 for (i=0; i<number_opaque; i++)
8264 if (opaque[i].red == image->background_color.red &&
8265 opaque[i].green == image->background_color.green &&
8266 opaque[i].blue == image->background_color.blue)
8269 if (number_opaque < 259 && i == number_opaque)
8271 opaque[i] = image->background_color;
8272 ping_background.index = i;
8273 if (logging != MagickFalse)
8275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8276 " background_color index is %d",(int) i);
8280 else if (logging != MagickFalse)
8281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8282 " No room in the colormap to add background color");
8285 image_colors=number_opaque+number_transparent+number_semitransparent;
8287 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8289 /* No room for the background color; remove it. */
8294 if (logging != MagickFalse)
8296 if (image_colors > 256)
8297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8298 " image has more than 256 colors");
8301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8302 " image has %d colors",image_colors);
8305 if (ping_preserve_colormap != MagickFalse)
8308 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8310 ping_have_color=MagickFalse;
8311 ping_have_non_bw=MagickFalse;
8313 if(image_colors > 256)
8315 for (y=0; y < (ssize_t) image->rows; y++)
8317 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8319 if (q == (Quantum *) NULL)
8323 for (x=0; x < (ssize_t) image->columns; x++)
8325 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8326 GetPixelRed(image,s) != GetPixelBlue(image,s))
8328 ping_have_color=MagickTrue;
8329 ping_have_non_bw=MagickTrue;
8332 s+=GetPixelChannels(image);
8335 if (ping_have_color != MagickFalse)
8338 /* Worst case is black-and-white; we are looking at every
8342 if (ping_have_non_bw == MagickFalse)
8345 for (x=0; x < (ssize_t) image->columns; x++)
8347 if (GetPixelRed(image,s) != 0 &&
8348 GetPixelRed(image,s) != QuantumRange)
8350 ping_have_non_bw=MagickTrue;
8353 s+=GetPixelChannels(image);
8360 if (image_colors < 257)
8366 * Initialize image colormap.
8369 if (logging != MagickFalse)
8370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8371 " Sort the new colormap");
8373 /* Sort palette, transparent first */;
8377 for (i=0; i<number_transparent; i++)
8378 colormap[n++] = transparent[i];
8380 for (i=0; i<number_semitransparent; i++)
8381 colormap[n++] = semitransparent[i];
8383 for (i=0; i<number_opaque; i++)
8384 colormap[n++] = opaque[i];
8386 ping_background.index +=
8387 (number_transparent + number_semitransparent);
8389 /* image_colors < 257; search the colormap instead of the pixels
8390 * to get ping_have_color and ping_have_non_bw
8394 if (ping_have_color == MagickFalse)
8396 if (colormap[i].red != colormap[i].green ||
8397 colormap[i].red != colormap[i].blue)
8399 ping_have_color=MagickTrue;
8400 ping_have_non_bw=MagickTrue;
8405 if (ping_have_non_bw == MagickFalse)
8407 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8408 ping_have_non_bw=MagickTrue;
8412 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8413 (number_transparent == 0 && number_semitransparent == 0)) &&
8414 (((mng_info->write_png_colortype-1) ==
8415 PNG_COLOR_TYPE_PALETTE) ||
8416 (mng_info->write_png_colortype == 0)))
8418 if (logging != MagickFalse)
8420 if (n != (ssize_t) image_colors)
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " image_colors (%d) and n (%d) don't match",
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " AcquireImageColormap");
8429 image->colors = image_colors;
8431 if (AcquireImageColormap(image,image_colors,exception) ==
8433 ThrowWriterException(ResourceLimitError,
8434 "MemoryAllocationFailed");
8436 for (i=0; i< (ssize_t) image_colors; i++)
8437 image->colormap[i] = colormap[i];
8439 if (logging != MagickFalse)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " image->colors=%d (%d)",
8443 (int) image->colors, image_colors);
8445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8446 " Update the pixel indexes");
8449 /* Sync the pixel indices with the new colormap */
8451 for (y=0; y < (ssize_t) image->rows; y++)
8453 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8455 if (q == (Quantum *) NULL)
8458 for (x=0; x < (ssize_t) image->columns; x++)
8460 for (i=0; i< (ssize_t) image_colors; i++)
8462 if ((image->matte == MagickFalse ||
8463 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8464 image->colormap[i].red == GetPixelRed(image,q) &&
8465 image->colormap[i].green == GetPixelGreen(image,q) &&
8466 image->colormap[i].blue == GetPixelBlue(image,q))
8468 SetPixelIndex(image,i,q);
8472 q+=GetPixelChannels(image);
8475 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8481 if (logging != MagickFalse)
8483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484 " image->colors=%d", (int) image->colors);
8486 if (image->colormap != NULL)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " i (red,green,blue,alpha)");
8491 for (i=0; i < (ssize_t) image->colors; i++)
8493 if (i < 300 || i >= (ssize_t) image->colors - 10)
8495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496 " %d (%d,%d,%d,%d)",
8498 (int) image->colormap[i].red,
8499 (int) image->colormap[i].green,
8500 (int) image->colormap[i].blue,
8501 (int) image->colormap[i].alpha);
8506 if (number_transparent < 257)
8507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8508 " number_transparent = %d",
8509 number_transparent);
8512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8513 " number_transparent > 256");
8515 if (number_opaque < 257)
8516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8517 " number_opaque = %d",
8521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8522 " number_opaque > 256");
8524 if (number_semitransparent < 257)
8525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526 " number_semitransparent = %d",
8527 number_semitransparent);
8530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8531 " number_semitransparent > 256");
8533 if (ping_have_non_bw == MagickFalse)
8534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8535 " All pixels and the background are black or white");
8537 else if (ping_have_color == MagickFalse)
8538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539 " All pixels and the background are gray");
8542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8543 " At least one pixel or the background is non-gray");
8545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8546 " Exit BUILD_PALETTE:");
8549 if (mng_info->write_png8 == MagickFalse)
8552 /* Make any reductions necessary for the PNG8 format */
8553 if (image_colors <= 256 &&
8554 image_colors != 0 && image->colormap != NULL &&
8555 number_semitransparent == 0 &&
8556 number_transparent <= 1)
8559 /* PNG8 can't have semitransparent colors so we threshold the
8560 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8561 * transparent color so if more than one is transparent we merge
8562 * them into image->background_color.
8564 if (number_semitransparent != 0 || number_transparent > 1)
8566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8567 " Thresholding the alpha channel to binary");
8569 for (y=0; y < (ssize_t) image->rows; y++)
8571 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8573 if (r == (Quantum *) NULL)
8576 for (x=0; x < (ssize_t) image->columns; x++)
8578 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8580 SetPixelInfoPixel(image,&image->background_color,r);
8581 SetPixelAlpha(image,TransparentAlpha,r);
8584 SetPixelAlpha(image,OpaqueAlpha,r);
8585 r+=GetPixelChannels(image);
8588 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8591 if (image_colors != 0 && image_colors <= 256 &&
8592 image->colormap != NULL)
8593 for (i=0; i<image_colors; i++)
8594 image->colormap[i].alpha =
8595 (image->colormap[i].alpha > TransparentAlpha/2 ?
8596 TransparentAlpha : OpaqueAlpha);
8601 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8602 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8603 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8606 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8608 if (logging != MagickFalse)
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " Quantizing the background color to 4-4-4");
8612 tried_444 = MagickTrue;
8614 LBR04PacketRGB(image->background_color);
8616 if (logging != MagickFalse)
8617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8618 " Quantizing the pixel colors to 4-4-4");
8620 if (image->colormap == NULL)
8622 for (y=0; y < (ssize_t) image->rows; y++)
8624 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8626 if (r == (Quantum *) NULL)
8629 for (x=0; x < (ssize_t) image->columns; x++)
8631 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8633 r+=GetPixelChannels(image);
8636 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8641 else /* Should not reach this; colormap already exists and
8644 if (logging != MagickFalse)
8645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8646 " Quantizing the colormap to 4-4-4");
8648 for (i=0; i<image_colors; i++)
8650 LBR04PacketRGB(image->colormap[i]);
8656 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8658 if (logging != MagickFalse)
8659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8660 " Quantizing the background color to 3-3-3");
8662 tried_333 = MagickTrue;
8664 LBR03PacketRGB(image->background_color);
8666 if (logging != MagickFalse)
8667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8668 " Quantizing the pixel colors to 3-3-3-1");
8670 if (image->colormap == NULL)
8672 for (y=0; y < (ssize_t) image->rows; y++)
8674 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8676 if (r == (Quantum *) NULL)
8679 for (x=0; x < (ssize_t) image->columns; x++)
8681 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8683 r+=GetPixelChannels(image);
8686 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8691 else /* Should not reach this; colormap already exists and
8694 if (logging != MagickFalse)
8695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8696 " Quantizing the colormap to 3-3-3-1");
8697 for (i=0; i<image_colors; i++)
8699 LBR03PacketRGB(image->colormap[i]);
8705 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8707 if (logging != MagickFalse)
8708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8709 " Quantizing the background color to 3-3-2");
8711 tried_332 = MagickTrue;
8713 /* Red and green were already done so we only quantize the blue
8717 LBR02PacketBlue(image->background_color);
8719 if (logging != MagickFalse)
8720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8721 " Quantizing the pixel colors to 3-3-2-1");
8723 if (image->colormap == NULL)
8725 for (y=0; y < (ssize_t) image->rows; y++)
8727 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8729 if (r == (Quantum *) NULL)
8732 for (x=0; x < (ssize_t) image->columns; x++)
8734 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8736 r+=GetPixelChannels(image);
8739 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8744 else /* Should not reach this; colormap already exists and
8747 if (logging != MagickFalse)
8748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8749 " Quantizing the colormap to 3-3-2-1");
8750 for (i=0; i<image_colors; i++)
8752 LBR02PacketBlue(image->colormap[i]);
8759 if (image_colors == 0 || image_colors > 256)
8761 /* Take care of special case with 256 colors + 1 transparent
8762 * color. We don't need to quantize to 2-3-2-1; we only need to
8763 * eliminate one color, so we'll merge the two darkest red
8764 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8766 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8767 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8768 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8770 image->background_color.red=ScaleCharToQuantum(0x24);
8773 if (image->colormap == NULL)
8775 for (y=0; y < (ssize_t) image->rows; y++)
8777 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8779 if (r == (Quantum *) NULL)
8782 for (x=0; x < (ssize_t) image->columns; x++)
8784 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8785 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8786 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8787 GetPixelAlpha(image,r) == OpaqueAlpha)
8789 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8791 r+=GetPixelChannels(image);
8794 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8802 for (i=0; i<image_colors; i++)
8804 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8805 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8806 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8808 image->colormap[i].red=ScaleCharToQuantum(0x24);
8814 /* END OF BUILD_PALETTE */
8816 /* If we are excluding the tRNS chunk and there is transparency,
8817 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8820 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8821 (number_transparent != 0 || number_semitransparent != 0))
8823 unsigned int colortype=mng_info->write_png_colortype;
8825 if (ping_have_color == MagickFalse)
8826 mng_info->write_png_colortype = 5;
8829 mng_info->write_png_colortype = 7;
8831 if (colortype != 0 &&
8832 mng_info->write_png_colortype != colortype)
8833 ping_need_colortype_warning=MagickTrue;
8837 /* See if cheap transparency is possible. It is only possible
8838 * when there is a single transparent color, no semitransparent
8839 * color, and no opaque color that has the same RGB components
8840 * as the transparent color. We only need this information if
8841 * we are writing a PNG with colortype 0 or 2, and we have not
8842 * excluded the tRNS chunk.
8844 if (number_transparent == 1 &&
8845 mng_info->write_png_colortype < 4)
8847 ping_have_cheap_transparency = MagickTrue;
8849 if (number_semitransparent != 0)
8850 ping_have_cheap_transparency = MagickFalse;
8852 else if (image_colors == 0 || image_colors > 256 ||
8853 image->colormap == NULL)
8855 register const Quantum
8858 for (y=0; y < (ssize_t) image->rows; y++)
8860 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8862 if (q == (Quantum *) NULL)
8865 for (x=0; x < (ssize_t) image->columns; x++)
8867 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8868 (unsigned short) GetPixelRed(image,q) ==
8869 ping_trans_color.red &&
8870 (unsigned short) GetPixelGreen(image,q) ==
8871 ping_trans_color.green &&
8872 (unsigned short) GetPixelBlue(image,q) ==
8873 ping_trans_color.blue)
8875 ping_have_cheap_transparency = MagickFalse;
8879 q+=GetPixelChannels(image);
8882 if (ping_have_cheap_transparency == MagickFalse)
8888 /* Assuming that image->colormap[0] is the one transparent color
8889 * and that all others are opaque.
8891 if (image_colors > 1)
8892 for (i=1; i<image_colors; i++)
8893 if (image->colormap[i].red == image->colormap[0].red &&
8894 image->colormap[i].green == image->colormap[0].green &&
8895 image->colormap[i].blue == image->colormap[0].blue)
8897 ping_have_cheap_transparency = MagickFalse;
8902 if (logging != MagickFalse)
8904 if (ping_have_cheap_transparency == MagickFalse)
8905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8906 " Cheap transparency is not possible.");
8909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8910 " Cheap transparency is possible.");
8914 ping_have_cheap_transparency = MagickFalse;
8916 image_depth=image->depth;
8918 quantum_info = (QuantumInfo *) NULL;
8920 image_colors=(int) image->colors;
8921 image_matte=image->matte;
8923 mng_info->IsPalette=image->storage_class == PseudoClass &&
8924 image_colors <= 256 && image->colormap != NULL;
8926 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8927 (image->colors == 0 || image->colormap == NULL))
8929 image_info=DestroyImageInfo(image_info);
8930 image=DestroyImage(image);
8931 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8932 "Cannot write PNG8 or color-type 3; colormap is NULL",
8933 "`%s'",IMimage->filename);
8934 return(MagickFalse);
8938 Allocate the PNG structures
8940 #ifdef PNG_USER_MEM_SUPPORTED
8941 error_info.image=image;
8942 error_info.exception=exception;
8943 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
8944 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8945 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8948 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
8949 MagickPNGErrorHandler,MagickPNGWarningHandler);
8952 if (ping == (png_struct *) NULL)
8953 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8955 ping_info=png_create_info_struct(ping);
8957 if (ping_info == (png_info *) NULL)
8959 png_destroy_write_struct(&ping,(png_info **) NULL);
8960 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8963 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8964 ping_pixels=(unsigned char *) NULL;
8966 if (setjmp(png_jmpbuf(ping)))
8972 if (image_info->verbose)
8973 (void) printf("PNG write has failed.\n");
8975 png_destroy_write_struct(&ping,&ping_info);
8976 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
8977 UnlockSemaphoreInfo(ping_semaphore);
8980 if (ping_pixels != (unsigned char *) NULL)
8981 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8983 if (quantum_info != (QuantumInfo *) NULL)
8984 quantum_info=DestroyQuantumInfo(quantum_info);
8986 if (ping_have_blob != MagickFalse)
8987 (void) CloseBlob(image);
8988 image_info=DestroyImageInfo(image_info);
8989 image=DestroyImage(image);
8990 return(MagickFalse);
8993 /* { For navigation to end of SETJMP-protected block. Within this
8994 * block, use png_error() instead of Throwing an Exception, to ensure
8995 * that libpng is able to clean up, and that the semaphore is unlocked.
8998 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
8999 LockSemaphoreInfo(ping_semaphore);
9003 Prepare PNG for writing.
9006 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9007 if (mng_info->write_mng)
9009 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9010 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9011 /* Disable new libpng-1.5.10 feature when writing a MNG because
9012 * zero-length PLTE is OK
9014 png_set_check_for_invalid_index (ping, 0);
9019 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9020 if (mng_info->write_mng)
9021 png_permit_empty_plte(ping,MagickTrue);
9028 ping_width=(png_uint_32) image->columns;
9029 ping_height=(png_uint_32) image->rows;
9031 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9034 if (mng_info->write_png_depth != 0)
9035 image_depth=mng_info->write_png_depth;
9037 /* Adjust requested depth to next higher valid depth if necessary */
9038 if (image_depth > 8)
9041 if ((image_depth > 4) && (image_depth < 8))
9044 if (image_depth == 3)
9047 if (logging != MagickFalse)
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " width=%.20g",(double) ping_width);
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " height=%.20g",(double) ping_height);
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " image_matte=%.20g",(double) image->matte);
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " image->depth=%.20g",(double) image->depth);
9057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9061 save_image_depth=image_depth;
9062 ping_bit_depth=(png_byte) save_image_depth;
9065 #if defined(PNG_pHYs_SUPPORTED)
9066 if (ping_exclude_pHYs == MagickFalse)
9068 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9069 (!mng_info->write_mng || !mng_info->equal_physs))
9071 if (logging != MagickFalse)
9072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073 " Setting up pHYs chunk");
9075 if (image->units == PixelsPerInchResolution)
9077 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9078 ping_pHYs_x_resolution=
9079 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9080 ping_pHYs_y_resolution=
9081 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9084 else if (image->units == PixelsPerCentimeterResolution)
9086 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9087 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9088 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9093 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9094 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9095 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9098 if (logging != MagickFalse)
9099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9100 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9101 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9102 (int) ping_pHYs_unit_type);
9103 ping_have_pHYs = MagickTrue;
9108 if (ping_exclude_bKGD == MagickFalse)
9110 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9116 if (ping_bit_depth == 8)
9119 if (ping_bit_depth == 4)
9122 if (ping_bit_depth == 2)
9125 if (ping_bit_depth == 1)
9128 ping_background.red=(png_uint_16)
9129 (ScaleQuantumToShort(image->background_color.red) & mask);
9131 ping_background.green=(png_uint_16)
9132 (ScaleQuantumToShort(image->background_color.green) & mask);
9134 ping_background.blue=(png_uint_16)
9135 (ScaleQuantumToShort(image->background_color.blue) & mask);
9137 ping_background.gray=(png_uint_16) ping_background.green;
9140 if (logging != MagickFalse)
9142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9143 " Setting up bKGD chunk (1)");
9144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " background_color index is %d",
9146 (int) ping_background.index);
9148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149 " ping_bit_depth=%d",ping_bit_depth);
9152 ping_have_bKGD = MagickTrue;
9156 Select the color type.
9161 if (mng_info->IsPalette && mng_info->write_png8)
9164 /* To do: make this a function cause it's used twice, except
9165 for reducing the sample depth from 8. */
9167 number_colors=image_colors;
9169 ping_have_tRNS=MagickFalse;
9174 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9176 if (logging != MagickFalse)
9177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9178 " Setting up PLTE chunk with %d colors (%d)",
9179 number_colors, image_colors);
9181 for (i=0; i < (ssize_t) number_colors; i++)
9183 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9184 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9185 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9186 if (logging != MagickFalse)
9187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188 #if MAGICKCORE_QUANTUM_DEPTH == 8
9189 " %3ld (%3d,%3d,%3d)",
9191 " %5ld (%5d,%5d,%5d)",
9193 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9197 ping_have_PLTE=MagickTrue;
9198 image_depth=ping_bit_depth;
9201 if (matte != MagickFalse)
9204 Identify which colormap entry is transparent.
9206 assert(number_colors <= 256);
9207 assert(image->colormap != NULL);
9209 for (i=0; i < (ssize_t) number_transparent; i++)
9210 ping_trans_alpha[i]=0;
9213 ping_num_trans=(unsigned short) (number_transparent +
9214 number_semitransparent);
9216 if (ping_num_trans == 0)
9217 ping_have_tRNS=MagickFalse;
9220 ping_have_tRNS=MagickTrue;
9223 if (ping_exclude_bKGD == MagickFalse)
9226 * Identify which colormap entry is the background color.
9229 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9230 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9233 ping_background.index=(png_byte) i;
9235 if (logging != MagickFalse)
9237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238 " background_color index is %d",
9239 (int) ping_background.index);
9242 } /* end of write_png8 */
9244 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9246 image_matte=MagickFalse;
9247 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9250 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9252 image_matte=MagickTrue;
9253 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9256 else /* mng_info->write_pngNN not specified */
9258 image_depth=ping_bit_depth;
9260 if (mng_info->write_png_colortype != 0)
9262 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9264 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9265 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9266 image_matte=MagickTrue;
9269 image_matte=MagickFalse;
9271 if (logging != MagickFalse)
9272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9273 " PNG colortype %d was specified:",(int) ping_color_type);
9276 else /* write_png_colortype not specified */
9278 if (logging != MagickFalse)
9279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9280 " Selecting PNG colortype:");
9282 ping_color_type=(png_byte) ((matte != MagickFalse)?
9283 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9285 if (image_info->type == TrueColorType)
9287 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9288 image_matte=MagickFalse;
9291 if (image_info->type == TrueColorMatteType)
9293 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9294 image_matte=MagickTrue;
9297 if (image_info->type == PaletteType ||
9298 image_info->type == PaletteMatteType)
9299 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9301 if (mng_info->write_png_colortype == 0 &&
9302 (image_info->type == UndefinedType ||
9303 image_info->type == OptimizeType))
9305 if (ping_have_color == MagickFalse)
9307 if (image_matte == MagickFalse)
9309 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9310 image_matte=MagickFalse;
9315 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9316 image_matte=MagickTrue;
9321 if (image_matte == MagickFalse)
9323 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9324 image_matte=MagickFalse;
9329 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9330 image_matte=MagickTrue;
9337 if (logging != MagickFalse)
9338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9339 " Selected PNG colortype=%d",ping_color_type);
9341 if (ping_bit_depth < 8)
9343 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9344 ping_color_type == PNG_COLOR_TYPE_RGB ||
9345 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9349 old_bit_depth=ping_bit_depth;
9351 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9353 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9357 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9362 if (image->colors == 0)
9365 png_error(ping,"image has 0 colors");
9368 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9369 ping_bit_depth <<= 1;
9372 if (logging != MagickFalse)
9374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9375 " Number of colors: %.20g",(double) image_colors);
9377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9378 " Tentative PNG bit depth: %d",ping_bit_depth);
9381 if (ping_bit_depth < (int) mng_info->write_png_depth)
9382 ping_bit_depth = mng_info->write_png_depth;
9385 image_depth=ping_bit_depth;
9387 if (logging != MagickFalse)
9389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390 " Tentative PNG color type: %s (%.20g)",
9391 PngColorTypeToString(ping_color_type),
9392 (double) ping_color_type);
9394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9395 " image_info->type: %.20g",(double) image_info->type);
9397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9398 " image_depth: %.20g",(double) image_depth);
9400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9402 " image->depth: %.20g",(double) image->depth);
9404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9405 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9408 if (matte != MagickFalse)
9410 if (mng_info->IsPalette)
9412 if (mng_info->write_png_colortype == 0)
9414 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9416 if (ping_have_color != MagickFalse)
9417 ping_color_type=PNG_COLOR_TYPE_RGBA;
9421 * Determine if there is any transparent color.
9423 if (number_transparent + number_semitransparent == 0)
9426 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9429 image_matte=MagickFalse;
9431 if (mng_info->write_png_colortype == 0)
9432 ping_color_type&=0x03;
9442 if (ping_bit_depth == 8)
9445 if (ping_bit_depth == 4)
9448 if (ping_bit_depth == 2)
9451 if (ping_bit_depth == 1)
9454 ping_trans_color.red=(png_uint_16)
9455 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9457 ping_trans_color.green=(png_uint_16)
9458 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9460 ping_trans_color.blue=(png_uint_16)
9461 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9463 ping_trans_color.gray=(png_uint_16)
9464 (ScaleQuantumToShort(GetPixelInfoIntensity(
9465 image->colormap)) & mask);
9467 ping_trans_color.index=(png_byte) 0;
9469 ping_have_tRNS=MagickTrue;
9472 if (ping_have_tRNS != MagickFalse)
9475 * Determine if there is one and only one transparent color
9476 * and if so if it is fully transparent.
9478 if (ping_have_cheap_transparency == MagickFalse)
9479 ping_have_tRNS=MagickFalse;
9482 if (ping_have_tRNS != MagickFalse)
9484 if (mng_info->write_png_colortype == 0)
9485 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9487 if (image_depth == 8)
9489 ping_trans_color.red&=0xff;
9490 ping_trans_color.green&=0xff;
9491 ping_trans_color.blue&=0xff;
9492 ping_trans_color.gray&=0xff;
9498 if (image_depth == 8)
9500 ping_trans_color.red&=0xff;
9501 ping_trans_color.green&=0xff;
9502 ping_trans_color.blue&=0xff;
9503 ping_trans_color.gray&=0xff;
9510 if (ping_have_tRNS != MagickFalse)
9511 image_matte=MagickFalse;
9513 if ((mng_info->IsPalette) &&
9514 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9515 ping_have_color == MagickFalse &&
9516 (image_matte == MagickFalse || image_depth >= 8))
9520 if (image_matte != MagickFalse)
9521 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9523 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9525 ping_color_type=PNG_COLOR_TYPE_GRAY;
9527 if (save_image_depth == 16 && image_depth == 8)
9529 if (logging != MagickFalse)
9531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9532 " Scaling ping_trans_color (0)");
9534 ping_trans_color.gray*=0x0101;
9538 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9539 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9541 if ((image_colors == 0) ||
9542 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9543 image_colors=(int) (one << image_depth);
9545 if (image_depth > 8)
9551 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9553 if(!mng_info->write_png_depth)
9557 while ((int) (one << ping_bit_depth)
9558 < (ssize_t) image_colors)
9559 ping_bit_depth <<= 1;
9563 else if (ping_color_type ==
9564 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9565 mng_info->IsPalette)
9567 /* Check if grayscale is reducible */
9570 depth_4_ok=MagickTrue,
9571 depth_2_ok=MagickTrue,
9572 depth_1_ok=MagickTrue;
9574 for (i=0; i < (ssize_t) image_colors; i++)
9579 intensity=ScaleQuantumToChar(image->colormap[i].red);
9581 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9582 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9583 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9584 depth_2_ok=depth_1_ok=MagickFalse;
9585 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9586 depth_1_ok=MagickFalse;
9589 if (depth_1_ok && mng_info->write_png_depth <= 1)
9592 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9595 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9600 image_depth=ping_bit_depth;
9605 if (mng_info->IsPalette)
9607 number_colors=image_colors;
9609 if (image_depth <= 8)
9614 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9616 if (mng_info->have_write_global_plte && matte == MagickFalse)
9618 png_set_PLTE(ping,ping_info,NULL,0);
9620 if (logging != MagickFalse)
9621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9622 " Setting up empty PLTE chunk");
9627 for (i=0; i < (ssize_t) number_colors; i++)
9629 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9630 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9631 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9634 if (logging != MagickFalse)
9635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9636 " Setting up PLTE chunk with %d colors",
9639 ping_have_PLTE=MagickTrue;
9642 /* color_type is PNG_COLOR_TYPE_PALETTE */
9643 if (mng_info->write_png_depth == 0)
9651 while ((one << ping_bit_depth) < (size_t) number_colors)
9652 ping_bit_depth <<= 1;
9657 if (matte != MagickFalse)
9660 * Set up trans_colors array.
9662 assert(number_colors <= 256);
9664 ping_num_trans=(unsigned short) (number_transparent +
9665 number_semitransparent);
9667 if (ping_num_trans == 0)
9668 ping_have_tRNS=MagickFalse;
9672 if (logging != MagickFalse)
9674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " Scaling ping_trans_color (1)");
9677 ping_have_tRNS=MagickTrue;
9679 for (i=0; i < ping_num_trans; i++)
9681 ping_trans_alpha[i]= (png_byte)
9682 ScaleQuantumToChar(image->colormap[i].alpha);
9692 if (image_depth < 8)
9695 if ((save_image_depth == 16) && (image_depth == 8))
9697 if (logging != MagickFalse)
9699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9700 " Scaling ping_trans_color from (%d,%d,%d)",
9701 (int) ping_trans_color.red,
9702 (int) ping_trans_color.green,
9703 (int) ping_trans_color.blue);
9706 ping_trans_color.red*=0x0101;
9707 ping_trans_color.green*=0x0101;
9708 ping_trans_color.blue*=0x0101;
9709 ping_trans_color.gray*=0x0101;
9711 if (logging != MagickFalse)
9713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9715 (int) ping_trans_color.red,
9716 (int) ping_trans_color.green,
9717 (int) ping_trans_color.blue);
9722 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9723 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9726 Adjust background and transparency samples in sub-8-bit grayscale files.
9728 if (ping_bit_depth < 8 && ping_color_type ==
9729 PNG_COLOR_TYPE_GRAY)
9737 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9739 if (ping_exclude_bKGD == MagickFalse)
9742 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9743 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9744 &image->background_color))) +.5)));
9746 if (logging != MagickFalse)
9747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9748 " Setting up bKGD chunk (2)");
9749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9750 " background_color index is %d",
9751 (int) ping_background.index);
9753 ping_have_bKGD = MagickTrue;
9756 if (logging != MagickFalse)
9757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9758 " Scaling ping_trans_color.gray from %d",
9759 (int)ping_trans_color.gray);
9761 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9762 ping_trans_color.gray)+.5);
9764 if (logging != MagickFalse)
9765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9766 " to %d", (int)ping_trans_color.gray);
9769 if (ping_exclude_bKGD == MagickFalse)
9771 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9774 Identify which colormap entry is the background color.
9777 number_colors=image_colors;
9779 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9780 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9783 ping_background.index=(png_byte) i;
9785 if (logging != MagickFalse)
9787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9788 " Setting up bKGD chunk with index=%d",(int) i);
9791 if (i < (ssize_t) number_colors)
9793 ping_have_bKGD = MagickTrue;
9795 if (logging != MagickFalse)
9797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " background =(%d,%d,%d)",
9799 (int) ping_background.red,
9800 (int) ping_background.green,
9801 (int) ping_background.blue);
9805 else /* Can't happen */
9807 if (logging != MagickFalse)
9808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9809 " No room in PLTE to add bKGD color");
9810 ping_have_bKGD = MagickFalse;
9815 if (logging != MagickFalse)
9816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9817 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9820 Initialize compression level and filtering.
9822 if (logging != MagickFalse)
9824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825 " Setting up deflate compression");
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Compression buffer size: 32768");
9831 png_set_compression_buffer_size(ping,32768L);
9833 if (logging != MagickFalse)
9834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9835 " Compression mem level: 9");
9837 png_set_compression_mem_level(ping, 9);
9839 /* Untangle the "-quality" setting:
9841 Undefined is 0; the default is used.
9846 0: Use Z_HUFFMAN_ONLY strategy with the
9847 zlib default compression level
9849 1-9: the zlib compression level
9853 0-4: the PNG filter method
9855 5: libpng adaptive filtering if compression level > 5
9856 libpng filter type "none" if compression level <= 5
9857 or if image is grayscale or palette
9859 6: libpng adaptive filtering
9861 7: "LOCO" filtering (intrapixel differing) if writing
9862 a MNG, othewise "none". Did not work in IM-6.7.0-9
9863 and earlier because of a missing "else".
9865 8: Z_RLE strategy, all filters
9866 Unused prior to IM-6.7.0-10, was same as 6
9868 9: Z_RLE strategy, no PNG filters
9869 Unused prior to IM-6.7.0-10, was same as 6
9871 Note that using the -quality option, not all combinations of
9872 PNG filter type, zlib compression level, and zlib compression
9873 strategy are possible. This will be addressed soon in a
9874 release that accomodates "-define png:compression-strategy", etc.
9878 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9883 if (mng_info->write_png_compression_strategy == 0)
9884 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9887 else if (mng_info->write_png_compression_level == 0)
9892 level=(int) MagickMin((ssize_t) quality/10,9);
9894 mng_info->write_png_compression_level = level+1;
9897 if (mng_info->write_png_compression_strategy == 0)
9899 if ((quality %10) == 8 || (quality %10) == 9)
9900 mng_info->write_png_compression_strategy=Z_RLE;
9903 if (mng_info->write_png_compression_filter == 0)
9904 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9906 if (logging != MagickFalse)
9908 if (mng_info->write_png_compression_level)
9909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9910 " Compression level: %d",
9911 (int) mng_info->write_png_compression_level-1);
9913 if (mng_info->write_png_compression_strategy)
9914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9915 " Compression strategy: %d",
9916 (int) mng_info->write_png_compression_strategy-1);
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919 " Setting up filtering");
9921 if (mng_info->write_png_compression_filter == 6)
9922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " Base filter method: ADAPTIVE");
9924 else if (mng_info->write_png_compression_filter == 0 ||
9925 mng_info->write_png_compression_filter == 1)
9926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9927 " Base filter method: NONE");
9929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9930 " Base filter method: %d",
9931 (int) mng_info->write_png_compression_filter-1);
9934 if (mng_info->write_png_compression_level != 0)
9935 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9937 if (mng_info->write_png_compression_filter == 6)
9939 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9940 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9942 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9944 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9946 else if (mng_info->write_png_compression_filter == 7 ||
9947 mng_info->write_png_compression_filter == 10)
9948 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9950 else if (mng_info->write_png_compression_filter == 8)
9952 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9953 if (mng_info->write_mng)
9955 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9956 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9957 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9960 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9963 else if (mng_info->write_png_compression_filter == 9)
9964 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9966 else if (mng_info->write_png_compression_filter != 0)
9967 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9968 mng_info->write_png_compression_filter-1);
9970 if (mng_info->write_png_compression_strategy != 0)
9971 png_set_compression_strategy(ping,
9972 mng_info->write_png_compression_strategy-1);
9974 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9975 if (ping_exclude_sRGB != MagickFalse ||
9976 (image->rendering_intent == UndefinedIntent))
9978 if ((ping_exclude_tEXt == MagickFalse ||
9979 ping_exclude_zTXt == MagickFalse) &&
9980 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9982 ResetImageProfileIterator(image);
9983 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9985 profile=GetImageProfile(image,name);
9987 if (profile != (StringInfo *) NULL)
9989 #ifdef PNG_WRITE_iCCP_SUPPORTED
9990 if ((LocaleCompare(name,"ICC") == 0) ||
9991 (LocaleCompare(name,"ICM") == 0))
9994 if (ping_exclude_iCCP == MagickFalse)
9996 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9997 #if (PNG_LIBPNG_VER < 10500)
9998 (png_charp) GetStringInfoDatum(profile),
10000 (png_const_bytep) GetStringInfoDatum(profile),
10002 (png_uint_32) GetStringInfoLength(profile));
10008 if (ping_exclude_zCCP == MagickFalse)
10010 Magick_png_write_raw_profile(image_info,ping,ping_info,
10011 (unsigned char *) name,(unsigned char *) name,
10012 GetStringInfoDatum(profile),
10013 (png_uint_32) GetStringInfoLength(profile));
10017 if (logging != MagickFalse)
10018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019 " Setting up text chunk with %s profile",name);
10021 name=GetNextImageProfile(image);
10026 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10027 if ((mng_info->have_write_global_srgb == 0) &&
10028 (image->rendering_intent != UndefinedIntent))
10030 if (ping_exclude_sRGB == MagickFalse)
10033 Note image rendering intent.
10035 if (logging != MagickFalse)
10036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10037 " Setting up sRGB chunk");
10039 (void) png_set_sRGB(ping,ping_info,(
10040 Magick_RenderingIntent_to_PNG_RenderingIntent(
10041 image->rendering_intent)));
10045 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10048 if (ping_exclude_gAMA == MagickFalse &&
10049 (ping_exclude_sRGB == MagickFalse ||
10050 (image->gamma < .45 || image->gamma > .46)))
10052 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10056 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10058 if (logging != MagickFalse)
10059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10060 " Setting up gAMA chunk");
10062 png_set_gAMA(ping,ping_info,image->gamma);
10066 if (ping_exclude_cHRM == MagickFalse)
10068 if ((mng_info->have_write_global_chrm == 0) &&
10069 (image->chromaticity.red_primary.x != 0.0))
10072 Note image chromaticity.
10073 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10081 wp=image->chromaticity.white_point;
10082 rp=image->chromaticity.red_primary;
10083 gp=image->chromaticity.green_primary;
10084 bp=image->chromaticity.blue_primary;
10086 if (logging != MagickFalse)
10087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10088 " Setting up cHRM chunk");
10090 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10096 ping_interlace_method=image_info->interlace != NoInterlace;
10098 if (mng_info->write_mng)
10099 png_set_sig_bytes(ping,8);
10101 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10103 if (mng_info->write_png_colortype != 0)
10105 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10106 if (ping_have_color != MagickFalse)
10108 ping_color_type = PNG_COLOR_TYPE_RGB;
10110 if (ping_bit_depth < 8)
10114 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10115 if (ping_have_color != MagickFalse)
10116 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10119 if (ping_need_colortype_warning != MagickFalse ||
10120 ((mng_info->write_png_depth &&
10121 (int) mng_info->write_png_depth != ping_bit_depth) ||
10122 (mng_info->write_png_colortype &&
10123 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10124 mng_info->write_png_colortype != 7 &&
10125 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10127 if (logging != MagickFalse)
10129 if (ping_need_colortype_warning != MagickFalse)
10131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132 " Image has transparency but tRNS chunk was excluded");
10135 if (mng_info->write_png_depth)
10137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10138 " Defined png:bit-depth=%u, Computed depth=%u",
10139 mng_info->write_png_depth,
10143 if (mng_info->write_png_colortype)
10145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10146 " Defined png:color-type=%u, Computed color type=%u",
10147 mng_info->write_png_colortype-1,
10153 "Cannot write image with defined png:bit-depth or png:color-type.");
10156 if (image_matte != MagickFalse && image->matte == MagickFalse)
10158 /* Add an opaque matte channel */
10159 image->matte = MagickTrue;
10160 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10162 if (logging != MagickFalse)
10163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10164 " Added an opaque matte channel");
10167 if (number_transparent != 0 || number_semitransparent != 0)
10169 if (ping_color_type < 4)
10171 ping_have_tRNS=MagickTrue;
10172 if (logging != MagickFalse)
10173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10174 " Setting ping_have_tRNS=MagickTrue.");
10178 if (logging != MagickFalse)
10179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10180 " Writing PNG header chunks");
10182 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10183 ping_bit_depth,ping_color_type,
10184 ping_interlace_method,ping_compression_method,
10185 ping_filter_method);
10187 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10189 png_set_PLTE(ping,ping_info,palette,number_colors);
10191 if (logging != MagickFalse)
10193 for (i=0; i< (ssize_t) number_colors; i++)
10195 if (i < ping_num_trans)
10196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10197 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10199 (int) palette[i].red,
10200 (int) palette[i].green,
10201 (int) palette[i].blue,
10203 (int) ping_trans_alpha[i]);
10205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10206 " PLTE[%d] = (%d,%d,%d)",
10208 (int) palette[i].red,
10209 (int) palette[i].green,
10210 (int) palette[i].blue);
10215 if (ping_exclude_bKGD == MagickFalse)
10217 if (ping_have_bKGD != MagickFalse)
10219 png_set_bKGD(ping,ping_info,&ping_background);
10222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10223 " Setting up bKGD chunk");
10224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10225 " background color = (%d,%d,%d)",
10226 (int) ping_background.red,
10227 (int) ping_background.green,
10228 (int) ping_background.blue);
10229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10230 " index = %d, gray=%d",
10231 (int) ping_background.index,
10232 (int) ping_background.gray);
10237 if (ping_exclude_pHYs == MagickFalse)
10239 if (ping_have_pHYs != MagickFalse)
10241 png_set_pHYs(ping,ping_info,
10242 ping_pHYs_x_resolution,
10243 ping_pHYs_y_resolution,
10244 ping_pHYs_unit_type);
10248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10249 " Setting up pHYs chunk");
10250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10251 " x_resolution=%lu",
10252 (unsigned long) ping_pHYs_x_resolution);
10253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10254 " y_resolution=%lu",
10255 (unsigned long) ping_pHYs_y_resolution);
10256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10258 (unsigned long) ping_pHYs_unit_type);
10263 #if defined(PNG_oFFs_SUPPORTED)
10264 if (ping_exclude_oFFs == MagickFalse)
10266 if (image->page.x || image->page.y)
10268 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10269 (png_int_32) image->page.y, 0);
10271 if (logging != MagickFalse)
10272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10273 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10274 (int) image->page.x, (int) image->page.y);
10279 if (mng_info->need_blob != MagickFalse)
10281 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10283 png_error(ping,"WriteBlob Failed");
10285 ping_have_blob=MagickTrue;
10288 png_write_info_before_PLTE(ping, ping_info);
10290 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10292 if (logging != MagickFalse)
10294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10295 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10298 if (ping_color_type == 3)
10299 (void) png_set_tRNS(ping, ping_info,
10306 (void) png_set_tRNS(ping, ping_info,
10309 &ping_trans_color);
10311 if (logging != MagickFalse)
10313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10314 " tRNS color =(%d,%d,%d)",
10315 (int) ping_trans_color.red,
10316 (int) ping_trans_color.green,
10317 (int) ping_trans_color.blue);
10322 /* write any png-chunk-b profiles */
10323 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10325 png_write_info(ping,ping_info);
10327 /* write any PNG-chunk-m profiles */
10328 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10330 if (ping_exclude_vpAg == MagickFalse)
10332 if ((image->page.width != 0 && image->page.width != image->columns) ||
10333 (image->page.height != 0 && image->page.height != image->rows))
10338 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10339 PNGType(chunk,mng_vpAg);
10340 LogPNGChunk(logging,mng_vpAg,9L);
10341 PNGLong(chunk+4,(png_uint_32) image->page.width);
10342 PNGLong(chunk+8,(png_uint_32) image->page.height);
10343 chunk[12]=0; /* unit = pixels */
10344 (void) WriteBlob(image,13,chunk);
10345 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10349 #if (PNG_LIBPNG_VER == 10206)
10350 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10351 #define PNG_HAVE_IDAT 0x04
10352 ping->mode |= PNG_HAVE_IDAT;
10353 #undef PNG_HAVE_IDAT
10356 png_set_packing(ping);
10360 rowbytes=image->columns;
10361 if (image_depth > 8)
10363 switch (ping_color_type)
10365 case PNG_COLOR_TYPE_RGB:
10369 case PNG_COLOR_TYPE_GRAY_ALPHA:
10373 case PNG_COLOR_TYPE_RGBA:
10381 if (logging != MagickFalse)
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " Writing PNG image data");
10386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10389 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10390 sizeof(*ping_pixels));
10392 if (ping_pixels == (unsigned char *) NULL)
10393 png_error(ping,"Allocation of memory for pixels failed");
10396 Initialize image scanlines.
10398 quantum_info=AcquireQuantumInfo(image_info,image);
10399 if (quantum_info == (QuantumInfo *) NULL)
10400 png_error(ping,"Memory allocation for quantum_info failed");
10401 quantum_info->format=UndefinedQuantumFormat;
10402 quantum_info->depth=image_depth;
10403 num_passes=png_set_interlace_handling(ping);
10405 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10406 !mng_info->write_png32) &&
10407 (mng_info->IsPalette ||
10408 (image_info->type == BilevelType)) &&
10409 image_matte == MagickFalse &&
10410 ping_have_non_bw == MagickFalse)
10412 /* Palette, Bilevel, or Opaque Monochrome */
10413 register const Quantum
10416 quantum_info->depth=8;
10417 for (pass=0; pass < num_passes; pass++)
10420 Convert PseudoClass image to a PNG monochrome image.
10422 for (y=0; y < (ssize_t) image->rows; y++)
10424 if (logging != MagickFalse && y == 0)
10425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426 " Writing row of pixels (0)");
10428 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10430 if (p == (const Quantum *) NULL)
10433 if (mng_info->IsPalette)
10435 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10436 quantum_info,GrayQuantum,ping_pixels,exception);
10437 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10438 mng_info->write_png_depth &&
10439 mng_info->write_png_depth != old_bit_depth)
10441 /* Undo pixel scaling */
10442 for (i=0; i < (ssize_t) image->columns; i++)
10443 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10444 >> (8-old_bit_depth));
10450 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10451 quantum_info,RedQuantum,ping_pixels,exception);
10454 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10455 for (i=0; i < (ssize_t) image->columns; i++)
10456 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10459 if (logging != MagickFalse && y == 0)
10460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10461 " Writing row of pixels (1)");
10463 png_write_row(ping,ping_pixels);
10465 if (image->previous == (Image *) NULL)
10467 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10468 if (status == MagickFalse)
10474 else /* Not Palette, Bilevel, or Opaque Monochrome */
10476 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10477 !mng_info->write_png32) &&
10478 (image_matte != MagickFalse ||
10479 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10480 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10482 register const Quantum
10485 for (pass=0; pass < num_passes; pass++)
10488 for (y=0; y < (ssize_t) image->rows; y++)
10490 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10492 if (p == (const Quantum *) NULL)
10495 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10497 if (mng_info->IsPalette)
10498 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10499 quantum_info,GrayQuantum,ping_pixels,exception);
10502 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10503 quantum_info,RedQuantum,ping_pixels,exception);
10505 if (logging != MagickFalse && y == 0)
10506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10507 " Writing GRAY PNG pixels (2)");
10510 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10512 if (logging != MagickFalse && y == 0)
10513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514 " Writing GRAY_ALPHA PNG pixels (2)");
10516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10517 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10520 if (logging != MagickFalse && y == 0)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Writing row of pixels (2)");
10524 png_write_row(ping,ping_pixels);
10527 if (image->previous == (Image *) NULL)
10529 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10530 if (status == MagickFalse)
10538 register const Quantum
10541 for (pass=0; pass < num_passes; pass++)
10543 if ((image_depth > 8) || (mng_info->write_png24 ||
10544 mng_info->write_png32 ||
10545 (!mng_info->write_png8 && !mng_info->IsPalette)))
10547 for (y=0; y < (ssize_t) image->rows; y++)
10549 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10551 if (p == (const Quantum *) NULL)
10554 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10556 if (image->storage_class == DirectClass)
10557 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10558 quantum_info,RedQuantum,ping_pixels,exception);
10561 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10562 quantum_info,GrayQuantum,ping_pixels,exception);
10565 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10567 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10568 quantum_info,GrayAlphaQuantum,ping_pixels,
10571 if (logging != MagickFalse && y == 0)
10572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10573 " Writing GRAY_ALPHA PNG pixels (3)");
10576 else if (image_matte != MagickFalse)
10577 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10578 quantum_info,RGBAQuantum,ping_pixels,exception);
10581 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10582 quantum_info,RGBQuantum,ping_pixels,exception);
10584 if (logging != MagickFalse && y == 0)
10585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586 " Writing row of pixels (3)");
10588 png_write_row(ping,ping_pixels);
10593 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10594 mng_info->write_png32 ||
10595 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10597 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10598 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10600 if (logging != MagickFalse)
10601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10604 quantum_info->depth=8;
10608 for (y=0; y < (ssize_t) image->rows; y++)
10610 if (logging != MagickFalse && y == 0)
10611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10612 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10614 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10616 if (p == (const Quantum *) NULL)
10619 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10621 quantum_info->depth=image->depth;
10623 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10624 quantum_info,GrayQuantum,ping_pixels,exception);
10627 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10629 if (logging != MagickFalse && y == 0)
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " Writing GRAY_ALPHA PNG pixels (4)");
10633 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10634 quantum_info,GrayAlphaQuantum,ping_pixels,
10640 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10641 quantum_info,IndexQuantum,ping_pixels,exception);
10643 if (logging != MagickFalse && y <= 2)
10645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646 " Writing row of non-gray pixels (4)");
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10650 (int)ping_pixels[0],(int)ping_pixels[1]);
10653 png_write_row(ping,ping_pixels);
10657 if (image->previous == (Image *) NULL)
10659 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10660 if (status == MagickFalse)
10667 if (quantum_info != (QuantumInfo *) NULL)
10668 quantum_info=DestroyQuantumInfo(quantum_info);
10670 if (logging != MagickFalse)
10672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10673 " Wrote PNG image data");
10675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10676 " Width: %.20g",(double) ping_width);
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " Height: %.20g",(double) ping_height);
10681 if (mng_info->write_png_depth)
10683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " PNG bit-depth written: %d",ping_bit_depth);
10690 if (mng_info->write_png_colortype)
10692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10693 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10697 " PNG color-type written: %d",ping_color_type);
10699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700 " PNG Interlace method: %d",ping_interlace_method);
10703 Generate text chunks after IDAT.
10705 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10707 ResetImagePropertyIterator(image);
10708 property=GetNextImageProperty(image);
10709 while (property != (const char *) NULL)
10714 value=GetImageProperty(image,property,exception);
10716 /* Don't write any "png:" properties; those are just for "identify" */
10717 if (LocaleNCompare(property,"png:",4) != 0 &&
10719 /* Suppress density and units if we wrote a pHYs chunk */
10720 (ping_exclude_pHYs != MagickFalse ||
10721 LocaleCompare(property,"density") != 0 ||
10722 LocaleCompare(property,"units") != 0) &&
10724 /* Suppress the IM-generated Date:create and Date:modify */
10725 (ping_exclude_date == MagickFalse ||
10726 LocaleNCompare(property, "Date:",5) != 0))
10728 if (value != (const char *) NULL)
10730 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10731 text[0].key=(char *) property;
10732 text[0].text=(char *) value;
10733 text[0].text_length=strlen(value);
10735 if (ping_exclude_tEXt != MagickFalse)
10736 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10738 else if (ping_exclude_zTXt != MagickFalse)
10739 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10743 text[0].compression=image_info->compression == NoCompression ||
10744 (image_info->compression == UndefinedCompression &&
10745 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10746 PNG_TEXT_COMPRESSION_zTXt ;
10749 if (logging != MagickFalse)
10751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10752 " Setting up text chunk");
10754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10755 " keyword: %s",text[0].key);
10758 png_set_text(ping,ping_info,text,1);
10759 png_free(ping,text);
10762 property=GetNextImageProperty(image);
10766 /* write any PNG-chunk-e profiles */
10767 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10769 if (logging != MagickFalse)
10770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771 " Writing PNG end info");
10773 png_write_end(ping,ping_info);
10775 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10777 if (mng_info->page.x || mng_info->page.y ||
10778 (ping_width != mng_info->page.width) ||
10779 (ping_height != mng_info->page.height))
10785 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10787 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10788 PNGType(chunk,mng_FRAM);
10789 LogPNGChunk(logging,mng_FRAM,27L);
10791 chunk[5]=0; /* frame name separator (no name) */
10792 chunk[6]=1; /* flag for changing delay, for next frame only */
10793 chunk[7]=0; /* flag for changing frame timeout */
10794 chunk[8]=1; /* flag for changing frame clipping for next frame */
10795 chunk[9]=0; /* flag for changing frame sync_id */
10796 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10797 chunk[14]=0; /* clipping boundaries delta type */
10798 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10800 (png_uint_32) (mng_info->page.x + ping_width));
10801 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10803 (png_uint_32) (mng_info->page.y + ping_height));
10804 (void) WriteBlob(image,31,chunk);
10805 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10806 mng_info->old_framing_mode=4;
10807 mng_info->framing_mode=1;
10811 mng_info->framing_mode=3;
10813 if (mng_info->write_mng && !mng_info->need_fram &&
10814 ((int) image->dispose == 3))
10815 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10818 Free PNG resources.
10821 png_destroy_write_struct(&ping,&ping_info);
10823 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10825 if (ping_have_blob != MagickFalse)
10826 (void) CloseBlob(image);
10828 image_info=DestroyImageInfo(image_info);
10829 image=DestroyImage(image);
10831 /* Store bit depth actually written */
10832 s[0]=(char) ping_bit_depth;
10835 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10837 if (logging != MagickFalse)
10838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10839 " exit WriteOnePNGImage()");
10841 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10842 UnlockSemaphoreInfo(ping_semaphore);
10845 /* } for navigation to beginning of SETJMP-protected block. Revert to
10846 * Throwing an Exception when an error occurs.
10849 return(MagickTrue);
10850 /* End write one PNG image */
10855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10859 % W r i t e P N G I m a g e %
10863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10865 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10866 % Multiple-image Network Graphics (MNG) image file.
10868 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10870 % The format of the WritePNGImage method is:
10872 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10873 % Image *image,ExceptionInfo *exception)
10875 % A description of each parameter follows:
10877 % o image_info: the image info.
10879 % o image: The image.
10881 % o exception: return any errors or warnings in this structure.
10883 % Returns MagickTrue on success, MagickFalse on failure.
10885 % Communicating with the PNG encoder:
10887 % While the datastream written is always in PNG format and normally would
10888 % be given the "png" file extension, this method also writes the following
10889 % pseudo-formats which are subsets of png:
10891 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10892 % a depth greater than 8, the depth is reduced. If transparency
10893 % is present, the tRNS chunk must only have values 0 and 255
10894 % (i.e., transparency is binary: fully opaque or fully
10895 % transparent). If other values are present they will be
10896 % 50%-thresholded to binary transparency. If more than 256
10897 % colors are present, they will be quantized to the 4-4-4-1,
10898 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10899 % of any resulting fully-transparent pixels is changed to
10900 % the image's background color.
10902 % If you want better quantization or dithering of the colors
10903 % or alpha than that, you need to do it before calling the
10904 % PNG encoder. The pixels contain 8-bit indices even if
10905 % they could be represented with 1, 2, or 4 bits. Grayscale
10906 % images will be written as indexed PNG files even though the
10907 % PNG grayscale type might be slightly more efficient. Please
10908 % note that writing to the PNG8 format may result in loss
10909 % of color and alpha data.
10911 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10912 % chunk can be present to convey binary transparency by naming
10913 % one of the colors as transparent. The only loss incurred
10914 % is reduction of sample depth to 8. If the image has more
10915 % than one transparent color, has semitransparent pixels, or
10916 % has an opaque pixel with the same RGB components as the
10917 % transparent color, an image is not written.
10919 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10920 % transparency is permitted, i.e., the alpha sample for
10921 % each pixel can have any value from 0 to 255. The alpha
10922 % channel is present even if the image is fully opaque.
10923 % The only loss in data is the reduction of the sample depth
10926 % o -define: For more precise control of the PNG output, you can use the
10927 % Image options "png:bit-depth" and "png:color-type". These
10928 % can be set from the commandline with "-define" and also
10929 % from the application programming interfaces. The options
10930 % are case-independent and are converted to lowercase before
10931 % being passed to this encoder.
10933 % png:color-type can be 0, 2, 3, 4, or 6.
10935 % When png:color-type is 0 (Grayscale), png:bit-depth can
10936 % be 1, 2, 4, 8, or 16.
10938 % When png:color-type is 2 (RGB), png:bit-depth can
10941 % When png:color-type is 3 (Indexed), png:bit-depth can
10942 % be 1, 2, 4, or 8. This refers to the number of bits
10943 % used to store the index. The color samples always have
10944 % bit-depth 8 in indexed PNG files.
10946 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10947 % png:bit-depth can be 8 or 16.
10949 % If the image cannot be written without loss with the requested bit-depth
10950 % and color-type, a PNG file will not be written, and the encoder will
10951 % return MagickFalse.
10953 % Since image encoders should not be responsible for the "heavy lifting",
10954 % the user should make sure that ImageMagick has already reduced the
10955 % image depth and number of colors and limit transparency to binary
10956 % transparency prior to attempting to write the image with depth, color,
10957 % or transparency limitations.
10959 % Note that another definition, "png:bit-depth-written" exists, but it
10960 % is not intended for external use. It is only used internally by the
10961 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10963 % It is possible to request that the PNG encoder write previously-formatted
10964 % ancillary chunks in the output PNG file, using the "-profile" commandline
10965 % option as shown below or by setting the profile via a programming
10968 % -profile PNG-chunk-x:<file>
10970 % where x is a location flag and <file> is a file containing the chunk
10971 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10972 % This encoder will compute the chunk length and CRC, so those must not
10973 % be included in the file.
10975 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10976 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10977 % of the same type, then add a short unique string after the "x" to prevent
10978 % subsequent profiles from overwriting the preceding ones, e.g.,
10980 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10982 % As of version 6.6.6 the following optimizations are always done:
10984 % o 32-bit depth is reduced to 16.
10985 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10986 % high byte and low byte are identical.
10987 % o Palette is sorted to remove unused entries and to put a
10988 % transparent color first, if BUILD_PNG_PALETTE is defined.
10989 % o Opaque matte channel is removed when writing an indexed PNG.
10990 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10991 % this can be done without loss and a larger bit depth N was not
10992 % requested via the "-define png:bit-depth=N" option.
10993 % o If matte channel is present but only one transparent color is
10994 % present, RGB+tRNS is written instead of RGBA
10995 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10996 % was requested when converting an opaque image).
10998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11000 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11001 Image *image,ExceptionInfo *exception)
11006 have_mng_structure,
11022 assert(image_info != (const ImageInfo *) NULL);
11023 assert(image_info->signature == MagickSignature);
11024 assert(image != (Image *) NULL);
11025 assert(image->signature == MagickSignature);
11026 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11027 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11029 Allocate a MngInfo structure.
11031 have_mng_structure=MagickFalse;
11032 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11034 if (mng_info == (MngInfo *) NULL)
11035 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11038 Initialize members of the MngInfo structure.
11040 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11041 mng_info->image=image;
11042 mng_info->equal_backgrounds=MagickTrue;
11043 have_mng_structure=MagickTrue;
11045 /* See if user has requested a specific PNG subformat */
11047 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11048 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11049 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11051 value=GetImageOption(image_info,"png:format");
11053 if (value != (char *) NULL)
11055 if (LocaleCompare(value,"png8") == 0)
11057 mng_info->write_png8 = MagickTrue;
11058 mng_info->write_png24 = MagickFalse;
11059 mng_info->write_png32 = MagickFalse;
11062 else if (LocaleCompare(value,"png24") == 0)
11064 mng_info->write_png8 = MagickFalse;
11065 mng_info->write_png24 = MagickTrue;
11066 mng_info->write_png32 = MagickFalse;
11069 else if (LocaleCompare(value,"png32") == 0)
11071 mng_info->write_png8 = MagickFalse;
11072 mng_info->write_png24 = MagickFalse;
11073 mng_info->write_png32 = MagickTrue;
11076 if (mng_info->write_png8)
11078 mng_info->write_png_colortype = /* 3 */ 4;
11079 mng_info->write_png_depth = 8;
11083 if (mng_info->write_png24)
11085 mng_info->write_png_colortype = /* 2 */ 3;
11086 mng_info->write_png_depth = 8;
11089 if (image->matte == MagickTrue)
11090 (void) SetImageType(image,TrueColorMatteType,exception);
11093 (void) SetImageType(image,TrueColorType,exception);
11095 (void) SyncImage(image,exception);
11098 if (mng_info->write_png32)
11100 mng_info->write_png_colortype = /* 6 */ 7;
11101 mng_info->write_png_depth = 8;
11104 if (image->matte == MagickTrue)
11105 (void) SetImageType(image,TrueColorMatteType,exception);
11108 (void) SetImageType(image,TrueColorType,exception);
11110 (void) SyncImage(image,exception);
11113 value=GetImageOption(image_info,"png:bit-depth");
11115 if (value != (char *) NULL)
11117 if (LocaleCompare(value,"1") == 0)
11118 mng_info->write_png_depth = 1;
11120 else if (LocaleCompare(value,"2") == 0)
11121 mng_info->write_png_depth = 2;
11123 else if (LocaleCompare(value,"4") == 0)
11124 mng_info->write_png_depth = 4;
11126 else if (LocaleCompare(value,"8") == 0)
11127 mng_info->write_png_depth = 8;
11129 else if (LocaleCompare(value,"16") == 0)
11130 mng_info->write_png_depth = 16;
11133 (void) ThrowMagickException(exception,
11134 GetMagickModule(),CoderWarning,
11135 "ignoring invalid defined png:bit-depth",
11138 if (logging != MagickFalse)
11139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11140 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11143 value=GetImageOption(image_info,"png:color-type");
11145 if (value != (char *) NULL)
11147 /* We must store colortype+1 because 0 is a valid colortype */
11148 if (LocaleCompare(value,"0") == 0)
11149 mng_info->write_png_colortype = 1;
11151 else if (LocaleCompare(value,"1") == 0)
11152 mng_info->write_png_colortype = 2;
11154 else if (LocaleCompare(value,"2") == 0)
11155 mng_info->write_png_colortype = 3;
11157 else if (LocaleCompare(value,"3") == 0)
11158 mng_info->write_png_colortype = 4;
11160 else if (LocaleCompare(value,"4") == 0)
11161 mng_info->write_png_colortype = 5;
11163 else if (LocaleCompare(value,"6") == 0)
11164 mng_info->write_png_colortype = 7;
11167 (void) ThrowMagickException(exception,
11168 GetMagickModule(),CoderWarning,
11169 "ignoring invalid defined png:color-type",
11172 if (logging != MagickFalse)
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11177 /* Check for chunks to be excluded:
11179 * The default is to not exclude any known chunks except for any
11180 * listed in the "unused_chunks" array, above.
11182 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11183 * define (in the image properties or in the image artifacts)
11184 * or via a mng_info member. For convenience, in addition
11185 * to or instead of a comma-separated list of chunks, the
11186 * "exclude-chunk" string can be simply "all" or "none".
11188 * The exclude-chunk define takes priority over the mng_info.
11190 * A "png:include-chunk" define takes priority over both the
11191 * mng_info and the "png:exclude-chunk" define. Like the
11192 * "exclude-chunk" string, it can define "all" or "none" as
11193 * well as a comma-separated list. Chunks that are unknown to
11194 * ImageMagick are always excluded, regardless of their "copy-safe"
11195 * status according to the PNG specification, and even if they
11196 * appear in the "include-chunk" list. Such defines appearing among
11197 * the image options take priority over those found among the image
11200 * Finally, all chunks listed in the "unused_chunks" array are
11201 * automatically excluded, regardless of the other instructions
11204 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11205 * will not be written and the gAMA chunk will only be written if it
11206 * is not between .45 and .46, or approximately (1.0/2.2).
11208 * If you exclude tRNS and the image has transparency, the colortype
11209 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11211 * The -strip option causes StripImage() to set the png:include-chunk
11212 * artifact to "none,trns,gama".
11215 mng_info->ping_exclude_bKGD=MagickFalse;
11216 mng_info->ping_exclude_cHRM=MagickFalse;
11217 mng_info->ping_exclude_date=MagickFalse;
11218 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11219 mng_info->ping_exclude_gAMA=MagickFalse;
11220 mng_info->ping_exclude_iCCP=MagickFalse;
11221 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11222 mng_info->ping_exclude_oFFs=MagickFalse;
11223 mng_info->ping_exclude_pHYs=MagickFalse;
11224 mng_info->ping_exclude_sRGB=MagickFalse;
11225 mng_info->ping_exclude_tEXt=MagickFalse;
11226 mng_info->ping_exclude_tRNS=MagickFalse;
11227 mng_info->ping_exclude_vpAg=MagickFalse;
11228 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11229 mng_info->ping_exclude_zTXt=MagickFalse;
11231 mng_info->ping_preserve_colormap=MagickFalse;
11233 value=GetImageArtifact(image,"png:preserve-colormap");
11235 value=GetImageOption(image_info,"png:preserve-colormap");
11237 mng_info->ping_preserve_colormap=MagickTrue;
11239 /* Thes compression-level, compression-strategy, and compression-filter
11240 * defines take precedence over values from the -quality option.
11242 value=GetImageArtifact(image,"png:compression-level");
11244 value=GetImageOption(image_info,"png:compression-level");
11247 /* We have to add 1 to everything because 0 is a valid input,
11248 * and we want to use 0 (the default) to mean undefined.
11250 if (LocaleCompare(value,"0") == 0)
11251 mng_info->write_png_compression_level = 1;
11253 else if (LocaleCompare(value,"1") == 0)
11254 mng_info->write_png_compression_level = 2;
11256 else if (LocaleCompare(value,"2") == 0)
11257 mng_info->write_png_compression_level = 3;
11259 else if (LocaleCompare(value,"3") == 0)
11260 mng_info->write_png_compression_level = 4;
11262 else if (LocaleCompare(value,"4") == 0)
11263 mng_info->write_png_compression_level = 5;
11265 else if (LocaleCompare(value,"5") == 0)
11266 mng_info->write_png_compression_level = 6;
11268 else if (LocaleCompare(value,"6") == 0)
11269 mng_info->write_png_compression_level = 7;
11271 else if (LocaleCompare(value,"7") == 0)
11272 mng_info->write_png_compression_level = 8;
11274 else if (LocaleCompare(value,"8") == 0)
11275 mng_info->write_png_compression_level = 9;
11277 else if (LocaleCompare(value,"9") == 0)
11278 mng_info->write_png_compression_level = 10;
11281 (void) ThrowMagickException(exception,
11282 GetMagickModule(),CoderWarning,
11283 "ignoring invalid defined png:compression-level",
11287 value=GetImageArtifact(image,"png:compression-strategy");
11289 value=GetImageOption(image_info,"png:compression-strategy");
11293 if (LocaleCompare(value,"0") == 0)
11294 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11296 else if (LocaleCompare(value,"1") == 0)
11297 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11299 else if (LocaleCompare(value,"2") == 0)
11300 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11302 else if (LocaleCompare(value,"3") == 0)
11303 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11304 mng_info->write_png_compression_strategy = Z_RLE+1;
11306 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11309 else if (LocaleCompare(value,"4") == 0)
11310 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11311 mng_info->write_png_compression_strategy = Z_FIXED+1;
11313 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11317 (void) ThrowMagickException(exception,
11318 GetMagickModule(),CoderWarning,
11319 "ignoring invalid defined png:compression-strategy",
11323 value=GetImageArtifact(image,"png:compression-filter");
11325 value=GetImageOption(image_info,"png:compression-filter");
11329 /* To do: combinations of filters allowed by libpng
11330 * masks 0x08 through 0xf8
11332 * Implement this as a comma-separated list of 0,1,2,3,4,5
11333 * where 5 is a special case meaning PNG_ALL_FILTERS.
11336 if (LocaleCompare(value,"0") == 0)
11337 mng_info->write_png_compression_filter = 1;
11339 if (LocaleCompare(value,"1") == 0)
11340 mng_info->write_png_compression_filter = 2;
11342 else if (LocaleCompare(value,"2") == 0)
11343 mng_info->write_png_compression_filter = 3;
11345 else if (LocaleCompare(value,"3") == 0)
11346 mng_info->write_png_compression_filter = 4;
11348 else if (LocaleCompare(value,"4") == 0)
11349 mng_info->write_png_compression_filter = 5;
11351 else if (LocaleCompare(value,"5") == 0)
11352 mng_info->write_png_compression_filter = 6;
11355 (void) ThrowMagickException(exception,
11356 GetMagickModule(),CoderWarning,
11357 "ignoring invalid defined png:compression-filter",
11361 excluding=MagickFalse;
11363 for (source=0; source<1; source++)
11367 value=GetImageArtifact(image,"png:exclude-chunk");
11370 value=GetImageArtifact(image,"png:exclude-chunks");
11374 value=GetImageOption(image_info,"png:exclude-chunk");
11377 value=GetImageOption(image_info,"png:exclude-chunks");
11386 excluding=MagickTrue;
11388 if (logging != MagickFalse)
11391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11392 " png:exclude-chunk=%s found in image artifacts.\n", value);
11394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11395 " png:exclude-chunk=%s found in image properties.\n", value);
11398 last=strlen(value);
11400 for (i=0; i<(int) last; i+=5)
11403 if (LocaleNCompare(value+i,"all",3) == 0)
11405 mng_info->ping_exclude_bKGD=MagickTrue;
11406 mng_info->ping_exclude_cHRM=MagickTrue;
11407 mng_info->ping_exclude_date=MagickTrue;
11408 mng_info->ping_exclude_EXIF=MagickTrue;
11409 mng_info->ping_exclude_gAMA=MagickTrue;
11410 mng_info->ping_exclude_iCCP=MagickTrue;
11411 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11412 mng_info->ping_exclude_oFFs=MagickTrue;
11413 mng_info->ping_exclude_pHYs=MagickTrue;
11414 mng_info->ping_exclude_sRGB=MagickTrue;
11415 mng_info->ping_exclude_tEXt=MagickTrue;
11416 mng_info->ping_exclude_tRNS=MagickTrue;
11417 mng_info->ping_exclude_vpAg=MagickTrue;
11418 mng_info->ping_exclude_zCCP=MagickTrue;
11419 mng_info->ping_exclude_zTXt=MagickTrue;
11423 if (LocaleNCompare(value+i,"none",4) == 0)
11425 mng_info->ping_exclude_bKGD=MagickFalse;
11426 mng_info->ping_exclude_cHRM=MagickFalse;
11427 mng_info->ping_exclude_date=MagickFalse;
11428 mng_info->ping_exclude_EXIF=MagickFalse;
11429 mng_info->ping_exclude_gAMA=MagickFalse;
11430 mng_info->ping_exclude_iCCP=MagickFalse;
11431 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11432 mng_info->ping_exclude_oFFs=MagickFalse;
11433 mng_info->ping_exclude_pHYs=MagickFalse;
11434 mng_info->ping_exclude_sRGB=MagickFalse;
11435 mng_info->ping_exclude_tEXt=MagickFalse;
11436 mng_info->ping_exclude_tRNS=MagickFalse;
11437 mng_info->ping_exclude_vpAg=MagickFalse;
11438 mng_info->ping_exclude_zCCP=MagickFalse;
11439 mng_info->ping_exclude_zTXt=MagickFalse;
11442 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11443 mng_info->ping_exclude_bKGD=MagickTrue;
11445 if (LocaleNCompare(value+i,"chrm",4) == 0)
11446 mng_info->ping_exclude_cHRM=MagickTrue;
11448 if (LocaleNCompare(value+i,"date",4) == 0)
11449 mng_info->ping_exclude_date=MagickTrue;
11451 if (LocaleNCompare(value+i,"exif",4) == 0)
11452 mng_info->ping_exclude_EXIF=MagickTrue;
11454 if (LocaleNCompare(value+i,"gama",4) == 0)
11455 mng_info->ping_exclude_gAMA=MagickTrue;
11457 if (LocaleNCompare(value+i,"iccp",4) == 0)
11458 mng_info->ping_exclude_iCCP=MagickTrue;
11461 if (LocaleNCompare(value+i,"itxt",4) == 0)
11462 mng_info->ping_exclude_iTXt=MagickTrue;
11465 if (LocaleNCompare(value+i,"gama",4) == 0)
11466 mng_info->ping_exclude_gAMA=MagickTrue;
11468 if (LocaleNCompare(value+i,"offs",4) == 0)
11469 mng_info->ping_exclude_oFFs=MagickTrue;
11471 if (LocaleNCompare(value+i,"phys",4) == 0)
11472 mng_info->ping_exclude_pHYs=MagickTrue;
11474 if (LocaleNCompare(value+i,"srgb",4) == 0)
11475 mng_info->ping_exclude_sRGB=MagickTrue;
11477 if (LocaleNCompare(value+i,"text",4) == 0)
11478 mng_info->ping_exclude_tEXt=MagickTrue;
11480 if (LocaleNCompare(value+i,"trns",4) == 0)
11481 mng_info->ping_exclude_tRNS=MagickTrue;
11483 if (LocaleNCompare(value+i,"vpag",4) == 0)
11484 mng_info->ping_exclude_vpAg=MagickTrue;
11486 if (LocaleNCompare(value+i,"zccp",4) == 0)
11487 mng_info->ping_exclude_zCCP=MagickTrue;
11489 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11490 mng_info->ping_exclude_zTXt=MagickTrue;
11496 for (source=0; source<1; source++)
11500 value=GetImageArtifact(image,"png:include-chunk");
11503 value=GetImageArtifact(image,"png:include-chunks");
11507 value=GetImageOption(image_info,"png:include-chunk");
11510 value=GetImageOption(image_info,"png:include-chunks");
11518 excluding=MagickTrue;
11520 if (logging != MagickFalse)
11523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11524 " png:include-chunk=%s found in image artifacts.\n", value);
11526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11527 " png:include-chunk=%s found in image properties.\n", value);
11530 last=strlen(value);
11532 for (i=0; i<(int) last; i+=5)
11534 if (LocaleNCompare(value+i,"all",3) == 0)
11536 mng_info->ping_exclude_bKGD=MagickFalse;
11537 mng_info->ping_exclude_cHRM=MagickFalse;
11538 mng_info->ping_exclude_date=MagickFalse;
11539 mng_info->ping_exclude_EXIF=MagickFalse;
11540 mng_info->ping_exclude_gAMA=MagickFalse;
11541 mng_info->ping_exclude_iCCP=MagickFalse;
11542 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11543 mng_info->ping_exclude_oFFs=MagickFalse;
11544 mng_info->ping_exclude_pHYs=MagickFalse;
11545 mng_info->ping_exclude_sRGB=MagickFalse;
11546 mng_info->ping_exclude_tEXt=MagickFalse;
11547 mng_info->ping_exclude_tRNS=MagickFalse;
11548 mng_info->ping_exclude_vpAg=MagickFalse;
11549 mng_info->ping_exclude_zCCP=MagickFalse;
11550 mng_info->ping_exclude_zTXt=MagickFalse;
11554 if (LocaleNCompare(value+i,"none",4) == 0)
11556 mng_info->ping_exclude_bKGD=MagickTrue;
11557 mng_info->ping_exclude_cHRM=MagickTrue;
11558 mng_info->ping_exclude_date=MagickTrue;
11559 mng_info->ping_exclude_EXIF=MagickTrue;
11560 mng_info->ping_exclude_gAMA=MagickTrue;
11561 mng_info->ping_exclude_iCCP=MagickTrue;
11562 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11563 mng_info->ping_exclude_oFFs=MagickTrue;
11564 mng_info->ping_exclude_pHYs=MagickTrue;
11565 mng_info->ping_exclude_sRGB=MagickTrue;
11566 mng_info->ping_exclude_tEXt=MagickTrue;
11567 mng_info->ping_exclude_tRNS=MagickTrue;
11568 mng_info->ping_exclude_vpAg=MagickTrue;
11569 mng_info->ping_exclude_zCCP=MagickTrue;
11570 mng_info->ping_exclude_zTXt=MagickTrue;
11573 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11574 mng_info->ping_exclude_bKGD=MagickFalse;
11576 if (LocaleNCompare(value+i,"chrm",4) == 0)
11577 mng_info->ping_exclude_cHRM=MagickFalse;
11579 if (LocaleNCompare(value+i,"date",4) == 0)
11580 mng_info->ping_exclude_date=MagickFalse;
11582 if (LocaleNCompare(value+i,"exif",4) == 0)
11583 mng_info->ping_exclude_EXIF=MagickFalse;
11585 if (LocaleNCompare(value+i,"gama",4) == 0)
11586 mng_info->ping_exclude_gAMA=MagickFalse;
11588 if (LocaleNCompare(value+i,"iccp",4) == 0)
11589 mng_info->ping_exclude_iCCP=MagickFalse;
11592 if (LocaleNCompare(value+i,"itxt",4) == 0)
11593 mng_info->ping_exclude_iTXt=MagickFalse;
11596 if (LocaleNCompare(value+i,"gama",4) == 0)
11597 mng_info->ping_exclude_gAMA=MagickFalse;
11599 if (LocaleNCompare(value+i,"offs",4) == 0)
11600 mng_info->ping_exclude_oFFs=MagickFalse;
11602 if (LocaleNCompare(value+i,"phys",4) == 0)
11603 mng_info->ping_exclude_pHYs=MagickFalse;
11605 if (LocaleNCompare(value+i,"srgb",4) == 0)
11606 mng_info->ping_exclude_sRGB=MagickFalse;
11608 if (LocaleNCompare(value+i,"text",4) == 0)
11609 mng_info->ping_exclude_tEXt=MagickFalse;
11611 if (LocaleNCompare(value+i,"trns",4) == 0)
11612 mng_info->ping_exclude_tRNS=MagickFalse;
11614 if (LocaleNCompare(value+i,"vpag",4) == 0)
11615 mng_info->ping_exclude_vpAg=MagickFalse;
11617 if (LocaleNCompare(value+i,"zccp",4) == 0)
11618 mng_info->ping_exclude_zCCP=MagickFalse;
11620 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11621 mng_info->ping_exclude_zTXt=MagickFalse;
11627 if (excluding != MagickFalse && logging != MagickFalse)
11629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630 " Chunks to be excluded from the output png:");
11631 if (mng_info->ping_exclude_bKGD != MagickFalse)
11632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11634 if (mng_info->ping_exclude_cHRM != MagickFalse)
11635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11637 if (mng_info->ping_exclude_date != MagickFalse)
11638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640 if (mng_info->ping_exclude_EXIF != MagickFalse)
11641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11643 if (mng_info->ping_exclude_gAMA != MagickFalse)
11644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11646 if (mng_info->ping_exclude_iCCP != MagickFalse)
11647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11650 if (mng_info->ping_exclude_iTXt != MagickFalse)
11651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11654 if (mng_info->ping_exclude_oFFs != MagickFalse)
11655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11657 if (mng_info->ping_exclude_pHYs != MagickFalse)
11658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11660 if (mng_info->ping_exclude_sRGB != MagickFalse)
11661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11663 if (mng_info->ping_exclude_tEXt != MagickFalse)
11664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11666 if (mng_info->ping_exclude_tRNS != MagickFalse)
11667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11669 if (mng_info->ping_exclude_vpAg != MagickFalse)
11670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672 if (mng_info->ping_exclude_zCCP != MagickFalse)
11673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675 if (mng_info->ping_exclude_zTXt != MagickFalse)
11676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11680 mng_info->need_blob = MagickTrue;
11682 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11684 MngInfoFreeStruct(mng_info,&have_mng_structure);
11686 if (logging != MagickFalse)
11687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11692 #if defined(JNG_SUPPORTED)
11694 /* Write one JNG image */
11695 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11696 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11717 jng_alpha_compression_method,
11718 jng_alpha_sample_depth,
11726 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11727 " Enter WriteOneJNGImage()");
11729 blob=(unsigned char *) NULL;
11730 jpeg_image=(Image *) NULL;
11731 jpeg_image_info=(ImageInfo *) NULL;
11734 transparent=image_info->type==GrayscaleMatteType ||
11735 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
11737 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11739 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11741 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11742 image_info->quality;
11744 if (jng_alpha_quality >= 1000)
11745 jng_alpha_quality /= 1000;
11751 /* Create JPEG blob, image, and image_info */
11752 if (logging != MagickFalse)
11753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11754 " Creating jpeg_image_info for alpha.");
11756 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11758 if (jpeg_image_info == (ImageInfo *) NULL)
11759 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11761 if (logging != MagickFalse)
11762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11763 " Creating jpeg_image.");
11765 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11766 if (jpeg_image == (Image *) NULL)
11767 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11768 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11769 jpeg_image->matte=MagickFalse;
11770 jpeg_image->quality=jng_alpha_quality;
11771 jpeg_image_info->type=GrayscaleType;
11772 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11773 (void) AcquireUniqueFilename(jpeg_image->filename);
11774 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11775 "%s",jpeg_image->filename);
11779 jng_alpha_compression_method=0;
11781 jng_alpha_sample_depth=0;
11784 /* To do: check bit depth of PNG alpha channel */
11786 /* Check if image is grayscale. */
11787 if (image_info->type != TrueColorMatteType && image_info->type !=
11788 TrueColorType && ImageIsGray(image,exception))
11791 if (logging != MagickFalse)
11793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11794 " JNG Quality = %d",(int) jng_quality);
11795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11796 " JNG Color Type = %d",jng_color_type);
11799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11800 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11802 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11810 if (jng_alpha_compression_method==0)
11815 /* Encode alpha as a grayscale PNG blob */
11816 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11818 if (logging != MagickFalse)
11819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11820 " Creating PNG blob.");
11823 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11824 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11825 jpeg_image_info->interlace=NoInterlace;
11827 /* Exclude all ancillary chunks */
11828 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11830 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11833 /* Retrieve sample depth used */
11834 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11835 if (value != (char *) NULL)
11836 jng_alpha_sample_depth= (unsigned int) value[0];
11840 /* Encode alpha as a grayscale JPEG blob */
11842 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11845 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11846 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11847 jpeg_image_info->interlace=NoInterlace;
11848 if (logging != MagickFalse)
11849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11850 " Creating blob.");
11851 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11853 jng_alpha_sample_depth=8;
11855 if (logging != MagickFalse)
11856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11857 " Successfully read jpeg_image into a blob, length=%.20g.",
11861 /* Destroy JPEG image and image_info */
11862 jpeg_image=DestroyImage(jpeg_image);
11863 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11864 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11867 /* Write JHDR chunk */
11868 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11869 PNGType(chunk,mng_JHDR);
11870 LogPNGChunk(logging,mng_JHDR,16L);
11871 PNGLong(chunk+4,(png_uint_32) image->columns);
11872 PNGLong(chunk+8,(png_uint_32) image->rows);
11873 chunk[12]=jng_color_type;
11874 chunk[13]=8; /* sample depth */
11875 chunk[14]=8; /*jng_image_compression_method */
11876 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11877 chunk[16]=jng_alpha_sample_depth;
11878 chunk[17]=jng_alpha_compression_method;
11879 chunk[18]=0; /*jng_alpha_filter_method */
11880 chunk[19]=0; /*jng_alpha_interlace_method */
11881 (void) WriteBlob(image,20,chunk);
11882 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11883 if (logging != MagickFalse)
11885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11886 " JNG width:%15lu",(unsigned long) image->columns);
11888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11889 " JNG height:%14lu",(unsigned long) image->rows);
11891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11892 " JNG color type:%10d",jng_color_type);
11894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11895 " JNG sample depth:%8d",8);
11897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11898 " JNG compression:%9d",8);
11900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11901 " JNG interlace:%11d",0);
11903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11904 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11907 " JNG alpha compression:%3d",jng_alpha_compression_method);
11909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11910 " JNG alpha filter:%8d",0);
11912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11913 " JNG alpha interlace:%5d",0);
11916 /* Write any JNG-chunk-b profiles */
11917 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11920 Write leading ancillary chunks
11926 Write JNG bKGD chunk
11937 if (jng_color_type == 8 || jng_color_type == 12)
11941 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11942 PNGType(chunk,mng_bKGD);
11943 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11944 red=ScaleQuantumToChar(image->background_color.red);
11945 green=ScaleQuantumToChar(image->background_color.green);
11946 blue=ScaleQuantumToChar(image->background_color.blue);
11953 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11954 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11957 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11960 Write JNG sRGB chunk
11962 (void) WriteBlobMSBULong(image,1L);
11963 PNGType(chunk,mng_sRGB);
11964 LogPNGChunk(logging,mng_sRGB,1L);
11966 if (image->rendering_intent != UndefinedIntent)
11967 chunk[4]=(unsigned char)
11968 Magick_RenderingIntent_to_PNG_RenderingIntent(
11969 (image->rendering_intent));
11972 chunk[4]=(unsigned char)
11973 Magick_RenderingIntent_to_PNG_RenderingIntent(
11974 (PerceptualIntent));
11976 (void) WriteBlob(image,5,chunk);
11977 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11981 if (image->gamma != 0.0)
11984 Write JNG gAMA chunk
11986 (void) WriteBlobMSBULong(image,4L);
11987 PNGType(chunk,mng_gAMA);
11988 LogPNGChunk(logging,mng_gAMA,4L);
11989 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11990 (void) WriteBlob(image,8,chunk);
11991 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11994 if ((mng_info->equal_chrms == MagickFalse) &&
11995 (image->chromaticity.red_primary.x != 0.0))
12001 Write JNG cHRM chunk
12003 (void) WriteBlobMSBULong(image,32L);
12004 PNGType(chunk,mng_cHRM);
12005 LogPNGChunk(logging,mng_cHRM,32L);
12006 primary=image->chromaticity.white_point;
12007 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12008 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12009 primary=image->chromaticity.red_primary;
12010 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12011 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12012 primary=image->chromaticity.green_primary;
12013 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12014 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12015 primary=image->chromaticity.blue_primary;
12016 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12017 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12018 (void) WriteBlob(image,36,chunk);
12019 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12023 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12026 Write JNG pHYs chunk
12028 (void) WriteBlobMSBULong(image,9L);
12029 PNGType(chunk,mng_pHYs);
12030 LogPNGChunk(logging,mng_pHYs,9L);
12031 if (image->units == PixelsPerInchResolution)
12033 PNGLong(chunk+4,(png_uint_32)
12034 (image->resolution.x*100.0/2.54+0.5));
12036 PNGLong(chunk+8,(png_uint_32)
12037 (image->resolution.y*100.0/2.54+0.5));
12044 if (image->units == PixelsPerCentimeterResolution)
12046 PNGLong(chunk+4,(png_uint_32)
12047 (image->resolution.x*100.0+0.5));
12049 PNGLong(chunk+8,(png_uint_32)
12050 (image->resolution.y*100.0+0.5));
12057 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12058 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12062 (void) WriteBlob(image,13,chunk);
12063 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12066 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12069 Write JNG oFFs chunk
12071 (void) WriteBlobMSBULong(image,9L);
12072 PNGType(chunk,mng_oFFs);
12073 LogPNGChunk(logging,mng_oFFs,9L);
12074 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12075 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12077 (void) WriteBlob(image,13,chunk);
12078 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12080 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12082 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12083 PNGType(chunk,mng_vpAg);
12084 LogPNGChunk(logging,mng_vpAg,9L);
12085 PNGLong(chunk+4,(png_uint_32) image->page.width);
12086 PNGLong(chunk+8,(png_uint_32) image->page.height);
12087 chunk[12]=0; /* unit = pixels */
12088 (void) WriteBlob(image,13,chunk);
12089 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12095 if (jng_alpha_compression_method==0)
12103 /* Write IDAT chunk header */
12104 if (logging != MagickFalse)
12105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12106 " Write IDAT chunks from blob, length=%.20g.",(double)
12109 /* Copy IDAT chunks */
12112 for (i=8; i<(ssize_t) length; i+=len+12)
12114 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12117 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12119 /* Found an IDAT chunk. */
12120 (void) WriteBlobMSBULong(image,(size_t) len);
12121 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12122 (void) WriteBlob(image,(size_t) len+4,p);
12123 (void) WriteBlobMSBULong(image,
12124 crc32(0,p,(uInt) len+4));
12129 if (logging != MagickFalse)
12130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12131 " Skipping %c%c%c%c chunk, length=%.20g.",
12132 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12139 /* Write JDAA chunk header */
12140 if (logging != MagickFalse)
12141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12142 " Write JDAA chunk, length=%.20g.",(double) length);
12143 (void) WriteBlobMSBULong(image,(size_t) length);
12144 PNGType(chunk,mng_JDAA);
12145 LogPNGChunk(logging,mng_JDAA,length);
12146 /* Write JDAT chunk(s) data */
12147 (void) WriteBlob(image,4,chunk);
12148 (void) WriteBlob(image,length,blob);
12149 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12152 blob=(unsigned char *) RelinquishMagickMemory(blob);
12155 /* Encode image as a JPEG blob */
12156 if (logging != MagickFalse)
12157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12158 " Creating jpeg_image_info.");
12159 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12160 if (jpeg_image_info == (ImageInfo *) NULL)
12161 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12163 if (logging != MagickFalse)
12164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12165 " Creating jpeg_image.");
12167 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12168 if (jpeg_image == (Image *) NULL)
12169 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12170 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12172 (void) AcquireUniqueFilename(jpeg_image->filename);
12173 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12174 jpeg_image->filename);
12176 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12179 if (logging != MagickFalse)
12180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12181 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12182 (double) jpeg_image->rows);
12184 if (jng_color_type == 8 || jng_color_type == 12)
12185 jpeg_image_info->type=GrayscaleType;
12187 jpeg_image_info->quality=jng_quality;
12188 jpeg_image->quality=jng_quality;
12189 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12190 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12192 if (logging != MagickFalse)
12193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12194 " Creating blob.");
12196 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12198 if (logging != MagickFalse)
12200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12201 " Successfully read jpeg_image into a blob, length=%.20g.",
12204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12205 " Write JDAT chunk, length=%.20g.",(double) length);
12208 /* Write JDAT chunk(s) */
12209 (void) WriteBlobMSBULong(image,(size_t) length);
12210 PNGType(chunk,mng_JDAT);
12211 LogPNGChunk(logging,mng_JDAT,length);
12212 (void) WriteBlob(image,4,chunk);
12213 (void) WriteBlob(image,length,blob);
12214 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12216 jpeg_image=DestroyImage(jpeg_image);
12217 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12218 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12219 blob=(unsigned char *) RelinquishMagickMemory(blob);
12221 /* Write any JNG-chunk-e profiles */
12222 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12224 /* Write IEND chunk */
12225 (void) WriteBlobMSBULong(image,0L);
12226 PNGType(chunk,mng_IEND);
12227 LogPNGChunk(logging,mng_IEND,0);
12228 (void) WriteBlob(image,4,chunk);
12229 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12231 if (logging != MagickFalse)
12232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12233 " exit WriteOneJNGImage()");
12240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12244 % W r i t e J N G I m a g e %
12248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12250 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12252 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12254 % The format of the WriteJNGImage method is:
12256 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12257 % Image *image,ExceptionInfo *exception)
12259 % A description of each parameter follows:
12261 % o image_info: the image info.
12263 % o image: The image.
12265 % o exception: return any errors or warnings in this structure.
12267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12269 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12270 ExceptionInfo *exception)
12273 have_mng_structure,
12283 assert(image_info != (const ImageInfo *) NULL);
12284 assert(image_info->signature == MagickSignature);
12285 assert(image != (Image *) NULL);
12286 assert(image->signature == MagickSignature);
12287 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12288 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12289 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12290 if (status == MagickFalse)
12294 Allocate a MngInfo structure.
12296 have_mng_structure=MagickFalse;
12297 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12298 if (mng_info == (MngInfo *) NULL)
12299 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12301 Initialize members of the MngInfo structure.
12303 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12304 mng_info->image=image;
12305 have_mng_structure=MagickTrue;
12307 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12309 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12310 (void) CloseBlob(image);
12312 (void) CatchImageException(image);
12313 MngInfoFreeStruct(mng_info,&have_mng_structure);
12314 if (logging != MagickFalse)
12315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12320 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12321 ExceptionInfo *exception)
12330 have_mng_structure,
12333 volatile MagickBooleanType
12345 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12346 defined(PNG_MNG_FEATURES_SUPPORTED)
12349 all_images_are_gray,
12359 volatile unsigned int
12370 #if (PNG_LIBPNG_VER < 10200)
12371 if (image_info->verbose)
12372 printf("Your PNG library (libpng-%s) is rather old.\n",
12373 PNG_LIBPNG_VER_STRING);
12379 assert(image_info != (const ImageInfo *) NULL);
12380 assert(image_info->signature == MagickSignature);
12381 assert(image != (Image *) NULL);
12382 assert(image->signature == MagickSignature);
12383 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12384 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12385 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12386 if (status == MagickFalse)
12390 Allocate a MngInfo structure.
12392 have_mng_structure=MagickFalse;
12393 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12394 if (mng_info == (MngInfo *) NULL)
12395 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12397 Initialize members of the MngInfo structure.
12399 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12400 mng_info->image=image;
12401 have_mng_structure=MagickTrue;
12402 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12405 * See if user has requested a specific PNG subformat to be used
12406 * for all of the PNGs in the MNG being written, e.g.,
12408 * convert *.png png8:animation.mng
12410 * To do: check -define png:bit_depth and png:color_type as well,
12411 * or perhaps use mng:bit_depth and mng:color_type instead for
12415 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12416 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12417 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12419 write_jng=MagickFalse;
12420 if (image_info->compression == JPEGCompression)
12421 write_jng=MagickTrue;
12423 mng_info->adjoin=image_info->adjoin &&
12424 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12426 if (logging != MagickFalse)
12428 /* Log some info about the input */
12432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12433 " Checking input image(s)");
12435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12436 " Image_info depth: %.20g",(double) image_info->depth);
12438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12439 " Type: %d",image_info->type);
12442 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12445 " Scene: %.20g",(double) scene++);
12447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12448 " Image depth: %.20g",(double) p->depth);
12451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12458 if (p->storage_class == PseudoClass)
12459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12460 " Storage class: PseudoClass");
12463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12464 " Storage class: DirectClass");
12467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12468 " Number of colors: %.20g",(double) p->colors);
12471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12472 " Number of colors: unspecified");
12474 if (mng_info->adjoin == MagickFalse)
12479 use_global_plte=MagickFalse;
12480 all_images_are_gray=MagickFalse;
12481 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12482 need_local_plte=MagickTrue;
12484 need_defi=MagickFalse;
12485 need_matte=MagickFalse;
12486 mng_info->framing_mode=1;
12487 mng_info->old_framing_mode=1;
12490 if (image_info->page != (char *) NULL)
12493 Determine image bounding box.
12495 SetGeometry(image,&mng_info->page);
12496 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12497 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12509 mng_info->page=image->page;
12510 need_geom=MagickTrue;
12511 if (mng_info->page.width || mng_info->page.height)
12512 need_geom=MagickFalse;
12514 Check all the scenes.
12516 initial_delay=image->delay;
12517 need_iterations=MagickFalse;
12518 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12519 mng_info->equal_physs=MagickTrue,
12520 mng_info->equal_gammas=MagickTrue;
12521 mng_info->equal_srgbs=MagickTrue;
12522 mng_info->equal_backgrounds=MagickTrue;
12524 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12525 defined(PNG_MNG_FEATURES_SUPPORTED)
12526 all_images_are_gray=MagickTrue;
12527 mng_info->equal_palettes=MagickFalse;
12528 need_local_plte=MagickFalse;
12530 for (next_image=image; next_image != (Image *) NULL; )
12534 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12535 mng_info->page.width=next_image->columns+next_image->page.x;
12537 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12538 mng_info->page.height=next_image->rows+next_image->page.y;
12541 if (next_image->page.x || next_image->page.y)
12542 need_defi=MagickTrue;
12544 if (next_image->matte)
12545 need_matte=MagickTrue;
12547 if ((int) next_image->dispose >= BackgroundDispose)
12548 if (next_image->matte || next_image->page.x || next_image->page.y ||
12549 ((next_image->columns < mng_info->page.width) &&
12550 (next_image->rows < mng_info->page.height)))
12551 mng_info->need_fram=MagickTrue;
12553 if (next_image->iterations)
12554 need_iterations=MagickTrue;
12556 final_delay=next_image->delay;
12558 if (final_delay != initial_delay || final_delay > 1UL*
12559 next_image->ticks_per_second)
12560 mng_info->need_fram=1;
12562 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12563 defined(PNG_MNG_FEATURES_SUPPORTED)
12565 check for global palette possibility.
12567 if (image->matte != MagickFalse)
12568 need_local_plte=MagickTrue;
12570 if (need_local_plte == 0)
12572 if (ImageIsGray(image,exception) == MagickFalse)
12573 all_images_are_gray=MagickFalse;
12574 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12575 if (use_global_plte == 0)
12576 use_global_plte=mng_info->equal_palettes;
12577 need_local_plte=!mng_info->equal_palettes;
12580 if (GetNextImageInList(next_image) != (Image *) NULL)
12582 if (next_image->background_color.red !=
12583 next_image->next->background_color.red ||
12584 next_image->background_color.green !=
12585 next_image->next->background_color.green ||
12586 next_image->background_color.blue !=
12587 next_image->next->background_color.blue)
12588 mng_info->equal_backgrounds=MagickFalse;
12590 if (next_image->gamma != next_image->next->gamma)
12591 mng_info->equal_gammas=MagickFalse;
12593 if (next_image->rendering_intent !=
12594 next_image->next->rendering_intent)
12595 mng_info->equal_srgbs=MagickFalse;
12597 if ((next_image->units != next_image->next->units) ||
12598 (next_image->resolution.x != next_image->next->resolution.x) ||
12599 (next_image->resolution.y != next_image->next->resolution.y))
12600 mng_info->equal_physs=MagickFalse;
12602 if (mng_info->equal_chrms)
12604 if (next_image->chromaticity.red_primary.x !=
12605 next_image->next->chromaticity.red_primary.x ||
12606 next_image->chromaticity.red_primary.y !=
12607 next_image->next->chromaticity.red_primary.y ||
12608 next_image->chromaticity.green_primary.x !=
12609 next_image->next->chromaticity.green_primary.x ||
12610 next_image->chromaticity.green_primary.y !=
12611 next_image->next->chromaticity.green_primary.y ||
12612 next_image->chromaticity.blue_primary.x !=
12613 next_image->next->chromaticity.blue_primary.x ||
12614 next_image->chromaticity.blue_primary.y !=
12615 next_image->next->chromaticity.blue_primary.y ||
12616 next_image->chromaticity.white_point.x !=
12617 next_image->next->chromaticity.white_point.x ||
12618 next_image->chromaticity.white_point.y !=
12619 next_image->next->chromaticity.white_point.y)
12620 mng_info->equal_chrms=MagickFalse;
12624 next_image=GetNextImageInList(next_image);
12626 if (image_count < 2)
12628 mng_info->equal_backgrounds=MagickFalse;
12629 mng_info->equal_chrms=MagickFalse;
12630 mng_info->equal_gammas=MagickFalse;
12631 mng_info->equal_srgbs=MagickFalse;
12632 mng_info->equal_physs=MagickFalse;
12633 use_global_plte=MagickFalse;
12634 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12635 need_local_plte=MagickTrue;
12637 need_iterations=MagickFalse;
12640 if (mng_info->need_fram == MagickFalse)
12643 Only certain framing rates 100/n are exactly representable without
12644 the FRAM chunk but we'll allow some slop in VLC files
12646 if (final_delay == 0)
12648 if (need_iterations != MagickFalse)
12651 It's probably a GIF with loop; don't run it *too* fast.
12653 if (mng_info->adjoin)
12656 (void) ThrowMagickException(exception,GetMagickModule(),
12658 "input has zero delay between all frames; assuming",
12663 mng_info->ticks_per_second=0;
12665 if (final_delay != 0)
12666 mng_info->ticks_per_second=(png_uint_32)
12667 (image->ticks_per_second/final_delay);
12668 if (final_delay > 50)
12669 mng_info->ticks_per_second=2;
12671 if (final_delay > 75)
12672 mng_info->ticks_per_second=1;
12674 if (final_delay > 125)
12675 mng_info->need_fram=MagickTrue;
12677 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12678 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12679 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12680 1UL*image->ticks_per_second))
12681 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12684 if (mng_info->need_fram != MagickFalse)
12685 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12687 If pseudocolor, we should also check to see if all the
12688 palettes are identical and write a global PLTE if they are.
12692 Write the MNG version 1.0 signature and MHDR chunk.
12694 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12695 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12696 PNGType(chunk,mng_MHDR);
12697 LogPNGChunk(logging,mng_MHDR,28L);
12698 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12699 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12700 PNGLong(chunk+12,mng_info->ticks_per_second);
12701 PNGLong(chunk+16,0L); /* layer count=unknown */
12702 PNGLong(chunk+20,0L); /* frame count=unknown */
12703 PNGLong(chunk+24,0L); /* play time=unknown */
12708 if (need_defi || mng_info->need_fram || use_global_plte)
12709 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12712 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12717 if (need_defi || mng_info->need_fram || use_global_plte)
12718 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12721 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12729 if (need_defi || mng_info->need_fram || use_global_plte)
12730 PNGLong(chunk+28,11L); /* simplicity=LC */
12733 PNGLong(chunk+28,9L); /* simplicity=VLC */
12738 if (need_defi || mng_info->need_fram || use_global_plte)
12739 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12742 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12745 (void) WriteBlob(image,32,chunk);
12746 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12747 option=GetImageOption(image_info,"mng:need-cacheoff");
12748 if (option != (const char *) NULL)
12754 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12756 PNGType(chunk,mng_nEED);
12757 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12758 (void) WriteBlobMSBULong(image,(size_t) length);
12759 LogPNGChunk(logging,mng_nEED,(size_t) length);
12761 (void) WriteBlob(image,length,chunk);
12762 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12764 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12765 (GetNextImageInList(image) != (Image *) NULL) &&
12766 (image->iterations != 1))
12769 Write MNG TERM chunk
12771 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12772 PNGType(chunk,mng_TERM);
12773 LogPNGChunk(logging,mng_TERM,10L);
12774 chunk[4]=3; /* repeat animation */
12775 chunk[5]=0; /* show last frame when done */
12776 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12777 final_delay/MagickMax(image->ticks_per_second,1)));
12779 if (image->iterations == 0)
12780 PNGLong(chunk+10,PNG_UINT_31_MAX);
12783 PNGLong(chunk+10,(png_uint_32) image->iterations);
12785 if (logging != MagickFalse)
12787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12788 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12789 final_delay/MagickMax(image->ticks_per_second,1)));
12791 if (image->iterations == 0)
12792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12793 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12797 " Image iterations: %.20g",(double) image->iterations);
12799 (void) WriteBlob(image,14,chunk);
12800 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12803 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12805 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12806 mng_info->equal_srgbs)
12809 Write MNG sRGB chunk
12811 (void) WriteBlobMSBULong(image,1L);
12812 PNGType(chunk,mng_sRGB);
12813 LogPNGChunk(logging,mng_sRGB,1L);
12815 if (image->rendering_intent != UndefinedIntent)
12816 chunk[4]=(unsigned char)
12817 Magick_RenderingIntent_to_PNG_RenderingIntent(
12818 (image->rendering_intent));
12821 chunk[4]=(unsigned char)
12822 Magick_RenderingIntent_to_PNG_RenderingIntent(
12823 (PerceptualIntent));
12825 (void) WriteBlob(image,5,chunk);
12826 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12827 mng_info->have_write_global_srgb=MagickTrue;
12832 if (image->gamma && mng_info->equal_gammas)
12835 Write MNG gAMA chunk
12837 (void) WriteBlobMSBULong(image,4L);
12838 PNGType(chunk,mng_gAMA);
12839 LogPNGChunk(logging,mng_gAMA,4L);
12840 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12841 (void) WriteBlob(image,8,chunk);
12842 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12843 mng_info->have_write_global_gama=MagickTrue;
12845 if (mng_info->equal_chrms)
12851 Write MNG cHRM chunk
12853 (void) WriteBlobMSBULong(image,32L);
12854 PNGType(chunk,mng_cHRM);
12855 LogPNGChunk(logging,mng_cHRM,32L);
12856 primary=image->chromaticity.white_point;
12857 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12858 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12859 primary=image->chromaticity.red_primary;
12860 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12861 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12862 primary=image->chromaticity.green_primary;
12863 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12864 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12865 primary=image->chromaticity.blue_primary;
12866 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12867 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12868 (void) WriteBlob(image,36,chunk);
12869 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12870 mng_info->have_write_global_chrm=MagickTrue;
12873 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12876 Write MNG pHYs chunk
12878 (void) WriteBlobMSBULong(image,9L);
12879 PNGType(chunk,mng_pHYs);
12880 LogPNGChunk(logging,mng_pHYs,9L);
12882 if (image->units == PixelsPerInchResolution)
12884 PNGLong(chunk+4,(png_uint_32)
12885 (image->resolution.x*100.0/2.54+0.5));
12887 PNGLong(chunk+8,(png_uint_32)
12888 (image->resolution.y*100.0/2.54+0.5));
12895 if (image->units == PixelsPerCentimeterResolution)
12897 PNGLong(chunk+4,(png_uint_32)
12898 (image->resolution.x*100.0+0.5));
12900 PNGLong(chunk+8,(png_uint_32)
12901 (image->resolution.y*100.0+0.5));
12908 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12909 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12913 (void) WriteBlob(image,13,chunk);
12914 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12917 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12918 or does not cover the entire frame.
12920 if (write_mng && (image->matte || image->page.x > 0 ||
12921 image->page.y > 0 || (image->page.width &&
12922 (image->page.width+image->page.x < mng_info->page.width))
12923 || (image->page.height && (image->page.height+image->page.y
12924 < mng_info->page.height))))
12926 (void) WriteBlobMSBULong(image,6L);
12927 PNGType(chunk,mng_BACK);
12928 LogPNGChunk(logging,mng_BACK,6L);
12929 red=ScaleQuantumToShort(image->background_color.red);
12930 green=ScaleQuantumToShort(image->background_color.green);
12931 blue=ScaleQuantumToShort(image->background_color.blue);
12932 PNGShort(chunk+4,red);
12933 PNGShort(chunk+6,green);
12934 PNGShort(chunk+8,blue);
12935 (void) WriteBlob(image,10,chunk);
12936 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12937 if (mng_info->equal_backgrounds)
12939 (void) WriteBlobMSBULong(image,6L);
12940 PNGType(chunk,mng_bKGD);
12941 LogPNGChunk(logging,mng_bKGD,6L);
12942 (void) WriteBlob(image,10,chunk);
12943 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12947 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12948 if ((need_local_plte == MagickFalse) &&
12949 (image->storage_class == PseudoClass) &&
12950 (all_images_are_gray == MagickFalse))
12956 Write MNG PLTE chunk
12958 data_length=3*image->colors;
12959 (void) WriteBlobMSBULong(image,data_length);
12960 PNGType(chunk,mng_PLTE);
12961 LogPNGChunk(logging,mng_PLTE,data_length);
12963 for (i=0; i < (ssize_t) image->colors; i++)
12965 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12966 image->colormap[i].red) & 0xff);
12967 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12968 image->colormap[i].green) & 0xff);
12969 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12970 image->colormap[i].blue) & 0xff);
12973 (void) WriteBlob(image,data_length+4,chunk);
12974 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12975 mng_info->have_write_global_plte=MagickTrue;
12981 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12982 defined(PNG_MNG_FEATURES_SUPPORTED)
12983 mng_info->equal_palettes=MagickFalse;
12987 if (mng_info->adjoin)
12989 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12990 defined(PNG_MNG_FEATURES_SUPPORTED)
12992 If we aren't using a global palette for the entire MNG, check to
12993 see if we can use one for two or more consecutive images.
12995 if (need_local_plte && use_global_plte && !all_images_are_gray)
12997 if (mng_info->IsPalette)
13000 When equal_palettes is true, this image has the same palette
13001 as the previous PseudoClass image
13003 mng_info->have_write_global_plte=mng_info->equal_palettes;
13004 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13005 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13008 Write MNG PLTE chunk
13013 data_length=3*image->colors;
13014 (void) WriteBlobMSBULong(image,data_length);
13015 PNGType(chunk,mng_PLTE);
13016 LogPNGChunk(logging,mng_PLTE,data_length);
13018 for (i=0; i < (ssize_t) image->colors; i++)
13020 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13021 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13022 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13025 (void) WriteBlob(image,data_length+4,chunk);
13026 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13027 (uInt) (data_length+4)));
13028 mng_info->have_write_global_plte=MagickTrue;
13032 mng_info->have_write_global_plte=MagickFalse;
13043 previous_x=mng_info->page.x;
13044 previous_y=mng_info->page.y;
13051 mng_info->page=image->page;
13052 if ((mng_info->page.x != previous_x) ||
13053 (mng_info->page.y != previous_y))
13055 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13056 PNGType(chunk,mng_DEFI);
13057 LogPNGChunk(logging,mng_DEFI,12L);
13058 chunk[4]=0; /* object 0 MSB */
13059 chunk[5]=0; /* object 0 LSB */
13060 chunk[6]=0; /* visible */
13061 chunk[7]=0; /* abstract */
13062 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13063 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13064 (void) WriteBlob(image,16,chunk);
13065 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13070 mng_info->write_mng=write_mng;
13072 if ((int) image->dispose >= 3)
13073 mng_info->framing_mode=3;
13075 if (mng_info->need_fram && mng_info->adjoin &&
13076 ((image->delay != mng_info->delay) ||
13077 (mng_info->framing_mode != mng_info->old_framing_mode)))
13079 if (image->delay == mng_info->delay)
13082 Write a MNG FRAM chunk with the new framing mode.
13084 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13085 PNGType(chunk,mng_FRAM);
13086 LogPNGChunk(logging,mng_FRAM,1L);
13087 chunk[4]=(unsigned char) mng_info->framing_mode;
13088 (void) WriteBlob(image,5,chunk);
13089 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13094 Write a MNG FRAM chunk with the delay.
13096 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13097 PNGType(chunk,mng_FRAM);
13098 LogPNGChunk(logging,mng_FRAM,10L);
13099 chunk[4]=(unsigned char) mng_info->framing_mode;
13100 chunk[5]=0; /* frame name separator (no name) */
13101 chunk[6]=2; /* flag for changing default delay */
13102 chunk[7]=0; /* flag for changing frame timeout */
13103 chunk[8]=0; /* flag for changing frame clipping */
13104 chunk[9]=0; /* flag for changing frame sync_id */
13105 PNGLong(chunk+10,(png_uint_32)
13106 ((mng_info->ticks_per_second*
13107 image->delay)/MagickMax(image->ticks_per_second,1)));
13108 (void) WriteBlob(image,14,chunk);
13109 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13110 mng_info->delay=(png_uint_32) image->delay;
13112 mng_info->old_framing_mode=mng_info->framing_mode;
13115 #if defined(JNG_SUPPORTED)
13116 if (image_info->compression == JPEGCompression)
13121 if (logging != MagickFalse)
13122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13123 " Writing JNG object.");
13124 /* To do: specify the desired alpha compression method. */
13125 write_info=CloneImageInfo(image_info);
13126 write_info->compression=UndefinedCompression;
13127 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13128 write_info=DestroyImageInfo(write_info);
13133 if (logging != MagickFalse)
13134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13135 " Writing PNG object.");
13137 mng_info->need_blob = MagickFalse;
13138 mng_info->ping_preserve_colormap = MagickFalse;
13140 /* We don't want any ancillary chunks written */
13141 mng_info->ping_exclude_bKGD=MagickTrue;
13142 mng_info->ping_exclude_cHRM=MagickTrue;
13143 mng_info->ping_exclude_date=MagickTrue;
13144 mng_info->ping_exclude_EXIF=MagickTrue;
13145 mng_info->ping_exclude_gAMA=MagickTrue;
13146 mng_info->ping_exclude_iCCP=MagickTrue;
13147 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13148 mng_info->ping_exclude_oFFs=MagickTrue;
13149 mng_info->ping_exclude_pHYs=MagickTrue;
13150 mng_info->ping_exclude_sRGB=MagickTrue;
13151 mng_info->ping_exclude_tEXt=MagickTrue;
13152 mng_info->ping_exclude_tRNS=MagickTrue;
13153 mng_info->ping_exclude_vpAg=MagickTrue;
13154 mng_info->ping_exclude_zCCP=MagickTrue;
13155 mng_info->ping_exclude_zTXt=MagickTrue;
13157 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13160 if (status == MagickFalse)
13162 MngInfoFreeStruct(mng_info,&have_mng_structure);
13163 (void) CloseBlob(image);
13164 return(MagickFalse);
13166 (void) CatchImageException(image);
13167 if (GetNextImageInList(image) == (Image *) NULL)
13169 image=SyncNextImageInList(image);
13170 status=SetImageProgress(image,SaveImagesTag,scene++,
13171 GetImageListLength(image));
13173 if (status == MagickFalse)
13176 } while (mng_info->adjoin);
13180 while (GetPreviousImageInList(image) != (Image *) NULL)
13181 image=GetPreviousImageInList(image);
13183 Write the MEND chunk.
13185 (void) WriteBlobMSBULong(image,0x00000000L);
13186 PNGType(chunk,mng_MEND);
13187 LogPNGChunk(logging,mng_MEND,0L);
13188 (void) WriteBlob(image,4,chunk);
13189 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13192 Relinquish resources.
13194 (void) CloseBlob(image);
13195 MngInfoFreeStruct(mng_info,&have_mng_structure);
13197 if (logging != MagickFalse)
13198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13200 return(MagickTrue);
13202 #else /* PNG_LIBPNG_VER > 10011 */
13204 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13207 printf("Your PNG library is too old: You have libpng-%s\n",
13208 PNG_LIBPNG_VER_STRING);
13210 ThrowBinaryException(CoderError,"PNG library is too old",
13211 image_info->filename);
13214 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13216 return(WritePNGImage(image_info,image));
13218 #endif /* PNG_LIBPNG_VER > 10011 */