2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
106 /* ImageMagick differences */
107 #define first_scene scene
109 #if PNG_LIBPNG_VER > 10011
111 Optional declarations. Define or undefine them as you like.
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116 Features under construction. Define these to work on them.
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
132 /* Macros for left-bit-replication to ensure that pixels
133 * and PixelInfos all have the same image->depth, and for use
134 * in PNG8 quantization.
138 /* LBR01: Replicate top bit */
140 #define LBR01PacketRed(pixelpacket) \
141 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
144 #define LBR01PacketGreen(pixelpacket) \
145 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
148 #define LBR01PacketBlue(pixelpacket) \
149 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
152 #define LBR01PacketAlpha(pixelpacket) \
153 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
156 #define LBR01PacketRGB(pixelpacket) \
158 LBR01PacketRed((pixelpacket)); \
159 LBR01PacketGreen((pixelpacket)); \
160 LBR01PacketBlue((pixelpacket)); \
163 #define LBR01PacketRGBO(pixelpacket) \
165 LBR01PacketRGB((pixelpacket)); \
166 LBR01PacketAlpha((pixelpacket)); \
169 #define LBR01PixelRed(pixel) \
170 (SetPixelRed(image, \
171 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
172 0 : QuantumRange,(pixel)));
174 #define LBR01PixelGreen(pixel) \
175 (SetPixelGreen(image, \
176 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177 0 : QuantumRange,(pixel)));
179 #define LBR01PixelBlue(pixel) \
180 (SetPixelBlue(image, \
181 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
182 0 : QuantumRange,(pixel)));
184 #define LBR01PixelAlpha(pixel) \
185 (SetPixelAlpha(image, \
186 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
187 0 : QuantumRange,(pixel)));
189 #define LBR01PixelRGB(pixel) \
191 LBR01PixelRed((pixel)); \
192 LBR01PixelGreen((pixel)); \
193 LBR01PixelBlue((pixel)); \
196 #define LBR01PixelRGBA(pixel) \
198 LBR01PixelRGB((pixel)); \
199 LBR01PixelAlpha((pixel)); \
202 /* LBR02: Replicate top 2 bits */
204 #define LBR02PacketRed(pixelpacket) \
206 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
207 (pixelpacket).red=ScaleCharToQuantum( \
208 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 #define LBR02PacketGreen(pixelpacket) \
212 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
213 (pixelpacket).green=ScaleCharToQuantum( \
214 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 #define LBR02PacketBlue(pixelpacket) \
218 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
219 (pixelpacket).blue=ScaleCharToQuantum( \
220 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 #define LBR02PacketAlpha(pixelpacket) \
224 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
225 (pixelpacket).alpha=ScaleCharToQuantum( \
226 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
229 #define LBR02PacketRGB(pixelpacket) \
231 LBR02PacketRed((pixelpacket)); \
232 LBR02PacketGreen((pixelpacket)); \
233 LBR02PacketBlue((pixelpacket)); \
236 #define LBR02PacketRGBO(pixelpacket) \
238 LBR02PacketRGB((pixelpacket)); \
239 LBR02PacketAlpha((pixelpacket)); \
242 #define LBR02PixelRed(pixel) \
244 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
246 SetPixelRed(image, ScaleCharToQuantum( \
247 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
250 #define LBR02PixelGreen(pixel) \
252 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
254 SetPixelGreen(image, ScaleCharToQuantum( \
255 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
258 #define LBR02PixelBlue(pixel) \
260 unsigned char lbr_bits= \
261 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
262 SetPixelBlue(image, ScaleCharToQuantum( \
263 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
266 #define LBR02PixelAlpha(pixel) \
268 unsigned char lbr_bits= \
269 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
270 SetPixelAlpha(image, ScaleCharToQuantum( \
271 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
275 #define LBR02PixelRGB(pixel) \
277 LBR02PixelRed((pixel)); \
278 LBR02PixelGreen((pixel)); \
279 LBR02PixelBlue((pixel)); \
282 #define LBR02PixelRGBA(pixel) \
284 LBR02PixelRGB((pixel)); \
285 LBR02PixelAlpha((pixel)); \
288 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
289 PNG8 quantization) */
291 #define LBR03PacketRed(pixelpacket) \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
294 (pixelpacket).red=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 #define LBR03PacketGreen(pixelpacket) \
299 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
300 (pixelpacket).green=ScaleCharToQuantum( \
301 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 #define LBR03PacketBlue(pixelpacket) \
305 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
306 (pixelpacket).blue=ScaleCharToQuantum( \
307 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
310 #define LBR03PacketRGB(pixelpacket) \
312 LBR03PacketRed((pixelpacket)); \
313 LBR03PacketGreen((pixelpacket)); \
314 LBR03PacketBlue((pixelpacket)); \
317 #define LBR03PixelRed(pixel) \
319 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
321 SetPixelRed(image, ScaleCharToQuantum( \
322 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
324 #define LBR03Green(pixel) \
326 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
328 SetPixelGreen(image, ScaleCharToQuantum( \
329 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
331 #define LBR03Blue(pixel) \
333 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
335 SetPixelBlue(image, ScaleCharToQuantum( \
336 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
339 #define LBR03RGB(pixel) \
341 LBR03PixelRed((pixel)); \
342 LBR03Green((pixel)); \
343 LBR03Blue((pixel)); \
346 /* LBR04: Replicate top 4 bits */
348 #define LBR04PacketRed(pixelpacket) \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
351 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
353 #define LBR04PacketGreen(pixelpacket) \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
356 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
358 #define LBR04PacketBlue(pixelpacket) \
360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
361 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
363 #define LBR04PacketAlpha(pixelpacket) \
365 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
366 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
369 #define LBR04PacketRGB(pixelpacket) \
371 LBR04PacketRed((pixelpacket)); \
372 LBR04PacketGreen((pixelpacket)); \
373 LBR04PacketBlue((pixelpacket)); \
376 #define LBR04PacketRGBO(pixelpacket) \
378 LBR04PacketRGB((pixelpacket)); \
379 LBR04PacketAlpha((pixelpacket)); \
382 #define LBR04PixelRed(pixel) \
384 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
387 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
389 #define LBR04PixelGreen(pixel) \
391 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
393 SetPixelGreen(image,\
394 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
396 #define LBR04PixelBlue(pixel) \
398 unsigned char lbr_bits= \
399 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
401 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
403 #define LBR04PixelAlpha(pixel) \
405 unsigned char lbr_bits= \
406 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
407 SetPixelAlpha(image,\
408 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
411 #define LBR04PixelRGB(pixel) \
413 LBR04PixelRed((pixel)); \
414 LBR04PixelGreen((pixel)); \
415 LBR04PixelBlue((pixel)); \
418 #define LBR04PixelRGBA(pixel) \
420 LBR04PixelRGB((pixel)); \
421 LBR04PixelAlpha((pixel)); \
425 /* LBR08: Replicate top 8 bits */
427 #define LBR08PacketRed(pixelpacket) \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
430 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
432 #define LBR08PacketGreen(pixelpacket) \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
435 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
437 #define LBR08PacketBlue(pixelpacket) \
439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
440 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
442 #define LBR08PacketAlpha(pixelpacket) \
444 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
445 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
448 #define LBR08PacketRGB(pixelpacket) \
450 LBR08PacketRed((pixelpacket)); \
451 LBR08PacketGreen((pixelpacket)); \
452 LBR08PacketBlue((pixelpacket)); \
455 #define LBR08PacketRGBO(pixelpacket) \
457 LBR08PacketRGB((pixelpacket)); \
458 LBR08PacketAlpha((pixelpacket)); \
461 #define LBR08PixelRed(pixel) \
463 unsigned char lbr_bits= \
464 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
466 ScaleCharToQuantum((lbr_bits)), (pixel)); \
468 #define LBR08PixelGreen(pixel) \
470 unsigned char lbr_bits= \
471 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
472 SetPixelGreen(image,\
473 ScaleCharToQuantum((lbr_bits)), (pixel)); \
475 #define LBR08PixelBlue(pixel) \
477 unsigned char lbr_bits= \
478 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
480 ScaleCharToQuantum((lbr_bits)), (pixel)); \
482 #define LBR08PixelAlpha(pixel) \
484 unsigned char lbr_bits= \
485 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
486 SetPixelAlpha(image,\
487 ScaleCharToQuantum((lbr_bits)), (pixel)); \
490 #define LBR08PixelRGB(pixel) \
492 LBR08PixelRed((pixel)); \
493 LBR08PixelGreen((pixel)); \
494 LBR08PixelBlue((pixel)); \
497 #define LBR08PixelRGBA(pixel) \
499 LBR08PixelRGB((pixel)); \
500 LBR08PixelAlpha((pixel)); \
504 /* LBR16: Replicate top 16 bits */
506 #define LBR16PacketRed(pixelpacket) \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
509 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
511 #define LBR16PacketGreen(pixelpacket) \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
514 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
516 #define LBR16PacketBlue(pixelpacket) \
518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
519 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
521 #define LBR16PacketAlpha(pixelpacket) \
523 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
524 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
527 #define LBR16PacketRGB(pixelpacket) \
529 LBR16PacketRed((pixelpacket)); \
530 LBR16PacketGreen((pixelpacket)); \
531 LBR16PacketBlue((pixelpacket)); \
534 #define LBR16PacketRGBO(pixelpacket) \
536 LBR16PacketRGB((pixelpacket)); \
537 LBR16PacketAlpha((pixelpacket)); \
540 #define LBR16PixelRed(pixel) \
542 unsigned short lbr_bits= \
543 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
545 ScaleShortToQuantum((lbr_bits)),(pixel)); \
547 #define LBR16PixelGreen(pixel) \
549 unsigned short lbr_bits= \
550 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
551 SetPixelGreen(image,\
552 ScaleShortToQuantum((lbr_bits)),(pixel)); \
554 #define LBR16PixelBlue(pixel) \
556 unsigned short lbr_bits= \
557 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
559 ScaleShortToQuantum((lbr_bits)),(pixel)); \
561 #define LBR16PixelAlpha(pixel) \
563 unsigned short lbr_bits= \
564 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
565 SetPixelAlpha(image,\
566 ScaleShortToQuantum((lbr_bits)),(pixel)); \
569 #define LBR16PixelRGB(pixel) \
571 LBR16PixelRed((pixel)); \
572 LBR16PixelGreen((pixel)); \
573 LBR16PixelBlue((pixel)); \
576 #define LBR16PixelRGBA(pixel) \
578 LBR16PixelRGB((pixel)); \
579 LBR16PixelAlpha((pixel)); \
583 Establish thread safety.
584 setjmp/longjmp is claimed to be safe on these platforms:
585 setjmp/longjmp is alleged to be unsafe on these platforms:
587 #ifndef SETJMP_IS_THREAD_SAFE
588 #define PNG_SETJMP_NOT_THREAD_SAFE
591 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
593 *ping_semaphore = (SemaphoreInfo *) NULL;
597 This temporary until I set up malloc'ed object attributes array.
598 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
601 #define MNG_MAX_OBJECTS 256
604 If this not defined, spec is interpreted strictly. If it is
605 defined, an attempt will be made to recover from some errors,
607 o global PLTE too short
612 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
613 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
614 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
615 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
616 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
617 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
618 will be enabled by default in libpng-1.2.0.
620 #ifdef PNG_MNG_FEATURES_SUPPORTED
621 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
622 # define PNG_READ_EMPTY_PLTE_SUPPORTED
624 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
625 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
630 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
631 This macro is only defined in libpng-1.0.3 and later.
632 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
634 #ifndef PNG_UINT_31_MAX
635 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
639 Constant strings for known chunk types. If you need to add a chunk,
640 add a string holding the name here. To make the code more
641 portable, we use ASCII numbers like this, not characters.
644 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
645 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
646 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
647 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
648 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
649 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
650 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
651 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
652 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
653 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
654 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
655 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
656 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
657 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
658 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
659 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
660 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
661 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
662 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
663 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
664 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
665 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
666 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
667 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
668 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
669 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
670 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
671 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
672 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
673 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
674 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
675 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
676 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
677 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
679 #if defined(JNG_SUPPORTED)
680 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
681 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
682 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
683 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
684 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
685 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
689 Other known chunks that are not yet supported by ImageMagick:
690 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
691 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
692 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
693 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
694 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
695 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
696 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
697 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
700 typedef struct _MngBox
709 typedef struct _MngPair
716 #ifdef MNG_OBJECT_BUFFERS
717 typedef struct _MngBuffer
749 typedef struct _MngInfo
752 #ifdef MNG_OBJECT_BUFFERS
754 *ob[MNG_MAX_OBJECTS];
765 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
766 bytes_in_read_buffer,
772 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
773 defined(PNG_MNG_FEATURES_SUPPORTED)
785 have_saved_bkgd_index,
786 have_write_global_chrm,
787 have_write_global_gama,
788 have_write_global_plte,
789 have_write_global_srgb,
803 x_off[MNG_MAX_OBJECTS],
804 y_off[MNG_MAX_OBJECTS];
810 object_clip[MNG_MAX_OBJECTS];
813 /* These flags could be combined into one byte */
814 exists[MNG_MAX_OBJECTS],
815 frozen[MNG_MAX_OBJECTS],
817 invisible[MNG_MAX_OBJECTS],
818 viewable[MNG_MAX_OBJECTS];
830 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
848 global_x_pixels_per_unit,
849 global_y_pixels_per_unit,
859 global_phys_unit_type,
874 write_png_compression_level,
875 write_png_compression_strategy,
876 write_png_compression_filter,
881 #ifdef MNG_BASI_SUPPORTED
889 basi_compression_method,
891 basi_interlace_method,
914 /* Added at version 6.6.6-7 */
922 /* ping_exclude_iTXt, */
929 ping_exclude_zCCP, /* hex-encoded iCCP */
931 ping_preserve_colormap;
937 Forward declarations.
939 static MagickBooleanType
940 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
942 static MagickBooleanType
943 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
945 #if defined(JNG_SUPPORTED)
946 static MagickBooleanType
947 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
950 #if PNG_LIBPNG_VER > 10011
953 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
954 static MagickBooleanType
955 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
957 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
959 * This is true if the high byte and the next highest byte of
960 * each sample of the image, the colormap, and the background color
961 * are equal to each other. We check this by seeing if the samples
962 * are unchanged when we scale them down to 8 and back up to Quantum.
964 * We don't use the method GetImageDepth() because it doesn't check
965 * background and doesn't handle PseudoClass specially.
968 #define QuantumToCharToQuantumEqQuantum(quantum) \
969 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
972 ok_to_reduce=MagickFalse;
974 if (image->depth >= 16)
981 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
982 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
983 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
984 MagickTrue : MagickFalse;
986 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
990 for (indx=0; indx < (ssize_t) image->colors; indx++)
993 QuantumToCharToQuantumEqQuantum(
994 image->colormap[indx].red) &&
995 QuantumToCharToQuantumEqQuantum(
996 image->colormap[indx].green) &&
997 QuantumToCharToQuantumEqQuantum(
998 image->colormap[indx].blue)) ?
999 MagickTrue : MagickFalse;
1001 if (ok_to_reduce == MagickFalse)
1006 if ((ok_to_reduce != MagickFalse) &&
1007 (image->storage_class != PseudoClass))
1015 for (y=0; y < (ssize_t) image->rows; y++)
1017 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1019 if (p == (const Quantum *) NULL)
1021 ok_to_reduce = MagickFalse;
1025 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1028 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1029 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1030 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1031 MagickTrue : MagickFalse;
1033 if (ok_to_reduce == MagickFalse)
1036 p+=GetPixelChannels(image);
1043 if (ok_to_reduce != MagickFalse)
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1046 " OK to reduce PNG bit depth to 8 without loss of info");
1050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1051 " Not OK to reduce PNG bit depth to 8 without loss of info");
1055 return ok_to_reduce;
1057 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1059 static const char* PngColorTypeToString(const unsigned int color_type)
1062 *result = "Unknown";
1066 case PNG_COLOR_TYPE_GRAY:
1069 case PNG_COLOR_TYPE_GRAY_ALPHA:
1070 result = "Gray+Alpha";
1072 case PNG_COLOR_TYPE_PALETTE:
1075 case PNG_COLOR_TYPE_RGB:
1078 case PNG_COLOR_TYPE_RGB_ALPHA:
1079 result = "RGB+Alpha";
1087 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1091 case PerceptualIntent:
1094 case RelativeIntent:
1097 case SaturationIntent:
1100 case AbsoluteIntent:
1108 static RenderingIntent
1109 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1111 switch (ping_intent)
1114 return PerceptualIntent;
1117 return RelativeIntent;
1120 return SaturationIntent;
1123 return AbsoluteIntent;
1126 return UndefinedIntent;
1131 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1133 switch (ping_intent)
1136 return "Perceptual Intent";
1139 return "Relative Intent";
1142 return "Saturation Intent";
1145 return "Absolute Intent";
1148 return "Undefined Intent";
1152 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1161 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1163 switch (ping_colortype)
1181 return "UndefinedColorType";
1186 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1193 #endif /* PNG_LIBPNG_VER > 10011 */
1194 #endif /* MAGICKCORE_PNG_DELEGATE */
1197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207 % IsMNG() returns MagickTrue if the image format type, identified by the
1208 % magick string, is MNG.
1210 % The format of the IsMNG method is:
1212 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1214 % A description of each parameter follows:
1216 % o magick: compare image format pattern against these bytes.
1218 % o length: Specifies the length of the magick string.
1222 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1225 return(MagickFalse);
1227 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1230 return(MagickFalse);
1234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244 % IsJNG() returns MagickTrue if the image format type, identified by the
1245 % magick string, is JNG.
1247 % The format of the IsJNG method is:
1249 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1251 % A description of each parameter follows:
1253 % o magick: compare image format pattern against these bytes.
1255 % o length: Specifies the length of the magick string.
1259 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1262 return(MagickFalse);
1264 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1267 return(MagickFalse);
1271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1281 % IsPNG() returns MagickTrue if the image format type, identified by the
1282 % magick string, is PNG.
1284 % The format of the IsPNG method is:
1286 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1288 % A description of each parameter follows:
1290 % o magick: compare image format pattern against these bytes.
1292 % o length: Specifies the length of the magick string.
1295 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1298 return(MagickFalse);
1300 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1303 return(MagickFalse);
1306 #if defined(MAGICKCORE_PNG_DELEGATE)
1307 #if defined(__cplusplus) || defined(c_plusplus)
1311 #if (PNG_LIBPNG_VER > 10011)
1312 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1317 assert(image != (Image *) NULL);
1318 assert(image->signature == MagickSignature);
1319 buffer[0]=(unsigned char) (value >> 24);
1320 buffer[1]=(unsigned char) (value >> 16);
1321 buffer[2]=(unsigned char) (value >> 8);
1322 buffer[3]=(unsigned char) value;
1323 return((size_t) WriteBlob(image,4,buffer));
1326 static void PNGLong(png_bytep p,png_uint_32 value)
1328 *p++=(png_byte) ((value >> 24) & 0xff);
1329 *p++=(png_byte) ((value >> 16) & 0xff);
1330 *p++=(png_byte) ((value >> 8) & 0xff);
1331 *p++=(png_byte) (value & 0xff);
1334 #if defined(JNG_SUPPORTED)
1335 static void PNGsLong(png_bytep p,png_int_32 value)
1337 *p++=(png_byte) ((value >> 24) & 0xff);
1338 *p++=(png_byte) ((value >> 16) & 0xff);
1339 *p++=(png_byte) ((value >> 8) & 0xff);
1340 *p++=(png_byte) (value & 0xff);
1344 static void PNGShort(png_bytep p,png_uint_16 value)
1346 *p++=(png_byte) ((value >> 8) & 0xff);
1347 *p++=(png_byte) (value & 0xff);
1350 static void PNGType(png_bytep p,png_bytep type)
1352 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1355 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1358 if (logging != MagickFalse)
1359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1360 " Writing %c%c%c%c chunk, length: %.20g",
1361 type[0],type[1],type[2],type[3],(double) length);
1363 #endif /* PNG_LIBPNG_VER > 10011 */
1365 #if defined(__cplusplus) || defined(c_plusplus)
1369 #if PNG_LIBPNG_VER > 10011
1371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1375 % R e a d P N G I m a g e %
1379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1381 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1382 % Multiple-image Network Graphics (MNG) image file and returns it. It
1383 % allocates the memory necessary for the new Image structure and returns a
1384 % pointer to the new image or set of images.
1386 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1388 % The format of the ReadPNGImage method is:
1390 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1392 % A description of each parameter follows:
1394 % o image_info: the image info.
1396 % o exception: return any errors or warnings in this structure.
1398 % To do, more or less in chronological order (as of version 5.5.2,
1399 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1401 % Get 16-bit cheap transparency working.
1403 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1405 % Preserve all unknown and not-yet-handled known chunks found in input
1406 % PNG file and copy them into output PNG files according to the PNG
1409 % (At this point, PNG encoding should be in full MNG compliance)
1411 % Provide options for choice of background to use when the MNG BACK
1412 % chunk is not present or is not mandatory (i.e., leave transparent,
1413 % user specified, MNG BACK, PNG bKGD)
1415 % Implement LOOP/ENDL [done, but could do discretionary loops more
1416 % efficiently by linking in the duplicate frames.].
1418 % Decode and act on the MHDR simplicity profile (offer option to reject
1419 % files or attempt to process them anyway when the profile isn't LC or VLC).
1421 % Upgrade to full MNG without Delta-PNG.
1423 % o BACK [done a while ago except for background image ID]
1424 % o MOVE [done 15 May 1999]
1425 % o CLIP [done 15 May 1999]
1426 % o DISC [done 19 May 1999]
1427 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1428 % o SEEK [partially done 19 May 1999 (discard function only)]
1432 % o MNG-level tEXt/iTXt/zTXt
1437 % o iTXt (wait for libpng implementation).
1439 % Use the scene signature to discover when an identical scene is
1440 % being reused, and just point to the original image->exception instead
1441 % of storing another set of pixels. This not specific to MNG
1442 % but could be applied generally.
1444 % Upgrade to full MNG with Delta-PNG.
1446 % JNG tEXt/iTXt/zTXt
1448 % We will not attempt to read files containing the CgBI chunk.
1449 % They are really Xcode files meant for display on the iPhone.
1450 % These are not valid PNG files and it is impossible to recover
1451 % the original PNG from files that have been converted to Xcode-PNG,
1452 % since irretrievable loss of color data has occurred due to the
1453 % use of premultiplied alpha.
1456 #if defined(__cplusplus) || defined(c_plusplus)
1461 This the function that does the actual reading of data. It is
1462 the same as the one supplied in libpng, except that it receives the
1463 datastream from the ReadBlob() function instead of standard input.
1465 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1470 image=(Image *) png_get_io_ptr(png_ptr);
1476 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1477 if (check != length)
1482 (void) FormatLocaleString(msg,MaxTextExtent,
1483 "Expected %.20g bytes; found %.20g bytes",(double) length,
1485 png_warning(png_ptr,msg);
1486 png_error(png_ptr,"Read Exception");
1491 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1492 !defined(PNG_MNG_FEATURES_SUPPORTED)
1493 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1494 * older than libpng-1.0.3a, which was the first to allow the empty
1495 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1496 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1497 * encountered after an empty PLTE, so we have to look ahead for bKGD
1498 * chunks and remove them from the datastream that is passed to libpng,
1499 * and store their contents for later use.
1501 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1516 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1517 image=(Image *) mng_info->image;
1518 while (mng_info->bytes_in_read_buffer && length)
1520 data[i]=mng_info->read_buffer[i];
1521 mng_info->bytes_in_read_buffer--;
1527 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1529 if (check != length)
1530 png_error(png_ptr,"Read Exception");
1534 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1537 check=(png_size_t) ReadBlob(image,(size_t) length,
1538 (char *) mng_info->read_buffer);
1539 mng_info->read_buffer[4]=0;
1540 mng_info->bytes_in_read_buffer=4;
1541 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1542 mng_info->found_empty_plte=MagickTrue;
1543 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1545 mng_info->found_empty_plte=MagickFalse;
1546 mng_info->have_saved_bkgd_index=MagickFalse;
1550 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1553 check=(png_size_t) ReadBlob(image,(size_t) length,
1554 (char *) mng_info->read_buffer);
1555 mng_info->read_buffer[4]=0;
1556 mng_info->bytes_in_read_buffer=4;
1557 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1558 if (mng_info->found_empty_plte)
1561 Skip the bKGD data byte and CRC.
1564 ReadBlob(image,5,(char *) mng_info->read_buffer);
1565 check=(png_size_t) ReadBlob(image,(size_t) length,
1566 (char *) mng_info->read_buffer);
1567 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1568 mng_info->have_saved_bkgd_index=MagickTrue;
1569 mng_info->bytes_in_read_buffer=0;
1577 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1582 image=(Image *) png_get_io_ptr(png_ptr);
1588 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1590 if (check != length)
1591 png_error(png_ptr,"WriteBlob Failed");
1595 static void png_flush_data(png_structp png_ptr)
1600 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1601 static int PalettesAreEqual(Image *a,Image *b)
1606 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1607 return((int) MagickFalse);
1609 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1610 return((int) MagickFalse);
1612 if (a->colors != b->colors)
1613 return((int) MagickFalse);
1615 for (i=0; i < (ssize_t) a->colors; i++)
1617 if ((a->colormap[i].red != b->colormap[i].red) ||
1618 (a->colormap[i].green != b->colormap[i].green) ||
1619 (a->colormap[i].blue != b->colormap[i].blue))
1620 return((int) MagickFalse);
1623 return((int) MagickTrue);
1627 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1629 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1630 mng_info->exists[i] && !mng_info->frozen[i])
1632 #ifdef MNG_OBJECT_BUFFERS
1633 if (mng_info->ob[i] != (MngBuffer *) NULL)
1635 if (mng_info->ob[i]->reference_count > 0)
1636 mng_info->ob[i]->reference_count--;
1638 if (mng_info->ob[i]->reference_count == 0)
1640 if (mng_info->ob[i]->image != (Image *) NULL)
1641 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1643 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1646 mng_info->ob[i]=(MngBuffer *) NULL;
1648 mng_info->exists[i]=MagickFalse;
1649 mng_info->invisible[i]=MagickFalse;
1650 mng_info->viewable[i]=MagickFalse;
1651 mng_info->frozen[i]=MagickFalse;
1652 mng_info->x_off[i]=0;
1653 mng_info->y_off[i]=0;
1654 mng_info->object_clip[i].left=0;
1655 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1656 mng_info->object_clip[i].top=0;
1657 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1661 static void MngInfoFreeStruct(MngInfo *mng_info,
1662 MagickBooleanType *have_mng_structure)
1664 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1669 for (i=1; i < MNG_MAX_OBJECTS; i++)
1670 MngInfoDiscardObject(mng_info,i);
1672 if (mng_info->global_plte != (png_colorp) NULL)
1673 mng_info->global_plte=(png_colorp)
1674 RelinquishMagickMemory(mng_info->global_plte);
1676 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1677 *have_mng_structure=MagickFalse;
1681 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1687 if (box.left < box2.left)
1690 if (box.top < box2.top)
1693 if (box.right > box2.right)
1694 box.right=box2.right;
1696 if (box.bottom > box2.bottom)
1697 box.bottom=box2.bottom;
1702 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1708 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1710 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1711 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1712 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1713 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1714 if (delta_type != 0)
1716 box.left+=previous_box.left;
1717 box.right+=previous_box.right;
1718 box.top+=previous_box.top;
1719 box.bottom+=previous_box.bottom;
1725 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1731 Read two ssize_ts from CLON, MOVE or PAST chunk
1733 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1734 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1736 if (delta_type != 0)
1738 pair.a+=previous_pair.a;
1739 pair.b+=previous_pair.b;
1745 static long mng_get_long(unsigned char *p)
1747 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1750 typedef struct _PNGErrorInfo
1759 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1770 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1771 image=error_info->image;
1772 exception=error_info->exception;
1774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1775 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1777 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1778 "`%s'",image->filename);
1780 #if (PNG_LIBPNG_VER < 10500)
1781 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1782 * are building with libpng-1.4.x and can be ignored.
1784 longjmp(ping->jmpbuf,1);
1786 png_longjmp(ping,1);
1790 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1801 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1802 png_error(ping, message);
1804 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1805 image=error_info->image;
1806 exception=error_info->exception;
1807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1808 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1810 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1811 message,"`%s'",image->filename);
1814 #ifdef PNG_USER_MEM_SUPPORTED
1815 #if PNG_LIBPNG_VER >= 14000
1816 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1818 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1822 return((png_voidp) AcquireMagickMemory((size_t) size));
1826 Free a pointer. It is removed from the list at the same time.
1828 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1831 ptr=RelinquishMagickMemory(ptr);
1832 return((png_free_ptr) NULL);
1836 #if defined(__cplusplus) || defined(c_plusplus)
1841 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1842 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1847 register unsigned char
1861 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1862 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1863 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1864 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1865 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1869 /* look for newline */
1873 /* look for length */
1874 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1877 length=(png_uint_32) StringToLong(sp);
1879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1880 " length: %lu",(unsigned long) length);
1882 while (*sp != ' ' && *sp != '\n')
1885 /* allocate space */
1888 png_warning(ping,"invalid profile length");
1889 return(MagickFalse);
1892 profile=BlobToStringInfo((const void *) NULL,length);
1894 if (profile == (StringInfo *) NULL)
1896 png_warning(ping, "unable to copy profile");
1897 return(MagickFalse);
1900 /* copy profile, skipping white space and column 1 "=" signs */
1901 dp=GetStringInfoDatum(profile);
1904 for (i=0; i < (ssize_t) nibbles; i++)
1906 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1910 png_warning(ping, "ran out of profile data");
1911 profile=DestroyStringInfo(profile);
1912 return(MagickFalse);
1918 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1921 (*dp++)+=unhex[(int) *sp++];
1924 We have already read "Raw profile type.
1926 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1927 profile=DestroyStringInfo(profile);
1929 if (image_info->verbose)
1930 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1935 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1936 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1942 /* The unknown chunk structure contains the chunk data:
1947 Note that libpng has already taken care of the CRC handling.
1951 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1952 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1953 return(0); /* Did not recognize */
1955 /* recognized vpAg */
1957 if (chunk->size != 9)
1958 return(-1); /* Error return */
1960 if (chunk->data[8] != 0)
1961 return(0); /* ImageMagick requires pixel units */
1963 image=(Image *) png_get_user_chunk_ptr(ping);
1965 image->page.width=(size_t) ((chunk->data[0] << 24) |
1966 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1968 image->page.height=(size_t) ((chunk->data[4] << 24) |
1969 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1971 /* Return one of the following: */
1972 /* return(-n); chunk had an error */
1973 /* return(0); did not recognize */
1974 /* return(n); success */
1982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1986 % R e a d O n e P N G I m a g e %
1990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1992 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1993 % (minus the 8-byte signature) and returns it. It allocates the memory
1994 % necessary for the new Image structure and returns a pointer to the new
1997 % The format of the ReadOnePNGImage method is:
1999 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2000 % ExceptionInfo *exception)
2002 % A description of each parameter follows:
2004 % o mng_info: Specifies a pointer to a MngInfo structure.
2006 % o image_info: the image info.
2008 % o exception: return any errors or warnings in this structure.
2011 static Image *ReadOnePNGImage(MngInfo *mng_info,
2012 const ImageInfo *image_info, ExceptionInfo *exception)
2014 /* Read one PNG image */
2016 /* To do: Read the tIME chunk into the date:modify property */
2017 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2023 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2033 ping_interlace_method,
2034 ping_compression_method,
2086 register unsigned char
2104 *volatile ping_pixels;
2106 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2107 png_byte unused_chunks[]=
2109 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2110 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2111 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2112 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2113 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2114 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2115 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2116 /* ignore the APNG chunks */
2117 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2118 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2119 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2124 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2125 " Enter ReadOnePNGImage()");
2127 #if (PNG_LIBPNG_VER < 10200)
2128 if (image_info->verbose)
2129 printf("Your PNG library (libpng-%s) is rather old.\n",
2130 PNG_LIBPNG_VER_STRING);
2133 #if (PNG_LIBPNG_VER >= 10400)
2134 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2135 if (image_info->verbose)
2137 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2138 PNG_LIBPNG_VER_STRING);
2139 printf("Please update it.\n");
2145 quantum_info = (QuantumInfo *) NULL;
2146 image=mng_info->image;
2148 if (logging != MagickFalse)
2150 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2151 " image->alpha_trait=%d",(int) image->alpha_trait);
2153 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2154 " image->rendering_intent=%d",(int) image->rendering_intent);
2156 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " image->colorspace=%d",(int) image->colorspace);
2159 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2161 /* Set to an out-of-range color unless tRNS chunk is present */
2162 transparent_color.red=65537;
2163 transparent_color.green=65537;
2164 transparent_color.blue=65537;
2165 transparent_color.alpha=65537;
2170 num_raw_profiles = 0;
2172 ping_found_cHRM = MagickFalse;
2173 ping_found_gAMA = MagickFalse;
2174 ping_found_iCCP = MagickFalse;
2175 ping_found_sRGB = MagickFalse;
2178 Allocate the PNG structures
2180 #ifdef PNG_USER_MEM_SUPPORTED
2181 error_info.image=image;
2182 error_info.exception=exception;
2183 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2184 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2185 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2187 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2188 MagickPNGErrorHandler,MagickPNGWarningHandler);
2190 if (ping == (png_struct *) NULL)
2191 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2193 ping_info=png_create_info_struct(ping);
2195 if (ping_info == (png_info *) NULL)
2197 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2198 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2201 end_info=png_create_info_struct(ping);
2203 if (end_info == (png_info *) NULL)
2205 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2206 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2209 ping_pixels=(unsigned char *) NULL;
2211 if (setjmp(png_jmpbuf(ping)))
2214 PNG image is corrupt.
2216 png_destroy_read_struct(&ping,&ping_info,&end_info);
2218 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2219 UnlockSemaphoreInfo(ping_semaphore);
2222 if (ping_pixels != (unsigned char *) NULL)
2223 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2225 if (logging != MagickFalse)
2226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2227 " exit ReadOnePNGImage() with error.");
2229 if (image != (Image *) NULL)
2231 InheritException(exception,exception);
2235 return(GetFirstImageInList(image));
2238 /* { For navigation to end of SETJMP-protected block. Within this
2239 * block, use png_error() instead of Throwing an Exception, to ensure
2240 * that libpng is able to clean up, and that the semaphore is unlocked.
2243 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2244 LockSemaphoreInfo(ping_semaphore);
2248 Prepare PNG for reading.
2251 mng_info->image_found++;
2252 png_set_sig_bytes(ping,8);
2254 if (LocaleCompare(image_info->magick,"MNG") == 0)
2256 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2257 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2258 png_set_read_fn(ping,image,png_get_data);
2260 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2261 png_permit_empty_plte(ping,MagickTrue);
2262 png_set_read_fn(ping,image,png_get_data);
2264 mng_info->image=image;
2265 mng_info->bytes_in_read_buffer=0;
2266 mng_info->found_empty_plte=MagickFalse;
2267 mng_info->have_saved_bkgd_index=MagickFalse;
2268 png_set_read_fn(ping,mng_info,mng_get_data);
2274 png_set_read_fn(ping,image,png_get_data);
2276 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2277 /* Ignore unused chunks and all unknown chunks except for vpAg */
2278 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2279 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2280 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2281 (int)sizeof(unused_chunks)/5);
2282 /* Callback for other unknown chunks */
2283 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2286 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2287 /* Disable new libpng-1.5.10 feature */
2288 png_set_check_for_invalid_index (ping, 0);
2291 #if (PNG_LIBPNG_VER < 10400)
2292 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2293 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2294 /* Disable thread-unsafe features of pnggccrd */
2295 if (png_access_version_number() >= 10200)
2297 png_uint_32 mmx_disable_mask=0;
2298 png_uint_32 asm_flags;
2300 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2301 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2302 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2303 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2304 asm_flags=png_get_asm_flags(ping);
2305 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2310 png_read_info(ping,ping_info);
2312 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2313 &ping_bit_depth,&ping_color_type,
2314 &ping_interlace_method,&ping_compression_method,
2315 &ping_filter_method);
2317 ping_file_depth = ping_bit_depth;
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 png_set_packing(ping);
2330 image->depth=ping_bit_depth;
2331 image->depth=GetImageQuantumDepth(image,MagickFalse);
2332 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2334 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2335 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2337 image->rendering_intent=UndefinedIntent;
2338 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2340 (void) ResetMagickMemory(&image->chromaticity,0,
2341 sizeof(image->chromaticity));
2344 if (logging != MagickFalse)
2346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2347 " PNG width: %.20g, height: %.20g",
2348 (double) ping_width, (double) ping_height);
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " PNG color_type: %d, bit_depth: %d",
2352 ping_color_type, ping_bit_depth);
2354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 " PNG compression_method: %d",
2356 ping_compression_method);
2358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2359 " PNG interlace_method: %d, filter_method: %d",
2360 ping_interlace_method,ping_filter_method);
2363 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2365 ping_found_gAMA=MagickTrue;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Found PNG gAMA chunk.");
2371 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2373 ping_found_cHRM=MagickTrue;
2374 if (logging != MagickFalse)
2375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2376 " Found PNG cHRM chunk.");
2379 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2381 ping_found_iCCP=MagickTrue;
2382 if (logging != MagickFalse)
2383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2384 " Found PNG iCCP chunk.");
2387 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2389 ping_found_sRGB=MagickTrue;
2390 if (logging != MagickFalse)
2391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2392 " Found PNG sRGB chunk.");
2395 #ifdef PNG_READ_iCCP_SUPPORTED
2396 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2401 #if (PNG_LIBPNG_VER < 10500)
2415 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2418 if (profile_length != 0)
2423 if (logging != MagickFalse)
2424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2425 " Reading PNG iCCP chunk.");
2426 profile=BlobToStringInfo(info,profile_length);
2427 if (profile == (StringInfo *) NULL)
2429 png_warning(ping, "ICC profile is NULL");
2430 profile=DestroyStringInfo(profile);
2434 (void) SetImageProfile(image,"icc",profile,exception);
2435 profile=DestroyStringInfo(profile);
2440 #if defined(PNG_READ_sRGB_SUPPORTED)
2442 if (mng_info->have_global_srgb)
2444 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2445 (mng_info->global_srgb_intent);
2448 if (png_get_sRGB(ping,ping_info,&intent))
2450 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2453 if (logging != MagickFalse)
2454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2455 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2460 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2461 if (mng_info->have_global_gama)
2462 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2464 if (png_get_gAMA(ping,ping_info,&file_gamma))
2466 image->gamma=(float) file_gamma;
2467 if (logging != MagickFalse)
2468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2469 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2473 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2475 if (mng_info->have_global_chrm != MagickFalse)
2477 (void) png_set_cHRM(ping,ping_info,
2478 mng_info->global_chrm.white_point.x,
2479 mng_info->global_chrm.white_point.y,
2480 mng_info->global_chrm.red_primary.x,
2481 mng_info->global_chrm.red_primary.y,
2482 mng_info->global_chrm.green_primary.x,
2483 mng_info->global_chrm.green_primary.y,
2484 mng_info->global_chrm.blue_primary.x,
2485 mng_info->global_chrm.blue_primary.y);
2489 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2491 (void) png_get_cHRM(ping,ping_info,
2492 &image->chromaticity.white_point.x,
2493 &image->chromaticity.white_point.y,
2494 &image->chromaticity.red_primary.x,
2495 &image->chromaticity.red_primary.y,
2496 &image->chromaticity.green_primary.x,
2497 &image->chromaticity.green_primary.y,
2498 &image->chromaticity.blue_primary.x,
2499 &image->chromaticity.blue_primary.y);
2503 if (image->rendering_intent != UndefinedIntent)
2505 png_set_sRGB(ping,ping_info,
2506 Magick_RenderingIntent_to_PNG_RenderingIntent
2507 (image->rendering_intent));
2508 png_set_gAMA(ping,ping_info,1.000f/2.200f);
2509 png_set_cHRM(ping,ping_info,
2510 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2511 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2513 #if defined(PNG_oFFs_SUPPORTED)
2514 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2516 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2517 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2519 if (logging != MagickFalse)
2520 if (image->page.x || image->page.y)
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2523 image->page.x,(double) image->page.y);
2526 #if defined(PNG_pHYs_SUPPORTED)
2527 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2529 if (mng_info->have_global_phys)
2531 png_set_pHYs(ping,ping_info,
2532 mng_info->global_x_pixels_per_unit,
2533 mng_info->global_y_pixels_per_unit,
2534 mng_info->global_phys_unit_type);
2538 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2541 Set image resolution.
2543 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2545 image->resolution.x=(double) x_resolution;
2546 image->resolution.y=(double) y_resolution;
2548 if (unit_type == PNG_RESOLUTION_METER)
2550 image->units=PixelsPerCentimeterResolution;
2551 image->resolution.x=(double) x_resolution/100.0;
2552 image->resolution.y=(double) y_resolution/100.0;
2555 if (logging != MagickFalse)
2556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2558 (double) x_resolution,(double) y_resolution,unit_type);
2562 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2567 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2569 if ((number_colors == 0) &&
2570 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2572 if (mng_info->global_plte_length)
2574 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2575 (int) mng_info->global_plte_length);
2577 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2579 if (mng_info->global_trns_length)
2582 "global tRNS has more entries than global PLTE");
2586 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2587 (int) mng_info->global_trns_length,NULL);
2590 #ifdef PNG_READ_bKGD_SUPPORTED
2592 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2593 mng_info->have_saved_bkgd_index ||
2595 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2600 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2601 if (mng_info->have_saved_bkgd_index)
2602 background.index=mng_info->saved_bkgd_index;
2604 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2605 background.index=ping_background->index;
2607 background.red=(png_uint_16)
2608 mng_info->global_plte[background.index].red;
2610 background.green=(png_uint_16)
2611 mng_info->global_plte[background.index].green;
2613 background.blue=(png_uint_16)
2614 mng_info->global_plte[background.index].blue;
2616 background.gray=(png_uint_16)
2617 mng_info->global_plte[background.index].green;
2619 png_set_bKGD(ping,ping_info,&background);
2624 png_error(ping,"No global PLTE in file");
2628 #ifdef PNG_READ_bKGD_SUPPORTED
2629 if (mng_info->have_global_bkgd &&
2630 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2631 image->background_color=mng_info->mng_global_bkgd;
2633 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2639 Set image background color.
2641 if (logging != MagickFalse)
2642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2643 " Reading PNG bKGD chunk.");
2645 /* Scale background components to 16-bit, then scale
2648 if (logging != MagickFalse)
2649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2650 " raw ping_background=(%d,%d,%d).",ping_background->red,
2651 ping_background->green,ping_background->blue);
2655 if (ping_file_depth == 1)
2658 else if (ping_file_depth == 2)
2661 else if (ping_file_depth == 4)
2664 if (ping_file_depth <= 8)
2667 ping_background->red *= bkgd_scale;
2668 ping_background->green *= bkgd_scale;
2669 ping_background->blue *= bkgd_scale;
2671 if (logging != MagickFalse)
2673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2674 " bkgd_scale=%d.",bkgd_scale);
2676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2677 " ping_background=(%d,%d,%d).",ping_background->red,
2678 ping_background->green,ping_background->blue);
2681 image->background_color.red=
2682 ScaleShortToQuantum(ping_background->red);
2684 image->background_color.green=
2685 ScaleShortToQuantum(ping_background->green);
2687 image->background_color.blue=
2688 ScaleShortToQuantum(ping_background->blue);
2690 image->background_color.alpha=OpaqueAlpha;
2692 if (logging != MagickFalse)
2693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2694 " image->background_color=(%.20g,%.20g,%.20g).",
2695 (double) image->background_color.red,
2696 (double) image->background_color.green,
2697 (double) image->background_color.blue);
2699 #endif /* PNG_READ_bKGD_SUPPORTED */
2701 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2704 Image has a tRNS chunk.
2712 if (logging != MagickFalse)
2713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2714 " Reading PNG tRNS chunk.");
2716 max_sample = (int) ((one << ping_file_depth) - 1);
2718 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2719 (int)ping_trans_color->gray > max_sample) ||
2720 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2721 ((int)ping_trans_color->red > max_sample ||
2722 (int)ping_trans_color->green > max_sample ||
2723 (int)ping_trans_color->blue > max_sample)))
2725 if (logging != MagickFalse)
2726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2727 " Ignoring PNG tRNS chunk with out-of-range sample.");
2728 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2729 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2730 image->alpha_trait=UndefinedPixelTrait;
2737 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2739 /* Scale transparent_color to short */
2740 transparent_color.red= scale_to_short*ping_trans_color->red;
2741 transparent_color.green= scale_to_short*ping_trans_color->green;
2742 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2743 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2745 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2747 if (logging != MagickFalse)
2749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2753 " scaled graylevel is %.20g.",transparent_color.alpha);
2755 transparent_color.red=transparent_color.alpha;
2756 transparent_color.green=transparent_color.alpha;
2757 transparent_color.blue=transparent_color.alpha;
2761 #if defined(PNG_READ_sBIT_SUPPORTED)
2762 if (mng_info->have_global_sbit)
2764 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2765 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2768 num_passes=png_set_interlace_handling(ping);
2770 png_read_update_info(ping,ping_info);
2772 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2775 Initialize image structure.
2777 mng_info->image_box.left=0;
2778 mng_info->image_box.right=(ssize_t) ping_width;
2779 mng_info->image_box.top=0;
2780 mng_info->image_box.bottom=(ssize_t) ping_height;
2781 if (mng_info->mng_type == 0)
2783 mng_info->mng_width=ping_width;
2784 mng_info->mng_height=ping_height;
2785 mng_info->frame=mng_info->image_box;
2786 mng_info->clip=mng_info->image_box;
2791 image->page.y=mng_info->y_off[mng_info->object_id];
2794 image->compression=ZipCompression;
2795 image->columns=ping_width;
2796 image->rows=ping_height;
2798 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2799 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2801 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2802 image->gamma == 1.0) &&
2803 !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
2804 !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2806 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2807 * image->colorspace to GRAY, and reset image->chromaticity.
2809 SetImageColorspace(image,GRAYColorspace,exception);
2813 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2814 " image->colorspace=%d",(int) image->colorspace);
2816 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2817 ((int) ping_bit_depth < 16 &&
2818 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2823 image->storage_class=PseudoClass;
2825 image->colors=one << ping_file_depth;
2826 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2827 if (image->colors > 256)
2830 if (image->colors > 65536L)
2831 image->colors=65536L;
2833 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2838 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2839 image->colors=(size_t) number_colors;
2841 if (logging != MagickFalse)
2842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2843 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2847 if (image->storage_class == PseudoClass)
2850 Initialize image colormap.
2852 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2853 png_error(ping,"Memory allocation failed");
2855 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2860 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2862 for (i=0; i < (ssize_t) number_colors; i++)
2864 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2865 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2866 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2869 for ( ; i < (ssize_t) image->colors; i++)
2871 image->colormap[i].red=0;
2872 image->colormap[i].green=0;
2873 image->colormap[i].blue=0;
2882 scale=(QuantumRange/((1UL << ping_file_depth)-1));
2887 for (i=0; i < (ssize_t) image->colors; i++)
2889 image->colormap[i].red=(Quantum) (i*scale);
2890 image->colormap[i].green=(Quantum) (i*scale);
2891 image->colormap[i].blue=(Quantum) (i*scale);
2896 /* Set some properties for reporting by "identify" */
2901 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
2902 ping_interlace_method in value */
2904 (void) FormatLocaleString(msg,MaxTextExtent,
2905 "%d, %d",(int) ping_width, (int) ping_height);
2906 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2908 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
2909 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2911 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2912 (int) ping_color_type,
2913 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
2914 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2916 if (ping_interlace_method == 0)
2918 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2919 (int) ping_interlace_method);
2921 else if (ping_interlace_method == 1)
2923 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2924 (int) ping_interlace_method);
2928 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2929 (int) ping_interlace_method);
2931 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2933 if (number_colors != 0)
2935 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2936 (int) number_colors);
2937 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
2943 Read image scanlines.
2945 if (image->delay != 0)
2946 mng_info->scenes_found++;
2948 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2949 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2950 (image_info->first_scene+image_info->number_scenes))))
2952 /* This happens later in non-ping decodes */
2953 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2954 image->storage_class=DirectClass;
2956 if (logging != MagickFalse)
2957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2958 " Skipping PNG image data for scene %.20g",(double)
2959 mng_info->scenes_found-1);
2960 png_destroy_read_struct(&ping,&ping_info,&end_info);
2962 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2963 UnlockSemaphoreInfo(ping_semaphore);
2966 if (logging != MagickFalse)
2967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2968 " exit ReadOnePNGImage().");
2973 if (logging != MagickFalse)
2974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2975 " Reading PNG IDAT chunk(s)");
2978 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2979 ping_rowbytes*sizeof(*ping_pixels));
2982 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2983 sizeof(*ping_pixels));
2985 if (ping_pixels == (unsigned char *) NULL)
2986 png_error(ping,"Memory allocation failed");
2988 if (logging != MagickFalse)
2989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2990 " Converting PNG pixels to pixel packets");
2992 Convert PNG pixels to pixel packets.
2994 quantum_info=AcquireQuantumInfo(image_info,image);
2996 if (quantum_info == (QuantumInfo *) NULL)
2997 png_error(ping,"Failed to allocate quantum_info");
2999 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3004 found_transparent_pixel;
3006 found_transparent_pixel=MagickFalse;
3008 if (image->storage_class == DirectClass)
3010 for (pass=0; pass < num_passes; pass++)
3013 Convert image to DirectClass pixel packets.
3015 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3016 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3017 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3018 BlendPixelTrait : UndefinedPixelTrait;
3020 for (y=0; y < (ssize_t) image->rows; y++)
3023 row_offset=ping_rowbytes*y;
3028 png_read_row(ping,ping_pixels+row_offset,NULL);
3030 if (pass < num_passes-1)
3033 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3035 if (q == (Quantum *) NULL)
3038 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3039 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3040 GrayQuantum,ping_pixels+row_offset,exception);
3042 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3043 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3044 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3046 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3047 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3048 RGBAQuantum,ping_pixels+row_offset,exception);
3050 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3051 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3052 IndexQuantum,ping_pixels+row_offset,exception);
3054 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3055 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3056 RGBQuantum,ping_pixels+row_offset,exception);
3058 if (found_transparent_pixel == MagickFalse)
3060 /* Is there a transparent pixel in the row? */
3061 if (y== 0 && logging != MagickFalse)
3062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3063 " Looking for cheap transparent pixel");
3065 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3067 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3068 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3069 (GetPixelAlpha(image,q) != OpaqueAlpha))
3071 if (logging != MagickFalse)
3072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3075 found_transparent_pixel = MagickTrue;
3078 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3079 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3080 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3081 transparent_color.red &&
3082 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3083 transparent_color.green &&
3084 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3085 transparent_color.blue))
3087 if (logging != MagickFalse)
3088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3090 found_transparent_pixel = MagickTrue;
3093 q+=GetPixelChannels(image);
3097 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3099 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3102 if (status == MagickFalse)
3105 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3109 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3111 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3112 if (status == MagickFalse)
3118 else /* image->storage_class != DirectClass */
3120 for (pass=0; pass < num_passes; pass++)
3129 Convert grayscale image to PseudoClass pixel packets.
3131 if (logging != MagickFalse)
3132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3133 " Converting grayscale pixels to pixel packets");
3135 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3136 BlendPixelTrait : UndefinedPixelTrait;
3138 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3139 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3140 sizeof(*quantum_scanline));
3142 if (quantum_scanline == (Quantum *) NULL)
3143 png_error(ping,"Memory allocation failed");
3145 for (y=0; y < (ssize_t) image->rows; y++)
3148 row_offset=ping_rowbytes*y;
3153 png_read_row(ping,ping_pixels+row_offset,NULL);
3155 if (pass < num_passes-1)
3158 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3160 if (q == (Quantum *) NULL)
3163 p=ping_pixels+row_offset;
3166 switch (ping_bit_depth)
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->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3281 UndefinedPixelTrait;
3283 if (logging != MagickFalse)
3285 if (found_transparent_pixel != MagickFalse)
3286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3287 " Found transparent pixel");
3290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3291 " No transparent pixel was found");
3293 ping_color_type&=0x03;
3298 if (quantum_info != (QuantumInfo *) NULL)
3299 quantum_info=DestroyQuantumInfo(quantum_info);
3301 if (image->storage_class == PseudoClass)
3306 alpha_trait=image->alpha_trait;
3307 image->alpha_trait=UndefinedPixelTrait;
3308 (void) SyncImage(image,exception);
3309 image->alpha_trait=alpha_trait;
3312 png_read_end(ping,end_info);
3314 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3315 (ssize_t) image_info->first_scene && image->delay != 0)
3317 png_destroy_read_struct(&ping,&ping_info,&end_info);
3318 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3320 (void) SetImageBackgroundColor(image,exception);
3321 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3322 UnlockSemaphoreInfo(ping_semaphore);
3324 if (logging != MagickFalse)
3325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3326 " exit ReadOnePNGImage() early.");
3330 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3336 Image has a transparent background.
3338 storage_class=image->storage_class;
3339 image->alpha_trait=BlendPixelTrait;
3341 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3343 if (storage_class == PseudoClass)
3345 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3347 for (x=0; x < ping_num_trans; x++)
3349 image->colormap[x].alpha_trait=BlendPixelTrait;
3350 image->colormap[x].alpha =
3351 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3355 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3357 for (x=0; x < (int) image->colors; x++)
3359 if (ScaleQuantumToShort(image->colormap[x].red) ==
3360 transparent_color.alpha)
3362 image->colormap[x].alpha_trait=BlendPixelTrait;
3363 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3367 (void) SyncImage(image,exception);
3370 #if 1 /* Should have already been done above, but glennrp problem P10
3375 for (y=0; y < (ssize_t) image->rows; y++)
3377 image->storage_class=storage_class;
3378 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3380 if (q == (Quantum *) NULL)
3384 /* Caution: on a Q8 build, this does not distinguish between
3385 * 16-bit colors that differ only in the low byte
3387 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3389 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3390 transparent_color.red &&
3391 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3392 transparent_color.green &&
3393 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3394 transparent_color.blue)
3396 SetPixelAlpha(image,TransparentAlpha,q);
3399 #if 0 /* I have not found a case where this is needed. */
3402 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3406 q+=GetPixelChannels(image);
3409 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3415 image->storage_class=DirectClass;
3418 for (j = 0; j < 2; j++)
3421 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3422 MagickTrue : MagickFalse;
3424 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3425 MagickTrue : MagickFalse;
3427 if (status != MagickFalse)
3428 for (i=0; i < (ssize_t) num_text; i++)
3430 /* Check for a profile */
3432 if (logging != MagickFalse)
3433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3434 " Reading PNG text chunk");
3436 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3438 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3448 length=text[i].text_length;
3449 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3451 if (value == (char *) NULL)
3453 png_error(ping,"Memory allocation failed");
3457 (void) ConcatenateMagickString(value,text[i].text,length+2);
3459 /* Don't save "density" or "units" property if we have a pHYs
3462 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3463 (LocaleCompare(text[i].key,"density") != 0 &&
3464 LocaleCompare(text[i].key,"units") != 0))
3465 (void) SetImageProperty(image,text[i].key,value,exception);
3467 if (logging != MagickFalse)
3469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3470 " length: %lu",(unsigned long) length);
3471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3472 " Keyword: %s",text[i].key);
3475 value=DestroyString(value);
3478 num_text_total += num_text;
3481 #ifdef MNG_OBJECT_BUFFERS
3483 Store the object if necessary.
3485 if (object_id && !mng_info->frozen[object_id])
3487 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3490 create a new object buffer.
3492 mng_info->ob[object_id]=(MngBuffer *)
3493 AcquireMagickMemory(sizeof(MngBuffer));
3495 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3497 mng_info->ob[object_id]->image=(Image *) NULL;
3498 mng_info->ob[object_id]->reference_count=1;
3502 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3503 mng_info->ob[object_id]->frozen)
3505 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3506 png_error(ping,"Memory allocation failed");
3508 if (mng_info->ob[object_id]->frozen)
3509 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3515 if (mng_info->ob[object_id]->image != (Image *) NULL)
3516 mng_info->ob[object_id]->image=DestroyImage
3517 (mng_info->ob[object_id]->image);
3519 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3522 if (mng_info->ob[object_id]->image != (Image *) NULL)
3523 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3526 png_error(ping, "Cloning image for object buffer failed");
3528 if (ping_width > 250000L || ping_height > 250000L)
3529 png_error(ping,"PNG Image dimensions are too large.");
3531 mng_info->ob[object_id]->width=ping_width;
3532 mng_info->ob[object_id]->height=ping_height;
3533 mng_info->ob[object_id]->color_type=ping_color_type;
3534 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3535 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3536 mng_info->ob[object_id]->compression_method=
3537 ping_compression_method;
3538 mng_info->ob[object_id]->filter_method=ping_filter_method;
3540 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3546 Copy the PLTE to the object buffer.
3548 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3549 mng_info->ob[object_id]->plte_length=number_colors;
3551 for (i=0; i < number_colors; i++)
3553 mng_info->ob[object_id]->plte[i]=plte[i];
3558 mng_info->ob[object_id]->plte_length=0;
3563 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3564 * alpha or if a valid tRNS chunk is present, no matter whether there
3565 * is actual transparency present.
3567 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3568 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3569 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3570 BlendPixelTrait : UndefinedPixelTrait;
3572 /* Set more properties for identify to retrieve */
3577 if (num_text_total != 0)
3579 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3580 (void) FormatLocaleString(msg,MaxTextExtent,
3581 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3582 (void) SetImageProperty(image,"png:text ",msg,
3586 if (num_raw_profiles != 0)
3588 (void) FormatLocaleString(msg,MaxTextExtent,
3589 "%d were found", num_raw_profiles);
3590 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3594 if (ping_found_cHRM != MagickFalse)
3596 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3597 "chunk was found (see Chromaticity, above)");
3598 (void) SetImageProperty(image,"png:cHRM ",msg,
3602 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3604 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3605 "chunk was found (see Background color, above)");
3606 (void) SetImageProperty(image,"png:bKGD ",msg,
3610 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3613 #if defined(PNG_iCCP_SUPPORTED)
3614 if (ping_found_iCCP != MagickFalse)
3615 (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 (ping_found_sRGB != MagickFalse)
3626 (void) FormatLocaleString(msg,MaxTextExtent,
3629 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3630 (void) SetImageProperty(image,"png:sRGB ",msg,
3635 if (ping_found_gAMA != MagickFalse)
3637 (void) FormatLocaleString(msg,MaxTextExtent,
3638 "gamma=%.8g (See Gamma, above)",
3640 (void) SetImageProperty(image,"png:gAMA ",msg,
3644 #if defined(PNG_pHYs_SUPPORTED)
3645 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3647 (void) FormatLocaleString(msg,MaxTextExtent,
3648 "x_res=%.10g, y_res=%.10g, units=%d",
3649 (double) x_resolution,(double) y_resolution, unit_type);
3650 (void) SetImageProperty(image,"png:pHYs ",msg,
3655 #if defined(PNG_oFFs_SUPPORTED)
3656 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3658 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3659 (double) image->page.x,(double) image->page.y);
3660 (void) SetImageProperty(image,"png:oFFs ",msg,
3665 if ((image->page.width != 0 && image->page.width != image->columns) ||
3666 (image->page.height != 0 && image->page.height != image->rows))
3668 (void) FormatLocaleString(msg,MaxTextExtent,
3669 "width=%.20g, height=%.20g",
3670 (double) image->page.width,(double) image->page.height);
3671 (void) SetImageProperty(image,"png:vpAg ",msg,
3677 Relinquish resources.
3679 png_destroy_read_struct(&ping,&ping_info,&end_info);
3681 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3683 if (logging != MagickFalse)
3684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3685 " exit ReadOnePNGImage()");
3687 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3688 UnlockSemaphoreInfo(ping_semaphore);
3691 /* } for navigation to beginning of SETJMP-protected block, revert to
3692 * Throwing an Exception when an error occurs.
3697 /* end of reading one PNG image */
3700 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3715 magic_number[MaxTextExtent];
3723 assert(image_info != (const ImageInfo *) NULL);
3724 assert(image_info->signature == MagickSignature);
3726 if (image_info->debug != MagickFalse)
3727 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3728 image_info->filename);
3730 assert(exception != (ExceptionInfo *) NULL);
3731 assert(exception->signature == MagickSignature);
3732 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3733 image=AcquireImage(image_info,exception);
3734 mng_info=(MngInfo *) NULL;
3735 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3737 if (status == MagickFalse)
3738 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3741 Verify PNG signature.
3743 count=ReadBlob(image,8,(unsigned char *) magic_number);
3745 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3746 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3749 Allocate a MngInfo structure.
3751 have_mng_structure=MagickFalse;
3752 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3754 if (mng_info == (MngInfo *) NULL)
3755 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3758 Initialize members of the MngInfo structure.
3760 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3761 mng_info->image=image;
3762 have_mng_structure=MagickTrue;
3765 image=ReadOnePNGImage(mng_info,image_info,exception);
3766 MngInfoFreeStruct(mng_info,&have_mng_structure);
3768 if (image == (Image *) NULL)
3770 if (previous != (Image *) NULL)
3772 if (previous->signature != MagickSignature)
3773 ThrowReaderException(CorruptImageError,"CorruptImage");
3775 (void) CloseBlob(previous);
3776 (void) DestroyImageList(previous);
3779 if (logging != MagickFalse)
3780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3781 "exit ReadPNGImage() with error");
3783 return((Image *) NULL);
3786 (void) CloseBlob(image);
3788 if ((image->columns == 0) || (image->rows == 0))
3790 if (logging != MagickFalse)
3791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3792 "exit ReadPNGImage() with error.");
3794 ThrowReaderException(CorruptImageError,"CorruptImage");
3797 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3798 ((image->gamma < .45) || (image->gamma > .46)))
3799 SetImageColorspace(image,RGBColorspace,exception);
3801 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3803 (void) SetImageType(image,TrueColorType,exception);
3804 image->alpha_trait=UndefinedPixelTrait;
3807 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3808 (void) SetImageType(image,TrueColorMatteType,exception);
3810 if (logging != MagickFalse)
3811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3812 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3813 (double) image->page.width,(double) image->page.height,
3814 (double) image->page.x,(double) image->page.y);
3816 if (logging != MagickFalse)
3817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3824 #if defined(JNG_SUPPORTED)
3826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3830 % R e a d O n e J N G I m a g e %
3834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3836 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3837 % (minus the 8-byte signature) and returns it. It allocates the memory
3838 % necessary for the new Image structure and returns a pointer to the new
3841 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3843 % The format of the ReadOneJNGImage method is:
3845 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3846 % ExceptionInfo *exception)
3848 % A description of each parameter follows:
3850 % o mng_info: Specifies a pointer to a MngInfo structure.
3852 % o image_info: the image info.
3854 % o exception: return any errors or warnings in this structure.
3857 static Image *ReadOneJNGImage(MngInfo *mng_info,
3858 const ImageInfo *image_info, ExceptionInfo *exception)
3885 jng_image_sample_depth,
3886 jng_image_compression_method,
3887 jng_image_interlace_method,
3888 jng_alpha_sample_depth,
3889 jng_alpha_compression_method,
3890 jng_alpha_filter_method,
3891 jng_alpha_interlace_method;
3893 register const Quantum
3903 register unsigned char
3914 jng_alpha_compression_method=0;
3915 jng_alpha_sample_depth=8;
3919 alpha_image=(Image *) NULL;
3920 color_image=(Image *) NULL;
3921 alpha_image_info=(ImageInfo *) NULL;
3922 color_image_info=(ImageInfo *) NULL;
3924 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3925 " Enter ReadOneJNGImage()");
3927 image=mng_info->image;
3929 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3932 Allocate next image structure.
3934 if (logging != MagickFalse)
3935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3936 " AcquireNextImage()");
3938 AcquireNextImage(image_info,image,exception);
3940 if (GetNextImageInList(image) == (Image *) NULL)
3941 return((Image *) NULL);
3943 image=SyncNextImageInList(image);
3945 mng_info->image=image;
3948 Signature bytes have already been read.
3951 read_JSEP=MagickFalse;
3952 reading_idat=MagickFalse;
3953 skip_to_iend=MagickFalse;
3957 type[MaxTextExtent];
3966 Read a new JNG chunk.
3968 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3969 2*GetBlobSize(image));
3971 if (status == MagickFalse)
3975 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3976 length=ReadBlobMSBLong(image);
3977 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3979 if (logging != MagickFalse)
3980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3981 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3982 type[0],type[1],type[2],type[3],(double) length);
3984 if (length > PNG_UINT_31_MAX || count == 0)
3985 ThrowReaderException(CorruptImageError,"CorruptImage");
3988 chunk=(unsigned char *) NULL;
3992 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3994 if (chunk == (unsigned char *) NULL)
3995 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3997 for (i=0; i < (ssize_t) length; i++)
3998 chunk[i]=(unsigned char) ReadBlobByte(image);
4003 (void) ReadBlobMSBLong(image); /* read crc word */
4008 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4013 if (memcmp(type,mng_JHDR,4) == 0)
4017 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4018 (p[2] << 8) | p[3]);
4019 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4020 (p[6] << 8) | p[7]);
4021 jng_color_type=p[8];
4022 jng_image_sample_depth=p[9];
4023 jng_image_compression_method=p[10];
4024 jng_image_interlace_method=p[11];
4026 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4029 jng_alpha_sample_depth=p[12];
4030 jng_alpha_compression_method=p[13];
4031 jng_alpha_filter_method=p[14];
4032 jng_alpha_interlace_method=p[15];
4034 if (logging != MagickFalse)
4036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4037 " jng_width: %16lu",(unsigned long) jng_width);
4039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4040 " jng_width: %16lu",(unsigned long) jng_height);
4042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4043 " jng_color_type: %16d",jng_color_type);
4045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4046 " jng_image_sample_depth: %3d",
4047 jng_image_sample_depth);
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " jng_image_compression_method:%3d",
4051 jng_image_compression_method);
4053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4054 " jng_image_interlace_method: %3d",
4055 jng_image_interlace_method);
4057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4058 " jng_alpha_sample_depth: %3d",
4059 jng_alpha_sample_depth);
4061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4062 " jng_alpha_compression_method:%3d",
4063 jng_alpha_compression_method);
4065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4066 " jng_alpha_filter_method: %3d",
4067 jng_alpha_filter_method);
4069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4070 " jng_alpha_interlace_method: %3d",
4071 jng_alpha_interlace_method);
4076 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4082 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4083 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4084 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4087 o create color_image
4088 o open color_blob, attached to color_image
4089 o if (color type has alpha)
4090 open alpha_blob, attached to alpha_image
4093 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4095 if (color_image_info == (ImageInfo *) NULL)
4096 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4098 GetImageInfo(color_image_info);
4099 color_image=AcquireImage(color_image_info,exception);
4101 if (color_image == (Image *) NULL)
4102 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4104 if (logging != MagickFalse)
4105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4106 " Creating color_blob.");
4108 (void) AcquireUniqueFilename(color_image->filename);
4109 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4112 if (status == MagickFalse)
4113 return((Image *) NULL);
4115 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4117 alpha_image_info=(ImageInfo *)
4118 AcquireMagickMemory(sizeof(ImageInfo));
4120 if (alpha_image_info == (ImageInfo *) NULL)
4121 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4123 GetImageInfo(alpha_image_info);
4124 alpha_image=AcquireImage(alpha_image_info,exception);
4126 if (alpha_image == (Image *) NULL)
4128 alpha_image=DestroyImage(alpha_image);
4129 ThrowReaderException(ResourceLimitError,
4130 "MemoryAllocationFailed");
4133 if (logging != MagickFalse)
4134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4135 " Creating alpha_blob.");
4137 (void) AcquireUniqueFilename(alpha_image->filename);
4138 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4141 if (status == MagickFalse)
4142 return((Image *) NULL);
4144 if (jng_alpha_compression_method == 0)
4149 if (logging != MagickFalse)
4150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4151 " Writing IHDR chunk to alpha_blob.");
4153 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4154 "\211PNG\r\n\032\n");
4156 (void) WriteBlobMSBULong(alpha_image,13L);
4157 PNGType(data,mng_IHDR);
4158 LogPNGChunk(logging,mng_IHDR,13L);
4159 PNGLong(data+4,jng_width);
4160 PNGLong(data+8,jng_height);
4161 data[12]=jng_alpha_sample_depth;
4162 data[13]=0; /* color_type gray */
4163 data[14]=0; /* compression method 0 */
4164 data[15]=0; /* filter_method 0 */
4165 data[16]=0; /* interlace_method 0 */
4166 (void) WriteBlob(alpha_image,17,data);
4167 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4170 reading_idat=MagickTrue;
4173 if (memcmp(type,mng_JDAT,4) == 0)
4175 /* Copy chunk to color_image->blob */
4177 if (logging != MagickFalse)
4178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4179 " Copying JDAT chunk data to color_blob.");
4181 (void) WriteBlob(color_image,length,chunk);
4184 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4189 if (memcmp(type,mng_IDAT,4) == 0)
4194 /* Copy IDAT header and chunk data to alpha_image->blob */
4196 if (image_info->ping == MagickFalse)
4198 if (logging != MagickFalse)
4199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4200 " Copying IDAT chunk data to alpha_blob.");
4202 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4203 PNGType(data,mng_IDAT);
4204 LogPNGChunk(logging,mng_IDAT,length);
4205 (void) WriteBlob(alpha_image,4,data);
4206 (void) WriteBlob(alpha_image,length,chunk);
4207 (void) WriteBlobMSBULong(alpha_image,
4208 crc32(crc32(0,data,4),chunk,(uInt) length));
4212 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4217 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4219 /* Copy chunk data to alpha_image->blob */
4221 if (image_info->ping == MagickFalse)
4223 if (logging != MagickFalse)
4224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4225 " Copying JDAA chunk data to alpha_blob.");
4227 (void) WriteBlob(alpha_image,length,chunk);
4231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4236 if (memcmp(type,mng_JSEP,4) == 0)
4238 read_JSEP=MagickTrue;
4241 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4246 if (memcmp(type,mng_bKGD,4) == 0)
4250 image->background_color.red=ScaleCharToQuantum(p[1]);
4251 image->background_color.green=image->background_color.red;
4252 image->background_color.blue=image->background_color.red;
4257 image->background_color.red=ScaleCharToQuantum(p[1]);
4258 image->background_color.green=ScaleCharToQuantum(p[3]);
4259 image->background_color.blue=ScaleCharToQuantum(p[5]);
4262 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4266 if (memcmp(type,mng_gAMA,4) == 0)
4269 image->gamma=((float) mng_get_long(p))*0.00001;
4271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4275 if (memcmp(type,mng_cHRM,4) == 0)
4279 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4280 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4281 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4282 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4283 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4284 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4285 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4286 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4289 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4293 if (memcmp(type,mng_sRGB,4) == 0)
4297 image->rendering_intent=
4298 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4299 image->gamma=1.000f/2.200f;
4300 image->chromaticity.red_primary.x=0.6400f;
4301 image->chromaticity.red_primary.y=0.3300f;
4302 image->chromaticity.green_primary.x=0.3000f;
4303 image->chromaticity.green_primary.y=0.6000f;
4304 image->chromaticity.blue_primary.x=0.1500f;
4305 image->chromaticity.blue_primary.y=0.0600f;
4306 image->chromaticity.white_point.x=0.3127f;
4307 image->chromaticity.white_point.y=0.3290f;
4310 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4314 if (memcmp(type,mng_oFFs,4) == 0)
4318 image->page.x=(ssize_t) mng_get_long(p);
4319 image->page.y=(ssize_t) mng_get_long(&p[4]);
4321 if ((int) p[8] != 0)
4323 image->page.x/=10000;
4324 image->page.y/=10000;
4329 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4334 if (memcmp(type,mng_pHYs,4) == 0)
4338 image->resolution.x=(double) mng_get_long(p);
4339 image->resolution.y=(double) mng_get_long(&p[4]);
4340 if ((int) p[8] == PNG_RESOLUTION_METER)
4342 image->units=PixelsPerCentimeterResolution;
4343 image->resolution.x=image->resolution.x/100.0f;
4344 image->resolution.y=image->resolution.y/100.0f;
4348 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4353 if (memcmp(type,mng_iCCP,4) == 0)
4357 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4364 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4366 if (memcmp(type,mng_IEND,4))
4376 Finish up reading image data:
4378 o read main image from color_blob.
4382 o if (color_type has alpha)
4383 if alpha_encoding is PNG
4384 read secondary image from alpha_blob via ReadPNG
4385 if alpha_encoding is JPEG
4386 read secondary image from alpha_blob via ReadJPEG
4390 o copy intensity of secondary image into
4391 alpha samples of main image.
4393 o destroy the secondary image.
4396 (void) CloseBlob(color_image);
4398 if (logging != MagickFalse)
4399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400 " Reading jng_image from color_blob.");
4402 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4403 color_image->filename);
4405 color_image_info->ping=MagickFalse; /* To do: avoid this */
4406 jng_image=ReadImage(color_image_info,exception);
4408 if (jng_image == (Image *) NULL)
4409 return((Image *) NULL);
4411 (void) RelinquishUniqueFileResource(color_image->filename);
4412 color_image=DestroyImage(color_image);
4413 color_image_info=DestroyImageInfo(color_image_info);
4415 if (jng_image == (Image *) NULL)
4416 return((Image *) NULL);
4418 if (logging != MagickFalse)
4419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4420 " Copying jng_image pixels to main image.");
4422 image->rows=jng_height;
4423 image->columns=jng_width;
4425 for (y=0; y < (ssize_t) image->rows; y++)
4427 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4428 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4429 for (x=(ssize_t) image->columns; x != 0; x--)
4431 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4432 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4433 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4434 q+=GetPixelChannels(image);
4435 s+=GetPixelChannels(jng_image);
4438 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4442 jng_image=DestroyImage(jng_image);
4444 if (image_info->ping == MagickFalse)
4446 if (jng_color_type >= 12)
4448 if (jng_alpha_compression_method == 0)
4452 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4453 PNGType(data,mng_IEND);
4454 LogPNGChunk(logging,mng_IEND,0L);
4455 (void) WriteBlob(alpha_image,4,data);
4456 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4459 (void) CloseBlob(alpha_image);
4461 if (logging != MagickFalse)
4462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4463 " Reading alpha from alpha_blob.");
4465 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4466 "%s",alpha_image->filename);
4468 jng_image=ReadImage(alpha_image_info,exception);
4470 if (jng_image != (Image *) NULL)
4471 for (y=0; y < (ssize_t) image->rows; y++)
4473 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4475 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4477 if (image->alpha_trait == BlendPixelTrait)
4478 for (x=(ssize_t) image->columns; x != 0; x--)
4480 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4481 q+=GetPixelChannels(image);
4482 s+=GetPixelChannels(jng_image);
4486 for (x=(ssize_t) image->columns; x != 0; x--)
4488 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4489 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4490 image->alpha_trait=BlendPixelTrait;
4491 q+=GetPixelChannels(image);
4492 s+=GetPixelChannels(jng_image);
4495 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4498 (void) RelinquishUniqueFileResource(alpha_image->filename);
4499 alpha_image=DestroyImage(alpha_image);
4500 alpha_image_info=DestroyImageInfo(alpha_image_info);
4501 if (jng_image != (Image *) NULL)
4502 jng_image=DestroyImage(jng_image);
4506 /* Read the JNG image. */
4508 if (mng_info->mng_type == 0)
4510 mng_info->mng_width=jng_width;
4511 mng_info->mng_height=jng_height;
4514 if (image->page.width == 0 && image->page.height == 0)
4516 image->page.width=jng_width;
4517 image->page.height=jng_height;
4520 if (image->page.x == 0 && image->page.y == 0)
4522 image->page.x=mng_info->x_off[mng_info->object_id];
4523 image->page.y=mng_info->y_off[mng_info->object_id];
4528 image->page.y=mng_info->y_off[mng_info->object_id];
4531 mng_info->image_found++;
4532 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4533 2*GetBlobSize(image));
4535 if (logging != MagickFalse)
4536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4537 " exit ReadOneJNGImage()");
4543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547 % R e a d J N G I m a g e %
4551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4553 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4554 % (including the 8-byte signature) and returns it. It allocates the memory
4555 % necessary for the new Image structure and returns a pointer to the new
4558 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4560 % The format of the ReadJNGImage method is:
4562 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4565 % A description of each parameter follows:
4567 % o image_info: the image info.
4569 % o exception: return any errors or warnings in this structure.
4573 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4588 magic_number[MaxTextExtent];
4596 assert(image_info != (const ImageInfo *) NULL);
4597 assert(image_info->signature == MagickSignature);
4598 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4599 assert(exception != (ExceptionInfo *) NULL);
4600 assert(exception->signature == MagickSignature);
4601 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4602 image=AcquireImage(image_info,exception);
4603 mng_info=(MngInfo *) NULL;
4604 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4606 if (status == MagickFalse)
4607 return((Image *) NULL);
4609 if (LocaleCompare(image_info->magick,"JNG") != 0)
4610 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4612 /* Verify JNG signature. */
4614 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4616 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4617 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4619 /* Allocate a MngInfo structure. */
4621 have_mng_structure=MagickFalse;
4622 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4624 if (mng_info == (MngInfo *) NULL)
4625 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4627 /* Initialize members of the MngInfo structure. */
4629 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4630 have_mng_structure=MagickTrue;
4632 mng_info->image=image;
4634 image=ReadOneJNGImage(mng_info,image_info,exception);
4635 MngInfoFreeStruct(mng_info,&have_mng_structure);
4637 if (image == (Image *) NULL)
4639 if (IsImageObject(previous) != MagickFalse)
4641 (void) CloseBlob(previous);
4642 (void) DestroyImageList(previous);
4645 if (logging != MagickFalse)
4646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4647 "exit ReadJNGImage() with error");
4649 return((Image *) NULL);
4651 (void) CloseBlob(image);
4653 if (image->columns == 0 || image->rows == 0)
4655 if (logging != MagickFalse)
4656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4657 "exit ReadJNGImage() with error");
4659 ThrowReaderException(CorruptImageError,"CorruptImage");
4662 if (logging != MagickFalse)
4663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4669 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4672 page_geometry[MaxTextExtent];
4705 #if defined(MNG_INSERT_LAYERS)
4707 mng_background_color;
4710 register unsigned char
4725 #if defined(MNG_INSERT_LAYERS)
4730 volatile unsigned int
4731 #ifdef MNG_OBJECT_BUFFERS
4732 mng_background_object=0,
4734 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4737 default_frame_timeout,
4739 #if defined(MNG_INSERT_LAYERS)
4745 /* These delays are all measured in image ticks_per_second,
4746 * not in MNG ticks_per_second
4749 default_frame_delay,
4753 #if defined(MNG_INSERT_LAYERS)
4762 previous_fb.bottom=0;
4764 previous_fb.right=0;
4766 default_fb.bottom=0;
4770 /* Open image file. */
4772 assert(image_info != (const ImageInfo *) NULL);
4773 assert(image_info->signature == MagickSignature);
4774 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4775 assert(exception != (ExceptionInfo *) NULL);
4776 assert(exception->signature == MagickSignature);
4777 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4778 image=AcquireImage(image_info,exception);
4779 mng_info=(MngInfo *) NULL;
4780 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4782 if (status == MagickFalse)
4783 return((Image *) NULL);
4785 first_mng_object=MagickFalse;
4787 have_mng_structure=MagickFalse;
4789 /* Allocate a MngInfo structure. */
4791 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4793 if (mng_info == (MngInfo *) NULL)
4794 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4796 /* Initialize members of the MngInfo structure. */
4798 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4799 mng_info->image=image;
4800 have_mng_structure=MagickTrue;
4802 if (LocaleCompare(image_info->magick,"MNG") == 0)
4805 magic_number[MaxTextExtent];
4807 /* Verify MNG signature. */
4808 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4809 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4810 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4812 /* Initialize some nonzero members of the MngInfo structure. */
4813 for (i=0; i < MNG_MAX_OBJECTS; i++)
4815 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4816 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4818 mng_info->exists[0]=MagickTrue;
4821 first_mng_object=MagickTrue;
4823 #if defined(MNG_INSERT_LAYERS)
4824 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4826 default_frame_delay=0;
4827 default_frame_timeout=0;
4830 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4832 skip_to_iend=MagickFalse;
4833 term_chunk_found=MagickFalse;
4834 mng_info->framing_mode=1;
4835 #if defined(MNG_INSERT_LAYERS)
4836 mandatory_back=MagickFalse;
4838 #if defined(MNG_INSERT_LAYERS)
4839 mng_background_color=image->background_color;
4841 default_fb=mng_info->frame;
4842 previous_fb=mng_info->frame;
4846 type[MaxTextExtent];
4848 if (LocaleCompare(image_info->magick,"MNG") == 0)
4857 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4858 length=ReadBlobMSBLong(image);
4859 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4861 if (logging != MagickFalse)
4862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4863 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4864 type[0],type[1],type[2],type[3],(double) length);
4866 if (length > PNG_UINT_31_MAX)
4870 ThrowReaderException(CorruptImageError,"CorruptImage");
4873 chunk=(unsigned char *) NULL;
4877 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4879 if (chunk == (unsigned char *) NULL)
4880 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4882 for (i=0; i < (ssize_t) length; i++)
4883 chunk[i]=(unsigned char) ReadBlobByte(image);
4888 (void) ReadBlobMSBLong(image); /* read crc word */
4890 #if !defined(JNG_SUPPORTED)
4891 if (memcmp(type,mng_JHDR,4) == 0)
4893 skip_to_iend=MagickTrue;
4895 if (mng_info->jhdr_warning == 0)
4896 (void) ThrowMagickException(exception,GetMagickModule(),
4897 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4899 mng_info->jhdr_warning++;
4902 if (memcmp(type,mng_DHDR,4) == 0)
4904 skip_to_iend=MagickTrue;
4906 if (mng_info->dhdr_warning == 0)
4907 (void) ThrowMagickException(exception,GetMagickModule(),
4908 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4910 mng_info->dhdr_warning++;
4912 if (memcmp(type,mng_MEND,4) == 0)
4917 if (memcmp(type,mng_IEND,4) == 0)
4918 skip_to_iend=MagickFalse;
4921 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4923 if (logging != MagickFalse)
4924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4930 if (memcmp(type,mng_MHDR,4) == 0)
4932 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4933 (p[2] << 8) | p[3]);
4935 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4936 (p[6] << 8) | p[7]);
4938 if (logging != MagickFalse)
4940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4941 " MNG width: %.20g",(double) mng_info->mng_width);
4942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4943 " MNG height: %.20g",(double) mng_info->mng_height);
4947 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4949 if (mng_info->ticks_per_second == 0)
4950 default_frame_delay=0;
4953 default_frame_delay=1UL*image->ticks_per_second/
4954 mng_info->ticks_per_second;
4956 frame_delay=default_frame_delay;
4962 simplicity=(size_t) mng_get_long(p);
4965 mng_type=1; /* Full MNG */
4967 if ((simplicity != 0) && ((simplicity | 11) == 11))
4968 mng_type=2; /* LC */
4970 if ((simplicity != 0) && ((simplicity | 9) == 9))
4971 mng_type=3; /* VLC */
4973 #if defined(MNG_INSERT_LAYERS)
4975 insert_layers=MagickTrue;
4977 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4979 /* Allocate next image structure. */
4980 AcquireNextImage(image_info,image,exception);
4982 if (GetNextImageInList(image) == (Image *) NULL)
4983 return((Image *) NULL);
4985 image=SyncNextImageInList(image);
4986 mng_info->image=image;
4989 if ((mng_info->mng_width > 65535L) ||
4990 (mng_info->mng_height > 65535L))
4991 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4993 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4994 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4995 mng_info->mng_height);
4997 mng_info->frame.left=0;
4998 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4999 mng_info->frame.top=0;
5000 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5001 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5003 for (i=0; i < MNG_MAX_OBJECTS; i++)
5004 mng_info->object_clip[i]=mng_info->frame;
5006 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5010 if (memcmp(type,mng_TERM,4) == 0)
5021 final_delay=(png_uint_32) mng_get_long(&p[2]);
5022 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5024 if (mng_iterations == PNG_UINT_31_MAX)
5027 image->iterations=mng_iterations;
5028 term_chunk_found=MagickTrue;
5031 if (logging != MagickFalse)
5033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5034 " repeat=%d",repeat);
5036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5037 " final_delay=%.20g",(double) final_delay);
5039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5040 " image->iterations=%.20g",(double) image->iterations);
5043 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5046 if (memcmp(type,mng_DEFI,4) == 0)
5049 (void) ThrowMagickException(exception,GetMagickModule(),
5050 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5053 object_id=(p[0] << 8) | p[1];
5055 if (mng_type == 2 && object_id != 0)
5056 (void) ThrowMagickException(exception,GetMagickModule(),
5057 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5060 if (object_id > MNG_MAX_OBJECTS)
5063 Instead of using a warning we should allocate a larger
5064 MngInfo structure and continue.
5066 (void) ThrowMagickException(exception,GetMagickModule(),
5067 CoderError,"object id too large","`%s'",image->filename);
5068 object_id=MNG_MAX_OBJECTS;
5071 if (mng_info->exists[object_id])
5072 if (mng_info->frozen[object_id])
5074 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5075 (void) ThrowMagickException(exception,
5076 GetMagickModule(),CoderError,
5077 "DEFI cannot redefine a frozen MNG object","`%s'",
5082 mng_info->exists[object_id]=MagickTrue;
5085 mng_info->invisible[object_id]=p[2];
5088 Extract object offset info.
5092 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5093 (p[5] << 16) | (p[6] << 8) | p[7]);
5095 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5096 (p[9] << 16) | (p[10] << 8) | p[11]);
5098 if (logging != MagickFalse)
5100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5101 " x_off[%d]: %.20g",object_id,(double)
5102 mng_info->x_off[object_id]);
5104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5105 " y_off[%d]: %.20g",object_id,(double)
5106 mng_info->y_off[object_id]);
5111 Extract object clipping info.
5114 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5117 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5120 if (memcmp(type,mng_bKGD,4) == 0)
5122 mng_info->have_global_bkgd=MagickFalse;
5126 mng_info->mng_global_bkgd.red=
5127 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5129 mng_info->mng_global_bkgd.green=
5130 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5132 mng_info->mng_global_bkgd.blue=
5133 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5135 mng_info->have_global_bkgd=MagickTrue;
5138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5141 if (memcmp(type,mng_BACK,4) == 0)
5143 #if defined(MNG_INSERT_LAYERS)
5145 mandatory_back=p[6];
5150 if (mandatory_back && length > 5)
5152 mng_background_color.red=
5153 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5155 mng_background_color.green=
5156 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5158 mng_background_color.blue=
5159 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5161 mng_background_color.alpha=OpaqueAlpha;
5164 #ifdef MNG_OBJECT_BUFFERS
5166 mng_background_object=(p[7] << 8) | p[8];
5169 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5173 if (memcmp(type,mng_PLTE,4) == 0)
5175 /* Read global PLTE. */
5177 if (length && (length < 769))
5179 if (mng_info->global_plte == (png_colorp) NULL)
5180 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5181 sizeof(*mng_info->global_plte));
5183 for (i=0; i < (ssize_t) (length/3); i++)
5185 mng_info->global_plte[i].red=p[3*i];
5186 mng_info->global_plte[i].green=p[3*i+1];
5187 mng_info->global_plte[i].blue=p[3*i+2];
5190 mng_info->global_plte_length=(unsigned int) (length/3);
5193 for ( ; i < 256; i++)
5195 mng_info->global_plte[i].red=i;
5196 mng_info->global_plte[i].green=i;
5197 mng_info->global_plte[i].blue=i;
5201 mng_info->global_plte_length=256;
5204 mng_info->global_plte_length=0;
5206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5210 if (memcmp(type,mng_tRNS,4) == 0)
5212 /* read global tRNS */
5215 for (i=0; i < (ssize_t) length; i++)
5216 mng_info->global_trns[i]=p[i];
5219 for ( ; i < 256; i++)
5220 mng_info->global_trns[i]=255;
5222 mng_info->global_trns_length=(unsigned int) length;
5223 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5226 if (memcmp(type,mng_gAMA,4) == 0)
5233 igamma=mng_get_long(p);
5234 mng_info->global_gamma=((float) igamma)*0.00001;
5235 mng_info->have_global_gama=MagickTrue;
5239 mng_info->have_global_gama=MagickFalse;
5241 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5245 if (memcmp(type,mng_cHRM,4) == 0)
5247 /* Read global cHRM */
5251 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5252 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5253 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5254 mng_info->global_chrm.red_primary.y=0.00001*
5255 mng_get_long(&p[12]);
5256 mng_info->global_chrm.green_primary.x=0.00001*
5257 mng_get_long(&p[16]);
5258 mng_info->global_chrm.green_primary.y=0.00001*
5259 mng_get_long(&p[20]);
5260 mng_info->global_chrm.blue_primary.x=0.00001*
5261 mng_get_long(&p[24]);
5262 mng_info->global_chrm.blue_primary.y=0.00001*
5263 mng_get_long(&p[28]);
5264 mng_info->have_global_chrm=MagickTrue;
5267 mng_info->have_global_chrm=MagickFalse;
5269 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5273 if (memcmp(type,mng_sRGB,4) == 0)
5280 mng_info->global_srgb_intent=
5281 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5282 mng_info->have_global_srgb=MagickTrue;
5285 mng_info->have_global_srgb=MagickFalse;
5287 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5291 if (memcmp(type,mng_iCCP,4) == 0)
5299 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5304 if (memcmp(type,mng_FRAM,4) == 0)
5307 (void) ThrowMagickException(exception,GetMagickModule(),
5308 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5311 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5312 image->delay=frame_delay;
5314 frame_delay=default_frame_delay;
5315 frame_timeout=default_frame_timeout;
5320 mng_info->framing_mode=p[0];
5322 if (logging != MagickFalse)
5323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5324 " Framing_mode=%d",mng_info->framing_mode);
5328 /* Note the delay and frame clipping boundaries. */
5330 p++; /* framing mode */
5332 while (*p && ((p-chunk) < (ssize_t) length))
5333 p++; /* frame name */
5335 p++; /* frame name terminator */
5337 if ((p-chunk) < (ssize_t) (length-4))
5344 change_delay=(*p++);
5345 change_timeout=(*p++);
5346 change_clipping=(*p++);
5347 p++; /* change_sync */
5351 frame_delay=1UL*image->ticks_per_second*
5354 if (mng_info->ticks_per_second != 0)
5355 frame_delay/=mng_info->ticks_per_second;
5358 frame_delay=PNG_UINT_31_MAX;
5360 if (change_delay == 2)
5361 default_frame_delay=frame_delay;
5365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5367 " Framing_delay=%.20g",(double) frame_delay);
5372 frame_timeout=1UL*image->ticks_per_second*
5375 if (mng_info->ticks_per_second != 0)
5376 frame_timeout/=mng_info->ticks_per_second;
5379 frame_timeout=PNG_UINT_31_MAX;
5381 if (change_delay == 2)
5382 default_frame_timeout=frame_timeout;
5386 if (logging != MagickFalse)
5387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5388 " Framing_timeout=%.20g",(double) frame_timeout);
5391 if (change_clipping)
5393 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5397 if (logging != MagickFalse)
5398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5399 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5400 (double) fb.left,(double) fb.right,(double) fb.top,
5401 (double) fb.bottom);
5403 if (change_clipping == 2)
5409 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5411 subframe_width=(size_t) (mng_info->clip.right
5412 -mng_info->clip.left);
5414 subframe_height=(size_t) (mng_info->clip.bottom
5415 -mng_info->clip.top);
5417 Insert a background layer behind the frame if framing_mode is 4.
5419 #if defined(MNG_INSERT_LAYERS)
5420 if (logging != MagickFalse)
5421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5422 " subframe_width=%.20g, subframe_height=%.20g",(double)
5423 subframe_width,(double) subframe_height);
5425 if (insert_layers && (mng_info->framing_mode == 4) &&
5426 (subframe_width) && (subframe_height))
5428 /* Allocate next image structure. */
5429 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5431 AcquireNextImage(image_info,image,exception);
5433 if (GetNextImageInList(image) == (Image *) NULL)
5435 image=DestroyImageList(image);
5436 MngInfoFreeStruct(mng_info,&have_mng_structure);
5437 return((Image *) NULL);
5440 image=SyncNextImageInList(image);
5443 mng_info->image=image;
5445 if (term_chunk_found)
5447 image->start_loop=MagickTrue;
5448 image->iterations=mng_iterations;
5449 term_chunk_found=MagickFalse;
5453 image->start_loop=MagickFalse;
5455 image->columns=subframe_width;
5456 image->rows=subframe_height;
5457 image->page.width=subframe_width;
5458 image->page.height=subframe_height;
5459 image->page.x=mng_info->clip.left;
5460 image->page.y=mng_info->clip.top;
5461 image->background_color=mng_background_color;
5462 image->alpha_trait=UndefinedPixelTrait;
5464 (void) SetImageBackgroundColor(image,exception);
5466 if (logging != MagickFalse)
5467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5468 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5469 (double) mng_info->clip.left,(double) mng_info->clip.right,
5470 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5476 if (memcmp(type,mng_CLIP,4) == 0)
5485 first_object=(p[0] << 8) | p[1];
5486 last_object=(p[2] << 8) | p[3];
5488 for (i=(int) first_object; i <= (int) last_object; i++)
5490 if (mng_info->exists[i] && !mng_info->frozen[i])
5495 box=mng_info->object_clip[i];
5496 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5500 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5503 if (memcmp(type,mng_SAVE,4) == 0)
5505 for (i=1; i < MNG_MAX_OBJECTS; i++)
5506 if (mng_info->exists[i])
5508 mng_info->frozen[i]=MagickTrue;
5509 #ifdef MNG_OBJECT_BUFFERS
5510 if (mng_info->ob[i] != (MngBuffer *) NULL)
5511 mng_info->ob[i]->frozen=MagickTrue;
5516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5521 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5523 /* Read DISC or SEEK. */
5525 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5527 for (i=1; i < MNG_MAX_OBJECTS; i++)
5528 MngInfoDiscardObject(mng_info,i);
5536 for (j=0; j < (ssize_t) length; j+=2)
5538 i=p[j] << 8 | p[j+1];
5539 MngInfoDiscardObject(mng_info,i);
5544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5549 if (memcmp(type,mng_MOVE,4) == 0)
5557 first_object=(p[0] << 8) | p[1];
5558 last_object=(p[2] << 8) | p[3];
5559 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5561 if (mng_info->exists[i] && !mng_info->frozen[i])
5569 old_pair.a=mng_info->x_off[i];
5570 old_pair.b=mng_info->y_off[i];
5571 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5572 mng_info->x_off[i]=new_pair.a;
5573 mng_info->y_off[i]=new_pair.b;
5577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5581 if (memcmp(type,mng_LOOP,4) == 0)
5583 ssize_t loop_iters=1;
5584 loop_level=chunk[0];
5585 mng_info->loop_active[loop_level]=1; /* mark loop active */
5587 /* Record starting point. */
5588 loop_iters=mng_get_long(&chunk[1]);
5590 if (logging != MagickFalse)
5591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5592 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5593 (double) loop_iters);
5595 if (loop_iters == 0)
5596 skipping_loop=loop_level;
5600 mng_info->loop_jump[loop_level]=TellBlob(image);
5601 mng_info->loop_count[loop_level]=loop_iters;
5604 mng_info->loop_iteration[loop_level]=0;
5605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5609 if (memcmp(type,mng_ENDL,4) == 0)
5611 loop_level=chunk[0];
5613 if (skipping_loop > 0)
5615 if (skipping_loop == loop_level)
5618 Found end of zero-iteration loop.
5621 mng_info->loop_active[loop_level]=0;
5627 if (mng_info->loop_active[loop_level] == 1)
5629 mng_info->loop_count[loop_level]--;
5630 mng_info->loop_iteration[loop_level]++;
5632 if (logging != MagickFalse)
5633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5634 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5635 (double) loop_level,(double)
5636 mng_info->loop_count[loop_level]);
5638 if (mng_info->loop_count[loop_level] != 0)
5640 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5644 ThrowReaderException(CorruptImageError,
5645 "ImproperImageHeader");
5656 mng_info->loop_active[loop_level]=0;
5658 for (i=0; i < loop_level; i++)
5659 if (mng_info->loop_active[i] == 1)
5660 last_level=(short) i;
5661 loop_level=last_level;
5666 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5670 if (memcmp(type,mng_CLON,4) == 0)
5672 if (mng_info->clon_warning == 0)
5673 (void) ThrowMagickException(exception,GetMagickModule(),
5674 CoderError,"CLON is not implemented yet","`%s'",
5677 mng_info->clon_warning++;
5680 if (memcmp(type,mng_MAGN,4) == 0)
5695 magn_first=(p[0] << 8) | p[1];
5701 magn_last=(p[2] << 8) | p[3];
5704 magn_last=magn_first;
5705 #ifndef MNG_OBJECT_BUFFERS
5706 if (magn_first || magn_last)
5707 if (mng_info->magn_warning == 0)
5709 (void) ThrowMagickException(exception,
5710 GetMagickModule(),CoderError,
5711 "MAGN is not implemented yet for nonzero objects",
5712 "`%s'",image->filename);
5714 mng_info->magn_warning++;
5724 magn_mx=(p[5] << 8) | p[6];
5733 magn_my=(p[7] << 8) | p[8];
5742 magn_ml=(p[9] << 8) | p[10];
5751 magn_mr=(p[11] << 8) | p[12];
5760 magn_mt=(p[13] << 8) | p[14];
5769 magn_mb=(p[15] << 8) | p[16];
5781 magn_methy=magn_methx;
5784 if (magn_methx > 5 || magn_methy > 5)
5785 if (mng_info->magn_warning == 0)
5787 (void) ThrowMagickException(exception,
5788 GetMagickModule(),CoderError,
5789 "Unknown MAGN method in MNG datastream","`%s'",
5792 mng_info->magn_warning++;
5794 #ifdef MNG_OBJECT_BUFFERS
5795 /* Magnify existing objects in the range magn_first to magn_last */
5797 if (magn_first == 0 || magn_last == 0)
5799 /* Save the magnification factors for object 0 */
5800 mng_info->magn_mb=magn_mb;
5801 mng_info->magn_ml=magn_ml;
5802 mng_info->magn_mr=magn_mr;
5803 mng_info->magn_mt=magn_mt;
5804 mng_info->magn_mx=magn_mx;
5805 mng_info->magn_my=magn_my;
5806 mng_info->magn_methx=magn_methx;
5807 mng_info->magn_methy=magn_methy;
5811 if (memcmp(type,mng_PAST,4) == 0)
5813 if (mng_info->past_warning == 0)
5814 (void) ThrowMagickException(exception,GetMagickModule(),
5815 CoderError,"PAST is not implemented yet","`%s'",
5818 mng_info->past_warning++;
5821 if (memcmp(type,mng_SHOW,4) == 0)
5823 if (mng_info->show_warning == 0)
5824 (void) ThrowMagickException(exception,GetMagickModule(),
5825 CoderError,"SHOW is not implemented yet","`%s'",
5828 mng_info->show_warning++;
5831 if (memcmp(type,mng_sBIT,4) == 0)
5834 mng_info->have_global_sbit=MagickFalse;
5838 mng_info->global_sbit.gray=p[0];
5839 mng_info->global_sbit.red=p[0];
5840 mng_info->global_sbit.green=p[1];
5841 mng_info->global_sbit.blue=p[2];
5842 mng_info->global_sbit.alpha=p[3];
5843 mng_info->have_global_sbit=MagickTrue;
5846 if (memcmp(type,mng_pHYs,4) == 0)
5850 mng_info->global_x_pixels_per_unit=
5851 (size_t) mng_get_long(p);
5852 mng_info->global_y_pixels_per_unit=
5853 (size_t) mng_get_long(&p[4]);
5854 mng_info->global_phys_unit_type=p[8];
5855 mng_info->have_global_phys=MagickTrue;
5859 mng_info->have_global_phys=MagickFalse;
5861 if (memcmp(type,mng_pHYg,4) == 0)
5863 if (mng_info->phyg_warning == 0)
5864 (void) ThrowMagickException(exception,GetMagickModule(),
5865 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5867 mng_info->phyg_warning++;
5869 if (memcmp(type,mng_BASI,4) == 0)
5871 skip_to_iend=MagickTrue;
5873 if (mng_info->basi_warning == 0)
5874 (void) ThrowMagickException(exception,GetMagickModule(),
5875 CoderError,"BASI is not implemented yet","`%s'",
5878 mng_info->basi_warning++;
5879 #ifdef MNG_BASI_SUPPORTED
5880 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5881 (p[2] << 8) | p[3]);
5882 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5883 (p[6] << 8) | p[7]);
5884 basi_color_type=p[8];
5885 basi_compression_method=p[9];
5886 basi_filter_type=p[10];
5887 basi_interlace_method=p[11];
5889 basi_red=(p[12] << 8) & p[13];
5895 basi_green=(p[14] << 8) & p[15];
5901 basi_blue=(p[16] << 8) & p[17];
5907 basi_alpha=(p[18] << 8) & p[19];
5911 if (basi_sample_depth == 16)
5918 basi_viewable=p[20];
5924 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5928 if (memcmp(type,mng_IHDR,4)
5929 #if defined(JNG_SUPPORTED)
5930 && memcmp(type,mng_JHDR,4)
5934 /* Not an IHDR or JHDR chunk */
5936 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5941 if (logging != MagickFalse)
5942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5943 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5945 mng_info->exists[object_id]=MagickTrue;
5946 mng_info->viewable[object_id]=MagickTrue;
5948 if (mng_info->invisible[object_id])
5950 if (logging != MagickFalse)
5951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5952 " Skipping invisible object");
5954 skip_to_iend=MagickTrue;
5955 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5958 #if defined(MNG_INSERT_LAYERS)
5960 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5962 image_width=(size_t) mng_get_long(p);
5963 image_height=(size_t) mng_get_long(&p[4]);
5965 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5968 Insert a transparent background layer behind the entire animation
5969 if it is not full screen.
5971 #if defined(MNG_INSERT_LAYERS)
5972 if (insert_layers && mng_type && first_mng_object)
5974 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5975 (image_width < mng_info->mng_width) ||
5976 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5977 (image_height < mng_info->mng_height) ||
5978 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5980 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5983 Allocate next image structure.
5985 AcquireNextImage(image_info,image,exception);
5987 if (GetNextImageInList(image) == (Image *) NULL)
5989 image=DestroyImageList(image);
5990 MngInfoFreeStruct(mng_info,&have_mng_structure);
5991 return((Image *) NULL);
5994 image=SyncNextImageInList(image);
5996 mng_info->image=image;
5998 if (term_chunk_found)
6000 image->start_loop=MagickTrue;
6001 image->iterations=mng_iterations;
6002 term_chunk_found=MagickFalse;
6006 image->start_loop=MagickFalse;
6008 /* Make a background rectangle. */
6011 image->columns=mng_info->mng_width;
6012 image->rows=mng_info->mng_height;
6013 image->page.width=mng_info->mng_width;
6014 image->page.height=mng_info->mng_height;
6017 image->background_color=mng_background_color;
6018 (void) SetImageBackgroundColor(image,exception);
6019 if (logging != MagickFalse)
6020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6021 " Inserted transparent background layer, W=%.20g, H=%.20g",
6022 (double) mng_info->mng_width,(double) mng_info->mng_height);
6026 Insert a background layer behind the upcoming image if
6027 framing_mode is 3, and we haven't already inserted one.
6029 if (insert_layers && (mng_info->framing_mode == 3) &&
6030 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6031 (simplicity & 0x08)))
6033 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6036 Allocate next image structure.
6038 AcquireNextImage(image_info,image,exception);
6040 if (GetNextImageInList(image) == (Image *) NULL)
6042 image=DestroyImageList(image);
6043 MngInfoFreeStruct(mng_info,&have_mng_structure);
6044 return((Image *) NULL);
6047 image=SyncNextImageInList(image);
6050 mng_info->image=image;
6052 if (term_chunk_found)
6054 image->start_loop=MagickTrue;
6055 image->iterations=mng_iterations;
6056 term_chunk_found=MagickFalse;
6060 image->start_loop=MagickFalse;
6063 image->columns=subframe_width;
6064 image->rows=subframe_height;
6065 image->page.width=subframe_width;
6066 image->page.height=subframe_height;
6067 image->page.x=mng_info->clip.left;
6068 image->page.y=mng_info->clip.top;
6069 image->background_color=mng_background_color;
6070 image->alpha_trait=UndefinedPixelTrait;
6071 (void) SetImageBackgroundColor(image,exception);
6073 if (logging != MagickFalse)
6074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6075 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6076 (double) mng_info->clip.left,(double) mng_info->clip.right,
6077 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6079 #endif /* MNG_INSERT_LAYERS */
6080 first_mng_object=MagickFalse;
6082 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6085 Allocate next image structure.
6087 AcquireNextImage(image_info,image,exception);
6089 if (GetNextImageInList(image) == (Image *) NULL)
6091 image=DestroyImageList(image);
6092 MngInfoFreeStruct(mng_info,&have_mng_structure);
6093 return((Image *) NULL);
6096 image=SyncNextImageInList(image);
6098 mng_info->image=image;
6099 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6100 GetBlobSize(image));
6102 if (status == MagickFalse)
6105 if (term_chunk_found)
6107 image->start_loop=MagickTrue;
6108 term_chunk_found=MagickFalse;
6112 image->start_loop=MagickFalse;
6114 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6116 image->delay=frame_delay;
6117 frame_delay=default_frame_delay;
6123 image->page.width=mng_info->mng_width;
6124 image->page.height=mng_info->mng_height;
6125 image->page.x=mng_info->x_off[object_id];
6126 image->page.y=mng_info->y_off[object_id];
6127 image->iterations=mng_iterations;
6130 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6133 if (logging != MagickFalse)
6134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6135 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6138 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6141 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6145 mng_info->image=image;
6146 mng_info->mng_type=mng_type;
6147 mng_info->object_id=object_id;
6149 if (memcmp(type,mng_IHDR,4) == 0)
6150 image=ReadOnePNGImage(mng_info,image_info,exception);
6152 #if defined(JNG_SUPPORTED)
6154 image=ReadOneJNGImage(mng_info,image_info,exception);
6157 if (image == (Image *) NULL)
6159 if (IsImageObject(previous) != MagickFalse)
6161 (void) DestroyImageList(previous);
6162 (void) CloseBlob(previous);
6165 MngInfoFreeStruct(mng_info,&have_mng_structure);
6166 return((Image *) NULL);
6169 if (image->columns == 0 || image->rows == 0)
6171 (void) CloseBlob(image);
6172 image=DestroyImageList(image);
6173 MngInfoFreeStruct(mng_info,&have_mng_structure);
6174 return((Image *) NULL);
6177 mng_info->image=image;
6184 if (mng_info->magn_methx || mng_info->magn_methy)
6190 if (logging != MagickFalse)
6191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6192 " Processing MNG MAGN chunk");
6194 if (mng_info->magn_methx == 1)
6196 magnified_width=mng_info->magn_ml;
6198 if (image->columns > 1)
6199 magnified_width += mng_info->magn_mr;
6201 if (image->columns > 2)
6202 magnified_width += (png_uint_32)
6203 ((image->columns-2)*(mng_info->magn_mx));
6208 magnified_width=(png_uint_32) image->columns;
6210 if (image->columns > 1)
6211 magnified_width += mng_info->magn_ml-1;
6213 if (image->columns > 2)
6214 magnified_width += mng_info->magn_mr-1;
6216 if (image->columns > 3)
6217 magnified_width += (png_uint_32)
6218 ((image->columns-3)*(mng_info->magn_mx-1));
6221 if (mng_info->magn_methy == 1)
6223 magnified_height=mng_info->magn_mt;
6225 if (image->rows > 1)
6226 magnified_height += mng_info->magn_mb;
6228 if (image->rows > 2)
6229 magnified_height += (png_uint_32)
6230 ((image->rows-2)*(mng_info->magn_my));
6235 magnified_height=(png_uint_32) image->rows;
6237 if (image->rows > 1)
6238 magnified_height += mng_info->magn_mt-1;
6240 if (image->rows > 2)
6241 magnified_height += mng_info->magn_mb-1;
6243 if (image->rows > 3)
6244 magnified_height += (png_uint_32)
6245 ((image->rows-3)*(mng_info->magn_my-1));
6248 if (magnified_height > image->rows ||
6249 magnified_width > image->columns)
6276 /* Allocate next image structure. */
6278 if (logging != MagickFalse)
6279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6280 " Allocate magnified image");
6282 AcquireNextImage(image_info,image,exception);
6284 if (GetNextImageInList(image) == (Image *) NULL)
6286 image=DestroyImageList(image);
6287 MngInfoFreeStruct(mng_info,&have_mng_structure);
6288 return((Image *) NULL);
6291 large_image=SyncNextImageInList(image);
6293 large_image->columns=magnified_width;
6294 large_image->rows=magnified_height;
6296 magn_methx=mng_info->magn_methx;
6297 magn_methy=mng_info->magn_methy;
6299 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6300 #define QM unsigned short
6301 if (magn_methx != 1 || magn_methy != 1)
6304 Scale pixels to unsigned shorts to prevent
6305 overflow of intermediate values of interpolations
6307 for (y=0; y < (ssize_t) image->rows; y++)
6309 q=GetAuthenticPixels(image,0,y,image->columns,1,
6312 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6314 SetPixelRed(image,ScaleQuantumToShort(
6315 GetPixelRed(image,q)),q);
6316 SetPixelGreen(image,ScaleQuantumToShort(
6317 GetPixelGreen(image,q)),q);
6318 SetPixelBlue(image,ScaleQuantumToShort(
6319 GetPixelBlue(image,q)),q);
6320 SetPixelAlpha(image,ScaleQuantumToShort(
6321 GetPixelAlpha(image,q)),q);
6322 q+=GetPixelChannels(image);
6325 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6333 if (image->alpha_trait == BlendPixelTrait)
6334 (void) SetImageBackgroundColor(large_image,exception);
6338 large_image->background_color.alpha=OpaqueAlpha;
6339 (void) SetImageBackgroundColor(large_image,exception);
6341 if (magn_methx == 4)
6344 if (magn_methx == 5)
6347 if (magn_methy == 4)
6350 if (magn_methy == 5)
6354 /* magnify the rows into the right side of the large image */
6356 if (logging != MagickFalse)
6357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6358 " Magnify the rows to %.20g",(double) large_image->rows);
6359 m=(ssize_t) mng_info->magn_mt;
6361 length=(size_t) image->columns*GetPixelChannels(image);
6362 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6363 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6365 if ((prev == (Quantum *) NULL) ||
6366 (next == (Quantum *) NULL))
6368 image=DestroyImageList(image);
6369 MngInfoFreeStruct(mng_info,&have_mng_structure);
6370 ThrowReaderException(ResourceLimitError,
6371 "MemoryAllocationFailed");
6374 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6375 (void) CopyMagickMemory(next,n,length);
6377 for (y=0; y < (ssize_t) image->rows; y++)
6380 m=(ssize_t) mng_info->magn_mt;
6382 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6383 m=(ssize_t) mng_info->magn_mb;
6385 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6386 m=(ssize_t) mng_info->magn_mb;
6388 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6392 m=(ssize_t) mng_info->magn_my;
6398 if (y < (ssize_t) image->rows-1)
6400 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6402 (void) CopyMagickMemory(next,n,length);
6405 for (i=0; i < m; i++, yy++)
6410 assert(yy < (ssize_t) large_image->rows);
6413 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6415 q+=(large_image->columns-image->columns)*
6416 GetPixelChannels(large_image);
6418 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6420 /* To do: get color as function of indexes[x] */
6422 if (image->storage_class == PseudoClass)
6427 if (magn_methy <= 1)
6429 /* replicate previous */
6430 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6431 SetPixelGreen(large_image,GetPixelGreen(image,
6433 SetPixelBlue(large_image,GetPixelBlue(image,
6435 SetPixelAlpha(large_image,GetPixelAlpha(image,
6439 else if (magn_methy == 2 || magn_methy == 4)
6443 SetPixelRed(large_image,GetPixelRed(image,
6445 SetPixelGreen(large_image,GetPixelGreen(image,
6447 SetPixelBlue(large_image,GetPixelBlue(image,
6449 SetPixelAlpha(large_image,GetPixelAlpha(image,
6456 SetPixelRed(large_image,((QM) (((ssize_t)
6457 (2*i*(GetPixelRed(image,n)
6458 -GetPixelRed(image,pixels)+m))/
6460 +GetPixelRed(image,pixels)))),q);
6461 SetPixelGreen(large_image,((QM) (((ssize_t)
6462 (2*i*(GetPixelGreen(image,n)
6463 -GetPixelGreen(image,pixels)+m))/
6465 +GetPixelGreen(image,pixels)))),q);
6466 SetPixelBlue(large_image,((QM) (((ssize_t)
6467 (2*i*(GetPixelBlue(image,n)
6468 -GetPixelBlue(image,pixels)+m))/
6470 +GetPixelBlue(image,pixels)))),q);
6472 if (image->alpha_trait == BlendPixelTrait)
6473 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6474 (2*i*(GetPixelAlpha(image,n)
6475 -GetPixelAlpha(image,pixels)+m))
6477 GetPixelAlpha(image,pixels)))),q);
6480 if (magn_methy == 4)
6482 /* Replicate nearest */
6483 if (i <= ((m+1) << 1))
6484 SetPixelAlpha(large_image,GetPixelAlpha(image,
6487 SetPixelAlpha(large_image,GetPixelAlpha(image,
6492 else /* if (magn_methy == 3 || magn_methy == 5) */
6494 /* Replicate nearest */
6495 if (i <= ((m+1) << 1))
6497 SetPixelRed(large_image,GetPixelRed(image,
6499 SetPixelGreen(large_image,GetPixelGreen(image,
6501 SetPixelBlue(large_image,GetPixelBlue(image,
6503 SetPixelAlpha(large_image,GetPixelAlpha(image,
6509 SetPixelRed(large_image,GetPixelRed(image,n),q);
6510 SetPixelGreen(large_image,GetPixelGreen(image,n),
6512 SetPixelBlue(large_image,GetPixelBlue(image,n),
6514 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6518 if (magn_methy == 5)
6520 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6521 (GetPixelAlpha(image,n)
6522 -GetPixelAlpha(image,pixels))
6523 +m))/((ssize_t) (m*2))
6524 +GetPixelAlpha(image,pixels)),q);
6527 n+=GetPixelChannels(image);
6528 q+=GetPixelChannels(large_image);
6529 pixels+=GetPixelChannels(image);
6532 if (SyncAuthenticPixels(large_image,exception) == 0)
6538 prev=(Quantum *) RelinquishMagickMemory(prev);
6539 next=(Quantum *) RelinquishMagickMemory(next);
6541 length=image->columns;
6543 if (logging != MagickFalse)
6544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6545 " Delete original image");
6547 DeleteImageFromList(&image);
6551 mng_info->image=image;
6553 /* magnify the columns */
6554 if (logging != MagickFalse)
6555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6556 " Magnify the columns to %.20g",(double) image->columns);
6558 for (y=0; y < (ssize_t) image->rows; y++)
6563 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6564 pixels=q+(image->columns-length)*GetPixelChannels(image);
6565 n=pixels+GetPixelChannels(image);
6567 for (x=(ssize_t) (image->columns-length);
6568 x < (ssize_t) image->columns; x++)
6570 /* To do: Rewrite using Get/Set***PixelChannel() */
6572 if (x == (ssize_t) (image->columns-length))
6573 m=(ssize_t) mng_info->magn_ml;
6575 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6576 m=(ssize_t) mng_info->magn_mr;
6578 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6579 m=(ssize_t) mng_info->magn_mr;
6581 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6585 m=(ssize_t) mng_info->magn_mx;
6587 for (i=0; i < m; i++)
6589 if (magn_methx <= 1)
6591 /* replicate previous */
6592 SetPixelRed(image,GetPixelRed(image,pixels),q);
6593 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6594 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6595 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6598 else if (magn_methx == 2 || magn_methx == 4)
6602 SetPixelRed(image,GetPixelRed(image,pixels),q);
6603 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6604 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6605 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6608 /* To do: Rewrite using Get/Set***PixelChannel() */
6612 SetPixelRed(image,(QM) ((2*i*(
6613 GetPixelRed(image,n)
6614 -GetPixelRed(image,pixels))+m)
6616 GetPixelRed(image,pixels)),q);
6618 SetPixelGreen(image,(QM) ((2*i*(
6619 GetPixelGreen(image,n)
6620 -GetPixelGreen(image,pixels))+m)
6622 GetPixelGreen(image,pixels)),q);
6624 SetPixelBlue(image,(QM) ((2*i*(
6625 GetPixelBlue(image,n)
6626 -GetPixelBlue(image,pixels))+m)
6628 GetPixelBlue(image,pixels)),q);
6629 if (image->alpha_trait == BlendPixelTrait)
6630 SetPixelAlpha(image,(QM) ((2*i*(
6631 GetPixelAlpha(image,n)
6632 -GetPixelAlpha(image,pixels))+m)
6634 GetPixelAlpha(image,pixels)),q);
6637 if (magn_methx == 4)
6639 /* Replicate nearest */
6640 if (i <= ((m+1) << 1))
6642 SetPixelAlpha(image,
6643 GetPixelAlpha(image,pixels)+0,q);
6647 SetPixelAlpha(image,
6648 GetPixelAlpha(image,n)+0,q);
6653 else /* if (magn_methx == 3 || magn_methx == 5) */
6655 /* Replicate nearest */
6656 if (i <= ((m+1) << 1))
6658 SetPixelRed(image,GetPixelRed(image,pixels),q);
6659 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6660 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6661 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6666 SetPixelRed(image,GetPixelRed(image,n),q);
6667 SetPixelGreen(image,GetPixelGreen(image,n),q);
6668 SetPixelBlue(image,GetPixelBlue(image,n),q);
6669 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6672 if (magn_methx == 5)
6675 SetPixelAlpha(image,
6676 (QM) ((2*i*( GetPixelAlpha(image,n)
6677 -GetPixelAlpha(image,pixels))+m)/
6679 +GetPixelAlpha(image,pixels)),q);
6682 q+=GetPixelChannels(image);
6684 n+=GetPixelChannels(image);
6685 p+=GetPixelChannels(image);
6688 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6691 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6692 if (magn_methx != 1 || magn_methy != 1)
6695 Rescale pixels to Quantum
6697 for (y=0; y < (ssize_t) image->rows; y++)
6699 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6701 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6703 SetPixelRed(image,ScaleShortToQuantum(
6704 GetPixelRed(image,q)),q);
6705 SetPixelGreen(image,ScaleShortToQuantum(
6706 GetPixelGreen(image,q)),q);
6707 SetPixelBlue(image,ScaleShortToQuantum(
6708 GetPixelBlue(image,q)),q);
6709 SetPixelAlpha(image,ScaleShortToQuantum(
6710 GetPixelAlpha(image,q)),q);
6711 q+=GetPixelChannels(image);
6714 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6719 if (logging != MagickFalse)
6720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6721 " Finished MAGN processing");
6726 Crop_box is with respect to the upper left corner of the MNG.
6728 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6729 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6730 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6731 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6732 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6733 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6734 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6735 if ((crop_box.left != (mng_info->image_box.left
6736 +mng_info->x_off[object_id])) ||
6737 (crop_box.right != (mng_info->image_box.right
6738 +mng_info->x_off[object_id])) ||
6739 (crop_box.top != (mng_info->image_box.top
6740 +mng_info->y_off[object_id])) ||
6741 (crop_box.bottom != (mng_info->image_box.bottom
6742 +mng_info->y_off[object_id])))
6744 if (logging != MagickFalse)
6745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6746 " Crop the PNG image");
6748 if ((crop_box.left < crop_box.right) &&
6749 (crop_box.top < crop_box.bottom))
6758 Crop_info is with respect to the upper left corner of
6761 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6762 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6763 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6764 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6765 image->page.width=image->columns;
6766 image->page.height=image->rows;
6769 im=CropImage(image,&crop_info,exception);
6771 if (im != (Image *) NULL)
6773 image->columns=im->columns;
6774 image->rows=im->rows;
6775 im=DestroyImage(im);
6776 image->page.width=image->columns;
6777 image->page.height=image->rows;
6778 image->page.x=crop_box.left;
6779 image->page.y=crop_box.top;
6786 No pixels in crop area. The MNG spec still requires
6787 a layer, though, so make a single transparent pixel in
6788 the top left corner.
6793 (void) SetImageBackgroundColor(image,exception);
6794 image->page.width=1;
6795 image->page.height=1;
6800 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6801 image=mng_info->image;
6805 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6806 /* PNG does not handle depths greater than 16 so reduce it even
6809 if (image->depth > 16)
6813 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6814 if (image->depth > 8)
6816 /* To do: fill low byte properly */
6820 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6824 if (image_info->number_scenes != 0)
6826 if (mng_info->scenes_found >
6827 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6833 " Finished reading image datastream.");
6835 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6837 (void) CloseBlob(image);
6839 if (logging != MagickFalse)
6840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6841 " Finished reading all image datastreams.");
6843 #if defined(MNG_INSERT_LAYERS)
6844 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6845 (mng_info->mng_height))
6848 Insert a background layer if nothing else was found.
6850 if (logging != MagickFalse)
6851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6852 " No images found. Inserting a background layer.");
6854 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6857 Allocate next image structure.
6859 AcquireNextImage(image_info,image,exception);
6860 if (GetNextImageInList(image) == (Image *) NULL)
6862 image=DestroyImageList(image);
6863 MngInfoFreeStruct(mng_info,&have_mng_structure);
6865 if (logging != MagickFalse)
6866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6867 " Allocation failed, returning NULL.");
6869 return((Image *) NULL);
6871 image=SyncNextImageInList(image);
6873 image->columns=mng_info->mng_width;
6874 image->rows=mng_info->mng_height;
6875 image->page.width=mng_info->mng_width;
6876 image->page.height=mng_info->mng_height;
6879 image->background_color=mng_background_color;
6880 image->alpha_trait=UndefinedPixelTrait;
6882 if (image_info->ping == MagickFalse)
6883 (void) SetImageBackgroundColor(image,exception);
6885 mng_info->image_found++;
6888 image->iterations=mng_iterations;
6890 if (mng_iterations == 1)
6891 image->start_loop=MagickTrue;
6893 while (GetPreviousImageInList(image) != (Image *) NULL)
6896 if (image_count > 10*mng_info->image_found)
6898 if (logging != MagickFalse)
6899 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6901 (void) ThrowMagickException(exception,GetMagickModule(),
6902 CoderError,"Linked list is corrupted, beginning of list not found",
6903 "`%s'",image_info->filename);
6905 return((Image *) NULL);
6908 image=GetPreviousImageInList(image);
6910 if (GetNextImageInList(image) == (Image *) NULL)
6912 if (logging != MagickFalse)
6913 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6915 (void) ThrowMagickException(exception,GetMagickModule(),
6916 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6917 image_info->filename);
6921 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6922 GetNextImageInList(image) ==
6925 if (logging != MagickFalse)
6926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6927 " First image null");
6929 (void) ThrowMagickException(exception,GetMagickModule(),
6930 CoderError,"image->next for first image is NULL but shouldn't be.",
6931 "`%s'",image_info->filename);
6934 if (mng_info->image_found == 0)
6936 if (logging != MagickFalse)
6937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6938 " No visible images found.");
6940 (void) ThrowMagickException(exception,GetMagickModule(),
6941 CoderError,"No visible images in file","`%s'",image_info->filename);
6943 if (image != (Image *) NULL)
6944 image=DestroyImageList(image);
6946 MngInfoFreeStruct(mng_info,&have_mng_structure);
6947 return((Image *) NULL);
6950 if (mng_info->ticks_per_second)
6951 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6952 final_delay/mng_info->ticks_per_second;
6955 image->start_loop=MagickTrue;
6957 /* Find final nonzero image delay */
6958 final_image_delay=0;
6960 while (GetNextImageInList(image) != (Image *) NULL)
6963 final_image_delay=image->delay;
6965 image=GetNextImageInList(image);
6968 if (final_delay < final_image_delay)
6969 final_delay=final_image_delay;
6971 image->delay=final_delay;
6973 if (logging != MagickFalse)
6974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6975 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6976 (double) final_delay);
6978 if (logging != MagickFalse)
6984 image=GetFirstImageInList(image);
6986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6987 " Before coalesce:");
6989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6990 " scene 0 delay=%.20g",(double) image->delay);
6992 while (GetNextImageInList(image) != (Image *) NULL)
6994 image=GetNextImageInList(image);
6995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7000 image=GetFirstImageInList(image);
7001 #ifdef MNG_COALESCE_LAYERS
7011 if (logging != MagickFalse)
7012 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7015 next_image=CoalesceImages(image,exception);
7017 if (next_image == (Image *) NULL)
7018 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7020 image=DestroyImageList(image);
7023 for (next=image; next != (Image *) NULL; next=next_image)
7025 next->page.width=mng_info->mng_width;
7026 next->page.height=mng_info->mng_height;
7029 next->scene=scene++;
7030 next_image=GetNextImageInList(next);
7032 if (next_image == (Image *) NULL)
7035 if (next->delay == 0)
7038 next_image->previous=GetPreviousImageInList(next);
7039 if (GetPreviousImageInList(next) == (Image *) NULL)
7042 next->previous->next=next_image;
7043 next=DestroyImage(next);
7049 while (GetNextImageInList(image) != (Image *) NULL)
7050 image=GetNextImageInList(image);
7052 image->dispose=BackgroundDispose;
7054 if (logging != MagickFalse)
7060 image=GetFirstImageInList(image);
7062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7063 " After coalesce:");
7065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7066 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7067 (double) image->dispose);
7069 while (GetNextImageInList(image) != (Image *) NULL)
7071 image=GetNextImageInList(image);
7073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7075 (double) image->delay,(double) image->dispose);
7079 image=GetFirstImageInList(image);
7080 MngInfoFreeStruct(mng_info,&have_mng_structure);
7081 have_mng_structure=MagickFalse;
7083 if (logging != MagickFalse)
7084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7086 return(GetFirstImageInList(image));
7088 #else /* PNG_LIBPNG_VER > 10011 */
7089 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7091 printf("Your PNG library is too old: You have libpng-%s\n",
7092 PNG_LIBPNG_VER_STRING);
7094 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7095 "PNG library is too old","`%s'",image_info->filename);
7097 return(Image *) NULL;
7100 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7102 return(ReadPNGImage(image_info,exception));
7104 #endif /* PNG_LIBPNG_VER > 10011 */
7108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7112 % R e g i s t e r P N G I m a g e %
7116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7118 % RegisterPNGImage() adds properties for the PNG image format to
7119 % the list of supported formats. The properties include the image format
7120 % tag, a method to read and/or write the format, whether the format
7121 % supports the saving of more than one frame to the same file or blob,
7122 % whether the format supports native in-memory I/O, and a brief
7123 % description of the format.
7125 % The format of the RegisterPNGImage method is:
7127 % size_t RegisterPNGImage(void)
7130 ModuleExport size_t RegisterPNGImage(void)
7133 version[MaxTextExtent];
7141 "See http://www.libpng.org/ for details about the PNG format."
7146 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7152 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7158 #if defined(PNG_LIBPNG_VER_STRING)
7159 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7160 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7162 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7164 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7165 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7170 entry=SetMagickInfo("MNG");
7171 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7173 #if defined(MAGICKCORE_PNG_DELEGATE)
7174 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7175 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7178 entry->magick=(IsImageFormatHandler *) IsMNG;
7179 entry->description=ConstantString("Multiple-image Network Graphics");
7181 if (*version != '\0')
7182 entry->version=ConstantString(version);
7184 entry->module=ConstantString("PNG");
7185 entry->note=ConstantString(MNGNote);
7186 (void) RegisterMagickInfo(entry);
7188 entry=SetMagickInfo("PNG");
7190 #if defined(MAGICKCORE_PNG_DELEGATE)
7191 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7192 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7195 entry->magick=(IsImageFormatHandler *) IsPNG;
7196 entry->adjoin=MagickFalse;
7197 entry->description=ConstantString("Portable Network Graphics");
7198 entry->module=ConstantString("PNG");
7200 if (*version != '\0')
7201 entry->version=ConstantString(version);
7203 entry->note=ConstantString(PNGNote);
7204 (void) RegisterMagickInfo(entry);
7206 entry=SetMagickInfo("PNG8");
7208 #if defined(MAGICKCORE_PNG_DELEGATE)
7209 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7210 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7213 entry->magick=(IsImageFormatHandler *) IsPNG;
7214 entry->adjoin=MagickFalse;
7215 entry->description=ConstantString(
7216 "8-bit indexed with optional binary transparency");
7217 entry->module=ConstantString("PNG");
7218 (void) RegisterMagickInfo(entry);
7220 entry=SetMagickInfo("PNG24");
7223 #if defined(ZLIB_VERSION)
7224 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7225 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7227 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7229 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7230 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7234 if (*version != '\0')
7235 entry->version=ConstantString(version);
7237 #if defined(MAGICKCORE_PNG_DELEGATE)
7238 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7239 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7242 entry->magick=(IsImageFormatHandler *) IsPNG;
7243 entry->adjoin=MagickFalse;
7244 entry->description=ConstantString("opaque 24-bit RGB");
7245 entry->module=ConstantString("PNG");
7246 (void) RegisterMagickInfo(entry);
7248 entry=SetMagickInfo("PNG32");
7250 #if defined(MAGICKCORE_PNG_DELEGATE)
7251 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7252 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7255 entry->magick=(IsImageFormatHandler *) IsPNG;
7256 entry->adjoin=MagickFalse;
7257 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7258 entry->module=ConstantString("PNG");
7259 (void) RegisterMagickInfo(entry);
7261 entry=SetMagickInfo("JNG");
7263 #if defined(JNG_SUPPORTED)
7264 #if defined(MAGICKCORE_PNG_DELEGATE)
7265 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7266 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7270 entry->magick=(IsImageFormatHandler *) IsJNG;
7271 entry->adjoin=MagickFalse;
7272 entry->description=ConstantString("JPEG Network Graphics");
7273 entry->module=ConstantString("PNG");
7274 entry->note=ConstantString(JNGNote);
7275 (void) RegisterMagickInfo(entry);
7277 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7278 ping_semaphore=AllocateSemaphoreInfo();
7281 return(MagickImageCoderSignature);
7285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7289 % U n r e g i s t e r P N G I m a g e %
7293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7295 % UnregisterPNGImage() removes format registrations made by the
7296 % PNG module from the list of supported formats.
7298 % The format of the UnregisterPNGImage method is:
7300 % UnregisterPNGImage(void)
7303 ModuleExport void UnregisterPNGImage(void)
7305 (void) UnregisterMagickInfo("MNG");
7306 (void) UnregisterMagickInfo("PNG");
7307 (void) UnregisterMagickInfo("PNG8");
7308 (void) UnregisterMagickInfo("PNG24");
7309 (void) UnregisterMagickInfo("PNG32");
7310 (void) UnregisterMagickInfo("JNG");
7312 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7313 if (ping_semaphore != (SemaphoreInfo *) NULL)
7314 DestroySemaphoreInfo(&ping_semaphore);
7318 #if defined(MAGICKCORE_PNG_DELEGATE)
7319 #if PNG_LIBPNG_VER > 10011
7321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7325 % W r i t e M N G I m a g e %
7329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7331 % WriteMNGImage() writes an image in the Portable Network Graphics
7332 % Group's "Multiple-image Network Graphics" encoded image format.
7334 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7336 % The format of the WriteMNGImage method is:
7338 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7339 % Image *image,ExceptionInfo *exception)
7341 % A description of each parameter follows.
7343 % o image_info: the image info.
7345 % o image: The image.
7347 % o exception: return any errors or warnings in this structure.
7349 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7350 % "To do" under ReadPNGImage):
7352 % Preserve all unknown and not-yet-handled known chunks found in input
7353 % PNG file and copy them into output PNG files according to the PNG
7356 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7358 % Improve selection of color type (use indexed-colour or indexed-colour
7359 % with tRNS when 256 or fewer unique RGBA values are present).
7361 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7362 % This will be complicated if we limit ourselves to generating MNG-LC
7363 % files. For now we ignore disposal method 3 and simply overlay the next
7366 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7367 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7368 % [mostly done 15 June 1999 but still need to take care of tRNS]
7370 % Check for identical sRGB and replace with a global sRGB (and remove
7371 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7372 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7373 % local gAMA/cHRM with local sRGB if appropriate).
7375 % Check for identical sBIT chunks and write global ones.
7377 % Provide option to skip writing the signature tEXt chunks.
7379 % Use signatures to detect identical objects and reuse the first
7380 % instance of such objects instead of writing duplicate objects.
7382 % Use a smaller-than-32k value of compression window size when
7385 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7386 % ancillary text chunks and save profiles.
7388 % Provide an option to force LC files (to ensure exact framing rate)
7391 % Provide an option to force VLC files instead of LC, even when offsets
7392 % are present. This will involve expanding the embedded images with a
7393 % transparent region at the top and/or left.
7397 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7398 png_info *ping_info, unsigned char *profile_type, unsigned char
7399 *profile_description, unsigned char *profile_data, png_uint_32 length)
7418 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7420 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7423 if (image_info->verbose)
7425 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7426 (char *) profile_type, (double) length);
7429 #if PNG_LIBPNG_VER >= 14000
7430 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7432 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7434 description_length=(png_uint_32) strlen((const char *) profile_description);
7435 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7436 + description_length);
7437 #if PNG_LIBPNG_VER >= 14000
7438 text[0].text=(png_charp) png_malloc(ping,
7439 (png_alloc_size_t) allocated_length);
7440 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7442 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7443 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7445 text[0].key[0]='\0';
7446 (void) ConcatenateMagickString(text[0].key,
7447 "Raw profile type ",MaxTextExtent);
7448 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7452 (void) CopyMagickString(dp,(const char *) profile_description,
7454 dp+=description_length;
7456 (void) FormatLocaleString(dp,allocated_length-
7457 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7460 for (i=0; i < (ssize_t) length; i++)
7464 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7465 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7470 text[0].text_length=(png_size_t) (dp-text[0].text);
7471 text[0].compression=image_info->compression == NoCompression ||
7472 (image_info->compression == UndefinedCompression &&
7473 text[0].text_length < 128) ? -1 : 0;
7475 if (text[0].text_length <= allocated_length)
7476 png_set_text(ping,ping_info,text,1);
7478 png_free(ping,text[0].text);
7479 png_free(ping,text[0].key);
7480 png_free(ping,text);
7483 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7484 const char *string, MagickBooleanType logging)
7497 ResetImageProfileIterator(image);
7499 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7501 profile=GetImageProfile(image,name);
7503 if (profile != (const StringInfo *) NULL)
7508 if (LocaleNCompare(name,string,11) == 0)
7510 if (logging != MagickFalse)
7511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7512 " Found %s profile",name);
7514 ping_profile=CloneStringInfo(profile);
7515 data=GetStringInfoDatum(ping_profile),
7516 length=(png_uint_32) GetStringInfoLength(ping_profile);
7521 (void) WriteBlobMSBULong(image,length-5); /* data length */
7522 (void) WriteBlob(image,length-1,data+1);
7523 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7524 ping_profile=DestroyStringInfo(ping_profile);
7528 name=GetNextImageProfile(image);
7535 /* Write one PNG image */
7536 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7537 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7561 ping_trans_alpha[256];
7589 ping_have_cheap_transparency,
7600 /* ping_exclude_EXIF, */
7603 /* ping_exclude_iTXt, */
7608 /* ping_exclude_tRNS, */
7610 ping_exclude_zCCP, /* hex-encoded iCCP */
7613 ping_preserve_colormap,
7614 ping_need_colortype_warning,
7632 *volatile ping_pixels;
7638 ping_interlace_method,
7639 ping_compression_method,
7656 number_semitransparent,
7658 ping_pHYs_unit_type;
7661 ping_pHYs_x_resolution,
7662 ping_pHYs_y_resolution;
7664 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7665 " Enter WriteOnePNGImage()");
7667 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7668 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7669 if (image_info == (ImageInfo *) NULL)
7670 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7672 /* Initialize some stuff */
7675 ping_interlace_method=0,
7676 ping_compression_method=0,
7677 ping_filter_method=0,
7680 ping_background.red = 0;
7681 ping_background.green = 0;
7682 ping_background.blue = 0;
7683 ping_background.gray = 0;
7684 ping_background.index = 0;
7686 ping_trans_color.red=0;
7687 ping_trans_color.green=0;
7688 ping_trans_color.blue=0;
7689 ping_trans_color.gray=0;
7691 ping_pHYs_unit_type = 0;
7692 ping_pHYs_x_resolution = 0;
7693 ping_pHYs_y_resolution = 0;
7695 ping_have_blob=MagickFalse;
7696 ping_have_color=MagickTrue;
7697 ping_have_non_bw=MagickTrue;
7698 ping_have_PLTE=MagickFalse;
7699 ping_have_bKGD=MagickFalse;
7700 ping_have_pHYs=MagickFalse;
7701 ping_have_tRNS=MagickFalse;
7703 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7704 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7705 ping_exclude_date=mng_info->ping_exclude_date;
7706 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7707 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7708 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7709 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7710 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7711 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7712 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7713 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7714 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7715 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7716 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7717 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7719 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7720 ping_need_colortype_warning = MagickFalse;
7722 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7723 * i.e., eliminate the ICC profile and set image->rendering_intent.
7724 * Note that this will not involve any changes to the actual pixels
7725 * but merely passes information to applications that read the resulting
7728 if (ping_exclude_sRGB == MagickFalse)
7736 ResetImageProfileIterator(image);
7737 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7739 profile=GetImageProfile(image,name);
7741 if (profile != (StringInfo *) NULL)
7743 if ((LocaleCompare(name,"ICC") == 0) ||
7744 (LocaleCompare(name,"ICM") == 0))
7749 /* 0: not a known sRGB profile
7750 * 1: HP-Microsoft sRGB v2
7751 * 2: ICC sRGB v4 perceptual
7752 * 3: ICC sRGB v2 perceptual no black-compensation
7755 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7756 check_len[4] = {0, 3144, 60960, 3052};
7765 length=(png_uint_32) GetStringInfoLength(profile);
7767 for (icheck=3; icheck > 0; icheck--)
7769 if (length == check_len[icheck])
7771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7772 " Got a %lu-byte ICC profile (potentially sRGB)",
7773 (unsigned long) length);
7775 data=GetStringInfoDatum(profile);
7776 profile_crc=crc32(0,data,length);
7778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7779 " with crc=%8x",(unsigned int) profile_crc);
7781 if (profile_crc == check_crc[icheck])
7783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7785 if (image->rendering_intent==UndefinedIntent)
7786 image->rendering_intent=PerceptualIntent;
7792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7793 " Got a %lu-byte ICC profile",
7794 (unsigned long) length);
7797 name=GetNextImageProfile(image);
7802 number_semitransparent = 0;
7803 number_transparent = 0;
7805 if (logging != MagickFalse)
7807 if (image->storage_class == UndefinedClass)
7808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7809 " storage_class=UndefinedClass");
7810 if (image->storage_class == DirectClass)
7811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7812 " storage_class=DirectClass");
7813 if (image->storage_class == PseudoClass)
7814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7815 " storage_class=PseudoClass");
7818 if (image->storage_class == PseudoClass &&
7819 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7820 (mng_info->write_png_colortype != 0 &&
7821 mng_info->write_png_colortype != 4)))
7823 (void) SyncImage(image,exception);
7824 image->storage_class = DirectClass;
7827 if (ping_preserve_colormap == MagickFalse)
7829 if (image->storage_class != PseudoClass && image->colormap != NULL)
7831 /* Free the bogus colormap; it can cause trouble later */
7832 if (logging != MagickFalse)
7833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7834 " Freeing bogus colormap");
7835 (void) RelinquishMagickMemory(image->colormap);
7836 image->colormap=NULL;
7840 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
7841 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7844 Sometimes we get PseudoClass images whose RGB values don't match
7845 the colors in the colormap. This code syncs the RGB values.
7847 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7848 (void) SyncImage(image,exception);
7850 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7851 if (image->depth > 8)
7853 if (logging != MagickFalse)
7854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7855 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7861 /* Respect the -depth option */
7862 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7867 if (image->depth > 8)
7869 #if MAGICKCORE_QUANTUM_DEPTH > 16
7870 /* Scale to 16-bit */
7871 LBR16PacketRGBO(image->background_color);
7873 for (y=0; y < (ssize_t) image->rows; y++)
7875 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7877 if (r == (Quantum *) NULL)
7880 for (x=0; x < (ssize_t) image->columns; x++)
7883 r+=GetPixelChannels(image);
7886 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7890 if (image->storage_class == PseudoClass && image->colormap != NULL)
7892 for (i=0; i < (ssize_t) image->colors; i++)
7894 LBR16PacketRGBO(image->colormap[i]);
7897 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7900 else if (image->depth > 4)
7902 #if MAGICKCORE_QUANTUM_DEPTH > 8
7903 /* Scale to 8-bit */
7904 LBR08PacketRGBO(image->background_color);
7906 for (y=0; y < (ssize_t) image->rows; y++)
7908 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7910 if (r == (Quantum *) NULL)
7913 for (x=0; x < (ssize_t) image->columns; x++)
7916 r+=GetPixelChannels(image);
7919 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7923 if (image->storage_class == PseudoClass && image->colormap != NULL)
7925 for (i=0; i < (ssize_t) image->colors; i++)
7927 LBR08PacketRGBO(image->colormap[i]);
7930 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7933 if (image->depth > 2)
7935 /* Scale to 4-bit */
7936 LBR04PacketRGBO(image->background_color);
7938 for (y=0; y < (ssize_t) image->rows; y++)
7940 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7942 if (r == (Quantum *) NULL)
7945 for (x=0; x < (ssize_t) image->columns; x++)
7948 r+=GetPixelChannels(image);
7951 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7955 if (image->storage_class == PseudoClass && image->colormap != NULL)
7957 for (i=0; i < (ssize_t) image->colors; i++)
7959 LBR04PacketRGBO(image->colormap[i]);
7964 else if (image->depth > 1)
7966 /* Scale to 2-bit */
7967 LBR02PacketRGBO(image->background_color);
7969 for (y=0; y < (ssize_t) image->rows; y++)
7971 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7973 if (r == (Quantum *) NULL)
7976 for (x=0; x < (ssize_t) image->columns; x++)
7979 r+=GetPixelChannels(image);
7982 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7986 if (image->storage_class == PseudoClass && image->colormap != NULL)
7988 for (i=0; i < (ssize_t) image->colors; i++)
7990 LBR02PacketRGBO(image->colormap[i]);
7996 /* Scale to 1-bit */
7997 LBR01PacketRGBO(image->background_color);
7999 for (y=0; y < (ssize_t) image->rows; y++)
8001 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8003 if (r == (Quantum *) NULL)
8006 for (x=0; x < (ssize_t) image->columns; x++)
8009 r+=GetPixelChannels(image);
8012 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8016 if (image->storage_class == PseudoClass && image->colormap != NULL)
8018 for (i=0; i < (ssize_t) image->colors; i++)
8020 LBR01PacketRGBO(image->colormap[i]);
8026 /* To do: set to next higher multiple of 8 */
8027 if (image->depth < 8)
8030 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8031 /* PNG does not handle depths greater than 16 so reduce it even
8034 if (image->depth > 8)
8038 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8039 if (image->depth > 8)
8041 /* To do: fill low byte properly */
8045 if (image->depth == 16 && mng_info->write_png_depth != 16)
8046 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8050 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8051 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8052 mng_info->write_png_colortype < 4 &&
8053 image->alpha_trait != BlendPixelTrait)))
8055 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8056 * are not going to need the result.
8058 image_colors=image->colors;
8059 number_opaque = image->colors;
8060 if (mng_info->write_png_colortype == 1 ||
8061 mng_info->write_png_colortype == 5)
8062 ping_have_color=MagickFalse;
8064 ping_have_color=MagickTrue;
8065 ping_have_non_bw=MagickFalse;
8067 if (image->alpha_trait == BlendPixelTrait)
8069 number_transparent = 2;
8070 number_semitransparent = 1;
8075 number_transparent = 0;
8076 number_semitransparent = 0;
8084 * Normally we run this just once, but in the case of writing PNG8
8085 * we reduce the transparency to binary and run again, then if there
8086 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8087 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8088 * palette. Then (To do) we take care of a final reduction that is only
8089 * needed if there are still 256 colors present and one of them has both
8090 * transparent and opaque instances.
8093 tried_332 = MagickFalse;
8094 tried_333 = MagickFalse;
8095 tried_444 = MagickFalse;
8100 * Sometimes we get DirectClass images that have 256 colors or fewer.
8101 * This code will build a colormap.
8103 * Also, sometimes we get PseudoClass images with an out-of-date
8104 * colormap. This code will replace the colormap with a new one.
8105 * Sometimes we get PseudoClass images that have more than 256 colors.
8106 * This code will delete the colormap and change the image to
8109 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8110 * even though it sometimes contains left-over non-opaque values.
8112 * Also we gather some information (number of opaque, transparent,
8113 * and semitransparent pixels, and whether the image has any non-gray
8114 * pixels or only black-and-white pixels) that we might need later.
8116 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8117 * we need to check for bogus non-opaque values, at least.
8125 semitransparent[260],
8128 register const Quantum
8135 if (logging != MagickFalse)
8136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8137 " Enter BUILD_PALETTE:");
8139 if (logging != MagickFalse)
8141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8142 " image->columns=%.20g",(double) image->columns);
8143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8144 " image->rows=%.20g",(double) image->rows);
8145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8146 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8148 " image->depth=%.20g",(double) image->depth);
8150 if (image->storage_class == PseudoClass && image->colormap != NULL)
8152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8153 " Original colormap:");
8154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8155 " i (red,green,blue,alpha)");
8157 for (i=0; i < 256; i++)
8159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8160 " %d (%d,%d,%d,%d)",
8162 (int) image->colormap[i].red,
8163 (int) image->colormap[i].green,
8164 (int) image->colormap[i].blue,
8165 (int) image->colormap[i].alpha);
8168 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8173 " %d (%d,%d,%d,%d)",
8175 (int) image->colormap[i].red,
8176 (int) image->colormap[i].green,
8177 (int) image->colormap[i].blue,
8178 (int) image->colormap[i].alpha);
8183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8184 " image->colors=%d",(int) image->colors);
8186 if (image->colors == 0)
8187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8188 " (zero means unknown)");
8190 if (ping_preserve_colormap == MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " Regenerate the colormap");
8197 number_semitransparent = 0;
8198 number_transparent = 0;
8200 for (y=0; y < (ssize_t) image->rows; y++)
8202 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8204 if (q == (Quantum *) NULL)
8207 for (x=0; x < (ssize_t) image->columns; x++)
8209 if (image->alpha_trait != BlendPixelTrait ||
8210 GetPixelAlpha(image,q) == OpaqueAlpha)
8212 if (number_opaque < 259)
8214 if (number_opaque == 0)
8216 GetPixelInfoPixel(image, q, opaque);
8217 opaque[0].alpha=OpaqueAlpha;
8221 for (i=0; i< (ssize_t) number_opaque; i++)
8223 if (IsPixelEquivalent(image,q, opaque+i))
8227 if (i == (ssize_t) number_opaque && number_opaque < 259)
8230 GetPixelInfoPixel(image, q, opaque+i);
8231 opaque[i].alpha=OpaqueAlpha;
8235 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8237 if (number_transparent < 259)
8239 if (number_transparent == 0)
8241 GetPixelInfoPixel(image, q, transparent);
8242 ping_trans_color.red=(unsigned short)
8243 GetPixelRed(image,q);
8244 ping_trans_color.green=(unsigned short)
8245 GetPixelGreen(image,q);
8246 ping_trans_color.blue=(unsigned short)
8247 GetPixelBlue(image,q);
8248 ping_trans_color.gray=(unsigned short)
8249 GetPixelGray(image,q);
8250 number_transparent = 1;
8253 for (i=0; i< (ssize_t) number_transparent; i++)
8255 if (IsPixelEquivalent(image,q, transparent+i))
8259 if (i == (ssize_t) number_transparent &&
8260 number_transparent < 259)
8262 number_transparent++;
8263 GetPixelInfoPixel(image,q,transparent+i);
8269 if (number_semitransparent < 259)
8271 if (number_semitransparent == 0)
8273 GetPixelInfoPixel(image,q,semitransparent);
8274 number_semitransparent = 1;
8277 for (i=0; i< (ssize_t) number_semitransparent; i++)
8279 if (IsPixelEquivalent(image,q, semitransparent+i)
8280 && GetPixelAlpha(image,q) ==
8281 semitransparent[i].alpha)
8285 if (i == (ssize_t) number_semitransparent &&
8286 number_semitransparent < 259)
8288 number_semitransparent++;
8289 GetPixelInfoPixel(image, q, semitransparent+i);
8293 q+=GetPixelChannels(image);
8297 if (mng_info->write_png8 == MagickFalse &&
8298 ping_exclude_bKGD == MagickFalse)
8300 /* Add the background color to the palette, if it
8301 * isn't already there.
8303 if (logging != MagickFalse)
8305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8306 " Check colormap for background (%d,%d,%d)",
8307 (int) image->background_color.red,
8308 (int) image->background_color.green,
8309 (int) image->background_color.blue);
8311 for (i=0; i<number_opaque; i++)
8313 if (opaque[i].red == image->background_color.red &&
8314 opaque[i].green == image->background_color.green &&
8315 opaque[i].blue == image->background_color.blue)
8318 if (number_opaque < 259 && i == number_opaque)
8320 opaque[i] = image->background_color;
8321 ping_background.index = i;
8323 if (logging != MagickFalse)
8325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8326 " background_color index is %d",(int) i);
8330 else if (logging != MagickFalse)
8331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332 " No room in the colormap to add background color");
8335 image_colors=number_opaque+number_transparent+number_semitransparent;
8337 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8339 /* No room for the background color; remove it. */
8344 if (logging != MagickFalse)
8346 if (image_colors > 256)
8347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8348 " image has more than 256 colors");
8351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8352 " image has %d colors",image_colors);
8355 if (ping_preserve_colormap != MagickFalse)
8358 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8360 ping_have_color=MagickFalse;
8361 ping_have_non_bw=MagickFalse;
8363 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8364 (IssRGBColorspace(image->colorspace) != MagickFalse))
8366 ping_have_color=MagickTrue;
8367 ping_have_non_bw=MagickTrue;
8370 if(image_colors > 256)
8372 for (y=0; y < (ssize_t) image->rows; y++)
8374 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8376 if (q == (Quantum *) NULL)
8380 for (x=0; x < (ssize_t) image->columns; x++)
8382 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8383 GetPixelRed(image,s) != GetPixelBlue(image,s))
8385 ping_have_color=MagickTrue;
8386 ping_have_non_bw=MagickTrue;
8389 s+=GetPixelChannels(image);
8392 if (ping_have_color != MagickFalse)
8395 /* Worst case is black-and-white; we are looking at every
8399 if (ping_have_non_bw == MagickFalse)
8402 for (x=0; x < (ssize_t) image->columns; x++)
8404 if (GetPixelRed(image,s) != 0 &&
8405 GetPixelRed(image,s) != QuantumRange)
8407 ping_have_non_bw=MagickTrue;
8410 s+=GetPixelChannels(image);
8417 if (image_colors < 257)
8423 * Initialize image colormap.
8426 if (logging != MagickFalse)
8427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8428 " Sort the new colormap");
8430 /* Sort palette, transparent first */;
8434 for (i=0; i<number_transparent; i++)
8435 colormap[n++] = transparent[i];
8437 for (i=0; i<number_semitransparent; i++)
8438 colormap[n++] = semitransparent[i];
8440 for (i=0; i<number_opaque; i++)
8441 colormap[n++] = opaque[i];
8443 ping_background.index +=
8444 (number_transparent + number_semitransparent);
8446 /* image_colors < 257; search the colormap instead of the pixels
8447 * to get ping_have_color and ping_have_non_bw
8451 if (ping_have_color == MagickFalse)
8453 if (colormap[i].red != colormap[i].green ||
8454 colormap[i].red != colormap[i].blue)
8456 ping_have_color=MagickTrue;
8457 ping_have_non_bw=MagickTrue;
8462 if (ping_have_non_bw == MagickFalse)
8464 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8465 ping_have_non_bw=MagickTrue;
8469 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8470 (number_transparent == 0 && number_semitransparent == 0)) &&
8471 (((mng_info->write_png_colortype-1) ==
8472 PNG_COLOR_TYPE_PALETTE) ||
8473 (mng_info->write_png_colortype == 0)))
8475 if (logging != MagickFalse)
8477 if (n != (ssize_t) image_colors)
8478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8479 " image_colors (%d) and n (%d) don't match",
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " AcquireImageColormap");
8486 image->colors = image_colors;
8488 if (AcquireImageColormap(image,image_colors,exception) ==
8490 ThrowWriterException(ResourceLimitError,
8491 "MemoryAllocationFailed");
8493 for (i=0; i< (ssize_t) image_colors; i++)
8494 image->colormap[i] = colormap[i];
8496 if (logging != MagickFalse)
8498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8499 " image->colors=%d (%d)",
8500 (int) image->colors, image_colors);
8502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503 " Update the pixel indexes");
8506 /* Sync the pixel indices with the new colormap */
8508 for (y=0; y < (ssize_t) image->rows; y++)
8510 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8512 if (q == (Quantum *) NULL)
8515 for (x=0; x < (ssize_t) image->columns; x++)
8517 for (i=0; i< (ssize_t) image_colors; i++)
8519 if ((image->alpha_trait != BlendPixelTrait ||
8520 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8521 image->colormap[i].red == GetPixelRed(image,q) &&
8522 image->colormap[i].green == GetPixelGreen(image,q) &&
8523 image->colormap[i].blue == GetPixelBlue(image,q))
8525 SetPixelIndex(image,i,q);
8529 q+=GetPixelChannels(image);
8532 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8538 if (logging != MagickFalse)
8540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8541 " image->colors=%d", (int) image->colors);
8543 if (image->colormap != NULL)
8545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8546 " i (red,green,blue,alpha)");
8548 for (i=0; i < (ssize_t) image->colors; i++)
8550 if (i < 300 || i >= (ssize_t) image->colors - 10)
8552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553 " %d (%d,%d,%d,%d)",
8555 (int) image->colormap[i].red,
8556 (int) image->colormap[i].green,
8557 (int) image->colormap[i].blue,
8558 (int) image->colormap[i].alpha);
8563 if (number_transparent < 257)
8564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8565 " number_transparent = %d",
8566 number_transparent);
8569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570 " number_transparent > 256");
8572 if (number_opaque < 257)
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " number_opaque = %d",
8578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8579 " number_opaque > 256");
8581 if (number_semitransparent < 257)
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " number_semitransparent = %d",
8584 number_semitransparent);
8587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8588 " number_semitransparent > 256");
8590 if (ping_have_non_bw == MagickFalse)
8591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8592 " All pixels and the background are black or white");
8594 else if (ping_have_color == MagickFalse)
8595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8596 " All pixels and the background are gray");
8599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600 " At least one pixel or the background is non-gray");
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " Exit BUILD_PALETTE:");
8606 if (mng_info->write_png8 == MagickFalse)
8609 /* Make any reductions necessary for the PNG8 format */
8610 if (image_colors <= 256 &&
8611 image_colors != 0 && image->colormap != NULL &&
8612 number_semitransparent == 0 &&
8613 number_transparent <= 1)
8616 /* PNG8 can't have semitransparent colors so we threshold the
8617 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8618 * transparent color so if more than one is transparent we merge
8619 * them into image->background_color.
8621 if (number_semitransparent != 0 || number_transparent > 1)
8623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8624 " Thresholding the alpha channel to binary");
8626 for (y=0; y < (ssize_t) image->rows; y++)
8628 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8630 if (r == (Quantum *) NULL)
8633 for (x=0; x < (ssize_t) image->columns; x++)
8635 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8637 SetPixelInfoPixel(image,&image->background_color,r);
8638 SetPixelAlpha(image,TransparentAlpha,r);
8641 SetPixelAlpha(image,OpaqueAlpha,r);
8642 r+=GetPixelChannels(image);
8645 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8648 if (image_colors != 0 && image_colors <= 256 &&
8649 image->colormap != NULL)
8650 for (i=0; i<image_colors; i++)
8651 image->colormap[i].alpha =
8652 (image->colormap[i].alpha > TransparentAlpha/2 ?
8653 TransparentAlpha : OpaqueAlpha);
8658 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8659 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8660 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8663 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8665 if (logging != MagickFalse)
8666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8667 " Quantizing the background color to 4-4-4");
8669 tried_444 = MagickTrue;
8671 LBR04PacketRGB(image->background_color);
8673 if (logging != MagickFalse)
8674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8675 " Quantizing the pixel colors to 4-4-4");
8677 if (image->colormap == NULL)
8679 for (y=0; y < (ssize_t) image->rows; y++)
8681 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8683 if (r == (Quantum *) NULL)
8686 for (x=0; x < (ssize_t) image->columns; x++)
8688 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8690 r+=GetPixelChannels(image);
8693 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8698 else /* Should not reach this; colormap already exists and
8701 if (logging != MagickFalse)
8702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8703 " Quantizing the colormap to 4-4-4");
8705 for (i=0; i<image_colors; i++)
8707 LBR04PacketRGB(image->colormap[i]);
8713 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8715 if (logging != MagickFalse)
8716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8717 " Quantizing the background color to 3-3-3");
8719 tried_333 = MagickTrue;
8721 LBR03PacketRGB(image->background_color);
8723 if (logging != MagickFalse)
8724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8725 " Quantizing the pixel colors to 3-3-3-1");
8727 if (image->colormap == NULL)
8729 for (y=0; y < (ssize_t) image->rows; y++)
8731 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8733 if (r == (Quantum *) NULL)
8736 for (x=0; x < (ssize_t) image->columns; x++)
8738 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8740 r+=GetPixelChannels(image);
8743 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8748 else /* Should not reach this; colormap already exists and
8751 if (logging != MagickFalse)
8752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753 " Quantizing the colormap to 3-3-3-1");
8754 for (i=0; i<image_colors; i++)
8756 LBR03PacketRGB(image->colormap[i]);
8762 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8764 if (logging != MagickFalse)
8765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8766 " Quantizing the background color to 3-3-2");
8768 tried_332 = MagickTrue;
8770 /* Red and green were already done so we only quantize the blue
8774 LBR02PacketBlue(image->background_color);
8776 if (logging != MagickFalse)
8777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8778 " Quantizing the pixel colors to 3-3-2-1");
8780 if (image->colormap == NULL)
8782 for (y=0; y < (ssize_t) image->rows; y++)
8784 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8786 if (r == (Quantum *) NULL)
8789 for (x=0; x < (ssize_t) image->columns; x++)
8791 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8793 r+=GetPixelChannels(image);
8796 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8801 else /* Should not reach this; colormap already exists and
8804 if (logging != MagickFalse)
8805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8806 " Quantizing the colormap to 3-3-2-1");
8807 for (i=0; i<image_colors; i++)
8809 LBR02PacketBlue(image->colormap[i]);
8816 if (image_colors == 0 || image_colors > 256)
8818 /* Take care of special case with 256 colors + 1 transparent
8819 * color. We don't need to quantize to 2-3-2-1; we only need to
8820 * eliminate one color, so we'll merge the two darkest red
8821 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8823 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8824 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8825 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8827 image->background_color.red=ScaleCharToQuantum(0x24);
8830 if (image->colormap == NULL)
8832 for (y=0; y < (ssize_t) image->rows; y++)
8834 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8836 if (r == (Quantum *) NULL)
8839 for (x=0; x < (ssize_t) image->columns; x++)
8841 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8842 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8843 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8844 GetPixelAlpha(image,r) == OpaqueAlpha)
8846 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8848 r+=GetPixelChannels(image);
8851 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8859 for (i=0; i<image_colors; i++)
8861 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8862 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8863 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8865 image->colormap[i].red=ScaleCharToQuantum(0x24);
8872 /* END OF BUILD_PALETTE */
8874 /* If we are excluding the tRNS chunk and there is transparency,
8875 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8878 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8879 (number_transparent != 0 || number_semitransparent != 0))
8881 unsigned int colortype=mng_info->write_png_colortype;
8883 if (ping_have_color == MagickFalse)
8884 mng_info->write_png_colortype = 5;
8887 mng_info->write_png_colortype = 7;
8889 if (colortype != 0 &&
8890 mng_info->write_png_colortype != colortype)
8891 ping_need_colortype_warning=MagickTrue;
8895 /* See if cheap transparency is possible. It is only possible
8896 * when there is a single transparent color, no semitransparent
8897 * color, and no opaque color that has the same RGB components
8898 * as the transparent color. We only need this information if
8899 * we are writing a PNG with colortype 0 or 2, and we have not
8900 * excluded the tRNS chunk.
8902 if (number_transparent == 1 &&
8903 mng_info->write_png_colortype < 4)
8905 ping_have_cheap_transparency = MagickTrue;
8907 if (number_semitransparent != 0)
8908 ping_have_cheap_transparency = MagickFalse;
8910 else if (image_colors == 0 || image_colors > 256 ||
8911 image->colormap == NULL)
8913 register const Quantum
8916 for (y=0; y < (ssize_t) image->rows; y++)
8918 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8920 if (q == (Quantum *) NULL)
8923 for (x=0; x < (ssize_t) image->columns; x++)
8925 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8926 (unsigned short) GetPixelRed(image,q) ==
8927 ping_trans_color.red &&
8928 (unsigned short) GetPixelGreen(image,q) ==
8929 ping_trans_color.green &&
8930 (unsigned short) GetPixelBlue(image,q) ==
8931 ping_trans_color.blue)
8933 ping_have_cheap_transparency = MagickFalse;
8937 q+=GetPixelChannels(image);
8940 if (ping_have_cheap_transparency == MagickFalse)
8946 /* Assuming that image->colormap[0] is the one transparent color
8947 * and that all others are opaque.
8949 if (image_colors > 1)
8950 for (i=1; i<image_colors; i++)
8951 if (image->colormap[i].red == image->colormap[0].red &&
8952 image->colormap[i].green == image->colormap[0].green &&
8953 image->colormap[i].blue == image->colormap[0].blue)
8955 ping_have_cheap_transparency = MagickFalse;
8960 if (logging != MagickFalse)
8962 if (ping_have_cheap_transparency == MagickFalse)
8963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8964 " Cheap transparency is not possible.");
8967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8968 " Cheap transparency is possible.");
8972 ping_have_cheap_transparency = MagickFalse;
8974 image_depth=image->depth;
8976 quantum_info = (QuantumInfo *) NULL;
8978 image_colors=(int) image->colors;
8979 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
8981 mng_info->IsPalette=image->storage_class == PseudoClass &&
8982 image_colors <= 256 && image->colormap != NULL;
8984 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8985 (image->colors == 0 || image->colormap == NULL))
8987 image_info=DestroyImageInfo(image_info);
8988 image=DestroyImage(image);
8989 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8990 "Cannot write PNG8 or color-type 3; colormap is NULL",
8991 "`%s'",IMimage->filename);
8992 return(MagickFalse);
8996 Allocate the PNG structures
8998 #ifdef PNG_USER_MEM_SUPPORTED
8999 error_info.image=image;
9000 error_info.exception=exception;
9001 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9002 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9003 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9006 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9007 MagickPNGErrorHandler,MagickPNGWarningHandler);
9010 if (ping == (png_struct *) NULL)
9011 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9013 ping_info=png_create_info_struct(ping);
9015 if (ping_info == (png_info *) NULL)
9017 png_destroy_write_struct(&ping,(png_info **) NULL);
9018 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9021 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9022 ping_pixels=(unsigned char *) NULL;
9024 if (setjmp(png_jmpbuf(ping)))
9030 if (image_info->verbose)
9031 (void) printf("PNG write has failed.\n");
9033 png_destroy_write_struct(&ping,&ping_info);
9034 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9035 UnlockSemaphoreInfo(ping_semaphore);
9038 if (ping_pixels != (unsigned char *) NULL)
9039 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9041 if (quantum_info != (QuantumInfo *) NULL)
9042 quantum_info=DestroyQuantumInfo(quantum_info);
9044 if (ping_have_blob != MagickFalse)
9045 (void) CloseBlob(image);
9046 image_info=DestroyImageInfo(image_info);
9047 image=DestroyImage(image);
9048 return(MagickFalse);
9051 /* { For navigation to end of SETJMP-protected block. Within this
9052 * block, use png_error() instead of Throwing an Exception, to ensure
9053 * that libpng is able to clean up, and that the semaphore is unlocked.
9056 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9057 LockSemaphoreInfo(ping_semaphore);
9061 Prepare PNG for writing.
9064 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9065 if (mng_info->write_mng)
9067 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9068 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9069 /* Disable new libpng-1.5.10 feature when writing a MNG because
9070 * zero-length PLTE is OK
9072 png_set_check_for_invalid_index (ping, 0);
9077 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9078 if (mng_info->write_mng)
9079 png_permit_empty_plte(ping,MagickTrue);
9086 ping_width=(png_uint_32) image->columns;
9087 ping_height=(png_uint_32) image->rows;
9089 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9092 if (mng_info->write_png_depth != 0)
9093 image_depth=mng_info->write_png_depth;
9095 /* Adjust requested depth to next higher valid depth if necessary */
9096 if (image_depth > 8)
9099 if ((image_depth > 4) && (image_depth < 8))
9102 if (image_depth == 3)
9105 if (logging != MagickFalse)
9107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9108 " width=%.20g",(double) ping_width);
9109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9110 " height=%.20g",(double) ping_height);
9111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112 " image_matte=%.20g",(double) image->alpha_trait);
9113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9114 " image->depth=%.20g",(double) image->depth);
9115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9116 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9119 save_image_depth=image_depth;
9120 ping_bit_depth=(png_byte) save_image_depth;
9123 #if defined(PNG_pHYs_SUPPORTED)
9124 if (ping_exclude_pHYs == MagickFalse)
9126 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9127 (!mng_info->write_mng || !mng_info->equal_physs))
9129 if (logging != MagickFalse)
9130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9131 " Setting up pHYs chunk");
9133 if (image->units == PixelsPerInchResolution)
9135 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9136 ping_pHYs_x_resolution=
9137 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9138 ping_pHYs_y_resolution=
9139 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9142 else if (image->units == PixelsPerCentimeterResolution)
9144 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9145 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9146 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9151 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9152 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9153 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9156 if (logging != MagickFalse)
9157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9158 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9159 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9160 (int) ping_pHYs_unit_type);
9161 ping_have_pHYs = MagickTrue;
9166 if (ping_exclude_bKGD == MagickFalse)
9168 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9174 if (ping_bit_depth == 8)
9177 if (ping_bit_depth == 4)
9180 if (ping_bit_depth == 2)
9183 if (ping_bit_depth == 1)
9186 ping_background.red=(png_uint_16)
9187 (ScaleQuantumToShort(image->background_color.red) & mask);
9189 ping_background.green=(png_uint_16)
9190 (ScaleQuantumToShort(image->background_color.green) & mask);
9192 ping_background.blue=(png_uint_16)
9193 (ScaleQuantumToShort(image->background_color.blue) & mask);
9195 ping_background.gray=(png_uint_16) ping_background.green;
9198 if (logging != MagickFalse)
9200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201 " Setting up bKGD chunk (1)");
9202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9203 " background_color index is %d",
9204 (int) ping_background.index);
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " ping_bit_depth=%d",ping_bit_depth);
9210 ping_have_bKGD = MagickTrue;
9214 Select the color type.
9219 if (mng_info->IsPalette && mng_info->write_png8)
9222 /* To do: make this a function cause it's used twice, except
9223 for reducing the sample depth from 8. */
9225 number_colors=image_colors;
9227 ping_have_tRNS=MagickFalse;
9232 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9234 if (logging != MagickFalse)
9235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9236 " Setting up PLTE chunk with %d colors (%d)",
9237 number_colors, image_colors);
9239 for (i=0; i < (ssize_t) number_colors; i++)
9241 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9242 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9243 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9244 if (logging != MagickFalse)
9245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9246 #if MAGICKCORE_QUANTUM_DEPTH == 8
9247 " %3ld (%3d,%3d,%3d)",
9249 " %5ld (%5d,%5d,%5d)",
9251 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9255 ping_have_PLTE=MagickTrue;
9256 image_depth=ping_bit_depth;
9259 if (matte != MagickFalse)
9262 Identify which colormap entry is transparent.
9264 assert(number_colors <= 256);
9265 assert(image->colormap != NULL);
9267 for (i=0; i < (ssize_t) number_transparent; i++)
9268 ping_trans_alpha[i]=0;
9271 ping_num_trans=(unsigned short) (number_transparent +
9272 number_semitransparent);
9274 if (ping_num_trans == 0)
9275 ping_have_tRNS=MagickFalse;
9278 ping_have_tRNS=MagickTrue;
9281 if (ping_exclude_bKGD == MagickFalse)
9284 * Identify which colormap entry is the background color.
9287 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9288 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9291 ping_background.index=(png_byte) i;
9293 if (logging != MagickFalse)
9295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9296 " background_color index is %d",
9297 (int) ping_background.index);
9300 } /* end of write_png8 */
9302 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9304 image_matte=MagickFalse;
9305 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9308 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9310 image_matte=MagickTrue;
9311 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9314 else /* mng_info->write_pngNN not specified */
9316 image_depth=ping_bit_depth;
9318 if (mng_info->write_png_colortype != 0)
9320 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9322 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9323 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9324 image_matte=MagickTrue;
9327 image_matte=MagickFalse;
9329 if (logging != MagickFalse)
9330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331 " PNG colortype %d was specified:",(int) ping_color_type);
9334 else /* write_png_colortype not specified */
9336 if (logging != MagickFalse)
9337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9338 " Selecting PNG colortype:");
9340 ping_color_type=(png_byte) ((matte != MagickFalse)?
9341 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9343 if (image_info->type == TrueColorType)
9345 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9346 image_matte=MagickFalse;
9349 if (image_info->type == TrueColorMatteType)
9351 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9352 image_matte=MagickTrue;
9355 if (image_info->type == PaletteType ||
9356 image_info->type == PaletteMatteType)
9357 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9359 if (mng_info->write_png_colortype == 0 &&
9360 (image_info->type == UndefinedType ||
9361 image_info->type == OptimizeType))
9363 if (ping_have_color == MagickFalse)
9365 if (image_matte == MagickFalse)
9367 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9368 image_matte=MagickFalse;
9373 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9374 image_matte=MagickTrue;
9379 if (image_matte == MagickFalse)
9381 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9382 image_matte=MagickFalse;
9387 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9388 image_matte=MagickTrue;
9395 if (logging != MagickFalse)
9396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9397 " Selected PNG colortype=%d",ping_color_type);
9399 if (ping_bit_depth < 8)
9401 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9402 ping_color_type == PNG_COLOR_TYPE_RGB ||
9403 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9407 old_bit_depth=ping_bit_depth;
9409 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9411 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9415 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9420 if (image->colors == 0)
9423 png_error(ping,"image has 0 colors");
9426 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9427 ping_bit_depth <<= 1;
9430 if (logging != MagickFalse)
9432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9433 " Number of colors: %.20g",(double) image_colors);
9435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436 " Tentative PNG bit depth: %d",ping_bit_depth);
9439 if (ping_bit_depth < (int) mng_info->write_png_depth)
9440 ping_bit_depth = mng_info->write_png_depth;
9443 image_depth=ping_bit_depth;
9445 if (logging != MagickFalse)
9447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9448 " Tentative PNG color type: %s (%.20g)",
9449 PngColorTypeToString(ping_color_type),
9450 (double) ping_color_type);
9452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453 " image_info->type: %.20g",(double) image_info->type);
9455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9456 " image_depth: %.20g",(double) image_depth);
9458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9460 " image->depth: %.20g",(double) image->depth);
9462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9463 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9466 if (matte != MagickFalse)
9468 if (mng_info->IsPalette)
9470 if (mng_info->write_png_colortype == 0)
9472 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9474 if (ping_have_color != MagickFalse)
9475 ping_color_type=PNG_COLOR_TYPE_RGBA;
9479 * Determine if there is any transparent color.
9481 if (number_transparent + number_semitransparent == 0)
9484 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9487 image_matte=MagickFalse;
9489 if (mng_info->write_png_colortype == 0)
9490 ping_color_type&=0x03;
9500 if (ping_bit_depth == 8)
9503 if (ping_bit_depth == 4)
9506 if (ping_bit_depth == 2)
9509 if (ping_bit_depth == 1)
9512 ping_trans_color.red=(png_uint_16)
9513 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9515 ping_trans_color.green=(png_uint_16)
9516 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9518 ping_trans_color.blue=(png_uint_16)
9519 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9521 ping_trans_color.gray=(png_uint_16)
9522 (ScaleQuantumToShort(GetPixelInfoIntensity(
9523 image->colormap)) & mask);
9525 ping_trans_color.index=(png_byte) 0;
9527 ping_have_tRNS=MagickTrue;
9530 if (ping_have_tRNS != MagickFalse)
9533 * Determine if there is one and only one transparent color
9534 * and if so if it is fully transparent.
9536 if (ping_have_cheap_transparency == MagickFalse)
9537 ping_have_tRNS=MagickFalse;
9540 if (ping_have_tRNS != MagickFalse)
9542 if (mng_info->write_png_colortype == 0)
9543 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9545 if (image_depth == 8)
9547 ping_trans_color.red&=0xff;
9548 ping_trans_color.green&=0xff;
9549 ping_trans_color.blue&=0xff;
9550 ping_trans_color.gray&=0xff;
9556 if (image_depth == 8)
9558 ping_trans_color.red&=0xff;
9559 ping_trans_color.green&=0xff;
9560 ping_trans_color.blue&=0xff;
9561 ping_trans_color.gray&=0xff;
9568 if (ping_have_tRNS != MagickFalse)
9569 image_matte=MagickFalse;
9571 if ((mng_info->IsPalette) &&
9572 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9573 ping_have_color == MagickFalse &&
9574 (image_matte == MagickFalse || image_depth >= 8))
9578 if (image_matte != MagickFalse)
9579 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9581 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9583 ping_color_type=PNG_COLOR_TYPE_GRAY;
9585 if (save_image_depth == 16 && image_depth == 8)
9587 if (logging != MagickFalse)
9589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590 " Scaling ping_trans_color (0)");
9592 ping_trans_color.gray*=0x0101;
9596 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9597 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9599 if ((image_colors == 0) ||
9600 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9601 image_colors=(int) (one << image_depth);
9603 if (image_depth > 8)
9609 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9611 if(!mng_info->write_png_depth)
9615 while ((int) (one << ping_bit_depth)
9616 < (ssize_t) image_colors)
9617 ping_bit_depth <<= 1;
9621 else if (ping_color_type ==
9622 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9623 mng_info->IsPalette)
9625 /* Check if grayscale is reducible */
9628 depth_4_ok=MagickTrue,
9629 depth_2_ok=MagickTrue,
9630 depth_1_ok=MagickTrue;
9632 for (i=0; i < (ssize_t) image_colors; i++)
9637 intensity=ScaleQuantumToChar(image->colormap[i].red);
9639 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9640 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9641 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9642 depth_2_ok=depth_1_ok=MagickFalse;
9643 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9644 depth_1_ok=MagickFalse;
9647 if (depth_1_ok && mng_info->write_png_depth <= 1)
9650 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9653 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9658 image_depth=ping_bit_depth;
9663 if (mng_info->IsPalette)
9665 number_colors=image_colors;
9667 if (image_depth <= 8)
9672 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9674 if (mng_info->have_write_global_plte && matte == MagickFalse)
9676 png_set_PLTE(ping,ping_info,NULL,0);
9678 if (logging != MagickFalse)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9680 " Setting up empty PLTE chunk");
9685 for (i=0; i < (ssize_t) number_colors; i++)
9687 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9688 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9689 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9692 if (logging != MagickFalse)
9693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694 " Setting up PLTE chunk with %d colors",
9697 ping_have_PLTE=MagickTrue;
9700 /* color_type is PNG_COLOR_TYPE_PALETTE */
9701 if (mng_info->write_png_depth == 0)
9709 while ((one << ping_bit_depth) < (size_t) number_colors)
9710 ping_bit_depth <<= 1;
9715 if (matte != MagickFalse)
9718 * Set up trans_colors array.
9720 assert(number_colors <= 256);
9722 ping_num_trans=(unsigned short) (number_transparent +
9723 number_semitransparent);
9725 if (ping_num_trans == 0)
9726 ping_have_tRNS=MagickFalse;
9730 if (logging != MagickFalse)
9732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9733 " Scaling ping_trans_color (1)");
9735 ping_have_tRNS=MagickTrue;
9737 for (i=0; i < ping_num_trans; i++)
9739 ping_trans_alpha[i]= (png_byte)
9740 ScaleQuantumToChar(image->colormap[i].alpha);
9750 if (image_depth < 8)
9753 if ((save_image_depth == 16) && (image_depth == 8))
9755 if (logging != MagickFalse)
9757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9758 " Scaling ping_trans_color from (%d,%d,%d)",
9759 (int) ping_trans_color.red,
9760 (int) ping_trans_color.green,
9761 (int) ping_trans_color.blue);
9764 ping_trans_color.red*=0x0101;
9765 ping_trans_color.green*=0x0101;
9766 ping_trans_color.blue*=0x0101;
9767 ping_trans_color.gray*=0x0101;
9769 if (logging != MagickFalse)
9771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9773 (int) ping_trans_color.red,
9774 (int) ping_trans_color.green,
9775 (int) ping_trans_color.blue);
9780 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9781 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9784 Adjust background and transparency samples in sub-8-bit grayscale files.
9786 if (ping_bit_depth < 8 && ping_color_type ==
9787 PNG_COLOR_TYPE_GRAY)
9795 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9797 if (ping_exclude_bKGD == MagickFalse)
9800 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9801 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9802 &image->background_color))) +.5)));
9804 if (logging != MagickFalse)
9805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9806 " Setting up bKGD chunk (2)");
9807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9808 " background_color index is %d",
9809 (int) ping_background.index);
9811 ping_have_bKGD = MagickTrue;
9814 if (logging != MagickFalse)
9815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9816 " Scaling ping_trans_color.gray from %d",
9817 (int)ping_trans_color.gray);
9819 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9820 ping_trans_color.gray)+.5);
9822 if (logging != MagickFalse)
9823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9824 " to %d", (int)ping_trans_color.gray);
9827 if (ping_exclude_bKGD == MagickFalse)
9829 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9832 Identify which colormap entry is the background color.
9835 number_colors=image_colors;
9837 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9838 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9841 ping_background.index=(png_byte) i;
9843 if (logging != MagickFalse)
9845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " Setting up bKGD chunk with index=%d",(int) i);
9849 if (i < (ssize_t) number_colors)
9851 ping_have_bKGD = MagickTrue;
9853 if (logging != MagickFalse)
9855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9856 " background =(%d,%d,%d)",
9857 (int) ping_background.red,
9858 (int) ping_background.green,
9859 (int) ping_background.blue);
9863 else /* Can't happen */
9865 if (logging != MagickFalse)
9866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9867 " No room in PLTE to add bKGD color");
9868 ping_have_bKGD = MagickFalse;
9873 if (logging != MagickFalse)
9874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9875 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9878 Initialize compression level and filtering.
9880 if (logging != MagickFalse)
9882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9883 " Setting up deflate compression");
9885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9886 " Compression buffer size: 32768");
9889 png_set_compression_buffer_size(ping,32768L);
9891 if (logging != MagickFalse)
9892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9893 " Compression mem level: 9");
9895 png_set_compression_mem_level(ping, 9);
9897 /* Untangle the "-quality" setting:
9899 Undefined is 0; the default is used.
9904 0: Use Z_HUFFMAN_ONLY strategy with the
9905 zlib default compression level
9907 1-9: the zlib compression level
9911 0-4: the PNG filter method
9913 5: libpng adaptive filtering if compression level > 5
9914 libpng filter type "none" if compression level <= 5
9915 or if image is grayscale or palette
9917 6: libpng adaptive filtering
9919 7: "LOCO" filtering (intrapixel differing) if writing
9920 a MNG, othewise "none". Did not work in IM-6.7.0-9
9921 and earlier because of a missing "else".
9923 8: Z_RLE strategy, all filters
9924 Unused prior to IM-6.7.0-10, was same as 6
9926 9: Z_RLE strategy, no PNG filters
9927 Unused prior to IM-6.7.0-10, was same as 6
9929 Note that using the -quality option, not all combinations of
9930 PNG filter type, zlib compression level, and zlib compression
9931 strategy are possible. This will be addressed soon in a
9932 release that accomodates "-define png:compression-strategy", etc.
9936 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9941 if (mng_info->write_png_compression_strategy == 0)
9942 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9945 else if (mng_info->write_png_compression_level == 0)
9950 level=(int) MagickMin((ssize_t) quality/10,9);
9952 mng_info->write_png_compression_level = level+1;
9955 if (mng_info->write_png_compression_strategy == 0)
9957 if ((quality %10) == 8 || (quality %10) == 9)
9958 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
9959 mng_info->write_png_compression_strategy=Z_RLE+1;
9961 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
9965 if (mng_info->write_png_compression_filter == 0)
9966 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9968 if (logging != MagickFalse)
9970 if (mng_info->write_png_compression_level)
9971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9972 " Compression level: %d",
9973 (int) mng_info->write_png_compression_level-1);
9975 if (mng_info->write_png_compression_strategy)
9976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9977 " Compression strategy: %d",
9978 (int) mng_info->write_png_compression_strategy-1);
9980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9981 " Setting up filtering");
9983 if (mng_info->write_png_compression_filter == 6)
9984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " Base filter method: ADAPTIVE");
9986 else if (mng_info->write_png_compression_filter == 0 ||
9987 mng_info->write_png_compression_filter == 1)
9988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989 " Base filter method: NONE");
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " Base filter method: %d",
9993 (int) mng_info->write_png_compression_filter-1);
9996 if (mng_info->write_png_compression_level != 0)
9997 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9999 if (mng_info->write_png_compression_filter == 6)
10001 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10002 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10004 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10006 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10008 else if (mng_info->write_png_compression_filter == 7 ||
10009 mng_info->write_png_compression_filter == 10)
10010 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10012 else if (mng_info->write_png_compression_filter == 8)
10014 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10015 if (mng_info->write_mng)
10017 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10018 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10019 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10022 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10025 else if (mng_info->write_png_compression_filter == 9)
10026 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10028 else if (mng_info->write_png_compression_filter != 0)
10029 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10030 mng_info->write_png_compression_filter-1);
10032 if (mng_info->write_png_compression_strategy != 0)
10033 png_set_compression_strategy(ping,
10034 mng_info->write_png_compression_strategy-1);
10036 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10037 if (ping_exclude_sRGB != MagickFalse ||
10038 (image->rendering_intent == UndefinedIntent))
10040 if ((ping_exclude_tEXt == MagickFalse ||
10041 ping_exclude_zTXt == MagickFalse) &&
10042 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10044 ResetImageProfileIterator(image);
10045 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10047 profile=GetImageProfile(image,name);
10049 if (profile != (StringInfo *) NULL)
10051 #ifdef PNG_WRITE_iCCP_SUPPORTED
10052 if ((LocaleCompare(name,"ICC") == 0) ||
10053 (LocaleCompare(name,"ICM") == 0))
10056 if (ping_exclude_iCCP == MagickFalse)
10058 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10059 #if (PNG_LIBPNG_VER < 10500)
10060 (png_charp) GetStringInfoDatum(profile),
10062 (png_const_bytep) GetStringInfoDatum(profile),
10064 (png_uint_32) GetStringInfoLength(profile));
10070 if (ping_exclude_zCCP == MagickFalse)
10072 Magick_png_write_raw_profile(image_info,ping,ping_info,
10073 (unsigned char *) name,(unsigned char *) name,
10074 GetStringInfoDatum(profile),
10075 (png_uint_32) GetStringInfoLength(profile));
10079 if (logging != MagickFalse)
10080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10081 " Setting up text chunk with %s profile",name);
10083 name=GetNextImageProfile(image);
10088 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10089 if ((mng_info->have_write_global_srgb == 0) &&
10090 (image->rendering_intent != UndefinedIntent))
10092 if (ping_exclude_sRGB == MagickFalse)
10095 Note image rendering intent.
10097 if (logging != MagickFalse)
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Setting up sRGB chunk");
10101 (void) png_set_sRGB(ping,ping_info,(
10102 Magick_RenderingIntent_to_PNG_RenderingIntent(
10103 image->rendering_intent)));
10107 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10110 if (ping_exclude_gAMA == MagickFalse &&
10111 (ping_exclude_sRGB == MagickFalse ||
10112 (image->gamma < .45 || image->gamma > .46)))
10114 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10118 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10120 if (logging != MagickFalse)
10121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10122 " Setting up gAMA chunk");
10124 png_set_gAMA(ping,ping_info,image->gamma);
10128 if (ping_exclude_cHRM == MagickFalse)
10130 if ((mng_info->have_write_global_chrm == 0) &&
10131 (image->chromaticity.red_primary.x != 0.0))
10134 Note image chromaticity.
10135 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10143 wp=image->chromaticity.white_point;
10144 rp=image->chromaticity.red_primary;
10145 gp=image->chromaticity.green_primary;
10146 bp=image->chromaticity.blue_primary;
10148 if (logging != MagickFalse)
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " Setting up cHRM chunk");
10152 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10158 ping_interlace_method=image_info->interlace != NoInterlace;
10160 if (mng_info->write_mng)
10161 png_set_sig_bytes(ping,8);
10163 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10165 if (mng_info->write_png_colortype != 0)
10167 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10168 if (ping_have_color != MagickFalse)
10170 ping_color_type = PNG_COLOR_TYPE_RGB;
10172 if (ping_bit_depth < 8)
10176 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10177 if (ping_have_color != MagickFalse)
10178 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10181 if (ping_need_colortype_warning != MagickFalse ||
10182 ((mng_info->write_png_depth &&
10183 (int) mng_info->write_png_depth != ping_bit_depth) ||
10184 (mng_info->write_png_colortype &&
10185 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10186 mng_info->write_png_colortype != 7 &&
10187 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10189 if (logging != MagickFalse)
10191 if (ping_need_colortype_warning != MagickFalse)
10193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10194 " Image has transparency but tRNS chunk was excluded");
10197 if (mng_info->write_png_depth)
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " Defined png:bit-depth=%u, Computed depth=%u",
10201 mng_info->write_png_depth,
10205 if (mng_info->write_png_colortype)
10207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10208 " Defined png:color-type=%u, Computed color type=%u",
10209 mng_info->write_png_colortype-1,
10215 "Cannot write image with defined png:bit-depth or png:color-type.");
10218 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10220 /* Add an opaque matte channel */
10221 image->alpha_trait = BlendPixelTrait;
10222 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10224 if (logging != MagickFalse)
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " Added an opaque matte channel");
10229 if (number_transparent != 0 || number_semitransparent != 0)
10231 if (ping_color_type < 4)
10233 ping_have_tRNS=MagickTrue;
10234 if (logging != MagickFalse)
10235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10236 " Setting ping_have_tRNS=MagickTrue.");
10240 if (logging != MagickFalse)
10241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10242 " Writing PNG header chunks");
10244 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10245 ping_bit_depth,ping_color_type,
10246 ping_interlace_method,ping_compression_method,
10247 ping_filter_method);
10249 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10251 png_set_PLTE(ping,ping_info,palette,number_colors);
10253 if (logging != MagickFalse)
10255 for (i=0; i< (ssize_t) number_colors; i++)
10257 if (i < ping_num_trans)
10258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10259 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10261 (int) palette[i].red,
10262 (int) palette[i].green,
10263 (int) palette[i].blue,
10265 (int) ping_trans_alpha[i]);
10267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10268 " PLTE[%d] = (%d,%d,%d)",
10270 (int) palette[i].red,
10271 (int) palette[i].green,
10272 (int) palette[i].blue);
10277 if (ping_exclude_bKGD == MagickFalse)
10279 if (ping_have_bKGD != MagickFalse)
10281 png_set_bKGD(ping,ping_info,&ping_background);
10284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10285 " Setting up bKGD chunk");
10286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287 " background color = (%d,%d,%d)",
10288 (int) ping_background.red,
10289 (int) ping_background.green,
10290 (int) ping_background.blue);
10291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10292 " index = %d, gray=%d",
10293 (int) ping_background.index,
10294 (int) ping_background.gray);
10299 if (ping_exclude_pHYs == MagickFalse)
10301 if (ping_have_pHYs != MagickFalse)
10303 png_set_pHYs(ping,ping_info,
10304 ping_pHYs_x_resolution,
10305 ping_pHYs_y_resolution,
10306 ping_pHYs_unit_type);
10310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311 " Setting up pHYs chunk");
10312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10313 " x_resolution=%lu",
10314 (unsigned long) ping_pHYs_x_resolution);
10315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10316 " y_resolution=%lu",
10317 (unsigned long) ping_pHYs_y_resolution);
10318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10320 (unsigned long) ping_pHYs_unit_type);
10325 #if defined(PNG_oFFs_SUPPORTED)
10326 if (ping_exclude_oFFs == MagickFalse)
10328 if (image->page.x || image->page.y)
10330 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10331 (png_int_32) image->page.y, 0);
10333 if (logging != MagickFalse)
10334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10335 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10336 (int) image->page.x, (int) image->page.y);
10341 if (mng_info->need_blob != MagickFalse)
10343 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10345 png_error(ping,"WriteBlob Failed");
10347 ping_have_blob=MagickTrue;
10350 png_write_info_before_PLTE(ping, ping_info);
10352 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10354 if (logging != MagickFalse)
10356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10357 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10360 if (ping_color_type == 3)
10361 (void) png_set_tRNS(ping, ping_info,
10368 (void) png_set_tRNS(ping, ping_info,
10371 &ping_trans_color);
10373 if (logging != MagickFalse)
10375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10376 " tRNS color =(%d,%d,%d)",
10377 (int) ping_trans_color.red,
10378 (int) ping_trans_color.green,
10379 (int) ping_trans_color.blue);
10384 /* write any png-chunk-b profiles */
10385 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10387 png_write_info(ping,ping_info);
10389 /* write any PNG-chunk-m profiles */
10390 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10392 if (ping_exclude_vpAg == MagickFalse)
10394 if ((image->page.width != 0 && image->page.width != image->columns) ||
10395 (image->page.height != 0 && image->page.height != image->rows))
10400 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10401 PNGType(chunk,mng_vpAg);
10402 LogPNGChunk(logging,mng_vpAg,9L);
10403 PNGLong(chunk+4,(png_uint_32) image->page.width);
10404 PNGLong(chunk+8,(png_uint_32) image->page.height);
10405 chunk[12]=0; /* unit = pixels */
10406 (void) WriteBlob(image,13,chunk);
10407 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10411 #if (PNG_LIBPNG_VER == 10206)
10412 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10413 #define PNG_HAVE_IDAT 0x04
10414 ping->mode |= PNG_HAVE_IDAT;
10415 #undef PNG_HAVE_IDAT
10418 png_set_packing(ping);
10422 rowbytes=image->columns;
10423 if (image_depth > 8)
10425 switch (ping_color_type)
10427 case PNG_COLOR_TYPE_RGB:
10431 case PNG_COLOR_TYPE_GRAY_ALPHA:
10435 case PNG_COLOR_TYPE_RGBA:
10443 if (logging != MagickFalse)
10445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10446 " Writing PNG image data");
10448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10449 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10451 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10452 sizeof(*ping_pixels));
10454 if (ping_pixels == (unsigned char *) NULL)
10455 png_error(ping,"Allocation of memory for pixels failed");
10458 Initialize image scanlines.
10460 quantum_info=AcquireQuantumInfo(image_info,image);
10461 if (quantum_info == (QuantumInfo *) NULL)
10462 png_error(ping,"Memory allocation for quantum_info failed");
10463 quantum_info->format=UndefinedQuantumFormat;
10464 quantum_info->depth=image_depth;
10465 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10466 num_passes=png_set_interlace_handling(ping);
10468 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10469 !mng_info->write_png32) &&
10470 (mng_info->IsPalette ||
10471 (image_info->type == BilevelType)) &&
10472 image_matte == MagickFalse &&
10473 ping_have_non_bw == MagickFalse)
10475 /* Palette, Bilevel, or Opaque Monochrome */
10476 register const Quantum
10479 quantum_info->depth=8;
10480 for (pass=0; pass < num_passes; pass++)
10483 Convert PseudoClass image to a PNG monochrome image.
10485 for (y=0; y < (ssize_t) image->rows; y++)
10487 if (logging != MagickFalse && y == 0)
10488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10489 " Writing row of pixels (0)");
10491 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10493 if (p == (const Quantum *) NULL)
10496 if (mng_info->IsPalette)
10498 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10499 quantum_info,GrayQuantum,ping_pixels,exception);
10500 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10501 mng_info->write_png_depth &&
10502 mng_info->write_png_depth != old_bit_depth)
10504 /* Undo pixel scaling */
10505 for (i=0; i < (ssize_t) image->columns; i++)
10506 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10507 >> (8-old_bit_depth));
10513 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10514 quantum_info,RedQuantum,ping_pixels,exception);
10517 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10518 for (i=0; i < (ssize_t) image->columns; i++)
10519 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10522 if (logging != MagickFalse && y == 0)
10523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10524 " Writing row of pixels (1)");
10526 png_write_row(ping,ping_pixels);
10528 if (image->previous == (Image *) NULL)
10530 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10531 if (status == MagickFalse)
10537 else /* Not Palette, Bilevel, or Opaque Monochrome */
10539 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10540 !mng_info->write_png32) &&
10541 (image_matte != MagickFalse ||
10542 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10543 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10545 register const Quantum
10548 for (pass=0; pass < num_passes; pass++)
10551 for (y=0; y < (ssize_t) image->rows; y++)
10553 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10555 if (p == (const Quantum *) NULL)
10558 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10560 if (mng_info->IsPalette)
10561 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10562 quantum_info,GrayQuantum,ping_pixels,exception);
10565 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10566 quantum_info,RedQuantum,ping_pixels,exception);
10568 if (logging != MagickFalse && y == 0)
10569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10570 " Writing GRAY PNG pixels (2)");
10573 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10575 if (logging != MagickFalse && y == 0)
10576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10577 " Writing GRAY_ALPHA PNG pixels (2)");
10579 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10580 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10583 if (logging != MagickFalse && y == 0)
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " Writing row of pixels (2)");
10587 png_write_row(ping,ping_pixels);
10590 if (image->previous == (Image *) NULL)
10592 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10593 if (status == MagickFalse)
10601 register const Quantum
10604 for (pass=0; pass < num_passes; pass++)
10606 if ((image_depth > 8) || (mng_info->write_png24 ||
10607 mng_info->write_png32 ||
10608 (!mng_info->write_png8 && !mng_info->IsPalette)))
10610 for (y=0; y < (ssize_t) image->rows; y++)
10612 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10614 if (p == (const Quantum *) NULL)
10617 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10619 if (image->storage_class == DirectClass)
10620 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10621 quantum_info,RedQuantum,ping_pixels,exception);
10624 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10625 quantum_info,GrayQuantum,ping_pixels,exception);
10628 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10630 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10631 quantum_info,GrayAlphaQuantum,ping_pixels,
10634 if (logging != MagickFalse && y == 0)
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " Writing GRAY_ALPHA PNG pixels (3)");
10639 else if (image_matte != MagickFalse)
10640 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10641 quantum_info,RGBAQuantum,ping_pixels,exception);
10644 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10645 quantum_info,RGBQuantum,ping_pixels,exception);
10647 if (logging != MagickFalse && y == 0)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " Writing row of pixels (3)");
10651 png_write_row(ping,ping_pixels);
10656 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10657 mng_info->write_png32 ||
10658 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10660 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10661 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10663 if (logging != MagickFalse)
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10667 quantum_info->depth=8;
10671 for (y=0; y < (ssize_t) image->rows; y++)
10673 if (logging != MagickFalse && y == 0)
10674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10675 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10677 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10679 if (p == (const Quantum *) NULL)
10682 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10684 quantum_info->depth=image->depth;
10686 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10687 quantum_info,GrayQuantum,ping_pixels,exception);
10690 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10692 if (logging != MagickFalse && y == 0)
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " Writing GRAY_ALPHA PNG pixels (4)");
10696 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10697 quantum_info,GrayAlphaQuantum,ping_pixels,
10703 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10704 quantum_info,IndexQuantum,ping_pixels,exception);
10706 if (logging != MagickFalse && y <= 2)
10708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10709 " Writing row of non-gray pixels (4)");
10711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10712 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10713 (int)ping_pixels[0],(int)ping_pixels[1]);
10716 png_write_row(ping,ping_pixels);
10720 if (image->previous == (Image *) NULL)
10722 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10723 if (status == MagickFalse)
10730 if (quantum_info != (QuantumInfo *) NULL)
10731 quantum_info=DestroyQuantumInfo(quantum_info);
10733 if (logging != MagickFalse)
10735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10736 " Wrote PNG image data");
10738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10739 " Width: %.20g",(double) ping_width);
10741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10742 " Height: %.20g",(double) ping_height);
10744 if (mng_info->write_png_depth)
10746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10747 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10751 " PNG bit-depth written: %d",ping_bit_depth);
10753 if (mng_info->write_png_colortype)
10755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10760 " PNG color-type written: %d",ping_color_type);
10762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10763 " PNG Interlace method: %d",ping_interlace_method);
10766 Generate text chunks after IDAT.
10768 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10770 ResetImagePropertyIterator(image);
10771 property=GetNextImageProperty(image);
10772 while (property != (const char *) NULL)
10777 value=GetImageProperty(image,property,exception);
10779 /* Don't write any "png:" properties; those are just for "identify" */
10780 if (LocaleNCompare(property,"png:",4) != 0 &&
10782 /* Suppress density and units if we wrote a pHYs chunk */
10783 (ping_exclude_pHYs != MagickFalse ||
10784 LocaleCompare(property,"density") != 0 ||
10785 LocaleCompare(property,"units") != 0) &&
10787 /* Suppress the IM-generated Date:create and Date:modify */
10788 (ping_exclude_date == MagickFalse ||
10789 LocaleNCompare(property, "Date:",5) != 0))
10791 if (value != (const char *) NULL)
10794 #if PNG_LIBPNG_VER >= 14000
10795 text=(png_textp) png_malloc(ping,
10796 (png_alloc_size_t) sizeof(png_text));
10798 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10800 text[0].key=(char *) property;
10801 text[0].text=(char *) value;
10802 text[0].text_length=strlen(value);
10804 if (ping_exclude_tEXt != MagickFalse)
10805 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10807 else if (ping_exclude_zTXt != MagickFalse)
10808 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10812 text[0].compression=image_info->compression == NoCompression ||
10813 (image_info->compression == UndefinedCompression &&
10814 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10815 PNG_TEXT_COMPRESSION_zTXt ;
10818 if (logging != MagickFalse)
10820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10821 " Setting up text chunk");
10823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10824 " keyword: %s",text[0].key);
10827 png_set_text(ping,ping_info,text,1);
10828 png_free(ping,text);
10831 property=GetNextImageProperty(image);
10835 /* write any PNG-chunk-e profiles */
10836 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10838 if (logging != MagickFalse)
10839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10840 " Writing PNG end info");
10842 png_write_end(ping,ping_info);
10844 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10846 if (mng_info->page.x || mng_info->page.y ||
10847 (ping_width != mng_info->page.width) ||
10848 (ping_height != mng_info->page.height))
10854 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10856 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10857 PNGType(chunk,mng_FRAM);
10858 LogPNGChunk(logging,mng_FRAM,27L);
10860 chunk[5]=0; /* frame name separator (no name) */
10861 chunk[6]=1; /* flag for changing delay, for next frame only */
10862 chunk[7]=0; /* flag for changing frame timeout */
10863 chunk[8]=1; /* flag for changing frame clipping for next frame */
10864 chunk[9]=0; /* flag for changing frame sync_id */
10865 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10866 chunk[14]=0; /* clipping boundaries delta type */
10867 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10869 (png_uint_32) (mng_info->page.x + ping_width));
10870 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10872 (png_uint_32) (mng_info->page.y + ping_height));
10873 (void) WriteBlob(image,31,chunk);
10874 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10875 mng_info->old_framing_mode=4;
10876 mng_info->framing_mode=1;
10880 mng_info->framing_mode=3;
10882 if (mng_info->write_mng && !mng_info->need_fram &&
10883 ((int) image->dispose == 3))
10884 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10887 Free PNG resources.
10890 png_destroy_write_struct(&ping,&ping_info);
10892 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10894 if (ping_have_blob != MagickFalse)
10895 (void) CloseBlob(image);
10897 image_info=DestroyImageInfo(image_info);
10898 image=DestroyImage(image);
10900 /* Store bit depth actually written */
10901 s[0]=(char) ping_bit_depth;
10904 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10906 if (logging != MagickFalse)
10907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10908 " exit WriteOnePNGImage()");
10910 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10911 UnlockSemaphoreInfo(ping_semaphore);
10914 /* } for navigation to beginning of SETJMP-protected block. Revert to
10915 * Throwing an Exception when an error occurs.
10918 return(MagickTrue);
10919 /* End write one PNG image */
10924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10928 % W r i t e P N G I m a g e %
10932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10934 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10935 % Multiple-image Network Graphics (MNG) image file.
10937 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10939 % The format of the WritePNGImage method is:
10941 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10942 % Image *image,ExceptionInfo *exception)
10944 % A description of each parameter follows:
10946 % o image_info: the image info.
10948 % o image: The image.
10950 % o exception: return any errors or warnings in this structure.
10952 % Returns MagickTrue on success, MagickFalse on failure.
10954 % Communicating with the PNG encoder:
10956 % While the datastream written is always in PNG format and normally would
10957 % be given the "png" file extension, this method also writes the following
10958 % pseudo-formats which are subsets of png:
10960 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10961 % a depth greater than 8, the depth is reduced. If transparency
10962 % is present, the tRNS chunk must only have values 0 and 255
10963 % (i.e., transparency is binary: fully opaque or fully
10964 % transparent). If other values are present they will be
10965 % 50%-thresholded to binary transparency. If more than 256
10966 % colors are present, they will be quantized to the 4-4-4-1,
10967 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10968 % of any resulting fully-transparent pixels is changed to
10969 % the image's background color.
10971 % If you want better quantization or dithering of the colors
10972 % or alpha than that, you need to do it before calling the
10973 % PNG encoder. The pixels contain 8-bit indices even if
10974 % they could be represented with 1, 2, or 4 bits. Grayscale
10975 % images will be written as indexed PNG files even though the
10976 % PNG grayscale type might be slightly more efficient. Please
10977 % note that writing to the PNG8 format may result in loss
10978 % of color and alpha data.
10980 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10981 % chunk can be present to convey binary transparency by naming
10982 % one of the colors as transparent. The only loss incurred
10983 % is reduction of sample depth to 8. If the image has more
10984 % than one transparent color, has semitransparent pixels, or
10985 % has an opaque pixel with the same RGB components as the
10986 % transparent color, an image is not written.
10988 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10989 % transparency is permitted, i.e., the alpha sample for
10990 % each pixel can have any value from 0 to 255. The alpha
10991 % channel is present even if the image is fully opaque.
10992 % The only loss in data is the reduction of the sample depth
10995 % o -define: For more precise control of the PNG output, you can use the
10996 % Image options "png:bit-depth" and "png:color-type". These
10997 % can be set from the commandline with "-define" and also
10998 % from the application programming interfaces. The options
10999 % are case-independent and are converted to lowercase before
11000 % being passed to this encoder.
11002 % png:color-type can be 0, 2, 3, 4, or 6.
11004 % When png:color-type is 0 (Grayscale), png:bit-depth can
11005 % be 1, 2, 4, 8, or 16.
11007 % When png:color-type is 2 (RGB), png:bit-depth can
11010 % When png:color-type is 3 (Indexed), png:bit-depth can
11011 % be 1, 2, 4, or 8. This refers to the number of bits
11012 % used to store the index. The color samples always have
11013 % bit-depth 8 in indexed PNG files.
11015 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11016 % png:bit-depth can be 8 or 16.
11018 % If the image cannot be written without loss with the requested bit-depth
11019 % and color-type, a PNG file will not be written, and the encoder will
11020 % return MagickFalse.
11022 % Since image encoders should not be responsible for the "heavy lifting",
11023 % the user should make sure that ImageMagick has already reduced the
11024 % image depth and number of colors and limit transparency to binary
11025 % transparency prior to attempting to write the image with depth, color,
11026 % or transparency limitations.
11028 % Note that another definition, "png:bit-depth-written" exists, but it
11029 % is not intended for external use. It is only used internally by the
11030 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11032 % It is possible to request that the PNG encoder write previously-formatted
11033 % ancillary chunks in the output PNG file, using the "-profile" commandline
11034 % option as shown below or by setting the profile via a programming
11037 % -profile PNG-chunk-x:<file>
11039 % where x is a location flag and <file> is a file containing the chunk
11040 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11041 % This encoder will compute the chunk length and CRC, so those must not
11042 % be included in the file.
11044 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11045 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11046 % of the same type, then add a short unique string after the "x" to prevent
11047 % subsequent profiles from overwriting the preceding ones, e.g.,
11049 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11051 % As of version 6.6.6 the following optimizations are always done:
11053 % o 32-bit depth is reduced to 16.
11054 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11055 % high byte and low byte are identical.
11056 % o Palette is sorted to remove unused entries and to put a
11057 % transparent color first, if BUILD_PNG_PALETTE is defined.
11058 % o Opaque matte channel is removed when writing an indexed PNG.
11059 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11060 % this can be done without loss and a larger bit depth N was not
11061 % requested via the "-define png:bit-depth=N" option.
11062 % o If matte channel is present but only one transparent color is
11063 % present, RGB+tRNS is written instead of RGBA
11064 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11065 % was requested when converting an opaque image).
11067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11069 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11070 Image *image,ExceptionInfo *exception)
11075 have_mng_structure,
11091 assert(image_info != (const ImageInfo *) NULL);
11092 assert(image_info->signature == MagickSignature);
11093 assert(image != (Image *) NULL);
11094 assert(image->signature == MagickSignature);
11095 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11096 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11098 Allocate a MngInfo structure.
11100 have_mng_structure=MagickFalse;
11101 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11103 if (mng_info == (MngInfo *) NULL)
11104 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11107 Initialize members of the MngInfo structure.
11109 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11110 mng_info->image=image;
11111 mng_info->equal_backgrounds=MagickTrue;
11112 have_mng_structure=MagickTrue;
11114 /* See if user has requested a specific PNG subformat */
11116 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11117 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11118 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11120 value=GetImageOption(image_info,"png:format");
11122 if (value != (char *) NULL)
11124 if (LocaleCompare(value,"png8") == 0)
11126 mng_info->write_png8 = MagickTrue;
11127 mng_info->write_png24 = MagickFalse;
11128 mng_info->write_png32 = MagickFalse;
11131 else if (LocaleCompare(value,"png24") == 0)
11133 mng_info->write_png8 = MagickFalse;
11134 mng_info->write_png24 = MagickTrue;
11135 mng_info->write_png32 = MagickFalse;
11138 else if (LocaleCompare(value,"png32") == 0)
11140 mng_info->write_png8 = MagickFalse;
11141 mng_info->write_png24 = MagickFalse;
11142 mng_info->write_png32 = MagickTrue;
11145 if (mng_info->write_png8)
11147 mng_info->write_png_colortype = /* 3 */ 4;
11148 mng_info->write_png_depth = 8;
11152 if (mng_info->write_png24)
11154 mng_info->write_png_colortype = /* 2 */ 3;
11155 mng_info->write_png_depth = 8;
11158 if (image->alpha_trait == BlendPixelTrait)
11159 (void) SetImageType(image,TrueColorMatteType,exception);
11162 (void) SetImageType(image,TrueColorType,exception);
11164 (void) SyncImage(image,exception);
11167 if (mng_info->write_png32)
11169 mng_info->write_png_colortype = /* 6 */ 7;
11170 mng_info->write_png_depth = 8;
11173 if (image->alpha_trait == BlendPixelTrait)
11174 (void) SetImageType(image,TrueColorMatteType,exception);
11177 (void) SetImageType(image,TrueColorType,exception);
11179 (void) SyncImage(image,exception);
11182 value=GetImageOption(image_info,"png:bit-depth");
11184 if (value != (char *) NULL)
11186 if (LocaleCompare(value,"1") == 0)
11187 mng_info->write_png_depth = 1;
11189 else if (LocaleCompare(value,"2") == 0)
11190 mng_info->write_png_depth = 2;
11192 else if (LocaleCompare(value,"4") == 0)
11193 mng_info->write_png_depth = 4;
11195 else if (LocaleCompare(value,"8") == 0)
11196 mng_info->write_png_depth = 8;
11198 else if (LocaleCompare(value,"16") == 0)
11199 mng_info->write_png_depth = 16;
11202 (void) ThrowMagickException(exception,
11203 GetMagickModule(),CoderWarning,
11204 "ignoring invalid defined png:bit-depth",
11207 if (logging != MagickFalse)
11208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11212 value=GetImageOption(image_info,"png:color-type");
11214 if (value != (char *) NULL)
11216 /* We must store colortype+1 because 0 is a valid colortype */
11217 if (LocaleCompare(value,"0") == 0)
11218 mng_info->write_png_colortype = 1;
11220 else if (LocaleCompare(value,"1") == 0)
11221 mng_info->write_png_colortype = 2;
11223 else if (LocaleCompare(value,"2") == 0)
11224 mng_info->write_png_colortype = 3;
11226 else if (LocaleCompare(value,"3") == 0)
11227 mng_info->write_png_colortype = 4;
11229 else if (LocaleCompare(value,"4") == 0)
11230 mng_info->write_png_colortype = 5;
11232 else if (LocaleCompare(value,"6") == 0)
11233 mng_info->write_png_colortype = 7;
11236 (void) ThrowMagickException(exception,
11237 GetMagickModule(),CoderWarning,
11238 "ignoring invalid defined png:color-type",
11241 if (logging != MagickFalse)
11242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11243 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11246 /* Check for chunks to be excluded:
11248 * The default is to not exclude any known chunks except for any
11249 * listed in the "unused_chunks" array, above.
11251 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11252 * define (in the image properties or in the image artifacts)
11253 * or via a mng_info member. For convenience, in addition
11254 * to or instead of a comma-separated list of chunks, the
11255 * "exclude-chunk" string can be simply "all" or "none".
11257 * The exclude-chunk define takes priority over the mng_info.
11259 * A "png:include-chunk" define takes priority over both the
11260 * mng_info and the "png:exclude-chunk" define. Like the
11261 * "exclude-chunk" string, it can define "all" or "none" as
11262 * well as a comma-separated list. Chunks that are unknown to
11263 * ImageMagick are always excluded, regardless of their "copy-safe"
11264 * status according to the PNG specification, and even if they
11265 * appear in the "include-chunk" list. Such defines appearing among
11266 * the image options take priority over those found among the image
11269 * Finally, all chunks listed in the "unused_chunks" array are
11270 * automatically excluded, regardless of the other instructions
11273 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11274 * will not be written and the gAMA chunk will only be written if it
11275 * is not between .45 and .46, or approximately (1.0/2.2).
11277 * If you exclude tRNS and the image has transparency, the colortype
11278 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11280 * The -strip option causes StripImage() to set the png:include-chunk
11281 * artifact to "none,trns,gama".
11284 mng_info->ping_exclude_bKGD=MagickFalse;
11285 mng_info->ping_exclude_cHRM=MagickFalse;
11286 mng_info->ping_exclude_date=MagickFalse;
11287 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11288 mng_info->ping_exclude_gAMA=MagickFalse;
11289 mng_info->ping_exclude_iCCP=MagickFalse;
11290 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11291 mng_info->ping_exclude_oFFs=MagickFalse;
11292 mng_info->ping_exclude_pHYs=MagickFalse;
11293 mng_info->ping_exclude_sRGB=MagickFalse;
11294 mng_info->ping_exclude_tEXt=MagickFalse;
11295 mng_info->ping_exclude_tRNS=MagickFalse;
11296 mng_info->ping_exclude_vpAg=MagickFalse;
11297 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11298 mng_info->ping_exclude_zTXt=MagickFalse;
11300 mng_info->ping_preserve_colormap=MagickFalse;
11302 value=GetImageArtifact(image,"png:preserve-colormap");
11304 value=GetImageOption(image_info,"png:preserve-colormap");
11306 mng_info->ping_preserve_colormap=MagickTrue;
11308 /* Thes compression-level, compression-strategy, and compression-filter
11309 * defines take precedence over values from the -quality option.
11311 value=GetImageArtifact(image,"png:compression-level");
11313 value=GetImageOption(image_info,"png:compression-level");
11316 /* We have to add 1 to everything because 0 is a valid input,
11317 * and we want to use 0 (the default) to mean undefined.
11319 if (LocaleCompare(value,"0") == 0)
11320 mng_info->write_png_compression_level = 1;
11322 else if (LocaleCompare(value,"1") == 0)
11323 mng_info->write_png_compression_level = 2;
11325 else if (LocaleCompare(value,"2") == 0)
11326 mng_info->write_png_compression_level = 3;
11328 else if (LocaleCompare(value,"3") == 0)
11329 mng_info->write_png_compression_level = 4;
11331 else if (LocaleCompare(value,"4") == 0)
11332 mng_info->write_png_compression_level = 5;
11334 else if (LocaleCompare(value,"5") == 0)
11335 mng_info->write_png_compression_level = 6;
11337 else if (LocaleCompare(value,"6") == 0)
11338 mng_info->write_png_compression_level = 7;
11340 else if (LocaleCompare(value,"7") == 0)
11341 mng_info->write_png_compression_level = 8;
11343 else if (LocaleCompare(value,"8") == 0)
11344 mng_info->write_png_compression_level = 9;
11346 else if (LocaleCompare(value,"9") == 0)
11347 mng_info->write_png_compression_level = 10;
11350 (void) ThrowMagickException(exception,
11351 GetMagickModule(),CoderWarning,
11352 "ignoring invalid defined png:compression-level",
11356 value=GetImageArtifact(image,"png:compression-strategy");
11358 value=GetImageOption(image_info,"png:compression-strategy");
11362 if (LocaleCompare(value,"0") == 0)
11363 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11365 else if (LocaleCompare(value,"1") == 0)
11366 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11368 else if (LocaleCompare(value,"2") == 0)
11369 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11371 else if (LocaleCompare(value,"3") == 0)
11372 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11373 mng_info->write_png_compression_strategy = Z_RLE+1;
11375 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11378 else if (LocaleCompare(value,"4") == 0)
11379 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11380 mng_info->write_png_compression_strategy = Z_FIXED+1;
11382 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11386 (void) ThrowMagickException(exception,
11387 GetMagickModule(),CoderWarning,
11388 "ignoring invalid defined png:compression-strategy",
11392 value=GetImageArtifact(image,"png:compression-filter");
11394 value=GetImageOption(image_info,"png:compression-filter");
11398 /* To do: combinations of filters allowed by libpng
11399 * masks 0x08 through 0xf8
11401 * Implement this as a comma-separated list of 0,1,2,3,4,5
11402 * where 5 is a special case meaning PNG_ALL_FILTERS.
11405 if (LocaleCompare(value,"0") == 0)
11406 mng_info->write_png_compression_filter = 1;
11408 else if (LocaleCompare(value,"1") == 0)
11409 mng_info->write_png_compression_filter = 2;
11411 else if (LocaleCompare(value,"2") == 0)
11412 mng_info->write_png_compression_filter = 3;
11414 else if (LocaleCompare(value,"3") == 0)
11415 mng_info->write_png_compression_filter = 4;
11417 else if (LocaleCompare(value,"4") == 0)
11418 mng_info->write_png_compression_filter = 5;
11420 else if (LocaleCompare(value,"5") == 0)
11421 mng_info->write_png_compression_filter = 6;
11424 (void) ThrowMagickException(exception,
11425 GetMagickModule(),CoderWarning,
11426 "ignoring invalid defined png:compression-filter",
11430 excluding=MagickFalse;
11432 for (source=0; source<1; source++)
11436 value=GetImageArtifact(image,"png:exclude-chunk");
11439 value=GetImageArtifact(image,"png:exclude-chunks");
11443 value=GetImageOption(image_info,"png:exclude-chunk");
11446 value=GetImageOption(image_info,"png:exclude-chunks");
11455 excluding=MagickTrue;
11457 if (logging != MagickFalse)
11460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11461 " png:exclude-chunk=%s found in image artifacts.\n", value);
11463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464 " png:exclude-chunk=%s found in image properties.\n", value);
11467 last=strlen(value);
11469 for (i=0; i<(int) last; i+=5)
11472 if (LocaleNCompare(value+i,"all",3) == 0)
11474 mng_info->ping_exclude_bKGD=MagickTrue;
11475 mng_info->ping_exclude_cHRM=MagickTrue;
11476 mng_info->ping_exclude_date=MagickTrue;
11477 mng_info->ping_exclude_EXIF=MagickTrue;
11478 mng_info->ping_exclude_gAMA=MagickTrue;
11479 mng_info->ping_exclude_iCCP=MagickTrue;
11480 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11481 mng_info->ping_exclude_oFFs=MagickTrue;
11482 mng_info->ping_exclude_pHYs=MagickTrue;
11483 mng_info->ping_exclude_sRGB=MagickTrue;
11484 mng_info->ping_exclude_tEXt=MagickTrue;
11485 mng_info->ping_exclude_tRNS=MagickTrue;
11486 mng_info->ping_exclude_vpAg=MagickTrue;
11487 mng_info->ping_exclude_zCCP=MagickTrue;
11488 mng_info->ping_exclude_zTXt=MagickTrue;
11492 if (LocaleNCompare(value+i,"none",4) == 0)
11494 mng_info->ping_exclude_bKGD=MagickFalse;
11495 mng_info->ping_exclude_cHRM=MagickFalse;
11496 mng_info->ping_exclude_date=MagickFalse;
11497 mng_info->ping_exclude_EXIF=MagickFalse;
11498 mng_info->ping_exclude_gAMA=MagickFalse;
11499 mng_info->ping_exclude_iCCP=MagickFalse;
11500 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11501 mng_info->ping_exclude_oFFs=MagickFalse;
11502 mng_info->ping_exclude_pHYs=MagickFalse;
11503 mng_info->ping_exclude_sRGB=MagickFalse;
11504 mng_info->ping_exclude_tEXt=MagickFalse;
11505 mng_info->ping_exclude_tRNS=MagickFalse;
11506 mng_info->ping_exclude_vpAg=MagickFalse;
11507 mng_info->ping_exclude_zCCP=MagickFalse;
11508 mng_info->ping_exclude_zTXt=MagickFalse;
11511 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11512 mng_info->ping_exclude_bKGD=MagickTrue;
11514 if (LocaleNCompare(value+i,"chrm",4) == 0)
11515 mng_info->ping_exclude_cHRM=MagickTrue;
11517 if (LocaleNCompare(value+i,"date",4) == 0)
11518 mng_info->ping_exclude_date=MagickTrue;
11520 if (LocaleNCompare(value+i,"exif",4) == 0)
11521 mng_info->ping_exclude_EXIF=MagickTrue;
11523 if (LocaleNCompare(value+i,"gama",4) == 0)
11524 mng_info->ping_exclude_gAMA=MagickTrue;
11526 if (LocaleNCompare(value+i,"iccp",4) == 0)
11527 mng_info->ping_exclude_iCCP=MagickTrue;
11530 if (LocaleNCompare(value+i,"itxt",4) == 0)
11531 mng_info->ping_exclude_iTXt=MagickTrue;
11534 if (LocaleNCompare(value+i,"gama",4) == 0)
11535 mng_info->ping_exclude_gAMA=MagickTrue;
11537 if (LocaleNCompare(value+i,"offs",4) == 0)
11538 mng_info->ping_exclude_oFFs=MagickTrue;
11540 if (LocaleNCompare(value+i,"phys",4) == 0)
11541 mng_info->ping_exclude_pHYs=MagickTrue;
11543 if (LocaleNCompare(value+i,"srgb",4) == 0)
11544 mng_info->ping_exclude_sRGB=MagickTrue;
11546 if (LocaleNCompare(value+i,"text",4) == 0)
11547 mng_info->ping_exclude_tEXt=MagickTrue;
11549 if (LocaleNCompare(value+i,"trns",4) == 0)
11550 mng_info->ping_exclude_tRNS=MagickTrue;
11552 if (LocaleNCompare(value+i,"vpag",4) == 0)
11553 mng_info->ping_exclude_vpAg=MagickTrue;
11555 if (LocaleNCompare(value+i,"zccp",4) == 0)
11556 mng_info->ping_exclude_zCCP=MagickTrue;
11558 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11559 mng_info->ping_exclude_zTXt=MagickTrue;
11565 for (source=0; source<1; source++)
11569 value=GetImageArtifact(image,"png:include-chunk");
11572 value=GetImageArtifact(image,"png:include-chunks");
11576 value=GetImageOption(image_info,"png:include-chunk");
11579 value=GetImageOption(image_info,"png:include-chunks");
11587 excluding=MagickTrue;
11589 if (logging != MagickFalse)
11592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11593 " png:include-chunk=%s found in image artifacts.\n", value);
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " png:include-chunk=%s found in image properties.\n", value);
11599 last=strlen(value);
11601 for (i=0; i<(int) last; i+=5)
11603 if (LocaleNCompare(value+i,"all",3) == 0)
11605 mng_info->ping_exclude_bKGD=MagickFalse;
11606 mng_info->ping_exclude_cHRM=MagickFalse;
11607 mng_info->ping_exclude_date=MagickFalse;
11608 mng_info->ping_exclude_EXIF=MagickFalse;
11609 mng_info->ping_exclude_gAMA=MagickFalse;
11610 mng_info->ping_exclude_iCCP=MagickFalse;
11611 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11612 mng_info->ping_exclude_oFFs=MagickFalse;
11613 mng_info->ping_exclude_pHYs=MagickFalse;
11614 mng_info->ping_exclude_sRGB=MagickFalse;
11615 mng_info->ping_exclude_tEXt=MagickFalse;
11616 mng_info->ping_exclude_tRNS=MagickFalse;
11617 mng_info->ping_exclude_vpAg=MagickFalse;
11618 mng_info->ping_exclude_zCCP=MagickFalse;
11619 mng_info->ping_exclude_zTXt=MagickFalse;
11623 if (LocaleNCompare(value+i,"none",4) == 0)
11625 mng_info->ping_exclude_bKGD=MagickTrue;
11626 mng_info->ping_exclude_cHRM=MagickTrue;
11627 mng_info->ping_exclude_date=MagickTrue;
11628 mng_info->ping_exclude_EXIF=MagickTrue;
11629 mng_info->ping_exclude_gAMA=MagickTrue;
11630 mng_info->ping_exclude_iCCP=MagickTrue;
11631 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11632 mng_info->ping_exclude_oFFs=MagickTrue;
11633 mng_info->ping_exclude_pHYs=MagickTrue;
11634 mng_info->ping_exclude_sRGB=MagickTrue;
11635 mng_info->ping_exclude_tEXt=MagickTrue;
11636 mng_info->ping_exclude_tRNS=MagickTrue;
11637 mng_info->ping_exclude_vpAg=MagickTrue;
11638 mng_info->ping_exclude_zCCP=MagickTrue;
11639 mng_info->ping_exclude_zTXt=MagickTrue;
11642 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11643 mng_info->ping_exclude_bKGD=MagickFalse;
11645 if (LocaleNCompare(value+i,"chrm",4) == 0)
11646 mng_info->ping_exclude_cHRM=MagickFalse;
11648 if (LocaleNCompare(value+i,"date",4) == 0)
11649 mng_info->ping_exclude_date=MagickFalse;
11651 if (LocaleNCompare(value+i,"exif",4) == 0)
11652 mng_info->ping_exclude_EXIF=MagickFalse;
11654 if (LocaleNCompare(value+i,"gama",4) == 0)
11655 mng_info->ping_exclude_gAMA=MagickFalse;
11657 if (LocaleNCompare(value+i,"iccp",4) == 0)
11658 mng_info->ping_exclude_iCCP=MagickFalse;
11661 if (LocaleNCompare(value+i,"itxt",4) == 0)
11662 mng_info->ping_exclude_iTXt=MagickFalse;
11665 if (LocaleNCompare(value+i,"gama",4) == 0)
11666 mng_info->ping_exclude_gAMA=MagickFalse;
11668 if (LocaleNCompare(value+i,"offs",4) == 0)
11669 mng_info->ping_exclude_oFFs=MagickFalse;
11671 if (LocaleNCompare(value+i,"phys",4) == 0)
11672 mng_info->ping_exclude_pHYs=MagickFalse;
11674 if (LocaleNCompare(value+i,"srgb",4) == 0)
11675 mng_info->ping_exclude_sRGB=MagickFalse;
11677 if (LocaleNCompare(value+i,"text",4) == 0)
11678 mng_info->ping_exclude_tEXt=MagickFalse;
11680 if (LocaleNCompare(value+i,"trns",4) == 0)
11681 mng_info->ping_exclude_tRNS=MagickFalse;
11683 if (LocaleNCompare(value+i,"vpag",4) == 0)
11684 mng_info->ping_exclude_vpAg=MagickFalse;
11686 if (LocaleNCompare(value+i,"zccp",4) == 0)
11687 mng_info->ping_exclude_zCCP=MagickFalse;
11689 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11690 mng_info->ping_exclude_zTXt=MagickFalse;
11696 if (excluding != MagickFalse && logging != MagickFalse)
11698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11699 " Chunks to be excluded from the output png:");
11700 if (mng_info->ping_exclude_bKGD != MagickFalse)
11701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703 if (mng_info->ping_exclude_cHRM != MagickFalse)
11704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11706 if (mng_info->ping_exclude_date != MagickFalse)
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11709 if (mng_info->ping_exclude_EXIF != MagickFalse)
11710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11712 if (mng_info->ping_exclude_gAMA != MagickFalse)
11713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11715 if (mng_info->ping_exclude_iCCP != MagickFalse)
11716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11719 if (mng_info->ping_exclude_iTXt != MagickFalse)
11720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 if (mng_info->ping_exclude_oFFs != MagickFalse)
11724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11726 if (mng_info->ping_exclude_pHYs != MagickFalse)
11727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11729 if (mng_info->ping_exclude_sRGB != MagickFalse)
11730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11732 if (mng_info->ping_exclude_tEXt != MagickFalse)
11733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11735 if (mng_info->ping_exclude_tRNS != MagickFalse)
11736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11738 if (mng_info->ping_exclude_vpAg != MagickFalse)
11739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11741 if (mng_info->ping_exclude_zCCP != MagickFalse)
11742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11744 if (mng_info->ping_exclude_zTXt != MagickFalse)
11745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11749 mng_info->need_blob = MagickTrue;
11751 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11753 MngInfoFreeStruct(mng_info,&have_mng_structure);
11755 if (logging != MagickFalse)
11756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11761 #if defined(JNG_SUPPORTED)
11763 /* Write one JNG image */
11764 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11765 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11786 jng_alpha_compression_method,
11787 jng_alpha_sample_depth,
11795 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11796 " Enter WriteOneJNGImage()");
11798 blob=(unsigned char *) NULL;
11799 jpeg_image=(Image *) NULL;
11800 jpeg_image_info=(ImageInfo *) NULL;
11803 transparent=image_info->type==GrayscaleMatteType ||
11804 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
11806 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11808 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11810 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11811 image_info->quality;
11813 if (jng_alpha_quality >= 1000)
11814 jng_alpha_quality /= 1000;
11820 /* Create JPEG blob, image, and image_info */
11821 if (logging != MagickFalse)
11822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11823 " Creating jpeg_image_info for alpha.");
11825 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11827 if (jpeg_image_info == (ImageInfo *) NULL)
11828 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11830 if (logging != MagickFalse)
11831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11832 " Creating jpeg_image.");
11834 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11835 if (jpeg_image == (Image *) NULL)
11836 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11837 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11838 jpeg_image->alpha_trait=UndefinedPixelTrait;
11839 jpeg_image->quality=jng_alpha_quality;
11840 jpeg_image_info->type=GrayscaleType;
11841 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11842 (void) AcquireUniqueFilename(jpeg_image->filename);
11843 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11844 "%s",jpeg_image->filename);
11848 jng_alpha_compression_method=0;
11850 jng_alpha_sample_depth=0;
11853 /* To do: check bit depth of PNG alpha channel */
11855 /* Check if image is grayscale. */
11856 if (image_info->type != TrueColorMatteType && image_info->type !=
11857 TrueColorType && IsImageGray(image,exception))
11860 if (logging != MagickFalse)
11862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11863 " JNG Quality = %d",(int) jng_quality);
11864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11865 " JNG Color Type = %d",jng_color_type);
11868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11869 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11871 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11873 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11879 if (jng_alpha_compression_method==0)
11884 /* Encode alpha as a grayscale PNG blob */
11885 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11887 if (logging != MagickFalse)
11888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11889 " Creating PNG blob.");
11892 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11893 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11894 jpeg_image_info->interlace=NoInterlace;
11896 /* Exclude all ancillary chunks */
11897 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11899 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11902 /* Retrieve sample depth used */
11903 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11904 if (value != (char *) NULL)
11905 jng_alpha_sample_depth= (unsigned int) value[0];
11909 /* Encode alpha as a grayscale JPEG blob */
11911 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11914 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11915 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11916 jpeg_image_info->interlace=NoInterlace;
11917 if (logging != MagickFalse)
11918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11919 " Creating blob.");
11920 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11922 jng_alpha_sample_depth=8;
11924 if (logging != MagickFalse)
11925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11926 " Successfully read jpeg_image into a blob, length=%.20g.",
11930 /* Destroy JPEG image and image_info */
11931 jpeg_image=DestroyImage(jpeg_image);
11932 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11933 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11936 /* Write JHDR chunk */
11937 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11938 PNGType(chunk,mng_JHDR);
11939 LogPNGChunk(logging,mng_JHDR,16L);
11940 PNGLong(chunk+4,(png_uint_32) image->columns);
11941 PNGLong(chunk+8,(png_uint_32) image->rows);
11942 chunk[12]=jng_color_type;
11943 chunk[13]=8; /* sample depth */
11944 chunk[14]=8; /*jng_image_compression_method */
11945 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11946 chunk[16]=jng_alpha_sample_depth;
11947 chunk[17]=jng_alpha_compression_method;
11948 chunk[18]=0; /*jng_alpha_filter_method */
11949 chunk[19]=0; /*jng_alpha_interlace_method */
11950 (void) WriteBlob(image,20,chunk);
11951 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11952 if (logging != MagickFalse)
11954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11955 " JNG width:%15lu",(unsigned long) image->columns);
11957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11958 " JNG height:%14lu",(unsigned long) image->rows);
11960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11961 " JNG color type:%10d",jng_color_type);
11963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11964 " JNG sample depth:%8d",8);
11966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11967 " JNG compression:%9d",8);
11969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11970 " JNG interlace:%11d",0);
11972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11973 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11976 " JNG alpha compression:%3d",jng_alpha_compression_method);
11978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11979 " JNG alpha filter:%8d",0);
11981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11982 " JNG alpha interlace:%5d",0);
11985 /* Write any JNG-chunk-b profiles */
11986 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11989 Write leading ancillary chunks
11995 Write JNG bKGD chunk
12006 if (jng_color_type == 8 || jng_color_type == 12)
12010 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12011 PNGType(chunk,mng_bKGD);
12012 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12013 red=ScaleQuantumToChar(image->background_color.red);
12014 green=ScaleQuantumToChar(image->background_color.green);
12015 blue=ScaleQuantumToChar(image->background_color.blue);
12022 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12023 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12026 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12029 Write JNG sRGB chunk
12031 (void) WriteBlobMSBULong(image,1L);
12032 PNGType(chunk,mng_sRGB);
12033 LogPNGChunk(logging,mng_sRGB,1L);
12035 if (image->rendering_intent != UndefinedIntent)
12036 chunk[4]=(unsigned char)
12037 Magick_RenderingIntent_to_PNG_RenderingIntent(
12038 (image->rendering_intent));
12041 chunk[4]=(unsigned char)
12042 Magick_RenderingIntent_to_PNG_RenderingIntent(
12043 (PerceptualIntent));
12045 (void) WriteBlob(image,5,chunk);
12046 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12050 if (image->gamma != 0.0)
12053 Write JNG gAMA chunk
12055 (void) WriteBlobMSBULong(image,4L);
12056 PNGType(chunk,mng_gAMA);
12057 LogPNGChunk(logging,mng_gAMA,4L);
12058 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12059 (void) WriteBlob(image,8,chunk);
12060 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12063 if ((mng_info->equal_chrms == MagickFalse) &&
12064 (image->chromaticity.red_primary.x != 0.0))
12070 Write JNG cHRM chunk
12072 (void) WriteBlobMSBULong(image,32L);
12073 PNGType(chunk,mng_cHRM);
12074 LogPNGChunk(logging,mng_cHRM,32L);
12075 primary=image->chromaticity.white_point;
12076 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12077 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12078 primary=image->chromaticity.red_primary;
12079 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12080 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12081 primary=image->chromaticity.green_primary;
12082 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12083 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12084 primary=image->chromaticity.blue_primary;
12085 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12086 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12087 (void) WriteBlob(image,36,chunk);
12088 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12092 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12095 Write JNG pHYs chunk
12097 (void) WriteBlobMSBULong(image,9L);
12098 PNGType(chunk,mng_pHYs);
12099 LogPNGChunk(logging,mng_pHYs,9L);
12100 if (image->units == PixelsPerInchResolution)
12102 PNGLong(chunk+4,(png_uint_32)
12103 (image->resolution.x*100.0/2.54+0.5));
12105 PNGLong(chunk+8,(png_uint_32)
12106 (image->resolution.y*100.0/2.54+0.5));
12113 if (image->units == PixelsPerCentimeterResolution)
12115 PNGLong(chunk+4,(png_uint_32)
12116 (image->resolution.x*100.0+0.5));
12118 PNGLong(chunk+8,(png_uint_32)
12119 (image->resolution.y*100.0+0.5));
12126 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12127 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12131 (void) WriteBlob(image,13,chunk);
12132 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12135 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12138 Write JNG oFFs chunk
12140 (void) WriteBlobMSBULong(image,9L);
12141 PNGType(chunk,mng_oFFs);
12142 LogPNGChunk(logging,mng_oFFs,9L);
12143 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12144 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12146 (void) WriteBlob(image,13,chunk);
12147 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12149 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12151 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12152 PNGType(chunk,mng_vpAg);
12153 LogPNGChunk(logging,mng_vpAg,9L);
12154 PNGLong(chunk+4,(png_uint_32) image->page.width);
12155 PNGLong(chunk+8,(png_uint_32) image->page.height);
12156 chunk[12]=0; /* unit = pixels */
12157 (void) WriteBlob(image,13,chunk);
12158 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12164 if (jng_alpha_compression_method==0)
12172 /* Write IDAT chunk header */
12173 if (logging != MagickFalse)
12174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12175 " Write IDAT chunks from blob, length=%.20g.",(double)
12178 /* Copy IDAT chunks */
12181 for (i=8; i<(ssize_t) length; i+=len+12)
12183 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12186 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12188 /* Found an IDAT chunk. */
12189 (void) WriteBlobMSBULong(image,(size_t) len);
12190 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12191 (void) WriteBlob(image,(size_t) len+4,p);
12192 (void) WriteBlobMSBULong(image,
12193 crc32(0,p,(uInt) len+4));
12198 if (logging != MagickFalse)
12199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12200 " Skipping %c%c%c%c chunk, length=%.20g.",
12201 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12208 /* Write JDAA chunk header */
12209 if (logging != MagickFalse)
12210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12211 " Write JDAA chunk, length=%.20g.",(double) length);
12212 (void) WriteBlobMSBULong(image,(size_t) length);
12213 PNGType(chunk,mng_JDAA);
12214 LogPNGChunk(logging,mng_JDAA,length);
12215 /* Write JDAT chunk(s) data */
12216 (void) WriteBlob(image,4,chunk);
12217 (void) WriteBlob(image,length,blob);
12218 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12221 blob=(unsigned char *) RelinquishMagickMemory(blob);
12224 /* Encode image as a JPEG blob */
12225 if (logging != MagickFalse)
12226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12227 " Creating jpeg_image_info.");
12228 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12229 if (jpeg_image_info == (ImageInfo *) NULL)
12230 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12232 if (logging != MagickFalse)
12233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12234 " Creating jpeg_image.");
12236 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12237 if (jpeg_image == (Image *) NULL)
12238 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12239 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12241 (void) AcquireUniqueFilename(jpeg_image->filename);
12242 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12243 jpeg_image->filename);
12245 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12248 if (logging != MagickFalse)
12249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12250 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12251 (double) jpeg_image->rows);
12253 if (jng_color_type == 8 || jng_color_type == 12)
12254 jpeg_image_info->type=GrayscaleType;
12256 jpeg_image_info->quality=jng_quality;
12257 jpeg_image->quality=jng_quality;
12258 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12259 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12261 if (logging != MagickFalse)
12262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12263 " Creating blob.");
12265 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12267 if (logging != MagickFalse)
12269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12270 " Successfully read jpeg_image into a blob, length=%.20g.",
12273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12274 " Write JDAT chunk, length=%.20g.",(double) length);
12277 /* Write JDAT chunk(s) */
12278 (void) WriteBlobMSBULong(image,(size_t) length);
12279 PNGType(chunk,mng_JDAT);
12280 LogPNGChunk(logging,mng_JDAT,length);
12281 (void) WriteBlob(image,4,chunk);
12282 (void) WriteBlob(image,length,blob);
12283 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12285 jpeg_image=DestroyImage(jpeg_image);
12286 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12287 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12288 blob=(unsigned char *) RelinquishMagickMemory(blob);
12290 /* Write any JNG-chunk-e profiles */
12291 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12293 /* Write IEND chunk */
12294 (void) WriteBlobMSBULong(image,0L);
12295 PNGType(chunk,mng_IEND);
12296 LogPNGChunk(logging,mng_IEND,0);
12297 (void) WriteBlob(image,4,chunk);
12298 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12300 if (logging != MagickFalse)
12301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12302 " exit WriteOneJNGImage()");
12309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12313 % W r i t e J N G I m a g e %
12317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12319 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12321 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12323 % The format of the WriteJNGImage method is:
12325 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12326 % Image *image,ExceptionInfo *exception)
12328 % A description of each parameter follows:
12330 % o image_info: the image info.
12332 % o image: The image.
12334 % o exception: return any errors or warnings in this structure.
12336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12338 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12339 ExceptionInfo *exception)
12342 have_mng_structure,
12352 assert(image_info != (const ImageInfo *) NULL);
12353 assert(image_info->signature == MagickSignature);
12354 assert(image != (Image *) NULL);
12355 assert(image->signature == MagickSignature);
12356 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12357 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12358 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12359 if (status == MagickFalse)
12363 Allocate a MngInfo structure.
12365 have_mng_structure=MagickFalse;
12366 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12367 if (mng_info == (MngInfo *) NULL)
12368 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12370 Initialize members of the MngInfo structure.
12372 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12373 mng_info->image=image;
12374 have_mng_structure=MagickTrue;
12376 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12378 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12379 (void) CloseBlob(image);
12381 (void) CatchImageException(image);
12382 MngInfoFreeStruct(mng_info,&have_mng_structure);
12383 if (logging != MagickFalse)
12384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12389 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12390 ExceptionInfo *exception)
12399 have_mng_structure,
12402 volatile MagickBooleanType
12414 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12415 defined(PNG_MNG_FEATURES_SUPPORTED)
12418 all_images_are_gray,
12428 volatile unsigned int
12439 #if (PNG_LIBPNG_VER < 10200)
12440 if (image_info->verbose)
12441 printf("Your PNG library (libpng-%s) is rather old.\n",
12442 PNG_LIBPNG_VER_STRING);
12448 assert(image_info != (const ImageInfo *) NULL);
12449 assert(image_info->signature == MagickSignature);
12450 assert(image != (Image *) NULL);
12451 assert(image->signature == MagickSignature);
12452 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12453 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12454 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12455 if (status == MagickFalse)
12459 Allocate a MngInfo structure.
12461 have_mng_structure=MagickFalse;
12462 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12463 if (mng_info == (MngInfo *) NULL)
12464 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12466 Initialize members of the MngInfo structure.
12468 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12469 mng_info->image=image;
12470 have_mng_structure=MagickTrue;
12471 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12474 * See if user has requested a specific PNG subformat to be used
12475 * for all of the PNGs in the MNG being written, e.g.,
12477 * convert *.png png8:animation.mng
12479 * To do: check -define png:bit_depth and png:color_type as well,
12480 * or perhaps use mng:bit_depth and mng:color_type instead for
12484 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12485 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12486 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12488 write_jng=MagickFalse;
12489 if (image_info->compression == JPEGCompression)
12490 write_jng=MagickTrue;
12492 mng_info->adjoin=image_info->adjoin &&
12493 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12495 if (logging != MagickFalse)
12497 /* Log some info about the input */
12501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12502 " Checking input image(s)");
12504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12505 " Image_info depth: %.20g",(double) image_info->depth);
12507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12508 " Type: %d",image_info->type);
12511 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12514 " Scene: %.20g",(double) scene++);
12516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12517 " Image depth: %.20g",(double) p->depth);
12519 if (p->alpha_trait)
12520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12527 if (p->storage_class == PseudoClass)
12528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12529 " Storage class: PseudoClass");
12532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12533 " Storage class: DirectClass");
12536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12537 " Number of colors: %.20g",(double) p->colors);
12540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12541 " Number of colors: unspecified");
12543 if (mng_info->adjoin == MagickFalse)
12548 use_global_plte=MagickFalse;
12549 all_images_are_gray=MagickFalse;
12550 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12551 need_local_plte=MagickTrue;
12553 need_defi=MagickFalse;
12554 need_matte=MagickFalse;
12555 mng_info->framing_mode=1;
12556 mng_info->old_framing_mode=1;
12559 if (image_info->page != (char *) NULL)
12562 Determine image bounding box.
12564 SetGeometry(image,&mng_info->page);
12565 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12566 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12578 mng_info->page=image->page;
12579 need_geom=MagickTrue;
12580 if (mng_info->page.width || mng_info->page.height)
12581 need_geom=MagickFalse;
12583 Check all the scenes.
12585 initial_delay=image->delay;
12586 need_iterations=MagickFalse;
12587 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12588 mng_info->equal_physs=MagickTrue,
12589 mng_info->equal_gammas=MagickTrue;
12590 mng_info->equal_srgbs=MagickTrue;
12591 mng_info->equal_backgrounds=MagickTrue;
12593 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12594 defined(PNG_MNG_FEATURES_SUPPORTED)
12595 all_images_are_gray=MagickTrue;
12596 mng_info->equal_palettes=MagickFalse;
12597 need_local_plte=MagickFalse;
12599 for (next_image=image; next_image != (Image *) NULL; )
12603 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12604 mng_info->page.width=next_image->columns+next_image->page.x;
12606 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12607 mng_info->page.height=next_image->rows+next_image->page.y;
12610 if (next_image->page.x || next_image->page.y)
12611 need_defi=MagickTrue;
12613 if (next_image->alpha_trait)
12614 need_matte=MagickTrue;
12616 if ((int) next_image->dispose >= BackgroundDispose)
12617 if (next_image->alpha_trait || next_image->page.x || next_image->page.y ||
12618 ((next_image->columns < mng_info->page.width) &&
12619 (next_image->rows < mng_info->page.height)))
12620 mng_info->need_fram=MagickTrue;
12622 if (next_image->iterations)
12623 need_iterations=MagickTrue;
12625 final_delay=next_image->delay;
12627 if (final_delay != initial_delay || final_delay > 1UL*
12628 next_image->ticks_per_second)
12629 mng_info->need_fram=1;
12631 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12632 defined(PNG_MNG_FEATURES_SUPPORTED)
12634 check for global palette possibility.
12636 if (image->alpha_trait == BlendPixelTrait)
12637 need_local_plte=MagickTrue;
12639 if (need_local_plte == 0)
12641 if (IsImageGray(image,exception) == MagickFalse)
12642 all_images_are_gray=MagickFalse;
12643 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12644 if (use_global_plte == 0)
12645 use_global_plte=mng_info->equal_palettes;
12646 need_local_plte=!mng_info->equal_palettes;
12649 if (GetNextImageInList(next_image) != (Image *) NULL)
12651 if (next_image->background_color.red !=
12652 next_image->next->background_color.red ||
12653 next_image->background_color.green !=
12654 next_image->next->background_color.green ||
12655 next_image->background_color.blue !=
12656 next_image->next->background_color.blue)
12657 mng_info->equal_backgrounds=MagickFalse;
12659 if (next_image->gamma != next_image->next->gamma)
12660 mng_info->equal_gammas=MagickFalse;
12662 if (next_image->rendering_intent !=
12663 next_image->next->rendering_intent)
12664 mng_info->equal_srgbs=MagickFalse;
12666 if ((next_image->units != next_image->next->units) ||
12667 (next_image->resolution.x != next_image->next->resolution.x) ||
12668 (next_image->resolution.y != next_image->next->resolution.y))
12669 mng_info->equal_physs=MagickFalse;
12671 if (mng_info->equal_chrms)
12673 if (next_image->chromaticity.red_primary.x !=
12674 next_image->next->chromaticity.red_primary.x ||
12675 next_image->chromaticity.red_primary.y !=
12676 next_image->next->chromaticity.red_primary.y ||
12677 next_image->chromaticity.green_primary.x !=
12678 next_image->next->chromaticity.green_primary.x ||
12679 next_image->chromaticity.green_primary.y !=
12680 next_image->next->chromaticity.green_primary.y ||
12681 next_image->chromaticity.blue_primary.x !=
12682 next_image->next->chromaticity.blue_primary.x ||
12683 next_image->chromaticity.blue_primary.y !=
12684 next_image->next->chromaticity.blue_primary.y ||
12685 next_image->chromaticity.white_point.x !=
12686 next_image->next->chromaticity.white_point.x ||
12687 next_image->chromaticity.white_point.y !=
12688 next_image->next->chromaticity.white_point.y)
12689 mng_info->equal_chrms=MagickFalse;
12693 next_image=GetNextImageInList(next_image);
12695 if (image_count < 2)
12697 mng_info->equal_backgrounds=MagickFalse;
12698 mng_info->equal_chrms=MagickFalse;
12699 mng_info->equal_gammas=MagickFalse;
12700 mng_info->equal_srgbs=MagickFalse;
12701 mng_info->equal_physs=MagickFalse;
12702 use_global_plte=MagickFalse;
12703 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12704 need_local_plte=MagickTrue;
12706 need_iterations=MagickFalse;
12709 if (mng_info->need_fram == MagickFalse)
12712 Only certain framing rates 100/n are exactly representable without
12713 the FRAM chunk but we'll allow some slop in VLC files
12715 if (final_delay == 0)
12717 if (need_iterations != MagickFalse)
12720 It's probably a GIF with loop; don't run it *too* fast.
12722 if (mng_info->adjoin)
12725 (void) ThrowMagickException(exception,GetMagickModule(),
12727 "input has zero delay between all frames; assuming",
12732 mng_info->ticks_per_second=0;
12734 if (final_delay != 0)
12735 mng_info->ticks_per_second=(png_uint_32)
12736 (image->ticks_per_second/final_delay);
12737 if (final_delay > 50)
12738 mng_info->ticks_per_second=2;
12740 if (final_delay > 75)
12741 mng_info->ticks_per_second=1;
12743 if (final_delay > 125)
12744 mng_info->need_fram=MagickTrue;
12746 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12747 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12748 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12749 1UL*image->ticks_per_second))
12750 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12753 if (mng_info->need_fram != MagickFalse)
12754 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12756 If pseudocolor, we should also check to see if all the
12757 palettes are identical and write a global PLTE if they are.
12761 Write the MNG version 1.0 signature and MHDR chunk.
12763 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12764 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12765 PNGType(chunk,mng_MHDR);
12766 LogPNGChunk(logging,mng_MHDR,28L);
12767 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12768 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12769 PNGLong(chunk+12,mng_info->ticks_per_second);
12770 PNGLong(chunk+16,0L); /* layer count=unknown */
12771 PNGLong(chunk+20,0L); /* frame count=unknown */
12772 PNGLong(chunk+24,0L); /* play time=unknown */
12777 if (need_defi || mng_info->need_fram || use_global_plte)
12778 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12781 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12786 if (need_defi || mng_info->need_fram || use_global_plte)
12787 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12790 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12798 if (need_defi || mng_info->need_fram || use_global_plte)
12799 PNGLong(chunk+28,11L); /* simplicity=LC */
12802 PNGLong(chunk+28,9L); /* simplicity=VLC */
12807 if (need_defi || mng_info->need_fram || use_global_plte)
12808 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12811 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12814 (void) WriteBlob(image,32,chunk);
12815 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12816 option=GetImageOption(image_info,"mng:need-cacheoff");
12817 if (option != (const char *) NULL)
12823 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12825 PNGType(chunk,mng_nEED);
12826 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12827 (void) WriteBlobMSBULong(image,(size_t) length);
12828 LogPNGChunk(logging,mng_nEED,(size_t) length);
12830 (void) WriteBlob(image,length,chunk);
12831 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12833 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12834 (GetNextImageInList(image) != (Image *) NULL) &&
12835 (image->iterations != 1))
12838 Write MNG TERM chunk
12840 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12841 PNGType(chunk,mng_TERM);
12842 LogPNGChunk(logging,mng_TERM,10L);
12843 chunk[4]=3; /* repeat animation */
12844 chunk[5]=0; /* show last frame when done */
12845 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12846 final_delay/MagickMax(image->ticks_per_second,1)));
12848 if (image->iterations == 0)
12849 PNGLong(chunk+10,PNG_UINT_31_MAX);
12852 PNGLong(chunk+10,(png_uint_32) image->iterations);
12854 if (logging != MagickFalse)
12856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12857 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12858 final_delay/MagickMax(image->ticks_per_second,1)));
12860 if (image->iterations == 0)
12861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12862 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12866 " Image iterations: %.20g",(double) image->iterations);
12868 (void) WriteBlob(image,14,chunk);
12869 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12872 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12874 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12875 mng_info->equal_srgbs)
12878 Write MNG sRGB chunk
12880 (void) WriteBlobMSBULong(image,1L);
12881 PNGType(chunk,mng_sRGB);
12882 LogPNGChunk(logging,mng_sRGB,1L);
12884 if (image->rendering_intent != UndefinedIntent)
12885 chunk[4]=(unsigned char)
12886 Magick_RenderingIntent_to_PNG_RenderingIntent(
12887 (image->rendering_intent));
12890 chunk[4]=(unsigned char)
12891 Magick_RenderingIntent_to_PNG_RenderingIntent(
12892 (PerceptualIntent));
12894 (void) WriteBlob(image,5,chunk);
12895 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12896 mng_info->have_write_global_srgb=MagickTrue;
12901 if (image->gamma && mng_info->equal_gammas)
12904 Write MNG gAMA chunk
12906 (void) WriteBlobMSBULong(image,4L);
12907 PNGType(chunk,mng_gAMA);
12908 LogPNGChunk(logging,mng_gAMA,4L);
12909 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12910 (void) WriteBlob(image,8,chunk);
12911 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12912 mng_info->have_write_global_gama=MagickTrue;
12914 if (mng_info->equal_chrms)
12920 Write MNG cHRM chunk
12922 (void) WriteBlobMSBULong(image,32L);
12923 PNGType(chunk,mng_cHRM);
12924 LogPNGChunk(logging,mng_cHRM,32L);
12925 primary=image->chromaticity.white_point;
12926 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12927 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12928 primary=image->chromaticity.red_primary;
12929 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12930 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12931 primary=image->chromaticity.green_primary;
12932 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12933 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12934 primary=image->chromaticity.blue_primary;
12935 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12936 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12937 (void) WriteBlob(image,36,chunk);
12938 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12939 mng_info->have_write_global_chrm=MagickTrue;
12942 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12945 Write MNG pHYs chunk
12947 (void) WriteBlobMSBULong(image,9L);
12948 PNGType(chunk,mng_pHYs);
12949 LogPNGChunk(logging,mng_pHYs,9L);
12951 if (image->units == PixelsPerInchResolution)
12953 PNGLong(chunk+4,(png_uint_32)
12954 (image->resolution.x*100.0/2.54+0.5));
12956 PNGLong(chunk+8,(png_uint_32)
12957 (image->resolution.y*100.0/2.54+0.5));
12964 if (image->units == PixelsPerCentimeterResolution)
12966 PNGLong(chunk+4,(png_uint_32)
12967 (image->resolution.x*100.0+0.5));
12969 PNGLong(chunk+8,(png_uint_32)
12970 (image->resolution.y*100.0+0.5));
12977 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12978 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12982 (void) WriteBlob(image,13,chunk);
12983 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12986 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12987 or does not cover the entire frame.
12989 if (write_mng && (image->alpha_trait || image->page.x > 0 ||
12990 image->page.y > 0 || (image->page.width &&
12991 (image->page.width+image->page.x < mng_info->page.width))
12992 || (image->page.height && (image->page.height+image->page.y
12993 < mng_info->page.height))))
12995 (void) WriteBlobMSBULong(image,6L);
12996 PNGType(chunk,mng_BACK);
12997 LogPNGChunk(logging,mng_BACK,6L);
12998 red=ScaleQuantumToShort(image->background_color.red);
12999 green=ScaleQuantumToShort(image->background_color.green);
13000 blue=ScaleQuantumToShort(image->background_color.blue);
13001 PNGShort(chunk+4,red);
13002 PNGShort(chunk+6,green);
13003 PNGShort(chunk+8,blue);
13004 (void) WriteBlob(image,10,chunk);
13005 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13006 if (mng_info->equal_backgrounds)
13008 (void) WriteBlobMSBULong(image,6L);
13009 PNGType(chunk,mng_bKGD);
13010 LogPNGChunk(logging,mng_bKGD,6L);
13011 (void) WriteBlob(image,10,chunk);
13012 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13016 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13017 if ((need_local_plte == MagickFalse) &&
13018 (image->storage_class == PseudoClass) &&
13019 (all_images_are_gray == MagickFalse))
13025 Write MNG PLTE chunk
13027 data_length=3*image->colors;
13028 (void) WriteBlobMSBULong(image,data_length);
13029 PNGType(chunk,mng_PLTE);
13030 LogPNGChunk(logging,mng_PLTE,data_length);
13032 for (i=0; i < (ssize_t) image->colors; i++)
13034 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13035 image->colormap[i].red) & 0xff);
13036 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13037 image->colormap[i].green) & 0xff);
13038 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13039 image->colormap[i].blue) & 0xff);
13042 (void) WriteBlob(image,data_length+4,chunk);
13043 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13044 mng_info->have_write_global_plte=MagickTrue;
13050 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13051 defined(PNG_MNG_FEATURES_SUPPORTED)
13052 mng_info->equal_palettes=MagickFalse;
13056 if (mng_info->adjoin)
13058 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13059 defined(PNG_MNG_FEATURES_SUPPORTED)
13061 If we aren't using a global palette for the entire MNG, check to
13062 see if we can use one for two or more consecutive images.
13064 if (need_local_plte && use_global_plte && !all_images_are_gray)
13066 if (mng_info->IsPalette)
13069 When equal_palettes is true, this image has the same palette
13070 as the previous PseudoClass image
13072 mng_info->have_write_global_plte=mng_info->equal_palettes;
13073 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13074 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13077 Write MNG PLTE chunk
13082 data_length=3*image->colors;
13083 (void) WriteBlobMSBULong(image,data_length);
13084 PNGType(chunk,mng_PLTE);
13085 LogPNGChunk(logging,mng_PLTE,data_length);
13087 for (i=0; i < (ssize_t) image->colors; i++)
13089 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13090 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13091 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13094 (void) WriteBlob(image,data_length+4,chunk);
13095 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13096 (uInt) (data_length+4)));
13097 mng_info->have_write_global_plte=MagickTrue;
13101 mng_info->have_write_global_plte=MagickFalse;
13112 previous_x=mng_info->page.x;
13113 previous_y=mng_info->page.y;
13120 mng_info->page=image->page;
13121 if ((mng_info->page.x != previous_x) ||
13122 (mng_info->page.y != previous_y))
13124 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13125 PNGType(chunk,mng_DEFI);
13126 LogPNGChunk(logging,mng_DEFI,12L);
13127 chunk[4]=0; /* object 0 MSB */
13128 chunk[5]=0; /* object 0 LSB */
13129 chunk[6]=0; /* visible */
13130 chunk[7]=0; /* abstract */
13131 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13132 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13133 (void) WriteBlob(image,16,chunk);
13134 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13139 mng_info->write_mng=write_mng;
13141 if ((int) image->dispose >= 3)
13142 mng_info->framing_mode=3;
13144 if (mng_info->need_fram && mng_info->adjoin &&
13145 ((image->delay != mng_info->delay) ||
13146 (mng_info->framing_mode != mng_info->old_framing_mode)))
13148 if (image->delay == mng_info->delay)
13151 Write a MNG FRAM chunk with the new framing mode.
13153 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13154 PNGType(chunk,mng_FRAM);
13155 LogPNGChunk(logging,mng_FRAM,1L);
13156 chunk[4]=(unsigned char) mng_info->framing_mode;
13157 (void) WriteBlob(image,5,chunk);
13158 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13163 Write a MNG FRAM chunk with the delay.
13165 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13166 PNGType(chunk,mng_FRAM);
13167 LogPNGChunk(logging,mng_FRAM,10L);
13168 chunk[4]=(unsigned char) mng_info->framing_mode;
13169 chunk[5]=0; /* frame name separator (no name) */
13170 chunk[6]=2; /* flag for changing default delay */
13171 chunk[7]=0; /* flag for changing frame timeout */
13172 chunk[8]=0; /* flag for changing frame clipping */
13173 chunk[9]=0; /* flag for changing frame sync_id */
13174 PNGLong(chunk+10,(png_uint_32)
13175 ((mng_info->ticks_per_second*
13176 image->delay)/MagickMax(image->ticks_per_second,1)));
13177 (void) WriteBlob(image,14,chunk);
13178 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13179 mng_info->delay=(png_uint_32) image->delay;
13181 mng_info->old_framing_mode=mng_info->framing_mode;
13184 #if defined(JNG_SUPPORTED)
13185 if (image_info->compression == JPEGCompression)
13190 if (logging != MagickFalse)
13191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13192 " Writing JNG object.");
13193 /* To do: specify the desired alpha compression method. */
13194 write_info=CloneImageInfo(image_info);
13195 write_info->compression=UndefinedCompression;
13196 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13197 write_info=DestroyImageInfo(write_info);
13202 if (logging != MagickFalse)
13203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13204 " Writing PNG object.");
13206 mng_info->need_blob = MagickFalse;
13207 mng_info->ping_preserve_colormap = MagickFalse;
13209 /* We don't want any ancillary chunks written */
13210 mng_info->ping_exclude_bKGD=MagickTrue;
13211 mng_info->ping_exclude_cHRM=MagickTrue;
13212 mng_info->ping_exclude_date=MagickTrue;
13213 mng_info->ping_exclude_EXIF=MagickTrue;
13214 mng_info->ping_exclude_gAMA=MagickTrue;
13215 mng_info->ping_exclude_iCCP=MagickTrue;
13216 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13217 mng_info->ping_exclude_oFFs=MagickTrue;
13218 mng_info->ping_exclude_pHYs=MagickTrue;
13219 mng_info->ping_exclude_sRGB=MagickTrue;
13220 mng_info->ping_exclude_tEXt=MagickTrue;
13221 mng_info->ping_exclude_tRNS=MagickTrue;
13222 mng_info->ping_exclude_vpAg=MagickTrue;
13223 mng_info->ping_exclude_zCCP=MagickTrue;
13224 mng_info->ping_exclude_zTXt=MagickTrue;
13226 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13229 if (status == MagickFalse)
13231 MngInfoFreeStruct(mng_info,&have_mng_structure);
13232 (void) CloseBlob(image);
13233 return(MagickFalse);
13235 (void) CatchImageException(image);
13236 if (GetNextImageInList(image) == (Image *) NULL)
13238 image=SyncNextImageInList(image);
13239 status=SetImageProgress(image,SaveImagesTag,scene++,
13240 GetImageListLength(image));
13242 if (status == MagickFalse)
13245 } while (mng_info->adjoin);
13249 while (GetPreviousImageInList(image) != (Image *) NULL)
13250 image=GetPreviousImageInList(image);
13252 Write the MEND chunk.
13254 (void) WriteBlobMSBULong(image,0x00000000L);
13255 PNGType(chunk,mng_MEND);
13256 LogPNGChunk(logging,mng_MEND,0L);
13257 (void) WriteBlob(image,4,chunk);
13258 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13261 Relinquish resources.
13263 (void) CloseBlob(image);
13264 MngInfoFreeStruct(mng_info,&have_mng_structure);
13266 if (logging != MagickFalse)
13267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13269 return(MagickTrue);
13271 #else /* PNG_LIBPNG_VER > 10011 */
13273 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13276 printf("Your PNG library is too old: You have libpng-%s\n",
13277 PNG_LIBPNG_VER_STRING);
13279 ThrowBinaryException(CoderError,"PNG library is too old",
13280 image_info->filename);
13283 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13285 return(WritePNGImage(image_info,image));
13287 #endif /* PNG_LIBPNG_VER > 10011 */