2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
106 /* ImageMagick differences */
107 #define first_scene scene
109 #if PNG_LIBPNG_VER > 10011
111 Optional declarations. Define or undefine them as you like.
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116 Features under construction. Define these to work on them.
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
132 /* Macros for left-bit-replication to ensure that pixels
133 * and PixelInfos all have the same image->depth, and for use
134 * in PNG8 quantization.
138 /* LBR01: Replicate top bit */
140 #define LBR01PacketRed(pixelpacket) \
141 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
144 #define LBR01PacketGreen(pixelpacket) \
145 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
148 #define LBR01PacketBlue(pixelpacket) \
149 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
152 #define LBR01PacketAlpha(pixelpacket) \
153 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
156 #define LBR01PacketRGB(pixelpacket) \
158 LBR01PacketRed((pixelpacket)); \
159 LBR01PacketGreen((pixelpacket)); \
160 LBR01PacketBlue((pixelpacket)); \
163 #define LBR01PacketRGBO(pixelpacket) \
165 LBR01PacketRGB((pixelpacket)); \
166 LBR01PacketAlpha((pixelpacket)); \
169 #define LBR01PixelRed(pixel) \
170 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
173 #define LBR01PixelGreen(pixel) \
174 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177 #define LBR01PixelBlue(pixel) \
178 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
181 #define LBR01PixelAlpha(pixel) \
182 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
185 #define LBR01PixelRGB(pixel) \
187 LBR01PixelRed((pixel)); \
188 LBR01PixelGreen((pixel)); \
189 LBR01PixelBlue((pixel)); \
192 #define LBR01PixelRGBA(pixel) \
194 LBR01PixelRGB((pixel)); \
195 LBR01PixelAlpha((pixel)); \
198 /* LBR02: Replicate top 2 bits */
200 #define LBR02PacketRed(pixelpacket) \
202 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
203 (pixelpacket).red=ScaleCharToQuantum( \
204 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
206 #define LBR02PacketGreen(pixelpacket) \
208 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
209 (pixelpacket).green=ScaleCharToQuantum( \
210 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
212 #define LBR02PacketBlue(pixelpacket) \
214 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
215 (pixelpacket).blue=ScaleCharToQuantum( \
216 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
218 #define LBR02PacketAlpha(pixelpacket) \
220 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
221 (pixelpacket).alpha=ScaleCharToQuantum( \
222 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
225 #define LBR02PacketRGB(pixelpacket) \
227 LBR02PacketRed((pixelpacket)); \
228 LBR02PacketGreen((pixelpacket)); \
229 LBR02PacketBlue((pixelpacket)); \
232 #define LBR02PacketRGBO(pixelpacket) \
234 LBR02PacketRGB((pixelpacket)); \
235 LBR02PacketAlpha((pixelpacket)); \
238 #define LBR02PixelRed(pixel) \
240 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
242 SetPixelRed(image, ScaleCharToQuantum( \
243 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
246 #define LBR02PixelGreen(pixel) \
248 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
250 SetPixelGreen(image, ScaleCharToQuantum( \
251 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
254 #define LBR02PixelBlue(pixel) \
256 unsigned char lbr_bits= \
257 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
258 SetPixelBlue(image, ScaleCharToQuantum( \
259 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
262 #define LBR02PixelAlpha(pixel) \
264 unsigned char lbr_bits= \
265 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
266 SetPixelAlpha(image, ScaleCharToQuantum( \
267 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
271 #define LBR02PixelRGB(pixel) \
273 LBR02PixelRed((pixel)); \
274 LBR02PixelGreen((pixel)); \
275 LBR02PixelBlue((pixel)); \
278 #define LBR02PixelRGBA(pixel) \
280 LBR02PixelRGB((pixel)); \
281 LBR02PixelAlpha((pixel)); \
284 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
285 PNG8 quantization) */
287 #define LBR03PacketRed(pixelpacket) \
289 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
290 (pixelpacket).red=ScaleCharToQuantum( \
291 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
293 #define LBR03PacketGreen(pixelpacket) \
295 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
296 (pixelpacket).green=ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
299 #define LBR03PacketBlue(pixelpacket) \
301 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
302 (pixelpacket).blue=ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
306 #define LBR03PacketRGB(pixelpacket) \
308 LBR03PacketRed((pixelpacket)); \
309 LBR03PacketGreen((pixelpacket)); \
310 LBR03PacketBlue((pixelpacket)); \
313 #define LBR03PixelRed(pixel) \
315 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
317 SetPixelRed(image, ScaleCharToQuantum( \
318 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
320 #define LBR03Green(pixel) \
322 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
324 SetPixelGreen(image, ScaleCharToQuantum( \
325 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
327 #define LBR03Blue(pixel) \
329 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
331 SetPixelBlue(image, ScaleCharToQuantum( \
332 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
335 #define LBR03RGB(pixel) \
337 LBR03PixelRed((pixel)); \
338 LBR03Green((pixel)); \
339 LBR03Blue((pixel)); \
342 /* LBR04: Replicate top 4 bits */
344 #define LBR04PacketRed(pixelpacket) \
346 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
347 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
349 #define LBR04PacketGreen(pixelpacket) \
351 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
352 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
354 #define LBR04PacketBlue(pixelpacket) \
356 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
357 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
359 #define LBR04PacketAlpha(pixelpacket) \
361 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
362 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
365 #define LBR04PacketRGB(pixelpacket) \
367 LBR04PacketRed((pixelpacket)); \
368 LBR04PacketGreen((pixelpacket)); \
369 LBR04PacketBlue((pixelpacket)); \
372 #define LBR04PacketRGBO(pixelpacket) \
374 LBR04PacketRGB((pixelpacket)); \
375 LBR04PacketAlpha((pixelpacket)); \
378 #define LBR04PixelRed(pixel) \
380 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
383 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
385 #define LBR04PixelGreen(pixel) \
387 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
389 SetPixelGreen(image,\
390 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
392 #define LBR04PixelBlue(pixel) \
394 unsigned char lbr_bits= \
395 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
397 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
399 #define LBR04PixelAlpha(pixel) \
401 unsigned char lbr_bits= \
402 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
403 SetPixelAlpha(image,\
404 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
407 #define LBR04PixelRGB(pixel) \
409 LBR04PixelRed((pixel)); \
410 LBR04PixelGreen((pixel)); \
411 LBR04PixelBlue((pixel)); \
414 #define LBR04PixelRGBA(pixel) \
416 LBR04PixelRGB((pixel)); \
417 LBR04PixelAlpha((pixel)); \
421 /* LBR08: Replicate top 8 bits */
423 #define LBR08PacketRed(pixelpacket) \
425 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
426 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
428 #define LBR08PacketGreen(pixelpacket) \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
431 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
433 #define LBR08PacketBlue(pixelpacket) \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
436 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
438 #define LBR08PacketAlpha(pixelpacket) \
440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
441 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
444 #define LBR08PacketRGB(pixelpacket) \
446 LBR08PacketRed((pixelpacket)); \
447 LBR08PacketGreen((pixelpacket)); \
448 LBR08PacketBlue((pixelpacket)); \
451 #define LBR08PacketRGBO(pixelpacket) \
453 LBR08PacketRGB((pixelpacket)); \
454 LBR08PacketAlpha((pixelpacket)); \
457 #define LBR08PixelRed(pixel) \
459 unsigned char lbr_bits= \
460 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
462 ScaleCharToQuantum((lbr_bits)), (pixel)); \
464 #define LBR08PixelGreen(pixel) \
466 unsigned char lbr_bits= \
467 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
468 SetPixelGreen(image,\
469 ScaleCharToQuantum((lbr_bits)), (pixel)); \
471 #define LBR08PixelBlue(pixel) \
473 unsigned char lbr_bits= \
474 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
476 ScaleCharToQuantum((lbr_bits)), (pixel)); \
478 #define LBR08PixelAlpha(pixel) \
480 unsigned char lbr_bits= \
481 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
482 SetPixelAlpha(image,\
483 ScaleCharToQuantum((lbr_bits)), (pixel)); \
486 #define LBR08PixelRGB(pixel) \
488 LBR08PixelRed((pixel)); \
489 LBR08PixelGreen((pixel)); \
490 LBR08PixelBlue((pixel)); \
493 #define LBR08PixelRGBA(pixel) \
495 LBR08PixelRGB((pixel)); \
496 LBR08PixelAlpha((pixel)); \
500 /* LBR16: Replicate top 16 bits */
502 #define LBR16PacketRed(pixelpacket) \
504 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
505 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
507 #define LBR16PacketGreen(pixelpacket) \
509 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
510 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
512 #define LBR16PacketBlue(pixelpacket) \
514 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
515 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
517 #define LBR16PacketAlpha(pixelpacket) \
519 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
520 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
523 #define LBR16PacketRGB(pixelpacket) \
525 LBR16PacketRed((pixelpacket)); \
526 LBR16PacketGreen((pixelpacket)); \
527 LBR16PacketBlue((pixelpacket)); \
530 #define LBR16PacketRGBO(pixelpacket) \
532 LBR16PacketRGB((pixelpacket)); \
533 LBR16PacketAlpha((pixelpacket)); \
536 #define LBR16PixelRed(pixel) \
538 unsigned short lbr_bits= \
539 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
541 ScaleShortToQuantum((lbr_bits)),(pixel)); \
543 #define LBR16PixelGreen(pixel) \
545 unsigned short lbr_bits= \
546 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
547 SetPixelGreen(image,\
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
550 #define LBR16PixelBlue(pixel) \
552 unsigned short lbr_bits= \
553 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
557 #define LBR16PixelAlpha(pixel) \
559 unsigned short lbr_bits= \
560 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
561 SetPixelAlpha(image,\
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
565 #define LBR16PixelRGB(pixel) \
567 LBR16PixelRed((pixel)); \
568 LBR16PixelGreen((pixel)); \
569 LBR16PixelBlue((pixel)); \
572 #define LBR16PixelRGBA(pixel) \
574 LBR16PixelRGB((pixel)); \
575 LBR16PixelAlpha((pixel)); \
579 Establish thread safety.
580 setjmp/longjmp is claimed to be safe on these platforms:
581 setjmp/longjmp is alleged to be unsafe on these platforms:
583 #ifndef SETJMP_IS_THREAD_SAFE
584 #define PNG_SETJMP_NOT_THREAD_SAFE
587 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
589 *ping_semaphore = (SemaphoreInfo *) NULL;
593 This temporary until I set up malloc'ed object attributes array.
594 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
597 #define MNG_MAX_OBJECTS 256
600 If this not defined, spec is interpreted strictly. If it is
601 defined, an attempt will be made to recover from some errors,
603 o global PLTE too short
608 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
609 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
610 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
611 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
612 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
613 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
614 will be enabled by default in libpng-1.2.0.
616 #ifdef PNG_MNG_FEATURES_SUPPORTED
617 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
618 # define PNG_READ_EMPTY_PLTE_SUPPORTED
620 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
621 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
626 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
627 This macro is only defined in libpng-1.0.3 and later.
628 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
630 #ifndef PNG_UINT_31_MAX
631 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
635 Constant strings for known chunk types. If you need to add a chunk,
636 add a string holding the name here. To make the code more
637 portable, we use ASCII numbers like this, not characters.
640 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
641 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
642 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
643 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
644 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
645 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
646 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
647 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
648 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
649 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
650 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
651 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
652 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
653 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
654 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
655 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
656 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
657 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
658 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
659 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
660 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
661 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
662 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
663 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
664 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
665 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
666 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
667 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
668 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
669 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
670 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
671 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
672 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
673 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
675 #if defined(JNG_SUPPORTED)
676 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
677 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
678 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
679 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
680 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
681 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
685 Other known chunks that are not yet supported by ImageMagick:
686 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
687 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
688 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
689 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
690 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
691 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
692 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
693 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
696 typedef struct _MngBox
705 typedef struct _MngPair
712 #ifdef MNG_OBJECT_BUFFERS
713 typedef struct _MngBuffer
745 typedef struct _MngInfo
748 #ifdef MNG_OBJECT_BUFFERS
750 *ob[MNG_MAX_OBJECTS];
761 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
762 bytes_in_read_buffer,
768 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
769 defined(PNG_MNG_FEATURES_SUPPORTED)
781 have_saved_bkgd_index,
782 have_write_global_chrm,
783 have_write_global_gama,
784 have_write_global_plte,
785 have_write_global_srgb,
799 x_off[MNG_MAX_OBJECTS],
800 y_off[MNG_MAX_OBJECTS];
806 object_clip[MNG_MAX_OBJECTS];
809 /* These flags could be combined into one byte */
810 exists[MNG_MAX_OBJECTS],
811 frozen[MNG_MAX_OBJECTS],
813 invisible[MNG_MAX_OBJECTS],
814 viewable[MNG_MAX_OBJECTS];
826 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
844 global_x_pixels_per_unit,
845 global_y_pixels_per_unit,
855 global_phys_unit_type,
870 write_png_compression_level,
871 write_png_compression_strategy,
872 write_png_compression_filter,
877 #ifdef MNG_BASI_SUPPORTED
885 basi_compression_method,
887 basi_interlace_method,
910 /* Added at version 6.6.6-7 */
918 /* ping_exclude_iTXt, */
925 ping_exclude_zCCP, /* hex-encoded iCCP */
927 ping_preserve_colormap;
933 Forward declarations.
935 static MagickBooleanType
936 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
938 static MagickBooleanType
939 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
941 #if defined(JNG_SUPPORTED)
942 static MagickBooleanType
943 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
946 #if PNG_LIBPNG_VER > 10011
949 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
950 static MagickBooleanType
951 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
953 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
955 * This is true if the high byte and the next highest byte of
956 * each sample of the image, the colormap, and the background color
957 * are equal to each other. We check this by seeing if the samples
958 * are unchanged when we scale them down to 8 and back up to Quantum.
960 * We don't use the method GetImageDepth() because it doesn't check
961 * background and doesn't handle PseudoClass specially.
964 #define QuantumToCharToQuantumEqQuantum(quantum) \
965 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
968 ok_to_reduce=MagickFalse;
970 if (image->depth >= 16)
977 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
979 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
980 MagickTrue : MagickFalse;
982 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
986 for (indx=0; indx < (ssize_t) image->colors; indx++)
989 QuantumToCharToQuantumEqQuantum(
990 image->colormap[indx].red) &&
991 QuantumToCharToQuantumEqQuantum(
992 image->colormap[indx].green) &&
993 QuantumToCharToQuantumEqQuantum(
994 image->colormap[indx].blue)) ?
995 MagickTrue : MagickFalse;
997 if (ok_to_reduce == MagickFalse)
1002 if ((ok_to_reduce != MagickFalse) &&
1003 (image->storage_class != PseudoClass))
1011 for (y=0; y < (ssize_t) image->rows; y++)
1013 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1015 if (p == (const Quantum *) NULL)
1017 ok_to_reduce = MagickFalse;
1021 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1024 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1026 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1027 MagickTrue : MagickFalse;
1029 if (ok_to_reduce == MagickFalse)
1032 p+=GetPixelChannels(image);
1039 if (ok_to_reduce != MagickFalse)
1041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1042 " OK to reduce PNG bit depth to 8 without loss of info");
1046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1047 " Not OK to reduce PNG bit depth to 8 without loss of info");
1051 return ok_to_reduce;
1053 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1056 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1060 case PerceptualIntent:
1063 case RelativeIntent:
1066 case SaturationIntent:
1069 case AbsoluteIntent:
1077 static RenderingIntent
1078 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1080 switch (ping_intent)
1083 return PerceptualIntent;
1086 return RelativeIntent;
1089 return SaturationIntent;
1092 return AbsoluteIntent;
1095 return UndefinedIntent;
1099 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1107 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1121 % I m a g e I s G r a y %
1125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1127 % Like IsImageGray except does not change DirectClass to PseudoClass %
1129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1131 static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
1133 register const Quantum
1141 assert(image != (Image *) NULL);
1142 assert(image->signature == MagickSignature);
1143 if (image->debug != MagickFalse)
1144 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1146 if (image->storage_class == PseudoClass)
1148 for (i=0; i < (ssize_t) image->colors; i++)
1149 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
1150 return(MagickFalse);
1153 for (y=0; y < (ssize_t) image->rows; y++)
1155 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1156 if (p == (const Quantum *) NULL)
1157 return(MagickFalse);
1158 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1160 if (IsPixelGray(image,p) == MagickFalse)
1161 return(MagickFalse);
1162 p+=GetPixelChannels(image);
1167 #endif /* PNG_LIBPNG_VER > 10011 */
1168 #endif /* MAGICKCORE_PNG_DELEGATE */
1171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1181 % IsMNG() returns MagickTrue if the image format type, identified by the
1182 % magick string, is MNG.
1184 % The format of the IsMNG method is:
1186 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1188 % A description of each parameter follows:
1190 % o magick: compare image format pattern against these bytes.
1192 % o length: Specifies the length of the magick string.
1196 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1199 return(MagickFalse);
1201 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1204 return(MagickFalse);
1208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1218 % IsJNG() returns MagickTrue if the image format type, identified by the
1219 % magick string, is JNG.
1221 % The format of the IsJNG method is:
1223 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1225 % A description of each parameter follows:
1227 % o magick: compare image format pattern against these bytes.
1229 % o length: Specifies the length of the magick string.
1233 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1236 return(MagickFalse);
1238 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1241 return(MagickFalse);
1245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1255 % IsPNG() returns MagickTrue if the image format type, identified by the
1256 % magick string, is PNG.
1258 % The format of the IsPNG method is:
1260 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1262 % A description of each parameter follows:
1264 % o magick: compare image format pattern against these bytes.
1266 % o length: Specifies the length of the magick string.
1269 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1272 return(MagickFalse);
1274 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1277 return(MagickFalse);
1280 #if defined(MAGICKCORE_PNG_DELEGATE)
1281 #if defined(__cplusplus) || defined(c_plusplus)
1285 #if (PNG_LIBPNG_VER > 10011)
1286 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1291 assert(image != (Image *) NULL);
1292 assert(image->signature == MagickSignature);
1293 buffer[0]=(unsigned char) (value >> 24);
1294 buffer[1]=(unsigned char) (value >> 16);
1295 buffer[2]=(unsigned char) (value >> 8);
1296 buffer[3]=(unsigned char) value;
1297 return((size_t) WriteBlob(image,4,buffer));
1300 static void PNGLong(png_bytep p,png_uint_32 value)
1302 *p++=(png_byte) ((value >> 24) & 0xff);
1303 *p++=(png_byte) ((value >> 16) & 0xff);
1304 *p++=(png_byte) ((value >> 8) & 0xff);
1305 *p++=(png_byte) (value & 0xff);
1308 #if defined(JNG_SUPPORTED)
1309 static void PNGsLong(png_bytep p,png_int_32 value)
1311 *p++=(png_byte) ((value >> 24) & 0xff);
1312 *p++=(png_byte) ((value >> 16) & 0xff);
1313 *p++=(png_byte) ((value >> 8) & 0xff);
1314 *p++=(png_byte) (value & 0xff);
1318 static void PNGShort(png_bytep p,png_uint_16 value)
1320 *p++=(png_byte) ((value >> 8) & 0xff);
1321 *p++=(png_byte) (value & 0xff);
1324 static void PNGType(png_bytep p,png_bytep type)
1326 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1329 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1332 if (logging != MagickFalse)
1333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1334 " Writing %c%c%c%c chunk, length: %.20g",
1335 type[0],type[1],type[2],type[3],(double) length);
1337 #endif /* PNG_LIBPNG_VER > 10011 */
1339 #if defined(__cplusplus) || defined(c_plusplus)
1343 #if PNG_LIBPNG_VER > 10011
1345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1349 % R e a d P N G I m a g e %
1353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1355 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1356 % Multiple-image Network Graphics (MNG) image file and returns it. It
1357 % allocates the memory necessary for the new Image structure and returns a
1358 % pointer to the new image or set of images.
1360 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1362 % The format of the ReadPNGImage method is:
1364 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1366 % A description of each parameter follows:
1368 % o image_info: the image info.
1370 % o exception: return any errors or warnings in this structure.
1372 % To do, more or less in chronological order (as of version 5.5.2,
1373 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1375 % Get 16-bit cheap transparency working.
1377 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1379 % Preserve all unknown and not-yet-handled known chunks found in input
1380 % PNG file and copy them into output PNG files according to the PNG
1383 % (At this point, PNG encoding should be in full MNG compliance)
1385 % Provide options for choice of background to use when the MNG BACK
1386 % chunk is not present or is not mandatory (i.e., leave transparent,
1387 % user specified, MNG BACK, PNG bKGD)
1389 % Implement LOOP/ENDL [done, but could do discretionary loops more
1390 % efficiently by linking in the duplicate frames.].
1392 % Decode and act on the MHDR simplicity profile (offer option to reject
1393 % files or attempt to process them anyway when the profile isn't LC or VLC).
1395 % Upgrade to full MNG without Delta-PNG.
1397 % o BACK [done a while ago except for background image ID]
1398 % o MOVE [done 15 May 1999]
1399 % o CLIP [done 15 May 1999]
1400 % o DISC [done 19 May 1999]
1401 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1402 % o SEEK [partially done 19 May 1999 (discard function only)]
1406 % o MNG-level tEXt/iTXt/zTXt
1411 % o iTXt (wait for libpng implementation).
1413 % Use the scene signature to discover when an identical scene is
1414 % being reused, and just point to the original image->exception instead
1415 % of storing another set of pixels. This not specific to MNG
1416 % but could be applied generally.
1418 % Upgrade to full MNG with Delta-PNG.
1420 % JNG tEXt/iTXt/zTXt
1422 % We will not attempt to read files containing the CgBI chunk.
1423 % They are really Xcode files meant for display on the iPhone.
1424 % These are not valid PNG files and it is impossible to recover
1425 % the original PNG from files that have been converted to Xcode-PNG,
1426 % since irretrievable loss of color data has occurred due to the
1427 % use of premultiplied alpha.
1430 #if defined(__cplusplus) || defined(c_plusplus)
1435 This the function that does the actual reading of data. It is
1436 the same as the one supplied in libpng, except that it receives the
1437 datastream from the ReadBlob() function instead of standard input.
1439 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1444 image=(Image *) png_get_io_ptr(png_ptr);
1450 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1451 if (check != length)
1456 (void) FormatLocaleString(msg,MaxTextExtent,
1457 "Expected %.20g bytes; found %.20g bytes",(double) length,
1459 png_warning(png_ptr,msg);
1460 png_error(png_ptr,"Read Exception");
1465 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1466 !defined(PNG_MNG_FEATURES_SUPPORTED)
1467 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1468 * older than libpng-1.0.3a, which was the first to allow the empty
1469 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1470 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1471 * encountered after an empty PLTE, so we have to look ahead for bKGD
1472 * chunks and remove them from the datastream that is passed to libpng,
1473 * and store their contents for later use.
1475 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1490 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1491 image=(Image *) mng_info->image;
1492 while (mng_info->bytes_in_read_buffer && length)
1494 data[i]=mng_info->read_buffer[i];
1495 mng_info->bytes_in_read_buffer--;
1501 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1503 if (check != length)
1504 png_error(png_ptr,"Read Exception");
1508 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1511 check=(png_size_t) ReadBlob(image,(size_t) length,
1512 (char *) mng_info->read_buffer);
1513 mng_info->read_buffer[4]=0;
1514 mng_info->bytes_in_read_buffer=4;
1515 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1516 mng_info->found_empty_plte=MagickTrue;
1517 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1519 mng_info->found_empty_plte=MagickFalse;
1520 mng_info->have_saved_bkgd_index=MagickFalse;
1524 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1527 check=(png_size_t) ReadBlob(image,(size_t) length,
1528 (char *) mng_info->read_buffer);
1529 mng_info->read_buffer[4]=0;
1530 mng_info->bytes_in_read_buffer=4;
1531 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1532 if (mng_info->found_empty_plte)
1535 Skip the bKGD data byte and CRC.
1538 ReadBlob(image,5,(char *) mng_info->read_buffer);
1539 check=(png_size_t) ReadBlob(image,(size_t) length,
1540 (char *) mng_info->read_buffer);
1541 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1542 mng_info->have_saved_bkgd_index=MagickTrue;
1543 mng_info->bytes_in_read_buffer=0;
1551 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1556 image=(Image *) png_get_io_ptr(png_ptr);
1562 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1564 if (check != length)
1565 png_error(png_ptr,"WriteBlob Failed");
1569 static void png_flush_data(png_structp png_ptr)
1574 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1575 static int PalettesAreEqual(Image *a,Image *b)
1580 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1581 return((int) MagickFalse);
1583 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1584 return((int) MagickFalse);
1586 if (a->colors != b->colors)
1587 return((int) MagickFalse);
1589 for (i=0; i < (ssize_t) a->colors; i++)
1591 if ((a->colormap[i].red != b->colormap[i].red) ||
1592 (a->colormap[i].green != b->colormap[i].green) ||
1593 (a->colormap[i].blue != b->colormap[i].blue))
1594 return((int) MagickFalse);
1597 return((int) MagickTrue);
1601 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1603 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1604 mng_info->exists[i] && !mng_info->frozen[i])
1606 #ifdef MNG_OBJECT_BUFFERS
1607 if (mng_info->ob[i] != (MngBuffer *) NULL)
1609 if (mng_info->ob[i]->reference_count > 0)
1610 mng_info->ob[i]->reference_count--;
1612 if (mng_info->ob[i]->reference_count == 0)
1614 if (mng_info->ob[i]->image != (Image *) NULL)
1615 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1617 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1620 mng_info->ob[i]=(MngBuffer *) NULL;
1622 mng_info->exists[i]=MagickFalse;
1623 mng_info->invisible[i]=MagickFalse;
1624 mng_info->viewable[i]=MagickFalse;
1625 mng_info->frozen[i]=MagickFalse;
1626 mng_info->x_off[i]=0;
1627 mng_info->y_off[i]=0;
1628 mng_info->object_clip[i].left=0;
1629 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1630 mng_info->object_clip[i].top=0;
1631 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1635 static void MngInfoFreeStruct(MngInfo *mng_info,
1636 MagickBooleanType *have_mng_structure)
1638 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1643 for (i=1; i < MNG_MAX_OBJECTS; i++)
1644 MngInfoDiscardObject(mng_info,i);
1646 if (mng_info->global_plte != (png_colorp) NULL)
1647 mng_info->global_plte=(png_colorp)
1648 RelinquishMagickMemory(mng_info->global_plte);
1650 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1651 *have_mng_structure=MagickFalse;
1655 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1661 if (box.left < box2.left)
1664 if (box.top < box2.top)
1667 if (box.right > box2.right)
1668 box.right=box2.right;
1670 if (box.bottom > box2.bottom)
1671 box.bottom=box2.bottom;
1676 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1682 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1684 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1685 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1686 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1687 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1688 if (delta_type != 0)
1690 box.left+=previous_box.left;
1691 box.right+=previous_box.right;
1692 box.top+=previous_box.top;
1693 box.bottom+=previous_box.bottom;
1699 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1705 Read two ssize_ts from CLON, MOVE or PAST chunk
1707 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1708 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1710 if (delta_type != 0)
1712 pair.a+=previous_pair.a;
1713 pair.b+=previous_pair.b;
1719 static long mng_get_long(unsigned char *p)
1721 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1724 typedef struct _PNGErrorInfo
1733 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1744 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1745 image=error_info->image;
1746 exception=error_info->exception;
1748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1749 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1751 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1752 "`%s'",image->filename);
1754 #if (PNG_LIBPNG_VER < 10500)
1755 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1756 * are building with libpng-1.4.x and can be ignored.
1758 longjmp(ping->jmpbuf,1);
1760 png_longjmp(ping,1);
1764 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1775 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1776 png_error(ping, message);
1778 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1779 image=error_info->image;
1780 exception=error_info->exception;
1781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1782 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1784 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1785 message,"`%s'",image->filename);
1788 #ifdef PNG_USER_MEM_SUPPORTED
1789 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1792 return((png_voidp) AcquireMagickMemory((size_t) size));
1796 Free a pointer. It is removed from the list at the same time.
1798 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1801 ptr=RelinquishMagickMemory(ptr);
1802 return((png_free_ptr) NULL);
1806 #if defined(__cplusplus) || defined(c_plusplus)
1811 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1812 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1817 register unsigned char
1831 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1832 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1833 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1834 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1835 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1839 /* look for newline */
1843 /* look for length */
1844 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1847 length=(png_uint_32) StringToLong(sp);
1849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1850 " length: %lu",(unsigned long) length);
1852 while (*sp != ' ' && *sp != '\n')
1855 /* allocate space */
1858 png_warning(ping,"invalid profile length");
1859 return(MagickFalse);
1862 profile=BlobToStringInfo((const void *) NULL,length);
1864 if (profile == (StringInfo *) NULL)
1866 png_warning(ping, "unable to copy profile");
1867 return(MagickFalse);
1870 /* copy profile, skipping white space and column 1 "=" signs */
1871 dp=GetStringInfoDatum(profile);
1874 for (i=0; i < (ssize_t) nibbles; i++)
1876 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1880 png_warning(ping, "ran out of profile data");
1881 profile=DestroyStringInfo(profile);
1882 return(MagickFalse);
1888 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1891 (*dp++)+=unhex[(int) *sp++];
1894 We have already read "Raw profile type.
1896 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1897 profile=DestroyStringInfo(profile);
1899 if (image_info->verbose)
1900 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1905 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1906 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1912 /* The unknown chunk structure contains the chunk data:
1917 Note that libpng has already taken care of the CRC handling.
1921 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1922 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1923 return(0); /* Did not recognize */
1925 /* recognized vpAg */
1927 if (chunk->size != 9)
1928 return(-1); /* Error return */
1930 if (chunk->data[8] != 0)
1931 return(0); /* ImageMagick requires pixel units */
1933 image=(Image *) png_get_user_chunk_ptr(ping);
1935 image->page.width=(size_t) ((chunk->data[0] << 24) |
1936 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1938 image->page.height=(size_t) ((chunk->data[4] << 24) |
1939 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1941 /* Return one of the following: */
1942 /* return(-n); chunk had an error */
1943 /* return(0); did not recognize */
1944 /* return(n); success */
1952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1956 % R e a d O n e P N G I m a g e %
1960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1962 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1963 % (minus the 8-byte signature) and returns it. It allocates the memory
1964 % necessary for the new Image structure and returns a pointer to the new
1967 % The format of the ReadOnePNGImage method is:
1969 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1970 % ExceptionInfo *exception)
1972 % A description of each parameter follows:
1974 % o mng_info: Specifies a pointer to a MngInfo structure.
1976 % o image_info: the image info.
1978 % o exception: return any errors or warnings in this structure.
1981 static Image *ReadOnePNGImage(MngInfo *mng_info,
1982 const ImageInfo *image_info, ExceptionInfo *exception)
1984 /* Read one PNG image */
1986 /* To do: Read the tIME chunk into the date:modify property */
1987 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2001 ping_interlace_method,
2002 ping_compression_method,
2053 register unsigned char
2070 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2071 png_byte unused_chunks[]=
2073 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2074 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2075 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2076 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2077 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2078 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2082 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2083 " Enter ReadOnePNGImage()");
2085 #if (PNG_LIBPNG_VER < 10200)
2086 if (image_info->verbose)
2087 printf("Your PNG library (libpng-%s) is rather old.\n",
2088 PNG_LIBPNG_VER_STRING);
2091 #if (PNG_LIBPNG_VER >= 10400)
2092 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2093 if (image_info->verbose)
2095 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2096 PNG_LIBPNG_VER_STRING);
2097 printf("Please update it.\n");
2103 quantum_info = (QuantumInfo *) NULL;
2104 image=mng_info->image;
2106 if (logging != MagickFalse)
2107 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2108 " image->matte=%d",(int) image->matte);
2110 /* Set to an out-of-range color unless tRNS chunk is present */
2111 transparent_color.red=65537;
2112 transparent_color.green=65537;
2113 transparent_color.blue=65537;
2114 transparent_color.alpha=65537;
2118 num_raw_profiles = 0;
2121 Allocate the PNG structures
2123 #ifdef PNG_USER_MEM_SUPPORTED
2124 error_info.image=image;
2125 error_info.exception=exception;
2126 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2127 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2128 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2130 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2131 MagickPNGErrorHandler,MagickPNGWarningHandler);
2133 if (ping == (png_struct *) NULL)
2134 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2136 ping_info=png_create_info_struct(ping);
2138 if (ping_info == (png_info *) NULL)
2140 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2144 end_info=png_create_info_struct(ping);
2146 if (end_info == (png_info *) NULL)
2148 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2149 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152 ping_pixels=(unsigned char *) NULL;
2154 if (setjmp(png_jmpbuf(ping)))
2157 PNG image is corrupt.
2159 png_destroy_read_struct(&ping,&ping_info,&end_info);
2161 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2162 UnlockSemaphoreInfo(ping_semaphore);
2165 if (ping_pixels != (unsigned char *) NULL)
2166 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2168 if (logging != MagickFalse)
2169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170 " exit ReadOnePNGImage() with error.");
2172 if (image != (Image *) NULL)
2174 InheritException(exception,exception);
2178 return(GetFirstImageInList(image));
2181 /* { For navigation to end of SETJMP-protected block. Within this
2182 * block, use png_error() instead of Throwing an Exception, to ensure
2183 * that libpng is able to clean up, and that the semaphore is unlocked.
2186 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2187 LockSemaphoreInfo(ping_semaphore);
2191 Prepare PNG for reading.
2194 mng_info->image_found++;
2195 png_set_sig_bytes(ping,8);
2197 if (LocaleCompare(image_info->magick,"MNG") == 0)
2199 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2200 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2201 png_set_read_fn(ping,image,png_get_data);
2203 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2204 png_permit_empty_plte(ping,MagickTrue);
2205 png_set_read_fn(ping,image,png_get_data);
2207 mng_info->image=image;
2208 mng_info->bytes_in_read_buffer=0;
2209 mng_info->found_empty_plte=MagickFalse;
2210 mng_info->have_saved_bkgd_index=MagickFalse;
2211 png_set_read_fn(ping,mng_info,mng_get_data);
2217 png_set_read_fn(ping,image,png_get_data);
2219 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2220 /* Ignore unused chunks and all unknown chunks except for vpAg */
2221 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2222 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2223 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2224 (int)sizeof(unused_chunks)/5);
2225 /* Callback for other unknown chunks */
2226 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2229 #if (PNG_LIBPNG_VER < 10400)
2230 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2231 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2232 /* Disable thread-unsafe features of pnggccrd */
2233 if (png_access_version_number() >= 10200)
2235 png_uint_32 mmx_disable_mask=0;
2236 png_uint_32 asm_flags;
2238 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2239 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2240 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2241 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2242 asm_flags=png_get_asm_flags(ping);
2243 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2248 png_read_info(ping,ping_info);
2250 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2251 &ping_bit_depth,&ping_color_type,
2252 &ping_interlace_method,&ping_compression_method,
2253 &ping_filter_method);
2255 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2258 (void) png_get_bKGD(ping, ping_info, &ping_background);
2260 if (ping_bit_depth < 8)
2262 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2264 png_set_packing(ping);
2269 image->depth=ping_bit_depth;
2270 image->depth=GetImageQuantumDepth(image,MagickFalse);
2271 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2272 if (logging != MagickFalse)
2274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2275 " PNG width: %.20g, height: %.20g",
2276 (double) ping_width, (double) ping_height);
2278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2279 " PNG color_type: %d, bit_depth: %d",
2280 ping_color_type, ping_bit_depth);
2282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2283 " PNG compression_method: %d",
2284 ping_compression_method);
2286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2287 " PNG interlace_method: %d, filter_method: %d",
2288 ping_interlace_method,ping_filter_method);
2291 #ifdef PNG_READ_iCCP_SUPPORTED
2292 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2297 #if (PNG_LIBPNG_VER < 10500)
2311 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2314 if (profile_length != 0)
2319 if (logging != MagickFalse)
2320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2321 " Reading PNG iCCP chunk.");
2322 profile=BlobToStringInfo(info,profile_length);
2323 if (profile == (StringInfo *) NULL)
2325 png_warning(ping, "ICC profile is NULL");
2326 profile=DestroyStringInfo(profile);
2330 (void) SetImageProfile(image,"icc",profile,exception);
2331 profile=DestroyStringInfo(profile);
2336 #if defined(PNG_READ_sRGB_SUPPORTED)
2338 if (mng_info->have_global_srgb)
2340 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2341 (mng_info->global_srgb_intent);
2344 if (png_get_sRGB(ping,ping_info,&intent))
2346 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2349 if (logging != MagickFalse)
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2356 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2357 if (mng_info->have_global_gama)
2358 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2360 if (png_get_gAMA(ping,ping_info,&file_gamma))
2362 image->gamma=(float) file_gamma;
2363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2365 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2368 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2370 if (mng_info->have_global_chrm != MagickFalse)
2372 (void) png_set_cHRM(ping,ping_info,
2373 mng_info->global_chrm.white_point.x,
2374 mng_info->global_chrm.white_point.y,
2375 mng_info->global_chrm.red_primary.x,
2376 mng_info->global_chrm.red_primary.y,
2377 mng_info->global_chrm.green_primary.x,
2378 mng_info->global_chrm.green_primary.y,
2379 mng_info->global_chrm.blue_primary.x,
2380 mng_info->global_chrm.blue_primary.y);
2384 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2386 (void) png_get_cHRM(ping,ping_info,
2387 &image->chromaticity.white_point.x,
2388 &image->chromaticity.white_point.y,
2389 &image->chromaticity.red_primary.x,
2390 &image->chromaticity.red_primary.y,
2391 &image->chromaticity.green_primary.x,
2392 &image->chromaticity.green_primary.y,
2393 &image->chromaticity.blue_primary.x,
2394 &image->chromaticity.blue_primary.y);
2396 if (logging != MagickFalse)
2397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2398 " Reading PNG cHRM chunk.");
2401 if (image->rendering_intent != UndefinedIntent)
2403 png_set_sRGB(ping,ping_info,
2404 Magick_RenderingIntent_to_PNG_RenderingIntent
2405 (image->rendering_intent));
2406 png_set_gAMA(ping,ping_info,0.45455f);
2407 png_set_cHRM(ping,ping_info,
2408 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2409 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2411 #if defined(PNG_oFFs_SUPPORTED)
2412 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2414 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2415 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2417 if (logging != MagickFalse)
2418 if (image->page.x || image->page.y)
2419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2420 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2421 image->page.x,(double) image->page.y);
2424 #if defined(PNG_pHYs_SUPPORTED)
2425 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2427 if (mng_info->have_global_phys)
2429 png_set_pHYs(ping,ping_info,
2430 mng_info->global_x_pixels_per_unit,
2431 mng_info->global_y_pixels_per_unit,
2432 mng_info->global_phys_unit_type);
2436 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2439 Set image resolution.
2441 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2443 image->resolution.x=(double) x_resolution;
2444 image->resolution.y=(double) y_resolution;
2446 if (unit_type == PNG_RESOLUTION_METER)
2448 image->units=PixelsPerCentimeterResolution;
2449 image->resolution.x=(double) x_resolution/100.0;
2450 image->resolution.y=(double) y_resolution/100.0;
2453 if (logging != MagickFalse)
2454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2455 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2456 (double) x_resolution,(double) y_resolution,unit_type);
2460 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2468 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2470 if ((number_colors == 0) &&
2471 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2473 if (mng_info->global_plte_length)
2475 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2476 (int) mng_info->global_plte_length);
2478 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2480 if (mng_info->global_trns_length)
2483 "global tRNS has more entries than global PLTE");
2487 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2488 (int) mng_info->global_trns_length,NULL);
2491 #ifdef PNG_READ_bKGD_SUPPORTED
2493 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2494 mng_info->have_saved_bkgd_index ||
2496 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2501 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2502 if (mng_info->have_saved_bkgd_index)
2503 background.index=mng_info->saved_bkgd_index;
2505 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2506 background.index=ping_background->index;
2508 background.red=(png_uint_16)
2509 mng_info->global_plte[background.index].red;
2511 background.green=(png_uint_16)
2512 mng_info->global_plte[background.index].green;
2514 background.blue=(png_uint_16)
2515 mng_info->global_plte[background.index].blue;
2517 background.gray=(png_uint_16)
2518 mng_info->global_plte[background.index].green;
2520 png_set_bKGD(ping,ping_info,&background);
2525 png_error(ping,"No global PLTE in file");
2529 #ifdef PNG_READ_bKGD_SUPPORTED
2530 if (mng_info->have_global_bkgd &&
2531 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2532 image->background_color=mng_info->mng_global_bkgd;
2534 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2540 Set image background color.
2542 if (logging != MagickFalse)
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " Reading PNG bKGD chunk.");
2546 /* Scale background components to 16-bit, then scale
2549 if (logging != MagickFalse)
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " raw ping_background=(%d,%d,%d).",ping_background->red,
2552 ping_background->green,ping_background->blue);
2556 if (ping_bit_depth == 1)
2559 else if (ping_bit_depth == 2)
2562 else if (ping_bit_depth == 4)
2565 if (ping_bit_depth <= 8)
2568 ping_background->red *= bkgd_scale;
2569 ping_background->green *= bkgd_scale;
2570 ping_background->blue *= bkgd_scale;
2572 if (logging != MagickFalse)
2574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575 " bkgd_scale=%d.",bkgd_scale);
2577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2578 " ping_background=(%d,%d,%d).",ping_background->red,
2579 ping_background->green,ping_background->blue);
2582 image->background_color.red=
2583 ScaleShortToQuantum(ping_background->red);
2585 image->background_color.green=
2586 ScaleShortToQuantum(ping_background->green);
2588 image->background_color.blue=
2589 ScaleShortToQuantum(ping_background->blue);
2591 image->background_color.alpha=OpaqueAlpha;
2593 if (logging != MagickFalse)
2594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2595 " image->background_color=(%.20g,%.20g,%.20g).",
2596 (double) image->background_color.red,
2597 (double) image->background_color.green,
2598 (double) image->background_color.blue);
2600 #endif /* PNG_READ_bKGD_SUPPORTED */
2602 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2605 Image has a tRNS chunk.
2613 if (logging != MagickFalse)
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 " Reading PNG tRNS chunk.");
2617 max_sample = (int) ((one << ping_bit_depth) - 1);
2619 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2620 (int)ping_trans_color->gray > max_sample) ||
2621 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2622 ((int)ping_trans_color->red > max_sample ||
2623 (int)ping_trans_color->green > max_sample ||
2624 (int)ping_trans_color->blue > max_sample)))
2626 if (logging != MagickFalse)
2627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2628 " Ignoring PNG tRNS chunk with out-of-range sample.");
2629 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2630 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2631 image->matte=MagickFalse;
2638 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2640 /* Scale transparent_color to short */
2641 transparent_color.red= scale_to_short*ping_trans_color->red;
2642 transparent_color.green= scale_to_short*ping_trans_color->green;
2643 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2644 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2646 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2648 if (logging != MagickFalse)
2650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2651 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2654 " scaled graylevel is %.20g.",transparent_color.alpha);
2656 transparent_color.red=transparent_color.alpha;
2657 transparent_color.green=transparent_color.alpha;
2658 transparent_color.blue=transparent_color.alpha;
2662 #if defined(PNG_READ_sBIT_SUPPORTED)
2663 if (mng_info->have_global_sbit)
2665 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2666 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2669 num_passes=png_set_interlace_handling(ping);
2671 png_read_update_info(ping,ping_info);
2673 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2676 Initialize image structure.
2678 mng_info->image_box.left=0;
2679 mng_info->image_box.right=(ssize_t) ping_width;
2680 mng_info->image_box.top=0;
2681 mng_info->image_box.bottom=(ssize_t) ping_height;
2682 if (mng_info->mng_type == 0)
2684 mng_info->mng_width=ping_width;
2685 mng_info->mng_height=ping_height;
2686 mng_info->frame=mng_info->image_box;
2687 mng_info->clip=mng_info->image_box;
2692 image->page.y=mng_info->y_off[mng_info->object_id];
2695 image->compression=ZipCompression;
2696 image->columns=ping_width;
2697 image->rows=ping_height;
2698 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2699 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2700 image->colorspace=GRAYColorspace;
2701 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2702 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2707 image->storage_class=PseudoClass;
2709 image->colors=one << ping_bit_depth;
2710 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2711 if (image->colors > 256)
2714 if (image->colors > 65536L)
2715 image->colors=65536L;
2717 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2725 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2726 image->colors=(size_t) number_colors;
2728 if (logging != MagickFalse)
2729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2730 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2734 if (image->storage_class == PseudoClass)
2737 Initialize image colormap.
2739 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2740 png_error(ping,"Memory allocation failed");
2742 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2750 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2752 for (i=0; i < (ssize_t) number_colors; i++)
2754 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2755 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2756 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2759 for ( ; i < (ssize_t) image->colors; i++)
2761 image->colormap[i].red=0;
2762 image->colormap[i].green=0;
2763 image->colormap[i].blue=0;
2772 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2777 for (i=0; i < (ssize_t) image->colors; i++)
2779 image->colormap[i].red=(Quantum) (i*scale);
2780 image->colormap[i].green=(Quantum) (i*scale);
2781 image->colormap[i].blue=(Quantum) (i*scale);
2786 /* Set some properties for reporting by "identify" */
2791 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2792 ping_interlace_method in value */
2794 (void) FormatLocaleString(msg,MaxTextExtent,
2795 "%d, %d",(int) ping_width, (int) ping_height);
2796 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2798 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2799 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2801 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2802 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2804 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2805 (int) ping_interlace_method);
2806 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2810 Read image scanlines.
2812 if (image->delay != 0)
2813 mng_info->scenes_found++;
2815 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2816 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2817 (image_info->first_scene+image_info->number_scenes))))
2819 /* This happens later in non-ping decodes */
2820 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2821 image->storage_class=DirectClass;
2823 if (logging != MagickFalse)
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2825 " Skipping PNG image data for scene %.20g",(double)
2826 mng_info->scenes_found-1);
2827 png_destroy_read_struct(&ping,&ping_info,&end_info);
2829 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2830 UnlockSemaphoreInfo(ping_semaphore);
2833 if (logging != MagickFalse)
2834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2835 " exit ReadOnePNGImage().");
2840 if (logging != MagickFalse)
2841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2842 " Reading PNG IDAT chunk(s)");
2845 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2846 ping_rowbytes*sizeof(*ping_pixels));
2849 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2850 sizeof(*ping_pixels));
2852 if (ping_pixels == (unsigned char *) NULL)
2853 png_error(ping,"Memory allocation failed");
2855 if (logging != MagickFalse)
2856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2857 " Converting PNG pixels to pixel packets");
2859 Convert PNG pixels to pixel packets.
2861 quantum_info=AcquireQuantumInfo(image_info,image);
2863 if (quantum_info == (QuantumInfo *) NULL)
2864 png_error(ping,"Failed to allocate quantum_info");
2869 found_transparent_pixel;
2871 found_transparent_pixel=MagickFalse;
2873 if (image->storage_class == DirectClass)
2875 for (pass=0; pass < num_passes; pass++)
2878 Convert image to DirectClass pixel packets.
2880 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2884 depth=(ssize_t) ping_bit_depth;
2886 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2887 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2888 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2889 MagickTrue : MagickFalse;
2891 for (y=0; y < (ssize_t) image->rows; y++)
2894 row_offset=ping_rowbytes*y;
2899 png_read_row(ping,ping_pixels+row_offset,NULL);
2900 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2902 if (q == (Quantum *) NULL)
2905 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2906 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2907 GrayQuantum,ping_pixels+row_offset,exception);
2909 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2910 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2911 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2913 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2914 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2915 RGBAQuantum,ping_pixels+row_offset,exception);
2917 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2918 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2919 IndexQuantum,ping_pixels+row_offset,exception);
2921 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2922 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2923 RGBQuantum,ping_pixels+row_offset,exception);
2925 if (found_transparent_pixel == MagickFalse)
2927 /* Is there a transparent pixel in the row? */
2928 if (y== 0 && logging != MagickFalse)
2929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2930 " Looking for cheap transparent pixel");
2932 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2934 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2935 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2936 (GetPixelAlpha(image,q) != OpaqueAlpha))
2938 if (logging != MagickFalse)
2939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2942 found_transparent_pixel = MagickTrue;
2945 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2946 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2947 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2948 transparent_color.red &&
2949 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2950 transparent_color.green &&
2951 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2952 transparent_color.blue))
2954 if (logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2957 found_transparent_pixel = MagickTrue;
2960 q+=GetPixelChannels(image);
2964 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2966 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2969 if (status == MagickFalse)
2972 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2976 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2978 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2979 if (status == MagickFalse)
2985 else /* image->storage_class != DirectClass */
2987 for (pass=0; pass < num_passes; pass++)
2996 Convert grayscale image to PseudoClass pixel packets.
2998 if (logging != MagickFalse)
2999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3000 " Converting grayscale pixels to pixel packets");
3002 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3003 MagickTrue : MagickFalse;
3005 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3006 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
3008 if (quantum_scanline == (Quantum *) NULL)
3009 png_error(ping,"Memory allocation failed");
3011 for (y=0; y < (ssize_t) image->rows; y++)
3014 row_offset=ping_rowbytes*y;
3019 png_read_row(ping,ping_pixels+row_offset,NULL);
3020 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3022 if (q == (Quantum *) NULL)
3025 p=ping_pixels+row_offset;
3028 switch (ping_bit_depth)
3035 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3037 for (bit=7; bit >= 0; bit--)
3038 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3042 if ((image->columns % 8) != 0)
3044 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3045 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3053 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3055 *r++=(*p >> 6) & 0x03;
3056 *r++=(*p >> 4) & 0x03;
3057 *r++=(*p >> 2) & 0x03;
3061 if ((image->columns % 4) != 0)
3063 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3064 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3072 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3074 *r++=(*p >> 4) & 0x0f;
3078 if ((image->columns % 2) != 0)
3079 *r++=(*p++ >> 4) & 0x0f;
3086 if (ping_color_type == 4)
3087 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3090 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3091 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3092 found_transparent_pixel = MagickTrue;
3093 q+=GetPixelChannels(image);
3097 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3105 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3107 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3111 if (image->colors > 256)
3112 quantum=((*p++) << 8);
3118 *r=ScaleShortToQuantum(quantum);
3121 if (ping_color_type == 4)
3123 if (image->colors > 256)
3124 quantum=((*p++) << 8);
3129 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3130 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3131 found_transparent_pixel = MagickTrue;
3132 q+=GetPixelChannels(image);
3135 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3137 p++; /* strip low byte */
3139 if (ping_color_type == 4)
3141 SetPixelAlpha(image,*p++,q);
3142 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3143 found_transparent_pixel = MagickTrue;
3145 q+=GetPixelChannels(image);
3158 Transfer image scanline.
3162 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3164 if (q == (Quantum *) NULL)
3166 for (x=0; x < (ssize_t) image->columns; x++)
3168 SetPixelIndex(image,*r++,q);
3169 q+=GetPixelChannels(image);
3172 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3175 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3177 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3180 if (status == MagickFalse)
3185 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3187 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3189 if (status == MagickFalse)
3193 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3196 image->matte=found_transparent_pixel;
3198 if (logging != MagickFalse)
3200 if (found_transparent_pixel != MagickFalse)
3201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3202 " Found transparent pixel");
3205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3206 " No transparent pixel was found");
3208 ping_color_type&=0x03;
3213 if (quantum_info != (QuantumInfo *) NULL)
3214 quantum_info=DestroyQuantumInfo(quantum_info);
3216 if (image->storage_class == PseudoClass)
3222 image->matte=MagickFalse;
3223 (void) SyncImage(image,exception);
3227 png_read_end(ping,end_info);
3229 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3230 (ssize_t) image_info->first_scene && image->delay != 0)
3232 png_destroy_read_struct(&ping,&ping_info,&end_info);
3233 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3235 (void) SetImageBackgroundColor(image,exception);
3236 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3237 UnlockSemaphoreInfo(ping_semaphore);
3239 if (logging != MagickFalse)
3240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3241 " exit ReadOnePNGImage() early.");
3245 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3251 Image has a transparent background.
3253 storage_class=image->storage_class;
3254 image->matte=MagickTrue;
3256 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3258 if (storage_class == PseudoClass)
3260 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3262 for (x=0; x < ping_num_trans; x++)
3264 image->colormap[x].matte=MagickTrue;
3265 image->colormap[x].alpha =
3266 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3270 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3272 for (x=0; x < (int) image->colors; x++)
3274 if (ScaleQuantumToShort(image->colormap[x].red) ==
3275 transparent_color.alpha)
3277 image->colormap[x].matte=MagickTrue;
3278 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3282 (void) SyncImage(image,exception);
3285 #if 1 /* Should have already been done above, but glennrp problem P10
3290 for (y=0; y < (ssize_t) image->rows; y++)
3292 image->storage_class=storage_class;
3293 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3295 if (q == (Quantum *) NULL)
3299 /* Caution: on a Q8 build, this does not distinguish between
3300 * 16-bit colors that differ only in the low byte
3302 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3304 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3305 transparent_color.red &&
3306 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3307 transparent_color.green &&
3308 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3309 transparent_color.blue)
3311 SetPixelAlpha(image,TransparentAlpha,q);
3314 #if 0 /* I have not found a case where this is needed. */
3317 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3321 q+=GetPixelChannels(image);
3324 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3330 image->storage_class=DirectClass;
3333 for (j = 0; j < 2; j++)
3336 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
3339 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3340 MagickTrue : MagickFalse;
3342 if (status != MagickFalse)
3343 for (i=0; i < (ssize_t) num_text; i++)
3345 /* Check for a profile */
3347 if (logging != MagickFalse)
3348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3349 " Reading PNG text chunk");
3351 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3353 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3363 length=text[i].text_length;
3364 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3366 if (value == (char *) NULL)
3368 png_error(ping,"Memory allocation failed");
3372 (void) ConcatenateMagickString(value,text[i].text,length+2);
3374 /* Don't save "density" or "units" property if we have a pHYs
3377 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3378 (LocaleCompare(text[i].key,"density") != 0 &&
3379 LocaleCompare(text[i].key,"units") != 0))
3380 (void) SetImageProperty(image,text[i].key,value,exception);
3382 if (logging != MagickFalse)
3384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3385 " length: %lu",(unsigned long) length);
3386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3387 " Keyword: %s",text[i].key);
3390 value=DestroyString(value);
3393 num_text_total += num_text;
3396 #ifdef MNG_OBJECT_BUFFERS
3398 Store the object if necessary.
3400 if (object_id && !mng_info->frozen[object_id])
3402 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3405 create a new object buffer.
3407 mng_info->ob[object_id]=(MngBuffer *)
3408 AcquireMagickMemory(sizeof(MngBuffer));
3410 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3412 mng_info->ob[object_id]->image=(Image *) NULL;
3413 mng_info->ob[object_id]->reference_count=1;
3417 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3418 mng_info->ob[object_id]->frozen)
3420 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3421 png_error(ping,"Memory allocation failed");
3423 if (mng_info->ob[object_id]->frozen)
3424 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3430 if (mng_info->ob[object_id]->image != (Image *) NULL)
3431 mng_info->ob[object_id]->image=DestroyImage
3432 (mng_info->ob[object_id]->image);
3434 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3437 if (mng_info->ob[object_id]->image != (Image *) NULL)
3438 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3441 png_error(ping, "Cloning image for object buffer failed");
3443 if (ping_width > 250000L || ping_height > 250000L)
3444 png_error(ping,"PNG Image dimensions are too large.");
3446 mng_info->ob[object_id]->width=ping_width;
3447 mng_info->ob[object_id]->height=ping_height;
3448 mng_info->ob[object_id]->color_type=ping_color_type;
3449 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3450 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3451 mng_info->ob[object_id]->compression_method=
3452 ping_compression_method;
3453 mng_info->ob[object_id]->filter_method=ping_filter_method;
3455 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3464 Copy the PLTE to the object buffer.
3466 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3467 mng_info->ob[object_id]->plte_length=number_colors;
3469 for (i=0; i < number_colors; i++)
3471 mng_info->ob[object_id]->plte[i]=plte[i];
3476 mng_info->ob[object_id]->plte_length=0;
3481 /* Set image->matte to MagickTrue if the input colortype supports
3482 * alpha or if a valid tRNS chunk is present, no matter whether there
3483 * is actual transparency present.
3485 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3486 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3487 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3488 MagickTrue : MagickFalse;
3490 /* Set more properties for identify to retrieve */
3495 if (num_text_total != 0)
3497 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3498 (void) FormatLocaleString(msg,MaxTextExtent,
3499 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3500 (void) SetImageProperty(image,"png:text ",msg,
3504 if (num_raw_profiles != 0)
3506 (void) FormatLocaleString(msg,MaxTextExtent,
3507 "%d were found", num_raw_profiles);
3508 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3512 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3514 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3515 "chunk was found (see Chromaticity, above)");
3516 (void) SetImageProperty(image,"png:cHRM ",msg,
3520 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3522 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3523 "chunk was found (see Background color, above)");
3524 (void) SetImageProperty(image,"png:bKGD ",msg,
3528 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3531 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3532 (void) SetImageProperty(image,"png:iCCP ",msg,
3535 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3536 (void) SetImageProperty(image,"png:tRNS ",msg,
3539 #if defined(PNG_sRGB_SUPPORTED)
3540 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3542 (void) FormatLocaleString(msg,MaxTextExtent,
3543 "intent=%d (See Rendering intent)", (int) intent);
3544 (void) SetImageProperty(image,"png:sRGB ",msg,
3549 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3551 (void) FormatLocaleString(msg,MaxTextExtent,
3552 "gamma=%.8g (See Gamma, above)",
3554 (void) SetImageProperty(image,"png:gAMA ",msg,
3558 #if defined(PNG_pHYs_SUPPORTED)
3559 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3561 (void) FormatLocaleString(msg,MaxTextExtent,
3562 "x_res=%.10g, y_res=%.10g, units=%d",
3563 (double) x_resolution,(double) y_resolution, unit_type);
3564 (void) SetImageProperty(image,"png:pHYs ",msg,
3569 #if defined(PNG_oFFs_SUPPORTED)
3570 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3572 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3573 (double) image->page.x,(double) image->page.y);
3574 (void) SetImageProperty(image,"png:oFFs ",msg,
3579 if ((image->page.width != 0 && image->page.width != image->columns) ||
3580 (image->page.height != 0 && image->page.height != image->rows))
3582 (void) FormatLocaleString(msg,MaxTextExtent,
3583 "width=%.20g, height=%.20g",
3584 (double) image->page.width,(double) image->page.height);
3585 (void) SetImageProperty(image,"png:vpAg ",msg,
3591 Relinquish resources.
3593 png_destroy_read_struct(&ping,&ping_info,&end_info);
3595 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3597 if (logging != MagickFalse)
3598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3599 " exit ReadOnePNGImage()");
3601 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3602 UnlockSemaphoreInfo(ping_semaphore);
3605 /* } for navigation to beginning of SETJMP-protected block, revert to
3606 * Throwing an Exception when an error occurs.
3611 /* end of reading one PNG image */
3614 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3629 magic_number[MaxTextExtent];
3637 assert(image_info != (const ImageInfo *) NULL);
3638 assert(image_info->signature == MagickSignature);
3640 if (image_info->debug != MagickFalse)
3641 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3642 image_info->filename);
3644 assert(exception != (ExceptionInfo *) NULL);
3645 assert(exception->signature == MagickSignature);
3646 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3647 image=AcquireImage(image_info,exception);
3648 mng_info=(MngInfo *) NULL;
3649 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3651 if (status == MagickFalse)
3652 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3655 Verify PNG signature.
3657 count=ReadBlob(image,8,(unsigned char *) magic_number);
3659 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3660 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3663 Allocate a MngInfo structure.
3665 have_mng_structure=MagickFalse;
3666 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3668 if (mng_info == (MngInfo *) NULL)
3669 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3672 Initialize members of the MngInfo structure.
3674 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3675 mng_info->image=image;
3676 have_mng_structure=MagickTrue;
3679 image=ReadOnePNGImage(mng_info,image_info,exception);
3680 MngInfoFreeStruct(mng_info,&have_mng_structure);
3682 if (image == (Image *) NULL)
3684 if (previous != (Image *) NULL)
3686 if (previous->signature != MagickSignature)
3687 ThrowReaderException(CorruptImageError,"CorruptImage");
3689 (void) CloseBlob(previous);
3690 (void) DestroyImageList(previous);
3693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadPNGImage() with error");
3697 return((Image *) NULL);
3700 (void) CloseBlob(image);
3702 if ((image->columns == 0) || (image->rows == 0))
3704 if (logging != MagickFalse)
3705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3706 "exit ReadPNGImage() with error.");
3708 ThrowReaderException(CorruptImageError,"CorruptImage");
3711 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3713 (void) SetImageType(image,TrueColorType,exception);
3714 image->matte=MagickFalse;
3717 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3718 (void) SetImageType(image,TrueColorMatteType,exception);
3720 if (logging != MagickFalse)
3721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3722 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3723 (double) image->page.width,(double) image->page.height,
3724 (double) image->page.x,(double) image->page.y);
3726 if (logging != MagickFalse)
3727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3734 #if defined(JNG_SUPPORTED)
3736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3740 % R e a d O n e J N G I m a g e %
3744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3746 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3747 % (minus the 8-byte signature) and returns it. It allocates the memory
3748 % necessary for the new Image structure and returns a pointer to the new
3751 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3753 % The format of the ReadOneJNGImage method is:
3755 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3756 % ExceptionInfo *exception)
3758 % A description of each parameter follows:
3760 % o mng_info: Specifies a pointer to a MngInfo structure.
3762 % o image_info: the image info.
3764 % o exception: return any errors or warnings in this structure.
3767 static Image *ReadOneJNGImage(MngInfo *mng_info,
3768 const ImageInfo *image_info, ExceptionInfo *exception)
3795 jng_image_sample_depth,
3796 jng_image_compression_method,
3797 jng_image_interlace_method,
3798 jng_alpha_sample_depth,
3799 jng_alpha_compression_method,
3800 jng_alpha_filter_method,
3801 jng_alpha_interlace_method;
3803 register const Quantum
3813 register unsigned char
3824 jng_alpha_compression_method=0;
3825 jng_alpha_sample_depth=8;
3829 alpha_image=(Image *) NULL;
3830 color_image=(Image *) NULL;
3831 alpha_image_info=(ImageInfo *) NULL;
3832 color_image_info=(ImageInfo *) NULL;
3834 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3835 " Enter ReadOneJNGImage()");
3837 image=mng_info->image;
3839 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3842 Allocate next image structure.
3844 if (logging != MagickFalse)
3845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3846 " AcquireNextImage()");
3848 AcquireNextImage(image_info,image,exception);
3850 if (GetNextImageInList(image) == (Image *) NULL)
3851 return((Image *) NULL);
3853 image=SyncNextImageInList(image);
3855 mng_info->image=image;
3858 Signature bytes have already been read.
3861 read_JSEP=MagickFalse;
3862 reading_idat=MagickFalse;
3863 skip_to_iend=MagickFalse;
3867 type[MaxTextExtent];
3876 Read a new JNG chunk.
3878 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3879 2*GetBlobSize(image));
3881 if (status == MagickFalse)
3885 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3886 length=ReadBlobMSBLong(image);
3887 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3889 if (logging != MagickFalse)
3890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3891 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3892 type[0],type[1],type[2],type[3],(double) length);
3894 if (length > PNG_UINT_31_MAX || count == 0)
3895 ThrowReaderException(CorruptImageError,"CorruptImage");
3898 chunk=(unsigned char *) NULL;
3902 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3904 if (chunk == (unsigned char *) NULL)
3905 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3907 for (i=0; i < (ssize_t) length; i++)
3908 chunk[i]=(unsigned char) ReadBlobByte(image);
3913 (void) ReadBlobMSBLong(image); /* read crc word */
3918 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3923 if (memcmp(type,mng_JHDR,4) == 0)
3927 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3928 (p[2] << 8) | p[3]);
3929 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3930 (p[6] << 8) | p[7]);
3931 jng_color_type=p[8];
3932 jng_image_sample_depth=p[9];
3933 jng_image_compression_method=p[10];
3934 jng_image_interlace_method=p[11];
3936 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3939 jng_alpha_sample_depth=p[12];
3940 jng_alpha_compression_method=p[13];
3941 jng_alpha_filter_method=p[14];
3942 jng_alpha_interlace_method=p[15];
3944 if (logging != MagickFalse)
3946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3947 " jng_width: %16lu",(unsigned long) jng_width);
3949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3950 " jng_width: %16lu",(unsigned long) jng_height);
3952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_color_type: %16d",jng_color_type);
3955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3956 " jng_image_sample_depth: %3d",
3957 jng_image_sample_depth);
3959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3960 " jng_image_compression_method:%3d",
3961 jng_image_compression_method);
3963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3964 " jng_image_interlace_method: %3d",
3965 jng_image_interlace_method);
3967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3968 " jng_alpha_sample_depth: %3d",
3969 jng_alpha_sample_depth);
3971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3972 " jng_alpha_compression_method:%3d",
3973 jng_alpha_compression_method);
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3976 " jng_alpha_filter_method: %3d",
3977 jng_alpha_filter_method);
3979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3980 " jng_alpha_interlace_method: %3d",
3981 jng_alpha_interlace_method);
3986 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3992 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3993 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3994 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3997 o create color_image
3998 o open color_blob, attached to color_image
3999 o if (color type has alpha)
4000 open alpha_blob, attached to alpha_image
4003 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4005 if (color_image_info == (ImageInfo *) NULL)
4006 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4008 GetImageInfo(color_image_info);
4009 color_image=AcquireImage(color_image_info,exception);
4011 if (color_image == (Image *) NULL)
4012 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4014 if (logging != MagickFalse)
4015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016 " Creating color_blob.");
4018 (void) AcquireUniqueFilename(color_image->filename);
4019 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4022 if (status == MagickFalse)
4023 return((Image *) NULL);
4025 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4027 alpha_image_info=(ImageInfo *)
4028 AcquireMagickMemory(sizeof(ImageInfo));
4030 if (alpha_image_info == (ImageInfo *) NULL)
4031 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4033 GetImageInfo(alpha_image_info);
4034 alpha_image=AcquireImage(alpha_image_info,exception);
4036 if (alpha_image == (Image *) NULL)
4038 alpha_image=DestroyImage(alpha_image);
4039 ThrowReaderException(ResourceLimitError,
4040 "MemoryAllocationFailed");
4043 if (logging != MagickFalse)
4044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4045 " Creating alpha_blob.");
4047 (void) AcquireUniqueFilename(alpha_image->filename);
4048 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4051 if (status == MagickFalse)
4052 return((Image *) NULL);
4054 if (jng_alpha_compression_method == 0)
4059 if (logging != MagickFalse)
4060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061 " Writing IHDR chunk to alpha_blob.");
4063 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4064 "\211PNG\r\n\032\n");
4066 (void) WriteBlobMSBULong(alpha_image,13L);
4067 PNGType(data,mng_IHDR);
4068 LogPNGChunk(logging,mng_IHDR,13L);
4069 PNGLong(data+4,jng_width);
4070 PNGLong(data+8,jng_height);
4071 data[12]=jng_alpha_sample_depth;
4072 data[13]=0; /* color_type gray */
4073 data[14]=0; /* compression method 0 */
4074 data[15]=0; /* filter_method 0 */
4075 data[16]=0; /* interlace_method 0 */
4076 (void) WriteBlob(alpha_image,17,data);
4077 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4080 reading_idat=MagickTrue;
4083 if (memcmp(type,mng_JDAT,4) == 0)
4085 /* Copy chunk to color_image->blob */
4087 if (logging != MagickFalse)
4088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4089 " Copying JDAT chunk data to color_blob.");
4091 (void) WriteBlob(color_image,length,chunk);
4094 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4099 if (memcmp(type,mng_IDAT,4) == 0)
4104 /* Copy IDAT header and chunk data to alpha_image->blob */
4106 if (image_info->ping == MagickFalse)
4108 if (logging != MagickFalse)
4109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4110 " Copying IDAT chunk data to alpha_blob.");
4112 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4113 PNGType(data,mng_IDAT);
4114 LogPNGChunk(logging,mng_IDAT,length);
4115 (void) WriteBlob(alpha_image,4,data);
4116 (void) WriteBlob(alpha_image,length,chunk);
4117 (void) WriteBlobMSBULong(alpha_image,
4118 crc32(crc32(0,data,4),chunk,(uInt) length));
4122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4127 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4129 /* Copy chunk data to alpha_image->blob */
4131 if (image_info->ping == MagickFalse)
4133 if (logging != MagickFalse)
4134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4135 " Copying JDAA chunk data to alpha_blob.");
4137 (void) WriteBlob(alpha_image,length,chunk);
4141 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4146 if (memcmp(type,mng_JSEP,4) == 0)
4148 read_JSEP=MagickTrue;
4151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4156 if (memcmp(type,mng_bKGD,4) == 0)
4160 image->background_color.red=ScaleCharToQuantum(p[1]);
4161 image->background_color.green=image->background_color.red;
4162 image->background_color.blue=image->background_color.red;
4167 image->background_color.red=ScaleCharToQuantum(p[1]);
4168 image->background_color.green=ScaleCharToQuantum(p[3]);
4169 image->background_color.blue=ScaleCharToQuantum(p[5]);
4172 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4176 if (memcmp(type,mng_gAMA,4) == 0)
4179 image->gamma=((float) mng_get_long(p))*0.00001;
4181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4185 if (memcmp(type,mng_cHRM,4) == 0)
4189 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4190 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4191 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4192 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4193 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4194 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4195 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4196 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4199 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4203 if (memcmp(type,mng_sRGB,4) == 0)
4207 image->rendering_intent=
4208 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4209 image->gamma=0.45455f;
4210 image->chromaticity.red_primary.x=0.6400f;
4211 image->chromaticity.red_primary.y=0.3300f;
4212 image->chromaticity.green_primary.x=0.3000f;
4213 image->chromaticity.green_primary.y=0.6000f;
4214 image->chromaticity.blue_primary.x=0.1500f;
4215 image->chromaticity.blue_primary.y=0.0600f;
4216 image->chromaticity.white_point.x=0.3127f;
4217 image->chromaticity.white_point.y=0.3290f;
4220 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4224 if (memcmp(type,mng_oFFs,4) == 0)
4228 image->page.x=(ssize_t) mng_get_long(p);
4229 image->page.y=(ssize_t) mng_get_long(&p[4]);
4231 if ((int) p[8] != 0)
4233 image->page.x/=10000;
4234 image->page.y/=10000;
4239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4244 if (memcmp(type,mng_pHYs,4) == 0)
4248 image->resolution.x=(double) mng_get_long(p);
4249 image->resolution.y=(double) mng_get_long(&p[4]);
4250 if ((int) p[8] == PNG_RESOLUTION_METER)
4252 image->units=PixelsPerCentimeterResolution;
4253 image->resolution.x=image->resolution.x/100.0f;
4254 image->resolution.y=image->resolution.y/100.0f;
4258 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4263 if (memcmp(type,mng_iCCP,4) == 0)
4267 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4274 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4276 if (memcmp(type,mng_IEND,4))
4286 Finish up reading image data:
4288 o read main image from color_blob.
4292 o if (color_type has alpha)
4293 if alpha_encoding is PNG
4294 read secondary image from alpha_blob via ReadPNG
4295 if alpha_encoding is JPEG
4296 read secondary image from alpha_blob via ReadJPEG
4300 o copy intensity of secondary image into
4301 alpha samples of main image.
4303 o destroy the secondary image.
4306 (void) CloseBlob(color_image);
4308 if (logging != MagickFalse)
4309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4310 " Reading jng_image from color_blob.");
4312 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4313 color_image->filename);
4315 color_image_info->ping=MagickFalse; /* To do: avoid this */
4316 jng_image=ReadImage(color_image_info,exception);
4318 if (jng_image == (Image *) NULL)
4319 return((Image *) NULL);
4321 (void) RelinquishUniqueFileResource(color_image->filename);
4322 color_image=DestroyImage(color_image);
4323 color_image_info=DestroyImageInfo(color_image_info);
4325 if (jng_image == (Image *) NULL)
4326 return((Image *) NULL);
4328 if (logging != MagickFalse)
4329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4330 " Copying jng_image pixels to main image.");
4332 image->rows=jng_height;
4333 image->columns=jng_width;
4335 for (y=0; y < (ssize_t) image->rows; y++)
4337 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4338 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4339 for (x=(ssize_t) image->columns; x != 0; x--)
4341 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4342 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4343 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4344 q+=GetPixelChannels(image);
4345 s+=GetPixelChannels(jng_image);
4348 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4352 jng_image=DestroyImage(jng_image);
4354 if (image_info->ping == MagickFalse)
4356 if (jng_color_type >= 12)
4358 if (jng_alpha_compression_method == 0)
4362 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4363 PNGType(data,mng_IEND);
4364 LogPNGChunk(logging,mng_IEND,0L);
4365 (void) WriteBlob(alpha_image,4,data);
4366 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4369 (void) CloseBlob(alpha_image);
4371 if (logging != MagickFalse)
4372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4373 " Reading alpha from alpha_blob.");
4375 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4376 "%s",alpha_image->filename);
4378 jng_image=ReadImage(alpha_image_info,exception);
4380 if (jng_image != (Image *) NULL)
4381 for (y=0; y < (ssize_t) image->rows; y++)
4383 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4385 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4387 if (image->matte != MagickFalse)
4388 for (x=(ssize_t) image->columns; x != 0; x--)
4390 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4391 q+=GetPixelChannels(image);
4392 s+=GetPixelChannels(jng_image);
4396 for (x=(ssize_t) image->columns; x != 0; x--)
4398 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4399 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4400 image->matte=MagickTrue;
4401 q+=GetPixelChannels(image);
4402 s+=GetPixelChannels(jng_image);
4405 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4408 (void) RelinquishUniqueFileResource(alpha_image->filename);
4409 alpha_image=DestroyImage(alpha_image);
4410 alpha_image_info=DestroyImageInfo(alpha_image_info);
4411 if (jng_image != (Image *) NULL)
4412 jng_image=DestroyImage(jng_image);
4416 /* Read the JNG image. */
4418 if (mng_info->mng_type == 0)
4420 mng_info->mng_width=jng_width;
4421 mng_info->mng_height=jng_height;
4424 if (image->page.width == 0 && image->page.height == 0)
4426 image->page.width=jng_width;
4427 image->page.height=jng_height;
4430 if (image->page.x == 0 && image->page.y == 0)
4432 image->page.x=mng_info->x_off[mng_info->object_id];
4433 image->page.y=mng_info->y_off[mng_info->object_id];
4438 image->page.y=mng_info->y_off[mng_info->object_id];
4441 mng_info->image_found++;
4442 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4443 2*GetBlobSize(image));
4445 if (logging != MagickFalse)
4446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4447 " exit ReadOneJNGImage()");
4453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4457 % R e a d J N G I m a g e %
4461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4464 % (including the 8-byte signature) and returns it. It allocates the memory
4465 % necessary for the new Image structure and returns a pointer to the new
4468 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4470 % The format of the ReadJNGImage method is:
4472 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4475 % A description of each parameter follows:
4477 % o image_info: the image info.
4479 % o exception: return any errors or warnings in this structure.
4483 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4498 magic_number[MaxTextExtent];
4506 assert(image_info != (const ImageInfo *) NULL);
4507 assert(image_info->signature == MagickSignature);
4508 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4509 assert(exception != (ExceptionInfo *) NULL);
4510 assert(exception->signature == MagickSignature);
4511 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4512 image=AcquireImage(image_info,exception);
4513 mng_info=(MngInfo *) NULL;
4514 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4516 if (status == MagickFalse)
4517 return((Image *) NULL);
4519 if (LocaleCompare(image_info->magick,"JNG") != 0)
4520 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4522 /* Verify JNG signature. */
4524 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4526 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4527 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4529 /* Allocate a MngInfo structure. */
4531 have_mng_structure=MagickFalse;
4532 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4534 if (mng_info == (MngInfo *) NULL)
4535 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4537 /* Initialize members of the MngInfo structure. */
4539 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4540 have_mng_structure=MagickTrue;
4542 mng_info->image=image;
4544 image=ReadOneJNGImage(mng_info,image_info,exception);
4545 MngInfoFreeStruct(mng_info,&have_mng_structure);
4547 if (image == (Image *) NULL)
4549 if (IsImageObject(previous) != MagickFalse)
4551 (void) CloseBlob(previous);
4552 (void) DestroyImageList(previous);
4555 if (logging != MagickFalse)
4556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4557 "exit ReadJNGImage() with error");
4559 return((Image *) NULL);
4561 (void) CloseBlob(image);
4563 if (image->columns == 0 || image->rows == 0)
4565 if (logging != MagickFalse)
4566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4567 "exit ReadJNGImage() with error");
4569 ThrowReaderException(CorruptImageError,"CorruptImage");
4572 if (logging != MagickFalse)
4573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4579 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4582 page_geometry[MaxTextExtent];
4615 #if defined(MNG_INSERT_LAYERS)
4617 mng_background_color;
4620 register unsigned char
4635 #if defined(MNG_INSERT_LAYERS)
4640 volatile unsigned int
4641 #ifdef MNG_OBJECT_BUFFERS
4642 mng_background_object=0,
4644 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4647 default_frame_timeout,
4649 #if defined(MNG_INSERT_LAYERS)
4655 /* These delays are all measured in image ticks_per_second,
4656 * not in MNG ticks_per_second
4659 default_frame_delay,
4663 #if defined(MNG_INSERT_LAYERS)
4672 previous_fb.bottom=0;
4674 previous_fb.right=0;
4676 default_fb.bottom=0;
4680 /* Open image file. */
4682 assert(image_info != (const ImageInfo *) NULL);
4683 assert(image_info->signature == MagickSignature);
4684 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4685 assert(exception != (ExceptionInfo *) NULL);
4686 assert(exception->signature == MagickSignature);
4687 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4688 image=AcquireImage(image_info,exception);
4689 mng_info=(MngInfo *) NULL;
4690 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4692 if (status == MagickFalse)
4693 return((Image *) NULL);
4695 first_mng_object=MagickFalse;
4697 have_mng_structure=MagickFalse;
4699 /* Allocate a MngInfo structure. */
4701 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4703 if (mng_info == (MngInfo *) NULL)
4704 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4706 /* Initialize members of the MngInfo structure. */
4708 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4709 mng_info->image=image;
4710 have_mng_structure=MagickTrue;
4712 if (LocaleCompare(image_info->magick,"MNG") == 0)
4715 magic_number[MaxTextExtent];
4717 /* Verify MNG signature. */
4718 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4719 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4720 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4722 /* Initialize some nonzero members of the MngInfo structure. */
4723 for (i=0; i < MNG_MAX_OBJECTS; i++)
4725 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4726 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4728 mng_info->exists[0]=MagickTrue;
4731 first_mng_object=MagickTrue;
4733 #if defined(MNG_INSERT_LAYERS)
4734 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4736 default_frame_delay=0;
4737 default_frame_timeout=0;
4740 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4742 skip_to_iend=MagickFalse;
4743 term_chunk_found=MagickFalse;
4744 mng_info->framing_mode=1;
4745 #if defined(MNG_INSERT_LAYERS)
4746 mandatory_back=MagickFalse;
4748 #if defined(MNG_INSERT_LAYERS)
4749 mng_background_color=image->background_color;
4751 default_fb=mng_info->frame;
4752 previous_fb=mng_info->frame;
4756 type[MaxTextExtent];
4758 if (LocaleCompare(image_info->magick,"MNG") == 0)
4767 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4768 length=ReadBlobMSBLong(image);
4769 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4771 if (logging != MagickFalse)
4772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4773 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4774 type[0],type[1],type[2],type[3],(double) length);
4776 if (length > PNG_UINT_31_MAX)
4780 ThrowReaderException(CorruptImageError,"CorruptImage");
4783 chunk=(unsigned char *) NULL;
4787 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4789 if (chunk == (unsigned char *) NULL)
4790 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4792 for (i=0; i < (ssize_t) length; i++)
4793 chunk[i]=(unsigned char) ReadBlobByte(image);
4798 (void) ReadBlobMSBLong(image); /* read crc word */
4800 #if !defined(JNG_SUPPORTED)
4801 if (memcmp(type,mng_JHDR,4) == 0)
4803 skip_to_iend=MagickTrue;
4805 if (mng_info->jhdr_warning == 0)
4806 (void) ThrowMagickException(exception,GetMagickModule(),
4807 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4809 mng_info->jhdr_warning++;
4812 if (memcmp(type,mng_DHDR,4) == 0)
4814 skip_to_iend=MagickTrue;
4816 if (mng_info->dhdr_warning == 0)
4817 (void) ThrowMagickException(exception,GetMagickModule(),
4818 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4820 mng_info->dhdr_warning++;
4822 if (memcmp(type,mng_MEND,4) == 0)
4827 if (memcmp(type,mng_IEND,4) == 0)
4828 skip_to_iend=MagickFalse;
4831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4833 if (logging != MagickFalse)
4834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4840 if (memcmp(type,mng_MHDR,4) == 0)
4842 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4843 (p[2] << 8) | p[3]);
4845 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4846 (p[6] << 8) | p[7]);
4848 if (logging != MagickFalse)
4850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4851 " MNG width: %.20g",(double) mng_info->mng_width);
4852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4853 " MNG height: %.20g",(double) mng_info->mng_height);
4857 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4859 if (mng_info->ticks_per_second == 0)
4860 default_frame_delay=0;
4863 default_frame_delay=1UL*image->ticks_per_second/
4864 mng_info->ticks_per_second;
4866 frame_delay=default_frame_delay;
4872 simplicity=(size_t) mng_get_long(p);
4875 mng_type=1; /* Full MNG */
4877 if ((simplicity != 0) && ((simplicity | 11) == 11))
4878 mng_type=2; /* LC */
4880 if ((simplicity != 0) && ((simplicity | 9) == 9))
4881 mng_type=3; /* VLC */
4883 #if defined(MNG_INSERT_LAYERS)
4885 insert_layers=MagickTrue;
4887 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4889 /* Allocate next image structure. */
4890 AcquireNextImage(image_info,image,exception);
4892 if (GetNextImageInList(image) == (Image *) NULL)
4893 return((Image *) NULL);
4895 image=SyncNextImageInList(image);
4896 mng_info->image=image;
4899 if ((mng_info->mng_width > 65535L) ||
4900 (mng_info->mng_height > 65535L))
4901 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4903 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4904 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4905 mng_info->mng_height);
4907 mng_info->frame.left=0;
4908 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4909 mng_info->frame.top=0;
4910 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4911 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4913 for (i=0; i < MNG_MAX_OBJECTS; i++)
4914 mng_info->object_clip[i]=mng_info->frame;
4916 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4920 if (memcmp(type,mng_TERM,4) == 0)
4931 final_delay=(png_uint_32) mng_get_long(&p[2]);
4932 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4934 if (mng_iterations == PNG_UINT_31_MAX)
4937 image->iterations=mng_iterations;
4938 term_chunk_found=MagickTrue;
4941 if (logging != MagickFalse)
4943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4944 " repeat=%d",repeat);
4946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4947 " final_delay=%.20g",(double) final_delay);
4949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4950 " image->iterations=%.20g",(double) image->iterations);
4953 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4956 if (memcmp(type,mng_DEFI,4) == 0)
4959 (void) ThrowMagickException(exception,GetMagickModule(),
4960 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4963 object_id=(p[0] << 8) | p[1];
4965 if (mng_type == 2 && object_id != 0)
4966 (void) ThrowMagickException(exception,GetMagickModule(),
4967 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4970 if (object_id > MNG_MAX_OBJECTS)
4973 Instead of using a warning we should allocate a larger
4974 MngInfo structure and continue.
4976 (void) ThrowMagickException(exception,GetMagickModule(),
4977 CoderError,"object id too large","`%s'",image->filename);
4978 object_id=MNG_MAX_OBJECTS;
4981 if (mng_info->exists[object_id])
4982 if (mng_info->frozen[object_id])
4984 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4985 (void) ThrowMagickException(exception,
4986 GetMagickModule(),CoderError,
4987 "DEFI cannot redefine a frozen MNG object","`%s'",
4992 mng_info->exists[object_id]=MagickTrue;
4995 mng_info->invisible[object_id]=p[2];
4998 Extract object offset info.
5002 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5003 (p[5] << 16) | (p[6] << 8) | p[7]);
5005 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5006 (p[9] << 16) | (p[10] << 8) | p[11]);
5008 if (logging != MagickFalse)
5010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5011 " x_off[%d]: %.20g",object_id,(double)
5012 mng_info->x_off[object_id]);
5014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5015 " y_off[%d]: %.20g",object_id,(double)
5016 mng_info->y_off[object_id]);
5021 Extract object clipping info.
5024 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5027 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5030 if (memcmp(type,mng_bKGD,4) == 0)
5032 mng_info->have_global_bkgd=MagickFalse;
5036 mng_info->mng_global_bkgd.red=
5037 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5039 mng_info->mng_global_bkgd.green=
5040 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5042 mng_info->mng_global_bkgd.blue=
5043 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5045 mng_info->have_global_bkgd=MagickTrue;
5048 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5051 if (memcmp(type,mng_BACK,4) == 0)
5053 #if defined(MNG_INSERT_LAYERS)
5055 mandatory_back=p[6];
5060 if (mandatory_back && length > 5)
5062 mng_background_color.red=
5063 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5065 mng_background_color.green=
5066 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5068 mng_background_color.blue=
5069 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5071 mng_background_color.alpha=OpaqueAlpha;
5074 #ifdef MNG_OBJECT_BUFFERS
5076 mng_background_object=(p[7] << 8) | p[8];
5079 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5083 if (memcmp(type,mng_PLTE,4) == 0)
5085 /* Read global PLTE. */
5087 if (length && (length < 769))
5089 if (mng_info->global_plte == (png_colorp) NULL)
5090 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5091 sizeof(*mng_info->global_plte));
5093 for (i=0; i < (ssize_t) (length/3); i++)
5095 mng_info->global_plte[i].red=p[3*i];
5096 mng_info->global_plte[i].green=p[3*i+1];
5097 mng_info->global_plte[i].blue=p[3*i+2];
5100 mng_info->global_plte_length=(unsigned int) (length/3);
5103 for ( ; i < 256; i++)
5105 mng_info->global_plte[i].red=i;
5106 mng_info->global_plte[i].green=i;
5107 mng_info->global_plte[i].blue=i;
5111 mng_info->global_plte_length=256;
5114 mng_info->global_plte_length=0;
5116 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5120 if (memcmp(type,mng_tRNS,4) == 0)
5122 /* read global tRNS */
5125 for (i=0; i < (ssize_t) length; i++)
5126 mng_info->global_trns[i]=p[i];
5129 for ( ; i < 256; i++)
5130 mng_info->global_trns[i]=255;
5132 mng_info->global_trns_length=(unsigned int) length;
5133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5136 if (memcmp(type,mng_gAMA,4) == 0)
5143 igamma=mng_get_long(p);
5144 mng_info->global_gamma=((float) igamma)*0.00001;
5145 mng_info->have_global_gama=MagickTrue;
5149 mng_info->have_global_gama=MagickFalse;
5151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5155 if (memcmp(type,mng_cHRM,4) == 0)
5157 /* Read global cHRM */
5161 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5162 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5163 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5164 mng_info->global_chrm.red_primary.y=0.00001*
5165 mng_get_long(&p[12]);
5166 mng_info->global_chrm.green_primary.x=0.00001*
5167 mng_get_long(&p[16]);
5168 mng_info->global_chrm.green_primary.y=0.00001*
5169 mng_get_long(&p[20]);
5170 mng_info->global_chrm.blue_primary.x=0.00001*
5171 mng_get_long(&p[24]);
5172 mng_info->global_chrm.blue_primary.y=0.00001*
5173 mng_get_long(&p[28]);
5174 mng_info->have_global_chrm=MagickTrue;
5177 mng_info->have_global_chrm=MagickFalse;
5179 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5183 if (memcmp(type,mng_sRGB,4) == 0)
5190 mng_info->global_srgb_intent=
5191 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5192 mng_info->have_global_srgb=MagickTrue;
5195 mng_info->have_global_srgb=MagickFalse;
5197 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5201 if (memcmp(type,mng_iCCP,4) == 0)
5209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5214 if (memcmp(type,mng_FRAM,4) == 0)
5217 (void) ThrowMagickException(exception,GetMagickModule(),
5218 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5221 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5222 image->delay=frame_delay;
5224 frame_delay=default_frame_delay;
5225 frame_timeout=default_frame_timeout;
5230 mng_info->framing_mode=p[0];
5232 if (logging != MagickFalse)
5233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5234 " Framing_mode=%d",mng_info->framing_mode);
5238 /* Note the delay and frame clipping boundaries. */
5240 p++; /* framing mode */
5242 while (*p && ((p-chunk) < (ssize_t) length))
5243 p++; /* frame name */
5245 p++; /* frame name terminator */
5247 if ((p-chunk) < (ssize_t) (length-4))
5254 change_delay=(*p++);
5255 change_timeout=(*p++);
5256 change_clipping=(*p++);
5257 p++; /* change_sync */
5261 frame_delay=1UL*image->ticks_per_second*
5264 if (mng_info->ticks_per_second != 0)
5265 frame_delay/=mng_info->ticks_per_second;
5268 frame_delay=PNG_UINT_31_MAX;
5270 if (change_delay == 2)
5271 default_frame_delay=frame_delay;
5275 if (logging != MagickFalse)
5276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5277 " Framing_delay=%.20g",(double) frame_delay);
5282 frame_timeout=1UL*image->ticks_per_second*
5285 if (mng_info->ticks_per_second != 0)
5286 frame_timeout/=mng_info->ticks_per_second;
5289 frame_timeout=PNG_UINT_31_MAX;
5291 if (change_delay == 2)
5292 default_frame_timeout=frame_timeout;
5296 if (logging != MagickFalse)
5297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5298 " Framing_timeout=%.20g",(double) frame_timeout);
5301 if (change_clipping)
5303 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5307 if (logging != MagickFalse)
5308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5309 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5310 (double) fb.left,(double) fb.right,(double) fb.top,
5311 (double) fb.bottom);
5313 if (change_clipping == 2)
5319 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5321 subframe_width=(size_t) (mng_info->clip.right
5322 -mng_info->clip.left);
5324 subframe_height=(size_t) (mng_info->clip.bottom
5325 -mng_info->clip.top);
5327 Insert a background layer behind the frame if framing_mode is 4.
5329 #if defined(MNG_INSERT_LAYERS)
5330 if (logging != MagickFalse)
5331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5332 " subframe_width=%.20g, subframe_height=%.20g",(double)
5333 subframe_width,(double) subframe_height);
5335 if (insert_layers && (mng_info->framing_mode == 4) &&
5336 (subframe_width) && (subframe_height))
5338 /* Allocate next image structure. */
5339 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5341 AcquireNextImage(image_info,image,exception);
5343 if (GetNextImageInList(image) == (Image *) NULL)
5345 image=DestroyImageList(image);
5346 MngInfoFreeStruct(mng_info,&have_mng_structure);
5347 return((Image *) NULL);
5350 image=SyncNextImageInList(image);
5353 mng_info->image=image;
5355 if (term_chunk_found)
5357 image->start_loop=MagickTrue;
5358 image->iterations=mng_iterations;
5359 term_chunk_found=MagickFalse;
5363 image->start_loop=MagickFalse;
5365 image->columns=subframe_width;
5366 image->rows=subframe_height;
5367 image->page.width=subframe_width;
5368 image->page.height=subframe_height;
5369 image->page.x=mng_info->clip.left;
5370 image->page.y=mng_info->clip.top;
5371 image->background_color=mng_background_color;
5372 image->matte=MagickFalse;
5374 (void) SetImageBackgroundColor(image,exception);
5376 if (logging != MagickFalse)
5377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5378 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5379 (double) mng_info->clip.left,(double) mng_info->clip.right,
5380 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5383 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5386 if (memcmp(type,mng_CLIP,4) == 0)
5395 first_object=(p[0] << 8) | p[1];
5396 last_object=(p[2] << 8) | p[3];
5398 for (i=(int) first_object; i <= (int) last_object; i++)
5400 if (mng_info->exists[i] && !mng_info->frozen[i])
5405 box=mng_info->object_clip[i];
5406 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5410 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5413 if (memcmp(type,mng_SAVE,4) == 0)
5415 for (i=1; i < MNG_MAX_OBJECTS; i++)
5416 if (mng_info->exists[i])
5418 mng_info->frozen[i]=MagickTrue;
5419 #ifdef MNG_OBJECT_BUFFERS
5420 if (mng_info->ob[i] != (MngBuffer *) NULL)
5421 mng_info->ob[i]->frozen=MagickTrue;
5426 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5431 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5433 /* Read DISC or SEEK. */
5435 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5437 for (i=1; i < MNG_MAX_OBJECTS; i++)
5438 MngInfoDiscardObject(mng_info,i);
5446 for (j=0; j < (ssize_t) length; j+=2)
5448 i=p[j] << 8 | p[j+1];
5449 MngInfoDiscardObject(mng_info,i);
5454 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5459 if (memcmp(type,mng_MOVE,4) == 0)
5467 first_object=(p[0] << 8) | p[1];
5468 last_object=(p[2] << 8) | p[3];
5469 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5471 if (mng_info->exists[i] && !mng_info->frozen[i])
5479 old_pair.a=mng_info->x_off[i];
5480 old_pair.b=mng_info->y_off[i];
5481 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5482 mng_info->x_off[i]=new_pair.a;
5483 mng_info->y_off[i]=new_pair.b;
5487 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5491 if (memcmp(type,mng_LOOP,4) == 0)
5493 ssize_t loop_iters=1;
5494 loop_level=chunk[0];
5495 mng_info->loop_active[loop_level]=1; /* mark loop active */
5497 /* Record starting point. */
5498 loop_iters=mng_get_long(&chunk[1]);
5500 if (logging != MagickFalse)
5501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5502 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5503 (double) loop_iters);
5505 if (loop_iters == 0)
5506 skipping_loop=loop_level;
5510 mng_info->loop_jump[loop_level]=TellBlob(image);
5511 mng_info->loop_count[loop_level]=loop_iters;
5514 mng_info->loop_iteration[loop_level]=0;
5515 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5519 if (memcmp(type,mng_ENDL,4) == 0)
5521 loop_level=chunk[0];
5523 if (skipping_loop > 0)
5525 if (skipping_loop == loop_level)
5528 Found end of zero-iteration loop.
5531 mng_info->loop_active[loop_level]=0;
5537 if (mng_info->loop_active[loop_level] == 1)
5539 mng_info->loop_count[loop_level]--;
5540 mng_info->loop_iteration[loop_level]++;
5542 if (logging != MagickFalse)
5543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5544 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5545 (double) loop_level,(double)
5546 mng_info->loop_count[loop_level]);
5548 if (mng_info->loop_count[loop_level] != 0)
5550 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5554 ThrowReaderException(CorruptImageError,
5555 "ImproperImageHeader");
5566 mng_info->loop_active[loop_level]=0;
5568 for (i=0; i < loop_level; i++)
5569 if (mng_info->loop_active[i] == 1)
5570 last_level=(short) i;
5571 loop_level=last_level;
5576 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5580 if (memcmp(type,mng_CLON,4) == 0)
5582 if (mng_info->clon_warning == 0)
5583 (void) ThrowMagickException(exception,GetMagickModule(),
5584 CoderError,"CLON is not implemented yet","`%s'",
5587 mng_info->clon_warning++;
5590 if (memcmp(type,mng_MAGN,4) == 0)
5605 magn_first=(p[0] << 8) | p[1];
5611 magn_last=(p[2] << 8) | p[3];
5614 magn_last=magn_first;
5615 #ifndef MNG_OBJECT_BUFFERS
5616 if (magn_first || magn_last)
5617 if (mng_info->magn_warning == 0)
5619 (void) ThrowMagickException(exception,
5620 GetMagickModule(),CoderError,
5621 "MAGN is not implemented yet for nonzero objects",
5622 "`%s'",image->filename);
5624 mng_info->magn_warning++;
5634 magn_mx=(p[5] << 8) | p[6];
5643 magn_my=(p[7] << 8) | p[8];
5652 magn_ml=(p[9] << 8) | p[10];
5661 magn_mr=(p[11] << 8) | p[12];
5670 magn_mt=(p[13] << 8) | p[14];
5679 magn_mb=(p[15] << 8) | p[16];
5691 magn_methy=magn_methx;
5694 if (magn_methx > 5 || magn_methy > 5)
5695 if (mng_info->magn_warning == 0)
5697 (void) ThrowMagickException(exception,
5698 GetMagickModule(),CoderError,
5699 "Unknown MAGN method in MNG datastream","`%s'",
5702 mng_info->magn_warning++;
5704 #ifdef MNG_OBJECT_BUFFERS
5705 /* Magnify existing objects in the range magn_first to magn_last */
5707 if (magn_first == 0 || magn_last == 0)
5709 /* Save the magnification factors for object 0 */
5710 mng_info->magn_mb=magn_mb;
5711 mng_info->magn_ml=magn_ml;
5712 mng_info->magn_mr=magn_mr;
5713 mng_info->magn_mt=magn_mt;
5714 mng_info->magn_mx=magn_mx;
5715 mng_info->magn_my=magn_my;
5716 mng_info->magn_methx=magn_methx;
5717 mng_info->magn_methy=magn_methy;
5721 if (memcmp(type,mng_PAST,4) == 0)
5723 if (mng_info->past_warning == 0)
5724 (void) ThrowMagickException(exception,GetMagickModule(),
5725 CoderError,"PAST is not implemented yet","`%s'",
5728 mng_info->past_warning++;
5731 if (memcmp(type,mng_SHOW,4) == 0)
5733 if (mng_info->show_warning == 0)
5734 (void) ThrowMagickException(exception,GetMagickModule(),
5735 CoderError,"SHOW is not implemented yet","`%s'",
5738 mng_info->show_warning++;
5741 if (memcmp(type,mng_sBIT,4) == 0)
5744 mng_info->have_global_sbit=MagickFalse;
5748 mng_info->global_sbit.gray=p[0];
5749 mng_info->global_sbit.red=p[0];
5750 mng_info->global_sbit.green=p[1];
5751 mng_info->global_sbit.blue=p[2];
5752 mng_info->global_sbit.alpha=p[3];
5753 mng_info->have_global_sbit=MagickTrue;
5756 if (memcmp(type,mng_pHYs,4) == 0)
5760 mng_info->global_x_pixels_per_unit=
5761 (size_t) mng_get_long(p);
5762 mng_info->global_y_pixels_per_unit=
5763 (size_t) mng_get_long(&p[4]);
5764 mng_info->global_phys_unit_type=p[8];
5765 mng_info->have_global_phys=MagickTrue;
5769 mng_info->have_global_phys=MagickFalse;
5771 if (memcmp(type,mng_pHYg,4) == 0)
5773 if (mng_info->phyg_warning == 0)
5774 (void) ThrowMagickException(exception,GetMagickModule(),
5775 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5777 mng_info->phyg_warning++;
5779 if (memcmp(type,mng_BASI,4) == 0)
5781 skip_to_iend=MagickTrue;
5783 if (mng_info->basi_warning == 0)
5784 (void) ThrowMagickException(exception,GetMagickModule(),
5785 CoderError,"BASI is not implemented yet","`%s'",
5788 mng_info->basi_warning++;
5789 #ifdef MNG_BASI_SUPPORTED
5790 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5791 (p[2] << 8) | p[3]);
5792 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5793 (p[6] << 8) | p[7]);
5794 basi_color_type=p[8];
5795 basi_compression_method=p[9];
5796 basi_filter_type=p[10];
5797 basi_interlace_method=p[11];
5799 basi_red=(p[12] << 8) & p[13];
5805 basi_green=(p[14] << 8) & p[15];
5811 basi_blue=(p[16] << 8) & p[17];
5817 basi_alpha=(p[18] << 8) & p[19];
5821 if (basi_sample_depth == 16)
5828 basi_viewable=p[20];
5834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5838 if (memcmp(type,mng_IHDR,4)
5839 #if defined(JNG_SUPPORTED)
5840 && memcmp(type,mng_JHDR,4)
5844 /* Not an IHDR or JHDR chunk */
5846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5851 if (logging != MagickFalse)
5852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5853 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5855 mng_info->exists[object_id]=MagickTrue;
5856 mng_info->viewable[object_id]=MagickTrue;
5858 if (mng_info->invisible[object_id])
5860 if (logging != MagickFalse)
5861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5862 " Skipping invisible object");
5864 skip_to_iend=MagickTrue;
5865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5868 #if defined(MNG_INSERT_LAYERS)
5870 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5872 image_width=(size_t) mng_get_long(p);
5873 image_height=(size_t) mng_get_long(&p[4]);
5875 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5878 Insert a transparent background layer behind the entire animation
5879 if it is not full screen.
5881 #if defined(MNG_INSERT_LAYERS)
5882 if (insert_layers && mng_type && first_mng_object)
5884 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5885 (image_width < mng_info->mng_width) ||
5886 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5887 (image_height < mng_info->mng_height) ||
5888 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5890 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5893 Allocate next image structure.
5895 AcquireNextImage(image_info,image,exception);
5897 if (GetNextImageInList(image) == (Image *) NULL)
5899 image=DestroyImageList(image);
5900 MngInfoFreeStruct(mng_info,&have_mng_structure);
5901 return((Image *) NULL);
5904 image=SyncNextImageInList(image);
5906 mng_info->image=image;
5908 if (term_chunk_found)
5910 image->start_loop=MagickTrue;
5911 image->iterations=mng_iterations;
5912 term_chunk_found=MagickFalse;
5916 image->start_loop=MagickFalse;
5918 /* Make a background rectangle. */
5921 image->columns=mng_info->mng_width;
5922 image->rows=mng_info->mng_height;
5923 image->page.width=mng_info->mng_width;
5924 image->page.height=mng_info->mng_height;
5927 image->background_color=mng_background_color;
5928 (void) SetImageBackgroundColor(image,exception);
5929 if (logging != MagickFalse)
5930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5931 " Inserted transparent background layer, W=%.20g, H=%.20g",
5932 (double) mng_info->mng_width,(double) mng_info->mng_height);
5936 Insert a background layer behind the upcoming image if
5937 framing_mode is 3, and we haven't already inserted one.
5939 if (insert_layers && (mng_info->framing_mode == 3) &&
5940 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5941 (simplicity & 0x08)))
5943 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5946 Allocate next image structure.
5948 AcquireNextImage(image_info,image,exception);
5950 if (GetNextImageInList(image) == (Image *) NULL)
5952 image=DestroyImageList(image);
5953 MngInfoFreeStruct(mng_info,&have_mng_structure);
5954 return((Image *) NULL);
5957 image=SyncNextImageInList(image);
5960 mng_info->image=image;
5962 if (term_chunk_found)
5964 image->start_loop=MagickTrue;
5965 image->iterations=mng_iterations;
5966 term_chunk_found=MagickFalse;
5970 image->start_loop=MagickFalse;
5973 image->columns=subframe_width;
5974 image->rows=subframe_height;
5975 image->page.width=subframe_width;
5976 image->page.height=subframe_height;
5977 image->page.x=mng_info->clip.left;
5978 image->page.y=mng_info->clip.top;
5979 image->background_color=mng_background_color;
5980 image->matte=MagickFalse;
5981 (void) SetImageBackgroundColor(image,exception);
5983 if (logging != MagickFalse)
5984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5985 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5986 (double) mng_info->clip.left,(double) mng_info->clip.right,
5987 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5989 #endif /* MNG_INSERT_LAYERS */
5990 first_mng_object=MagickFalse;
5992 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5995 Allocate next image structure.
5997 AcquireNextImage(image_info,image,exception);
5999 if (GetNextImageInList(image) == (Image *) NULL)
6001 image=DestroyImageList(image);
6002 MngInfoFreeStruct(mng_info,&have_mng_structure);
6003 return((Image *) NULL);
6006 image=SyncNextImageInList(image);
6008 mng_info->image=image;
6009 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6010 GetBlobSize(image));
6012 if (status == MagickFalse)
6015 if (term_chunk_found)
6017 image->start_loop=MagickTrue;
6018 term_chunk_found=MagickFalse;
6022 image->start_loop=MagickFalse;
6024 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6026 image->delay=frame_delay;
6027 frame_delay=default_frame_delay;
6033 image->page.width=mng_info->mng_width;
6034 image->page.height=mng_info->mng_height;
6035 image->page.x=mng_info->x_off[object_id];
6036 image->page.y=mng_info->y_off[object_id];
6037 image->iterations=mng_iterations;
6040 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6043 if (logging != MagickFalse)
6044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6045 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6048 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6051 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6055 mng_info->image=image;
6056 mng_info->mng_type=mng_type;
6057 mng_info->object_id=object_id;
6059 if (memcmp(type,mng_IHDR,4) == 0)
6060 image=ReadOnePNGImage(mng_info,image_info,exception);
6062 #if defined(JNG_SUPPORTED)
6064 image=ReadOneJNGImage(mng_info,image_info,exception);
6067 if (image == (Image *) NULL)
6069 if (IsImageObject(previous) != MagickFalse)
6071 (void) DestroyImageList(previous);
6072 (void) CloseBlob(previous);
6075 MngInfoFreeStruct(mng_info,&have_mng_structure);
6076 return((Image *) NULL);
6079 if (image->columns == 0 || image->rows == 0)
6081 (void) CloseBlob(image);
6082 image=DestroyImageList(image);
6083 MngInfoFreeStruct(mng_info,&have_mng_structure);
6084 return((Image *) NULL);
6087 mng_info->image=image;
6094 if (mng_info->magn_methx || mng_info->magn_methy)
6100 if (logging != MagickFalse)
6101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6102 " Processing MNG MAGN chunk");
6104 if (mng_info->magn_methx == 1)
6106 magnified_width=mng_info->magn_ml;
6108 if (image->columns > 1)
6109 magnified_width += mng_info->magn_mr;
6111 if (image->columns > 2)
6112 magnified_width += (png_uint_32)
6113 ((image->columns-2)*(mng_info->magn_mx));
6118 magnified_width=(png_uint_32) image->columns;
6120 if (image->columns > 1)
6121 magnified_width += mng_info->magn_ml-1;
6123 if (image->columns > 2)
6124 magnified_width += mng_info->magn_mr-1;
6126 if (image->columns > 3)
6127 magnified_width += (png_uint_32)
6128 ((image->columns-3)*(mng_info->magn_mx-1));
6131 if (mng_info->magn_methy == 1)
6133 magnified_height=mng_info->magn_mt;
6135 if (image->rows > 1)
6136 magnified_height += mng_info->magn_mb;
6138 if (image->rows > 2)
6139 magnified_height += (png_uint_32)
6140 ((image->rows-2)*(mng_info->magn_my));
6145 magnified_height=(png_uint_32) image->rows;
6147 if (image->rows > 1)
6148 magnified_height += mng_info->magn_mt-1;
6150 if (image->rows > 2)
6151 magnified_height += mng_info->magn_mb-1;
6153 if (image->rows > 3)
6154 magnified_height += (png_uint_32)
6155 ((image->rows-3)*(mng_info->magn_my-1));
6158 if (magnified_height > image->rows ||
6159 magnified_width > image->columns)
6186 /* Allocate next image structure. */
6188 if (logging != MagickFalse)
6189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6190 " Allocate magnified image");
6192 AcquireNextImage(image_info,image,exception);
6194 if (GetNextImageInList(image) == (Image *) NULL)
6196 image=DestroyImageList(image);
6197 MngInfoFreeStruct(mng_info,&have_mng_structure);
6198 return((Image *) NULL);
6201 large_image=SyncNextImageInList(image);
6203 large_image->columns=magnified_width;
6204 large_image->rows=magnified_height;
6206 magn_methx=mng_info->magn_methx;
6207 magn_methy=mng_info->magn_methy;
6209 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6210 #define QM unsigned short
6211 if (magn_methx != 1 || magn_methy != 1)
6214 Scale pixels to unsigned shorts to prevent
6215 overflow of intermediate values of interpolations
6217 for (y=0; y < (ssize_t) image->rows; y++)
6219 q=GetAuthenticPixels(image,0,y,image->columns,1,
6222 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6224 SetPixelRed(image,ScaleQuantumToShort(
6225 GetPixelRed(image,q)),q);
6226 SetPixelGreen(image,ScaleQuantumToShort(
6227 GetPixelGreen(image,q)),q);
6228 SetPixelBlue(image,ScaleQuantumToShort(
6229 GetPixelBlue(image,q)),q);
6230 SetPixelAlpha(image,ScaleQuantumToShort(
6231 GetPixelAlpha(image,q)),q);
6232 q+=GetPixelChannels(image);
6235 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6243 if (image->matte != MagickFalse)
6244 (void) SetImageBackgroundColor(large_image,exception);
6248 large_image->background_color.alpha=OpaqueAlpha;
6249 (void) SetImageBackgroundColor(large_image,exception);
6251 if (magn_methx == 4)
6254 if (magn_methx == 5)
6257 if (magn_methy == 4)
6260 if (magn_methy == 5)
6264 /* magnify the rows into the right side of the large image */
6266 if (logging != MagickFalse)
6267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6268 " Magnify the rows to %.20g",(double) large_image->rows);
6269 m=(ssize_t) mng_info->magn_mt;
6271 length=(size_t) image->columns*GetPixelChannels(image);
6272 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6273 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6275 if ((prev == (Quantum *) NULL) ||
6276 (next == (Quantum *) NULL))
6278 image=DestroyImageList(image);
6279 MngInfoFreeStruct(mng_info,&have_mng_structure);
6280 ThrowReaderException(ResourceLimitError,
6281 "MemoryAllocationFailed");
6284 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6285 (void) CopyMagickMemory(next,n,length);
6287 for (y=0; y < (ssize_t) image->rows; y++)
6290 m=(ssize_t) mng_info->magn_mt;
6292 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6293 m=(ssize_t) mng_info->magn_mb;
6295 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6296 m=(ssize_t) mng_info->magn_mb;
6298 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6302 m=(ssize_t) mng_info->magn_my;
6308 if (y < (ssize_t) image->rows-1)
6310 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6312 (void) CopyMagickMemory(next,n,length);
6315 for (i=0; i < m; i++, yy++)
6320 assert(yy < (ssize_t) large_image->rows);
6323 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6325 q+=(large_image->columns-image->columns)*
6326 GetPixelChannels(large_image);
6328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6330 /* To do: get color as function of indexes[x] */
6332 if (image->storage_class == PseudoClass)
6337 if (magn_methy <= 1)
6339 /* replicate previous */
6340 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6341 SetPixelGreen(large_image,GetPixelGreen(image,
6343 SetPixelBlue(large_image,GetPixelBlue(image,
6345 SetPixelAlpha(large_image,GetPixelAlpha(image,
6349 else if (magn_methy == 2 || magn_methy == 4)
6353 SetPixelRed(large_image,GetPixelRed(image,
6355 SetPixelGreen(large_image,GetPixelGreen(image,
6357 SetPixelBlue(large_image,GetPixelBlue(image,
6359 SetPixelAlpha(large_image,GetPixelAlpha(image,
6366 SetPixelRed(large_image,((QM) (((ssize_t)
6367 (2*i*(GetPixelRed(image,n)
6368 -GetPixelRed(image,pixels)+m))/
6370 +GetPixelRed(image,pixels)))),q);
6371 SetPixelGreen(large_image,((QM) (((ssize_t)
6372 (2*i*(GetPixelGreen(image,n)
6373 -GetPixelGreen(image,pixels)+m))/
6375 +GetPixelGreen(image,pixels)))),q);
6376 SetPixelBlue(large_image,((QM) (((ssize_t)
6377 (2*i*(GetPixelBlue(image,n)
6378 -GetPixelBlue(image,pixels)+m))/
6380 +GetPixelBlue(image,pixels)))),q);
6382 if (image->matte != MagickFalse)
6383 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6384 (2*i*(GetPixelAlpha(image,n)
6385 -GetPixelAlpha(image,pixels)+m))
6387 GetPixelAlpha(image,pixels)))),q);
6390 if (magn_methy == 4)
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
6394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6397 SetPixelAlpha(large_image,GetPixelAlpha(image,
6402 else /* if (magn_methy == 3 || magn_methy == 5) */
6404 /* Replicate nearest */
6405 if (i <= ((m+1) << 1))
6407 SetPixelRed(large_image,GetPixelRed(image,
6409 SetPixelGreen(large_image,GetPixelGreen(image,
6411 SetPixelBlue(large_image,GetPixelBlue(image,
6413 SetPixelAlpha(large_image,GetPixelAlpha(image,
6419 SetPixelRed(large_image,GetPixelRed(image,n),q);
6420 SetPixelGreen(large_image,GetPixelGreen(image,n),
6422 SetPixelBlue(large_image,GetPixelBlue(image,n),
6424 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6428 if (magn_methy == 5)
6430 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6431 (GetPixelAlpha(image,n)
6432 -GetPixelAlpha(image,pixels))
6433 +m))/((ssize_t) (m*2))
6434 +GetPixelAlpha(image,pixels)),q);
6437 n+=GetPixelChannels(image);
6438 q+=GetPixelChannels(large_image);
6439 pixels+=GetPixelChannels(image);
6442 if (SyncAuthenticPixels(large_image,exception) == 0)
6448 prev=(Quantum *) RelinquishMagickMemory(prev);
6449 next=(Quantum *) RelinquishMagickMemory(next);
6451 length=image->columns;
6453 if (logging != MagickFalse)
6454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6455 " Delete original image");
6457 DeleteImageFromList(&image);
6461 mng_info->image=image;
6463 /* magnify the columns */
6464 if (logging != MagickFalse)
6465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6466 " Magnify the columns to %.20g",(double) image->columns);
6468 for (y=0; y < (ssize_t) image->rows; y++)
6473 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6474 pixels=q+(image->columns-length)*GetPixelChannels(image);
6475 n=pixels+GetPixelChannels(image);
6477 for (x=(ssize_t) (image->columns-length);
6478 x < (ssize_t) image->columns; x++)
6480 /* To do: Rewrite using Get/Set***PixelChannel() */
6482 if (x == (ssize_t) (image->columns-length))
6483 m=(ssize_t) mng_info->magn_ml;
6485 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6486 m=(ssize_t) mng_info->magn_mr;
6488 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6489 m=(ssize_t) mng_info->magn_mr;
6491 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6495 m=(ssize_t) mng_info->magn_mx;
6497 for (i=0; i < m; i++)
6499 if (magn_methx <= 1)
6501 /* replicate previous */
6502 SetPixelRed(image,GetPixelRed(image,pixels),q);
6503 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6504 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6505 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6508 else if (magn_methx == 2 || magn_methx == 4)
6512 SetPixelRed(image,GetPixelRed(image,pixels),q);
6513 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6514 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6515 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6518 /* To do: Rewrite using Get/Set***PixelChannel() */
6522 SetPixelRed(image,(QM) ((2*i*(
6523 GetPixelRed(image,n)
6524 -GetPixelRed(image,pixels))+m)
6526 GetPixelRed(image,pixels)),q);
6528 SetPixelGreen(image,(QM) ((2*i*(
6529 GetPixelGreen(image,n)
6530 -GetPixelGreen(image,pixels))+m)
6532 GetPixelGreen(image,pixels)),q);
6534 SetPixelBlue(image,(QM) ((2*i*(
6535 GetPixelBlue(image,n)
6536 -GetPixelBlue(image,pixels))+m)
6538 GetPixelBlue(image,pixels)),q);
6539 if (image->matte != MagickFalse)
6540 SetPixelAlpha(image,(QM) ((2*i*(
6541 GetPixelAlpha(image,n)
6542 -GetPixelAlpha(image,pixels))+m)
6544 GetPixelAlpha(image,pixels)),q);
6547 if (magn_methx == 4)
6549 /* Replicate nearest */
6550 if (i <= ((m+1) << 1))
6552 SetPixelAlpha(image,
6553 GetPixelAlpha(image,pixels)+0,q);
6557 SetPixelAlpha(image,
6558 GetPixelAlpha(image,n)+0,q);
6563 else /* if (magn_methx == 3 || magn_methx == 5) */
6565 /* Replicate nearest */
6566 if (i <= ((m+1) << 1))
6568 SetPixelRed(image,GetPixelRed(image,pixels),q);
6569 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6570 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6571 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6576 SetPixelRed(image,GetPixelRed(image,n),q);
6577 SetPixelGreen(image,GetPixelGreen(image,n),q);
6578 SetPixelBlue(image,GetPixelBlue(image,n),q);
6579 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6582 if (magn_methx == 5)
6585 SetPixelAlpha(image,
6586 (QM) ((2*i*( GetPixelAlpha(image,n)
6587 -GetPixelAlpha(image,pixels))+m)/
6589 +GetPixelAlpha(image,pixels)),q);
6592 q+=GetPixelChannels(image);
6594 n+=GetPixelChannels(image);
6595 p+=GetPixelChannels(image);
6598 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6601 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6602 if (magn_methx != 1 || magn_methy != 1)
6605 Rescale pixels to Quantum
6607 for (y=0; y < (ssize_t) image->rows; y++)
6609 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6611 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6613 SetPixelRed(image,ScaleShortToQuantum(
6614 GetPixelRed(image,q)),q);
6615 SetPixelGreen(image,ScaleShortToQuantum(
6616 GetPixelGreen(image,q)),q);
6617 SetPixelBlue(image,ScaleShortToQuantum(
6618 GetPixelBlue(image,q)),q);
6619 SetPixelAlpha(image,ScaleShortToQuantum(
6620 GetPixelAlpha(image,q)),q);
6621 q+=GetPixelChannels(image);
6624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6629 if (logging != MagickFalse)
6630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6631 " Finished MAGN processing");
6636 Crop_box is with respect to the upper left corner of the MNG.
6638 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6639 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6640 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6641 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6642 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6643 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6644 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6645 if ((crop_box.left != (mng_info->image_box.left
6646 +mng_info->x_off[object_id])) ||
6647 (crop_box.right != (mng_info->image_box.right
6648 +mng_info->x_off[object_id])) ||
6649 (crop_box.top != (mng_info->image_box.top
6650 +mng_info->y_off[object_id])) ||
6651 (crop_box.bottom != (mng_info->image_box.bottom
6652 +mng_info->y_off[object_id])))
6654 if (logging != MagickFalse)
6655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6656 " Crop the PNG image");
6658 if ((crop_box.left < crop_box.right) &&
6659 (crop_box.top < crop_box.bottom))
6668 Crop_info is with respect to the upper left corner of
6671 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6672 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6673 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6674 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6675 image->page.width=image->columns;
6676 image->page.height=image->rows;
6679 im=CropImage(image,&crop_info,exception);
6681 if (im != (Image *) NULL)
6683 image->columns=im->columns;
6684 image->rows=im->rows;
6685 im=DestroyImage(im);
6686 image->page.width=image->columns;
6687 image->page.height=image->rows;
6688 image->page.x=crop_box.left;
6689 image->page.y=crop_box.top;
6696 No pixels in crop area. The MNG spec still requires
6697 a layer, though, so make a single transparent pixel in
6698 the top left corner.
6703 (void) SetImageBackgroundColor(image,exception);
6704 image->page.width=1;
6705 image->page.height=1;
6710 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6711 image=mng_info->image;
6715 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6716 /* PNG does not handle depths greater than 16 so reduce it even
6719 if (image->depth > 16)
6723 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6724 if (image->depth > 8)
6726 /* To do: fill low byte properly */
6730 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6734 if (image_info->number_scenes != 0)
6736 if (mng_info->scenes_found >
6737 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6741 if (logging != MagickFalse)
6742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6743 " Finished reading image datastream.");
6745 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6747 (void) CloseBlob(image);
6749 if (logging != MagickFalse)
6750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6751 " Finished reading all image datastreams.");
6753 #if defined(MNG_INSERT_LAYERS)
6754 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6755 (mng_info->mng_height))
6758 Insert a background layer if nothing else was found.
6760 if (logging != MagickFalse)
6761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6762 " No images found. Inserting a background layer.");
6764 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6767 Allocate next image structure.
6769 AcquireNextImage(image_info,image,exception);
6770 if (GetNextImageInList(image) == (Image *) NULL)
6772 image=DestroyImageList(image);
6773 MngInfoFreeStruct(mng_info,&have_mng_structure);
6775 if (logging != MagickFalse)
6776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6777 " Allocation failed, returning NULL.");
6779 return((Image *) NULL);
6781 image=SyncNextImageInList(image);
6783 image->columns=mng_info->mng_width;
6784 image->rows=mng_info->mng_height;
6785 image->page.width=mng_info->mng_width;
6786 image->page.height=mng_info->mng_height;
6789 image->background_color=mng_background_color;
6790 image->matte=MagickFalse;
6792 if (image_info->ping == MagickFalse)
6793 (void) SetImageBackgroundColor(image,exception);
6795 mng_info->image_found++;
6798 image->iterations=mng_iterations;
6800 if (mng_iterations == 1)
6801 image->start_loop=MagickTrue;
6803 while (GetPreviousImageInList(image) != (Image *) NULL)
6806 if (image_count > 10*mng_info->image_found)
6808 if (logging != MagickFalse)
6809 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6811 (void) ThrowMagickException(exception,GetMagickModule(),
6812 CoderError,"Linked list is corrupted, beginning of list not found",
6813 "`%s'",image_info->filename);
6815 return((Image *) NULL);
6818 image=GetPreviousImageInList(image);
6820 if (GetNextImageInList(image) == (Image *) NULL)
6822 if (logging != MagickFalse)
6823 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6825 (void) ThrowMagickException(exception,GetMagickModule(),
6826 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6827 image_info->filename);
6831 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6832 GetNextImageInList(image) ==
6835 if (logging != MagickFalse)
6836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6837 " First image null");
6839 (void) ThrowMagickException(exception,GetMagickModule(),
6840 CoderError,"image->next for first image is NULL but shouldn't be.",
6841 "`%s'",image_info->filename);
6844 if (mng_info->image_found == 0)
6846 if (logging != MagickFalse)
6847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6848 " No visible images found.");
6850 (void) ThrowMagickException(exception,GetMagickModule(),
6851 CoderError,"No visible images in file","`%s'",image_info->filename);
6853 if (image != (Image *) NULL)
6854 image=DestroyImageList(image);
6856 MngInfoFreeStruct(mng_info,&have_mng_structure);
6857 return((Image *) NULL);
6860 if (mng_info->ticks_per_second)
6861 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6862 final_delay/mng_info->ticks_per_second;
6865 image->start_loop=MagickTrue;
6867 /* Find final nonzero image delay */
6868 final_image_delay=0;
6870 while (GetNextImageInList(image) != (Image *) NULL)
6873 final_image_delay=image->delay;
6875 image=GetNextImageInList(image);
6878 if (final_delay < final_image_delay)
6879 final_delay=final_image_delay;
6881 image->delay=final_delay;
6883 if (logging != MagickFalse)
6884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6885 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6886 (double) final_delay);
6888 if (logging != MagickFalse)
6894 image=GetFirstImageInList(image);
6896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6897 " Before coalesce:");
6899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6900 " scene 0 delay=%.20g",(double) image->delay);
6902 while (GetNextImageInList(image) != (Image *) NULL)
6904 image=GetNextImageInList(image);
6905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6906 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6910 image=GetFirstImageInList(image);
6911 #ifdef MNG_COALESCE_LAYERS
6921 if (logging != MagickFalse)
6922 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6925 next_image=CoalesceImages(image,exception);
6927 if (next_image == (Image *) NULL)
6928 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6930 image=DestroyImageList(image);
6933 for (next=image; next != (Image *) NULL; next=next_image)
6935 next->page.width=mng_info->mng_width;
6936 next->page.height=mng_info->mng_height;
6939 next->scene=scene++;
6940 next_image=GetNextImageInList(next);
6942 if (next_image == (Image *) NULL)
6945 if (next->delay == 0)
6948 next_image->previous=GetPreviousImageInList(next);
6949 if (GetPreviousImageInList(next) == (Image *) NULL)
6952 next->previous->next=next_image;
6953 next=DestroyImage(next);
6959 while (GetNextImageInList(image) != (Image *) NULL)
6960 image=GetNextImageInList(image);
6962 image->dispose=BackgroundDispose;
6964 if (logging != MagickFalse)
6970 image=GetFirstImageInList(image);
6972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6973 " After coalesce:");
6975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6976 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6977 (double) image->dispose);
6979 while (GetNextImageInList(image) != (Image *) NULL)
6981 image=GetNextImageInList(image);
6983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6984 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6985 (double) image->delay,(double) image->dispose);
6989 image=GetFirstImageInList(image);
6990 MngInfoFreeStruct(mng_info,&have_mng_structure);
6991 have_mng_structure=MagickFalse;
6993 if (logging != MagickFalse)
6994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6996 return(GetFirstImageInList(image));
6998 #else /* PNG_LIBPNG_VER > 10011 */
6999 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7001 printf("Your PNG library is too old: You have libpng-%s\n",
7002 PNG_LIBPNG_VER_STRING);
7004 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7005 "PNG library is too old","`%s'",image_info->filename);
7007 return(Image *) NULL;
7010 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7012 return(ReadPNGImage(image_info,exception));
7014 #endif /* PNG_LIBPNG_VER > 10011 */
7018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7022 % R e g i s t e r P N G I m a g e %
7026 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7028 % RegisterPNGImage() adds properties for the PNG image format to
7029 % the list of supported formats. The properties include the image format
7030 % tag, a method to read and/or write the format, whether the format
7031 % supports the saving of more than one frame to the same file or blob,
7032 % whether the format supports native in-memory I/O, and a brief
7033 % description of the format.
7035 % The format of the RegisterPNGImage method is:
7037 % size_t RegisterPNGImage(void)
7040 ModuleExport size_t RegisterPNGImage(void)
7043 version[MaxTextExtent];
7051 "See http://www.libpng.org/ for details about the PNG format."
7056 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7062 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7068 #if defined(PNG_LIBPNG_VER_STRING)
7069 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7070 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7072 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7074 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7075 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7080 entry=SetMagickInfo("MNG");
7081 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7083 #if defined(MAGICKCORE_PNG_DELEGATE)
7084 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7085 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7088 entry->magick=(IsImageFormatHandler *) IsMNG;
7089 entry->description=ConstantString("Multiple-image Network Graphics");
7091 if (*version != '\0')
7092 entry->version=ConstantString(version);
7094 entry->module=ConstantString("PNG");
7095 entry->note=ConstantString(MNGNote);
7096 (void) RegisterMagickInfo(entry);
7098 entry=SetMagickInfo("PNG");
7100 #if defined(MAGICKCORE_PNG_DELEGATE)
7101 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7102 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7105 entry->magick=(IsImageFormatHandler *) IsPNG;
7106 entry->adjoin=MagickFalse;
7107 entry->description=ConstantString("Portable Network Graphics");
7108 entry->module=ConstantString("PNG");
7110 if (*version != '\0')
7111 entry->version=ConstantString(version);
7113 entry->note=ConstantString(PNGNote);
7114 (void) RegisterMagickInfo(entry);
7116 entry=SetMagickInfo("PNG8");
7118 #if defined(MAGICKCORE_PNG_DELEGATE)
7119 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7120 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7123 entry->magick=(IsImageFormatHandler *) IsPNG;
7124 entry->adjoin=MagickFalse;
7125 entry->description=ConstantString(
7126 "8-bit indexed with optional binary transparency");
7127 entry->module=ConstantString("PNG");
7128 (void) RegisterMagickInfo(entry);
7130 entry=SetMagickInfo("PNG24");
7133 #if defined(ZLIB_VERSION)
7134 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7135 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7137 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7139 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7140 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7144 if (*version != '\0')
7145 entry->version=ConstantString(version);
7147 #if defined(MAGICKCORE_PNG_DELEGATE)
7148 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7149 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7152 entry->magick=(IsImageFormatHandler *) IsPNG;
7153 entry->adjoin=MagickFalse;
7154 entry->description=ConstantString("opaque 24-bit RGB");
7155 entry->module=ConstantString("PNG");
7156 (void) RegisterMagickInfo(entry);
7158 entry=SetMagickInfo("PNG32");
7160 #if defined(MAGICKCORE_PNG_DELEGATE)
7161 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7162 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7165 entry->magick=(IsImageFormatHandler *) IsPNG;
7166 entry->adjoin=MagickFalse;
7167 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7168 entry->module=ConstantString("PNG");
7169 (void) RegisterMagickInfo(entry);
7171 entry=SetMagickInfo("JNG");
7173 #if defined(JNG_SUPPORTED)
7174 #if defined(MAGICKCORE_PNG_DELEGATE)
7175 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7176 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7180 entry->magick=(IsImageFormatHandler *) IsJNG;
7181 entry->adjoin=MagickFalse;
7182 entry->description=ConstantString("JPEG Network Graphics");
7183 entry->module=ConstantString("PNG");
7184 entry->note=ConstantString(JNGNote);
7185 (void) RegisterMagickInfo(entry);
7187 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7188 ping_semaphore=AllocateSemaphoreInfo();
7191 return(MagickImageCoderSignature);
7195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7199 % U n r e g i s t e r P N G I m a g e %
7203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7205 % UnregisterPNGImage() removes format registrations made by the
7206 % PNG module from the list of supported formats.
7208 % The format of the UnregisterPNGImage method is:
7210 % UnregisterPNGImage(void)
7213 ModuleExport void UnregisterPNGImage(void)
7215 (void) UnregisterMagickInfo("MNG");
7216 (void) UnregisterMagickInfo("PNG");
7217 (void) UnregisterMagickInfo("PNG8");
7218 (void) UnregisterMagickInfo("PNG24");
7219 (void) UnregisterMagickInfo("PNG32");
7220 (void) UnregisterMagickInfo("JNG");
7222 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7223 if (ping_semaphore != (SemaphoreInfo *) NULL)
7224 DestroySemaphoreInfo(&ping_semaphore);
7228 #if defined(MAGICKCORE_PNG_DELEGATE)
7229 #if PNG_LIBPNG_VER > 10011
7231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7235 % W r i t e M N G I m a g e %
7239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7241 % WriteMNGImage() writes an image in the Portable Network Graphics
7242 % Group's "Multiple-image Network Graphics" encoded image format.
7244 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7246 % The format of the WriteMNGImage method is:
7248 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7249 % Image *image,ExceptionInfo *exception)
7251 % A description of each parameter follows.
7253 % o image_info: the image info.
7255 % o image: The image.
7257 % o exception: return any errors or warnings in this structure.
7259 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7260 % "To do" under ReadPNGImage):
7262 % Preserve all unknown and not-yet-handled known chunks found in input
7263 % PNG file and copy them into output PNG files according to the PNG
7266 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7268 % Improve selection of color type (use indexed-colour or indexed-colour
7269 % with tRNS when 256 or fewer unique RGBA values are present).
7271 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7272 % This will be complicated if we limit ourselves to generating MNG-LC
7273 % files. For now we ignore disposal method 3 and simply overlay the next
7276 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7277 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7278 % [mostly done 15 June 1999 but still need to take care of tRNS]
7280 % Check for identical sRGB and replace with a global sRGB (and remove
7281 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7282 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7283 % local gAMA/cHRM with local sRGB if appropriate).
7285 % Check for identical sBIT chunks and write global ones.
7287 % Provide option to skip writing the signature tEXt chunks.
7289 % Use signatures to detect identical objects and reuse the first
7290 % instance of such objects instead of writing duplicate objects.
7292 % Use a smaller-than-32k value of compression window size when
7295 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7296 % ancillary text chunks and save profiles.
7298 % Provide an option to force LC files (to ensure exact framing rate)
7301 % Provide an option to force VLC files instead of LC, even when offsets
7302 % are present. This will involve expanding the embedded images with a
7303 % transparent region at the top and/or left.
7307 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7308 png_info *ping_info, unsigned char *profile_type, unsigned char
7309 *profile_description, unsigned char *profile_data, png_uint_32 length)
7328 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7330 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7333 if (image_info->verbose)
7335 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7336 (char *) profile_type, (double) length);
7339 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7340 description_length=(png_uint_32) strlen((const char *) profile_description);
7341 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7342 + description_length);
7343 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7344 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7345 text[0].key[0]='\0';
7346 (void) ConcatenateMagickString(text[0].key,
7347 "Raw profile type ",MaxTextExtent);
7348 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7352 (void) CopyMagickString(dp,(const char *) profile_description,
7354 dp+=description_length;
7356 (void) FormatLocaleString(dp,allocated_length-
7357 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7360 for (i=0; i < (ssize_t) length; i++)
7364 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7365 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7370 text[0].text_length=(png_size_t) (dp-text[0].text);
7371 text[0].compression=image_info->compression == NoCompression ||
7372 (image_info->compression == UndefinedCompression &&
7373 text[0].text_length < 128) ? -1 : 0;
7375 if (text[0].text_length <= allocated_length)
7376 png_set_text(ping,ping_info,text,1);
7378 png_free(ping,text[0].text);
7379 png_free(ping,text[0].key);
7380 png_free(ping,text);
7383 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7384 const char *string, MagickBooleanType logging)
7397 ResetImageProfileIterator(image);
7399 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7401 profile=GetImageProfile(image,name);
7403 if (profile != (const StringInfo *) NULL)
7408 if (LocaleNCompare(name,string,11) == 0)
7410 if (logging != MagickFalse)
7411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7412 " Found %s profile",name);
7414 ping_profile=CloneStringInfo(profile);
7415 data=GetStringInfoDatum(ping_profile),
7416 length=(png_uint_32) GetStringInfoLength(ping_profile);
7421 (void) WriteBlobMSBULong(image,length-5); /* data length */
7422 (void) WriteBlob(image,length-1,data+1);
7423 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7424 ping_profile=DestroyStringInfo(ping_profile);
7428 name=GetNextImageProfile(image);
7435 /* Write one PNG image */
7436 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7437 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7461 ping_trans_alpha[256];
7489 ping_have_cheap_transparency,
7500 /* ping_exclude_EXIF, */
7503 /* ping_exclude_iTXt, */
7508 /* ping_exclude_tRNS, */
7510 ping_exclude_zCCP, /* hex-encoded iCCP */
7513 ping_preserve_colormap,
7514 ping_need_colortype_warning,
7538 ping_interlace_method,
7539 ping_compression_method,
7556 number_semitransparent,
7558 ping_pHYs_unit_type;
7561 ping_pHYs_x_resolution,
7562 ping_pHYs_y_resolution;
7564 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7565 " Enter WriteOnePNGImage()");
7567 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7568 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7569 if (image_info == (ImageInfo *) NULL)
7570 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7572 /* Initialize some stuff */
7575 ping_interlace_method=0,
7576 ping_compression_method=0,
7577 ping_filter_method=0,
7580 ping_background.red = 0;
7581 ping_background.green = 0;
7582 ping_background.blue = 0;
7583 ping_background.gray = 0;
7584 ping_background.index = 0;
7586 ping_trans_color.red=0;
7587 ping_trans_color.green=0;
7588 ping_trans_color.blue=0;
7589 ping_trans_color.gray=0;
7591 ping_pHYs_unit_type = 0;
7592 ping_pHYs_x_resolution = 0;
7593 ping_pHYs_y_resolution = 0;
7595 ping_have_blob=MagickFalse;
7596 ping_have_color=MagickTrue;
7597 ping_have_non_bw=MagickTrue;
7598 ping_have_PLTE=MagickFalse;
7599 ping_have_bKGD=MagickFalse;
7600 ping_have_pHYs=MagickFalse;
7601 ping_have_tRNS=MagickFalse;
7603 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7604 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7605 ping_exclude_date=mng_info->ping_exclude_date;
7606 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7607 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7608 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7609 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7610 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7611 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7612 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7613 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7614 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7615 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7616 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7617 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7619 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7620 ping_need_colortype_warning = MagickFalse;
7622 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7623 * i.e., eliminate the ICC profile and set image->rendering_intent.
7624 * Note that this will not involve any changes to the actual pixels
7625 * but merely passes information to applications that read the resulting
7628 if (ping_exclude_sRGB == MagickFalse)
7636 ResetImageProfileIterator(image);
7637 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7639 profile=GetImageProfile(image,name);
7641 if (profile != (StringInfo *) NULL)
7643 if ((LocaleCompare(name,"ICC") == 0) ||
7644 (LocaleCompare(name,"ICM") == 0))
7649 /* 0: not a known sRGB profile
7650 * 1: HP-Microsoft sRGB v2
7651 * 2: ICC sRGB v4 perceptual
7652 * 3: ICC sRGB v2 perceptual no black-compensation
7655 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7656 check_len[4] = {0, 3144, 60960, 3052};
7665 length=(png_uint_32) GetStringInfoLength(profile);
7667 for (icheck=3; icheck > 0; icheck--)
7669 if (length == check_len[icheck])
7671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7672 " Got a %lu-byte ICC profile (potentially sRGB)",
7673 (unsigned long) length);
7675 data=GetStringInfoDatum(profile);
7676 profile_crc=crc32(0,data,length);
7678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7679 " with crc=%8x",(unsigned int) profile_crc);
7681 if (profile_crc == check_crc[icheck])
7683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7685 if (image->rendering_intent==UndefinedIntent)
7686 image->rendering_intent=PerceptualIntent;
7692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7693 " Got a %lu-byte ICC profile",
7694 (unsigned long) length);
7697 name=GetNextImageProfile(image);
7702 number_semitransparent = 0;
7703 number_transparent = 0;
7705 if (logging != MagickFalse)
7707 if (image->storage_class == UndefinedClass)
7708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7709 " storage_class=UndefinedClass");
7710 if (image->storage_class == DirectClass)
7711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 " storage_class=DirectClass");
7713 if (image->storage_class == PseudoClass)
7714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7715 " storage_class=PseudoClass");
7718 if (image->storage_class == PseudoClass &&
7719 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7720 (mng_info->write_png_colortype != 0 &&
7721 mng_info->write_png_colortype != 4)))
7723 (void) SyncImage(image,exception);
7724 image->storage_class = DirectClass;
7727 if (ping_preserve_colormap == MagickFalse)
7729 if (image->storage_class != PseudoClass && image->colormap != NULL)
7731 /* Free the bogus colormap; it can cause trouble later */
7732 if (logging != MagickFalse)
7733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7734 " Freeing bogus colormap");
7735 (void) RelinquishMagickMemory(image->colormap);
7736 image->colormap=NULL;
7740 if (IssRGBColorspace(image->colorspace) == MagickFalse)
7741 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7744 Sometimes we get PseudoClass images whose RGB values don't match
7745 the colors in the colormap. This code syncs the RGB values.
7747 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7748 (void) SyncImage(image,exception);
7750 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7751 if (image->depth > 8)
7753 if (logging != MagickFalse)
7754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7755 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7761 /* Respect the -depth option */
7762 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7767 if (image->depth > 8)
7769 #if MAGICKCORE_QUANTUM_DEPTH > 16
7770 /* Scale to 16-bit */
7771 LBR16PacketRGBO(image->background_color);
7773 for (y=0; y < (ssize_t) image->rows; y++)
7775 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7777 if (r == (Quantum *) NULL)
7780 for (x=0; x < (ssize_t) image->columns; x++)
7783 r+=GetPixelChannels(image);
7786 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7790 if (image->storage_class == PseudoClass && image->colormap != NULL)
7792 for (i=0; i < (ssize_t) image->colors; i++)
7794 LBR16PacketRGBO(image->colormap[i]);
7797 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7800 else if (image->depth > 4)
7802 #if MAGICKCORE_QUANTUM_DEPTH > 8
7803 /* Scale to 8-bit */
7804 LBR08PacketRGBO(image->background_color);
7806 for (y=0; y < (ssize_t) image->rows; y++)
7808 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7810 if (r == (Quantum *) NULL)
7813 for (x=0; x < (ssize_t) image->columns; x++)
7816 r+=GetPixelChannels(image);
7819 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7823 if (image->storage_class == PseudoClass && image->colormap != NULL)
7825 for (i=0; i < (ssize_t) image->colors; i++)
7827 LBR08PacketRGBO(image->colormap[i]);
7830 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7833 if (image->depth > 2)
7835 /* Scale to 4-bit */
7836 LBR04PacketRGBO(image->background_color);
7838 for (y=0; y < (ssize_t) image->rows; y++)
7840 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7842 if (r == (Quantum *) NULL)
7845 for (x=0; x < (ssize_t) image->columns; x++)
7848 r+=GetPixelChannels(image);
7851 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7855 if (image->storage_class == PseudoClass && image->colormap != NULL)
7857 for (i=0; i < (ssize_t) image->colors; i++)
7859 LBR04PacketRGBO(image->colormap[i]);
7864 else if (image->depth > 1)
7866 /* Scale to 2-bit */
7867 LBR02PacketRGBO(image->background_color);
7869 for (y=0; y < (ssize_t) image->rows; y++)
7871 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7873 if (r == (Quantum *) NULL)
7876 for (x=0; x < (ssize_t) image->columns; x++)
7879 r+=GetPixelChannels(image);
7882 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7886 if (image->storage_class == PseudoClass && image->colormap != NULL)
7888 for (i=0; i < (ssize_t) image->colors; i++)
7890 LBR02PacketRGBO(image->colormap[i]);
7896 /* Scale to 1-bit */
7897 LBR01PacketRGBO(image->background_color);
7899 for (y=0; y < (ssize_t) image->rows; y++)
7901 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7903 if (r == (Quantum *) NULL)
7906 for (x=0; x < (ssize_t) image->columns; x++)
7909 r+=GetPixelChannels(image);
7912 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7916 if (image->storage_class == PseudoClass && image->colormap != NULL)
7918 for (i=0; i < (ssize_t) image->colors; i++)
7920 LBR01PacketRGBO(image->colormap[i]);
7926 /* To do: set to next higher multiple of 8 */
7927 if (image->depth < 8)
7930 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7931 /* PNG does not handle depths greater than 16 so reduce it even
7934 if (image->depth > 8)
7938 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7939 if (image->depth > 8)
7941 /* To do: fill low byte properly */
7945 if (image->depth == 16 && mng_info->write_png_depth != 16)
7946 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
7950 /* Normally we run this just once, but in the case of writing PNG8
7951 * we reduce the transparency to binary and run again, then if there
7952 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7953 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7954 * palette. Then (To do) we take care of a final reduction that is only
7955 * needed if there are still 256 colors present and one of them has both
7956 * transparent and opaque instances.
7959 tried_332 = MagickFalse;
7960 tried_333 = MagickFalse;
7961 tried_444 = MagickFalse;
7967 * Sometimes we get DirectClass images that have 256 colors or fewer.
7968 * This code will build a colormap.
7970 * Also, sometimes we get PseudoClass images with an out-of-date
7971 * colormap. This code will replace the colormap with a new one.
7972 * Sometimes we get PseudoClass images that have more than 256 colors.
7973 * This code will delete the colormap and change the image to
7976 * If image->matte is MagickFalse, we ignore the alpha channel
7977 * even though it sometimes contains left-over non-opaque values.
7979 * Also we gather some information (number of opaque, transparent,
7980 * and semitransparent pixels, and whether the image has any non-gray
7981 * pixels or only black-and-white pixels) that we might need later.
7983 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7984 * we need to check for bogus non-opaque values, at least.
7992 semitransparent[260],
7995 register const Quantum
8002 if (logging != MagickFalse)
8003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8004 " Enter BUILD_PALETTE:");
8006 if (logging != MagickFalse)
8008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8009 " image->columns=%.20g",(double) image->columns);
8010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8011 " image->rows=%.20g",(double) image->rows);
8012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8013 " image->matte=%.20g",(double) image->matte);
8014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8015 " image->depth=%.20g",(double) image->depth);
8017 if (image->storage_class == PseudoClass && image->colormap != NULL)
8019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8020 " Original colormap:");
8021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8022 " i (red,green,blue,alpha)");
8024 for (i=0; i < 256; i++)
8026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8027 " %d (%d,%d,%d,%d)",
8029 (int) image->colormap[i].red,
8030 (int) image->colormap[i].green,
8031 (int) image->colormap[i].blue,
8032 (int) image->colormap[i].alpha);
8035 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8040 " %d (%d,%d,%d,%d)",
8042 (int) image->colormap[i].red,
8043 (int) image->colormap[i].green,
8044 (int) image->colormap[i].blue,
8045 (int) image->colormap[i].alpha);
8050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051 " image->colors=%d",(int) image->colors);
8053 if (image->colors == 0)
8054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8055 " (zero means unknown)");
8057 if (ping_preserve_colormap == MagickFalse)
8058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8059 " Regenerate the colormap");
8064 number_semitransparent = 0;
8065 number_transparent = 0;
8067 for (y=0; y < (ssize_t) image->rows; y++)
8069 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8071 if (q == (Quantum *) NULL)
8074 for (x=0; x < (ssize_t) image->columns; x++)
8076 if (image->matte == MagickFalse ||
8077 GetPixelAlpha(image,q) == OpaqueAlpha)
8079 if (number_opaque < 259)
8081 if (number_opaque == 0)
8083 GetPixelInfoPixel(image, q, opaque);
8084 opaque[0].alpha=OpaqueAlpha;
8088 for (i=0; i< (ssize_t) number_opaque; i++)
8090 if (IsPixelEquivalent(image,q, opaque+i))
8094 if (i == (ssize_t) number_opaque && number_opaque < 259)
8097 GetPixelInfoPixel(image, q, opaque+i);
8098 opaque[i].alpha=OpaqueAlpha;
8102 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8104 if (number_transparent < 259)
8106 if (number_transparent == 0)
8108 GetPixelInfoPixel(image, q, transparent);
8109 ping_trans_color.red=(unsigned short)
8110 GetPixelRed(image,q);
8111 ping_trans_color.green=(unsigned short)
8112 GetPixelGreen(image,q);
8113 ping_trans_color.blue=(unsigned short)
8114 GetPixelBlue(image,q);
8115 ping_trans_color.gray=(unsigned short)
8116 GetPixelRed(image,q);
8117 number_transparent = 1;
8120 for (i=0; i< (ssize_t) number_transparent; i++)
8122 if (IsPixelEquivalent(image,q, transparent+i))
8126 if (i == (ssize_t) number_transparent &&
8127 number_transparent < 259)
8129 number_transparent++;
8130 GetPixelInfoPixel(image,q,transparent+i);
8136 if (number_semitransparent < 259)
8138 if (number_semitransparent == 0)
8140 GetPixelInfoPixel(image,q,semitransparent);
8141 number_semitransparent = 1;
8144 for (i=0; i< (ssize_t) number_semitransparent; i++)
8146 if (IsPixelEquivalent(image,q, semitransparent+i)
8147 && GetPixelAlpha(image,q) ==
8148 semitransparent[i].alpha)
8152 if (i == (ssize_t) number_semitransparent &&
8153 number_semitransparent < 259)
8155 number_semitransparent++;
8156 GetPixelInfoPixel(image, q, semitransparent+i);
8160 q+=GetPixelChannels(image);
8164 if (mng_info->write_png8 == MagickFalse &&
8165 ping_exclude_bKGD == MagickFalse)
8167 /* Add the background color to the palette, if it
8168 * isn't already there.
8170 if (logging != MagickFalse)
8172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8173 " Check colormap for background (%d,%d,%d)",
8174 (int) image->background_color.red,
8175 (int) image->background_color.green,
8176 (int) image->background_color.blue);
8178 for (i=0; i<number_opaque; i++)
8180 if (opaque[i].red == image->background_color.red &&
8181 opaque[i].green == image->background_color.green &&
8182 opaque[i].blue == image->background_color.blue)
8185 if (number_opaque < 259 && i == number_opaque)
8187 opaque[i] = image->background_color;
8188 ping_background.index = i;
8189 if (logging != MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " background_color index is %d",(int) i);
8196 else if (logging != MagickFalse)
8197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " No room in the colormap to add background color");
8201 image_colors=number_opaque+number_transparent+number_semitransparent;
8203 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8205 /* No room for the background color; remove it. */
8210 if (logging != MagickFalse)
8212 if (image_colors > 256)
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " image has more than 256 colors");
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " image has %d colors",image_colors);
8221 if (ping_preserve_colormap != MagickFalse)
8224 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8226 ping_have_color=MagickFalse;
8227 ping_have_non_bw=MagickFalse;
8229 if(image_colors > 256)
8231 for (y=0; y < (ssize_t) image->rows; y++)
8233 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8235 if (q == (Quantum *) NULL)
8239 for (x=0; x < (ssize_t) image->columns; x++)
8241 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8242 GetPixelRed(image,s) != GetPixelBlue(image,s))
8244 ping_have_color=MagickTrue;
8245 ping_have_non_bw=MagickTrue;
8248 s+=GetPixelChannels(image);
8251 if (ping_have_color != MagickFalse)
8254 /* Worst case is black-and-white; we are looking at every
8258 if (ping_have_non_bw == MagickFalse)
8261 for (x=0; x < (ssize_t) image->columns; x++)
8263 if (GetPixelRed(image,s) != 0 &&
8264 GetPixelRed(image,s) != QuantumRange)
8266 ping_have_non_bw=MagickTrue;
8269 s+=GetPixelChannels(image);
8276 if (image_colors < 257)
8282 * Initialize image colormap.
8285 if (logging != MagickFalse)
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " Sort the new colormap");
8289 /* Sort palette, transparent first */;
8293 for (i=0; i<number_transparent; i++)
8294 colormap[n++] = transparent[i];
8296 for (i=0; i<number_semitransparent; i++)
8297 colormap[n++] = semitransparent[i];
8299 for (i=0; i<number_opaque; i++)
8300 colormap[n++] = opaque[i];
8302 ping_background.index +=
8303 (number_transparent + number_semitransparent);
8305 /* image_colors < 257; search the colormap instead of the pixels
8306 * to get ping_have_color and ping_have_non_bw
8310 if (ping_have_color == MagickFalse)
8312 if (colormap[i].red != colormap[i].green ||
8313 colormap[i].red != colormap[i].blue)
8315 ping_have_color=MagickTrue;
8316 ping_have_non_bw=MagickTrue;
8321 if (ping_have_non_bw == MagickFalse)
8323 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8324 ping_have_non_bw=MagickTrue;
8328 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8329 (number_transparent == 0 && number_semitransparent == 0)) &&
8330 (((mng_info->write_png_colortype-1) ==
8331 PNG_COLOR_TYPE_PALETTE) ||
8332 (mng_info->write_png_colortype == 0)))
8334 if (logging != MagickFalse)
8336 if (n != (ssize_t) image_colors)
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " image_colors (%d) and n (%d) don't match",
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " AcquireImageColormap");
8345 image->colors = image_colors;
8347 if (AcquireImageColormap(image,image_colors,exception) ==
8349 ThrowWriterException(ResourceLimitError,
8350 "MemoryAllocationFailed");
8352 for (i=0; i< (ssize_t) image_colors; i++)
8353 image->colormap[i] = colormap[i];
8355 if (logging != MagickFalse)
8357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " image->colors=%d (%d)",
8359 (int) image->colors, image_colors);
8361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " Update the pixel indexes");
8365 /* Sync the pixel indices with the new colormap */
8367 for (y=0; y < (ssize_t) image->rows; y++)
8369 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8371 if (q == (Quantum *) NULL)
8374 for (x=0; x < (ssize_t) image->columns; x++)
8376 for (i=0; i< (ssize_t) image_colors; i++)
8378 if ((image->matte == MagickFalse ||
8379 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8380 image->colormap[i].red == GetPixelRed(image,q) &&
8381 image->colormap[i].green == GetPixelGreen(image,q) &&
8382 image->colormap[i].blue == GetPixelBlue(image,q))
8384 SetPixelIndex(image,i,q);
8388 q+=GetPixelChannels(image);
8391 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8397 if (logging != MagickFalse)
8399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8400 " image->colors=%d", (int) image->colors);
8402 if (image->colormap != NULL)
8404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8405 " i (red,green,blue,alpha)");
8407 for (i=0; i < (ssize_t) image->colors; i++)
8409 if (i < 300 || i >= (ssize_t) image->colors - 10)
8411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8412 " %d (%d,%d,%d,%d)",
8414 (int) image->colormap[i].red,
8415 (int) image->colormap[i].green,
8416 (int) image->colormap[i].blue,
8417 (int) image->colormap[i].alpha);
8422 if (number_transparent < 257)
8423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8424 " number_transparent = %d",
8425 number_transparent);
8428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8429 " number_transparent > 256");
8431 if (number_opaque < 257)
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " number_opaque = %d",
8437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " number_opaque > 256");
8440 if (number_semitransparent < 257)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " number_semitransparent = %d",
8443 number_semitransparent);
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " number_semitransparent > 256");
8449 if (ping_have_non_bw == MagickFalse)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " All pixels and the background are black or white");
8453 else if (ping_have_color == MagickFalse)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " All pixels and the background are gray");
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " At least one pixel or the background is non-gray");
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8462 " Exit BUILD_PALETTE:");
8465 if (mng_info->write_png8 == MagickFalse)
8468 /* Make any reductions necessary for the PNG8 format */
8469 if (image_colors <= 256 &&
8470 image_colors != 0 && image->colormap != NULL &&
8471 number_semitransparent == 0 &&
8472 number_transparent <= 1)
8475 /* PNG8 can't have semitransparent colors so we threshold the
8476 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8477 * transparent color so if more than one is transparent we merge
8478 * them into image->background_color.
8480 if (number_semitransparent != 0 || number_transparent > 1)
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " Thresholding the alpha channel to binary");
8485 for (y=0; y < (ssize_t) image->rows; y++)
8487 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8489 if (r == (Quantum *) NULL)
8492 for (x=0; x < (ssize_t) image->columns; x++)
8494 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8496 SetPixelInfoPixel(image,&image->background_color,r);
8497 SetPixelAlpha(image,TransparentAlpha,r);
8500 SetPixelAlpha(image,OpaqueAlpha,r);
8501 r+=GetPixelChannels(image);
8504 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8507 if (image_colors != 0 && image_colors <= 256 &&
8508 image->colormap != NULL)
8509 for (i=0; i<image_colors; i++)
8510 image->colormap[i].alpha =
8511 (image->colormap[i].alpha > TransparentAlpha/2 ?
8512 TransparentAlpha : OpaqueAlpha);
8517 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8518 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8519 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8522 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8524 if (logging != MagickFalse)
8525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526 " Quantizing the background color to 4-4-4");
8528 tried_444 = MagickTrue;
8530 LBR04PacketRGB(image->background_color);
8532 if (logging != MagickFalse)
8533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8534 " Quantizing the pixel colors to 4-4-4");
8536 if (image->colormap == NULL)
8538 for (y=0; y < (ssize_t) image->rows; y++)
8540 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8542 if (r == (Quantum *) NULL)
8545 for (x=0; x < (ssize_t) image->columns; x++)
8547 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8549 r+=GetPixelChannels(image);
8552 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8557 else /* Should not reach this; colormap already exists and
8560 if (logging != MagickFalse)
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Quantizing the colormap to 4-4-4");
8564 for (i=0; i<image_colors; i++)
8566 LBR04PacketRGB(image->colormap[i]);
8572 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8574 if (logging != MagickFalse)
8575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8576 " Quantizing the background color to 3-3-3");
8578 tried_333 = MagickTrue;
8580 LBR03PacketRGB(image->background_color);
8582 if (logging != MagickFalse)
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584 " Quantizing the pixel colors to 3-3-3-1");
8586 if (image->colormap == NULL)
8588 for (y=0; y < (ssize_t) image->rows; y++)
8590 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8592 if (r == (Quantum *) NULL)
8595 for (x=0; x < (ssize_t) image->columns; x++)
8597 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8599 r+=GetPixelChannels(image);
8602 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8607 else /* Should not reach this; colormap already exists and
8610 if (logging != MagickFalse)
8611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8612 " Quantizing the colormap to 3-3-3-1");
8613 for (i=0; i<image_colors; i++)
8615 LBR03PacketRGB(image->colormap[i]);
8621 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8623 if (logging != MagickFalse)
8624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625 " Quantizing the background color to 3-3-2");
8627 tried_332 = MagickTrue;
8629 /* Red and green were already done so we only quantize the blue
8633 LBR02PacketBlue(image->background_color);
8635 if (logging != MagickFalse)
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637 " Quantizing the pixel colors to 3-3-2-1");
8639 if (image->colormap == NULL)
8641 for (y=0; y < (ssize_t) image->rows; y++)
8643 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8645 if (r == (Quantum *) NULL)
8648 for (x=0; x < (ssize_t) image->columns; x++)
8650 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8652 r+=GetPixelChannels(image);
8655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8660 else /* Should not reach this; colormap already exists and
8663 if (logging != MagickFalse)
8664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8665 " Quantizing the colormap to 3-3-2-1");
8666 for (i=0; i<image_colors; i++)
8668 LBR02PacketBlue(image->colormap[i]);
8675 if (image_colors == 0 || image_colors > 256)
8677 /* Take care of special case with 256 colors + 1 transparent
8678 * color. We don't need to quantize to 2-3-2-1; we only need to
8679 * eliminate one color, so we'll merge the two darkest red
8680 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8682 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8683 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8684 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8686 image->background_color.red=ScaleCharToQuantum(0x24);
8689 if (image->colormap == NULL)
8691 for (y=0; y < (ssize_t) image->rows; y++)
8693 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8695 if (r == (Quantum *) NULL)
8698 for (x=0; x < (ssize_t) image->columns; x++)
8700 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8701 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8702 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8703 GetPixelAlpha(image,r) == OpaqueAlpha)
8705 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8707 r+=GetPixelChannels(image);
8710 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8718 for (i=0; i<image_colors; i++)
8720 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8721 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8722 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8724 image->colormap[i].red=ScaleCharToQuantum(0x24);
8730 /* END OF BUILD_PALETTE */
8732 /* If we are excluding the tRNS chunk and there is transparency,
8733 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8736 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8737 (number_transparent != 0 || number_semitransparent != 0))
8739 unsigned int colortype=mng_info->write_png_colortype;
8741 if (ping_have_color == MagickFalse)
8742 mng_info->write_png_colortype = 5;
8745 mng_info->write_png_colortype = 7;
8747 if (colortype != 0 &&
8748 mng_info->write_png_colortype != colortype)
8749 ping_need_colortype_warning=MagickTrue;
8753 /* See if cheap transparency is possible. It is only possible
8754 * when there is a single transparent color, no semitransparent
8755 * color, and no opaque color that has the same RGB components
8756 * as the transparent color. We only need this information if
8757 * we are writing a PNG with colortype 0 or 2, and we have not
8758 * excluded the tRNS chunk.
8760 if (number_transparent == 1 &&
8761 mng_info->write_png_colortype < 4)
8763 ping_have_cheap_transparency = MagickTrue;
8765 if (number_semitransparent != 0)
8766 ping_have_cheap_transparency = MagickFalse;
8768 else if (image_colors == 0 || image_colors > 256 ||
8769 image->colormap == NULL)
8771 register const Quantum
8774 for (y=0; y < (ssize_t) image->rows; y++)
8776 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8778 if (q == (Quantum *) NULL)
8781 for (x=0; x < (ssize_t) image->columns; x++)
8783 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8784 (unsigned short) GetPixelRed(image,q) ==
8785 ping_trans_color.red &&
8786 (unsigned short) GetPixelGreen(image,q) ==
8787 ping_trans_color.green &&
8788 (unsigned short) GetPixelBlue(image,q) ==
8789 ping_trans_color.blue)
8791 ping_have_cheap_transparency = MagickFalse;
8795 q+=GetPixelChannels(image);
8798 if (ping_have_cheap_transparency == MagickFalse)
8804 /* Assuming that image->colormap[0] is the one transparent color
8805 * and that all others are opaque.
8807 if (image_colors > 1)
8808 for (i=1; i<image_colors; i++)
8809 if (image->colormap[i].red == image->colormap[0].red &&
8810 image->colormap[i].green == image->colormap[0].green &&
8811 image->colormap[i].blue == image->colormap[0].blue)
8813 ping_have_cheap_transparency = MagickFalse;
8818 if (logging != MagickFalse)
8820 if (ping_have_cheap_transparency == MagickFalse)
8821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8822 " Cheap transparency is not possible.");
8825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8826 " Cheap transparency is possible.");
8830 ping_have_cheap_transparency = MagickFalse;
8832 image_depth=image->depth;
8834 quantum_info = (QuantumInfo *) NULL;
8836 image_colors=(int) image->colors;
8837 image_matte=image->matte;
8839 mng_info->IsPalette=image->storage_class == PseudoClass &&
8840 image_colors <= 256 && image->colormap != NULL;
8842 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8843 (image->colors == 0 || image->colormap == NULL))
8845 image_info=DestroyImageInfo(image_info);
8846 image=DestroyImage(image);
8847 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8848 "Cannot write PNG8 or color-type 3; colormap is NULL",
8849 "`%s'",IMimage->filename);
8850 return(MagickFalse);
8854 Allocate the PNG structures
8856 #ifdef PNG_USER_MEM_SUPPORTED
8857 error_info.image=image;
8858 error_info.exception=exception;
8859 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
8860 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8861 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8864 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
8865 MagickPNGErrorHandler,MagickPNGWarningHandler);
8868 if (ping == (png_struct *) NULL)
8869 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8871 ping_info=png_create_info_struct(ping);
8873 if (ping_info == (png_info *) NULL)
8875 png_destroy_write_struct(&ping,(png_info **) NULL);
8876 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8879 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8880 ping_pixels=(unsigned char *) NULL;
8882 if (setjmp(png_jmpbuf(ping)))
8888 if (image_info->verbose)
8889 (void) printf("PNG write has failed.\n");
8891 png_destroy_write_struct(&ping,&ping_info);
8892 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
8893 UnlockSemaphoreInfo(ping_semaphore);
8896 if (ping_pixels != (unsigned char *) NULL)
8897 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8899 if (quantum_info != (QuantumInfo *) NULL)
8900 quantum_info=DestroyQuantumInfo(quantum_info);
8902 if (ping_have_blob != MagickFalse)
8903 (void) CloseBlob(image);
8904 image_info=DestroyImageInfo(image_info);
8905 image=DestroyImage(image);
8906 return(MagickFalse);
8909 /* { For navigation to end of SETJMP-protected block. Within this
8910 * block, use png_error() instead of Throwing an Exception, to ensure
8911 * that libpng is able to clean up, and that the semaphore is unlocked.
8914 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
8915 LockSemaphoreInfo(ping_semaphore);
8919 Prepare PNG for writing.
8921 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8922 if (mng_info->write_mng)
8923 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8926 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8927 if (mng_info->write_mng)
8928 png_permit_empty_plte(ping,MagickTrue);
8935 ping_width=(png_uint_32) image->columns;
8936 ping_height=(png_uint_32) image->rows;
8938 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8941 if (mng_info->write_png_depth != 0)
8942 image_depth=mng_info->write_png_depth;
8944 /* Adjust requested depth to next higher valid depth if necessary */
8945 if (image_depth > 8)
8948 if ((image_depth > 4) && (image_depth < 8))
8951 if (image_depth == 3)
8954 if (logging != MagickFalse)
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8957 " width=%.20g",(double) ping_width);
8958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959 " height=%.20g",(double) ping_height);
8960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8961 " image_matte=%.20g",(double) image->matte);
8962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963 " image->depth=%.20g",(double) image->depth);
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8965 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8968 save_image_depth=image_depth;
8969 ping_bit_depth=(png_byte) save_image_depth;
8972 #if defined(PNG_pHYs_SUPPORTED)
8973 if (ping_exclude_pHYs == MagickFalse)
8975 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
8976 (!mng_info->write_mng || !mng_info->equal_physs))
8978 if (logging != MagickFalse)
8979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8980 " Setting up pHYs chunk");
8982 if (image->units == PixelsPerInchResolution)
8984 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8985 ping_pHYs_x_resolution=
8986 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
8987 ping_pHYs_y_resolution=
8988 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
8991 else if (image->units == PixelsPerCentimeterResolution)
8993 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8994 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
8995 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9000 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9001 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9002 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9005 if (logging != MagickFalse)
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9008 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9009 (int) ping_pHYs_unit_type);
9010 ping_have_pHYs = MagickTrue;
9015 if (ping_exclude_bKGD == MagickFalse)
9017 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9023 if (ping_bit_depth == 8)
9026 if (ping_bit_depth == 4)
9029 if (ping_bit_depth == 2)
9032 if (ping_bit_depth == 1)
9035 ping_background.red=(png_uint_16)
9036 (ScaleQuantumToShort(image->background_color.red) & mask);
9038 ping_background.green=(png_uint_16)
9039 (ScaleQuantumToShort(image->background_color.green) & mask);
9041 ping_background.blue=(png_uint_16)
9042 (ScaleQuantumToShort(image->background_color.blue) & mask);
9044 ping_background.gray=(png_uint_16) ping_background.green;
9047 if (logging != MagickFalse)
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " Setting up bKGD chunk (1)");
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " background_color index is %d",
9053 (int) ping_background.index);
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " ping_bit_depth=%d",ping_bit_depth);
9059 ping_have_bKGD = MagickTrue;
9063 Select the color type.
9068 if (mng_info->IsPalette && mng_info->write_png8)
9071 /* To do: make this a function cause it's used twice, except
9072 for reducing the sample depth from 8. */
9074 number_colors=image_colors;
9076 ping_have_tRNS=MagickFalse;
9081 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9083 if (logging != MagickFalse)
9084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9085 " Setting up PLTE chunk with %d colors (%d)",
9086 number_colors, image_colors);
9088 for (i=0; i < (ssize_t) number_colors; i++)
9090 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9091 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9092 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9093 if (logging != MagickFalse)
9094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9095 #if MAGICKCORE_QUANTUM_DEPTH == 8
9096 " %3ld (%3d,%3d,%3d)",
9098 " %5ld (%5d,%5d,%5d)",
9100 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9104 ping_have_PLTE=MagickTrue;
9105 image_depth=ping_bit_depth;
9108 if (matte != MagickFalse)
9111 Identify which colormap entry is transparent.
9113 assert(number_colors <= 256);
9114 assert(image->colormap != NULL);
9116 for (i=0; i < (ssize_t) number_transparent; i++)
9117 ping_trans_alpha[i]=0;
9120 ping_num_trans=(unsigned short) (number_transparent +
9121 number_semitransparent);
9123 if (ping_num_trans == 0)
9124 ping_have_tRNS=MagickFalse;
9127 ping_have_tRNS=MagickTrue;
9130 if (ping_exclude_bKGD == MagickFalse)
9133 * Identify which colormap entry is the background color.
9136 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9137 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9140 ping_background.index=(png_byte) i;
9142 if (logging != MagickFalse)
9144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " background_color index is %d",
9146 (int) ping_background.index);
9149 } /* end of write_png8 */
9151 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9153 image_matte=MagickFalse;
9154 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9157 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9159 image_matte=MagickTrue;
9160 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9163 else /* mng_info->write_pngNN not specified */
9165 image_depth=ping_bit_depth;
9167 if (mng_info->write_png_colortype != 0)
9169 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9171 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9172 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9173 image_matte=MagickTrue;
9176 image_matte=MagickFalse;
9178 if (logging != MagickFalse)
9179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9180 " PNG colortype %d was specified:",(int) ping_color_type);
9183 else /* write_png_colortype not specified */
9185 if (logging != MagickFalse)
9186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9187 " Selecting PNG colortype:");
9189 ping_color_type=(png_byte) ((matte != MagickFalse)?
9190 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9192 if (image_info->type == TrueColorType)
9194 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9195 image_matte=MagickFalse;
9198 if (image_info->type == TrueColorMatteType)
9200 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9201 image_matte=MagickTrue;
9204 if (image_info->type == PaletteType ||
9205 image_info->type == PaletteMatteType)
9206 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9208 if (mng_info->write_png_colortype == 0 &&
9209 (image_info->type == UndefinedType ||
9210 image_info->type == OptimizeType))
9212 if (ping_have_color == MagickFalse)
9214 if (image_matte == MagickFalse)
9216 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9217 image_matte=MagickFalse;
9222 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9223 image_matte=MagickTrue;
9228 if (image_matte == MagickFalse)
9230 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9231 image_matte=MagickFalse;
9236 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9237 image_matte=MagickTrue;
9244 if (logging != MagickFalse)
9245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9246 " Selected PNG colortype=%d",ping_color_type);
9248 if (ping_bit_depth < 8)
9250 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9251 ping_color_type == PNG_COLOR_TYPE_RGB ||
9252 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9256 old_bit_depth=ping_bit_depth;
9258 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9260 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9264 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9269 if (image->colors == 0)
9272 png_error(ping,"image has 0 colors");
9275 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9276 ping_bit_depth <<= 1;
9279 if (logging != MagickFalse)
9281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9282 " Number of colors: %.20g",(double) image_colors);
9284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9285 " Tentative PNG bit depth: %d",ping_bit_depth);
9288 if (ping_bit_depth < (int) mng_info->write_png_depth)
9289 ping_bit_depth = mng_info->write_png_depth;
9292 image_depth=ping_bit_depth;
9294 if (logging != MagickFalse)
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " Tentative PNG color type: %.20g",(double) ping_color_type);
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " image_info->type: %.20g",(double) image_info->type);
9302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9303 " image_depth: %.20g",(double) image_depth);
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307 " image->depth: %.20g",(double) image->depth);
9309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9310 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9313 if (matte != MagickFalse)
9315 if (mng_info->IsPalette)
9317 if (mng_info->write_png_colortype == 0)
9319 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9321 if (ping_have_color != MagickFalse)
9322 ping_color_type=PNG_COLOR_TYPE_RGBA;
9326 * Determine if there is any transparent color.
9328 if (number_transparent + number_semitransparent == 0)
9331 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9334 image_matte=MagickFalse;
9336 if (mng_info->write_png_colortype == 0)
9337 ping_color_type&=0x03;
9347 if (ping_bit_depth == 8)
9350 if (ping_bit_depth == 4)
9353 if (ping_bit_depth == 2)
9356 if (ping_bit_depth == 1)
9359 ping_trans_color.red=(png_uint_16)
9360 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9362 ping_trans_color.green=(png_uint_16)
9363 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9365 ping_trans_color.blue=(png_uint_16)
9366 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9368 ping_trans_color.gray=(png_uint_16)
9369 (ScaleQuantumToShort(GetPixelInfoIntensity(
9370 image->colormap)) & mask);
9372 ping_trans_color.index=(png_byte) 0;
9374 ping_have_tRNS=MagickTrue;
9377 if (ping_have_tRNS != MagickFalse)
9380 * Determine if there is one and only one transparent color
9381 * and if so if it is fully transparent.
9383 if (ping_have_cheap_transparency == MagickFalse)
9384 ping_have_tRNS=MagickFalse;
9387 if (ping_have_tRNS != MagickFalse)
9389 if (mng_info->write_png_colortype == 0)
9390 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9392 if (image_depth == 8)
9394 ping_trans_color.red&=0xff;
9395 ping_trans_color.green&=0xff;
9396 ping_trans_color.blue&=0xff;
9397 ping_trans_color.gray&=0xff;
9403 if (image_depth == 8)
9405 ping_trans_color.red&=0xff;
9406 ping_trans_color.green&=0xff;
9407 ping_trans_color.blue&=0xff;
9408 ping_trans_color.gray&=0xff;
9415 if (ping_have_tRNS != MagickFalse)
9416 image_matte=MagickFalse;
9418 if ((mng_info->IsPalette) &&
9419 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9420 ping_have_color == MagickFalse &&
9421 (image_matte == MagickFalse || image_depth >= 8))
9425 if (image_matte != MagickFalse)
9426 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9428 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9430 ping_color_type=PNG_COLOR_TYPE_GRAY;
9432 if (save_image_depth == 16 && image_depth == 8)
9434 if (logging != MagickFalse)
9436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9437 " Scaling ping_trans_color (0)");
9439 ping_trans_color.gray*=0x0101;
9443 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9444 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9446 if ((image_colors == 0) ||
9447 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9448 image_colors=(int) (one << image_depth);
9450 if (image_depth > 8)
9456 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9458 if(!mng_info->write_png_depth)
9462 while ((int) (one << ping_bit_depth)
9463 < (ssize_t) image_colors)
9464 ping_bit_depth <<= 1;
9468 else if (ping_color_type ==
9469 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9470 mng_info->IsPalette)
9472 /* Check if grayscale is reducible */
9475 depth_4_ok=MagickTrue,
9476 depth_2_ok=MagickTrue,
9477 depth_1_ok=MagickTrue;
9479 for (i=0; i < (ssize_t) image_colors; i++)
9484 intensity=ScaleQuantumToChar(image->colormap[i].red);
9486 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9487 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9488 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9489 depth_2_ok=depth_1_ok=MagickFalse;
9490 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9491 depth_1_ok=MagickFalse;
9494 if (depth_1_ok && mng_info->write_png_depth <= 1)
9497 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9500 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9505 image_depth=ping_bit_depth;
9510 if (mng_info->IsPalette)
9512 number_colors=image_colors;
9514 if (image_depth <= 8)
9519 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9521 if (mng_info->have_write_global_plte && matte == MagickFalse)
9523 png_set_PLTE(ping,ping_info,NULL,0);
9525 if (logging != MagickFalse)
9526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9527 " Setting up empty PLTE chunk");
9532 for (i=0; i < (ssize_t) number_colors; i++)
9534 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9535 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9536 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9539 if (logging != MagickFalse)
9540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9541 " Setting up PLTE chunk with %d colors",
9544 ping_have_PLTE=MagickTrue;
9547 /* color_type is PNG_COLOR_TYPE_PALETTE */
9548 if (mng_info->write_png_depth == 0)
9556 while ((one << ping_bit_depth) < (size_t) number_colors)
9557 ping_bit_depth <<= 1;
9562 if (matte != MagickFalse)
9565 * Set up trans_colors array.
9567 assert(number_colors <= 256);
9569 ping_num_trans=(unsigned short) (number_transparent +
9570 number_semitransparent);
9572 if (ping_num_trans == 0)
9573 ping_have_tRNS=MagickFalse;
9577 if (logging != MagickFalse)
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580 " Scaling ping_trans_color (1)");
9582 ping_have_tRNS=MagickTrue;
9584 for (i=0; i < ping_num_trans; i++)
9586 ping_trans_alpha[i]= (png_byte)
9587 ScaleQuantumToChar(image->colormap[i].alpha);
9597 if (image_depth < 8)
9600 if ((save_image_depth == 16) && (image_depth == 8))
9602 if (logging != MagickFalse)
9604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " Scaling ping_trans_color from (%d,%d,%d)",
9606 (int) ping_trans_color.red,
9607 (int) ping_trans_color.green,
9608 (int) ping_trans_color.blue);
9611 ping_trans_color.red*=0x0101;
9612 ping_trans_color.green*=0x0101;
9613 ping_trans_color.blue*=0x0101;
9614 ping_trans_color.gray*=0x0101;
9616 if (logging != MagickFalse)
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620 (int) ping_trans_color.red,
9621 (int) ping_trans_color.green,
9622 (int) ping_trans_color.blue);
9627 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9628 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9631 Adjust background and transparency samples in sub-8-bit grayscale files.
9633 if (ping_bit_depth < 8 && ping_color_type ==
9634 PNG_COLOR_TYPE_GRAY)
9642 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9644 if (ping_exclude_bKGD == MagickFalse)
9647 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9648 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9649 &image->background_color))) +.5)));
9651 if (logging != MagickFalse)
9652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9653 " Setting up bKGD chunk (2)");
9654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9655 " background_color index is %d",
9656 (int) ping_background.index);
9658 ping_have_bKGD = MagickTrue;
9661 if (logging != MagickFalse)
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9663 " Scaling ping_trans_color.gray from %d",
9664 (int)ping_trans_color.gray);
9666 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9667 ping_trans_color.gray)+.5);
9669 if (logging != MagickFalse)
9670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9671 " to %d", (int)ping_trans_color.gray);
9674 if (ping_exclude_bKGD == MagickFalse)
9676 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9679 Identify which colormap entry is the background color.
9682 number_colors=image_colors;
9684 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9685 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9688 ping_background.index=(png_byte) i;
9690 if (logging != MagickFalse)
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693 " Setting up bKGD chunk with index=%d",(int) i);
9696 if (i < (ssize_t) number_colors)
9698 ping_have_bKGD = MagickTrue;
9700 if (logging != MagickFalse)
9702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9703 " background =(%d,%d,%d)",
9704 (int) ping_background.red,
9705 (int) ping_background.green,
9706 (int) ping_background.blue);
9710 else /* Can't happen */
9712 if (logging != MagickFalse)
9713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9714 " No room in PLTE to add bKGD color");
9715 ping_have_bKGD = MagickFalse;
9720 if (logging != MagickFalse)
9721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9722 " PNG color type: %d",ping_color_type);
9724 Initialize compression level and filtering.
9726 if (logging != MagickFalse)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " Setting up deflate compression");
9731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9732 " Compression buffer size: 32768");
9735 png_set_compression_buffer_size(ping,32768L);
9737 if (logging != MagickFalse)
9738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9739 " Compression mem level: 9");
9741 png_set_compression_mem_level(ping, 9);
9743 /* Untangle the "-quality" setting:
9745 Undefined is 0; the default is used.
9750 0: Use Z_HUFFMAN_ONLY strategy with the
9751 zlib default compression level
9753 1-9: the zlib compression level
9757 0-4: the PNG filter method
9759 5: libpng adaptive filtering if compression level > 5
9760 libpng filter type "none" if compression level <= 5
9761 or if image is grayscale or palette
9763 6: libpng adaptive filtering
9765 7: "LOCO" filtering (intrapixel differing) if writing
9766 a MNG, othewise "none". Did not work in IM-6.7.0-9
9767 and earlier because of a missing "else".
9769 8: Z_RLE strategy, all filters
9770 Unused prior to IM-6.7.0-10, was same as 6
9772 9: Z_RLE strategy, no PNG filters
9773 Unused prior to IM-6.7.0-10, was same as 6
9775 Note that using the -quality option, not all combinations of
9776 PNG filter type, zlib compression level, and zlib compression
9777 strategy are possible. This will be addressed soon in a
9778 release that accomodates "-define png:compression-strategy", etc.
9782 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9787 if (mng_info->write_png_compression_strategy == 0)
9788 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9791 else if (mng_info->write_png_compression_level == 0)
9796 level=(int) MagickMin((ssize_t) quality/10,9);
9798 mng_info->write_png_compression_level = level+1;
9801 if (mng_info->write_png_compression_strategy == 0)
9803 if ((quality %10) == 8 || (quality %10) == 9)
9804 mng_info->write_png_compression_strategy=Z_RLE;
9807 if (mng_info->write_png_compression_filter == 0)
9808 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9810 if (logging != MagickFalse)
9812 if (mng_info->write_png_compression_level)
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " Compression level: %d",
9815 (int) mng_info->write_png_compression_level-1);
9817 if (mng_info->write_png_compression_strategy)
9818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9819 " Compression strategy: %d",
9820 (int) mng_info->write_png_compression_strategy-1);
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " Setting up filtering");
9825 if (mng_info->write_png_compression_filter == 6)
9826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9827 " Base filter method: ADAPTIVE");
9828 else if (mng_info->write_png_compression_filter == 0 ||
9829 mng_info->write_png_compression_filter == 1)
9830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9831 " Base filter method: NONE");
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Base filter method: %d",
9835 (int) mng_info->write_png_compression_filter-1);
9838 if (mng_info->write_png_compression_level != 0)
9839 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9841 if (mng_info->write_png_compression_filter == 6)
9843 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9844 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9846 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9848 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9850 else if (mng_info->write_png_compression_filter == 7 ||
9851 mng_info->write_png_compression_filter == 10)
9852 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9854 else if (mng_info->write_png_compression_filter == 8)
9856 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9857 if (mng_info->write_mng)
9859 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9860 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9861 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9864 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9867 else if (mng_info->write_png_compression_filter == 9)
9868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9870 else if (mng_info->write_png_compression_filter != 0)
9871 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9872 mng_info->write_png_compression_filter-1);
9874 if (mng_info->write_png_compression_strategy != 0)
9875 png_set_compression_strategy(ping,
9876 mng_info->write_png_compression_strategy-1);
9878 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9879 if (ping_exclude_sRGB != MagickFalse ||
9880 (image->rendering_intent == UndefinedIntent))
9882 if ((ping_exclude_tEXt == MagickFalse ||
9883 ping_exclude_zTXt == MagickFalse) &&
9884 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9886 ResetImageProfileIterator(image);
9887 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9889 profile=GetImageProfile(image,name);
9891 if (profile != (StringInfo *) NULL)
9893 #ifdef PNG_WRITE_iCCP_SUPPORTED
9894 if ((LocaleCompare(name,"ICC") == 0) ||
9895 (LocaleCompare(name,"ICM") == 0))
9898 if (ping_exclude_iCCP == MagickFalse)
9900 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9901 #if (PNG_LIBPNG_VER < 10500)
9902 (png_charp) GetStringInfoDatum(profile),
9904 (png_const_bytep) GetStringInfoDatum(profile),
9906 (png_uint_32) GetStringInfoLength(profile));
9912 if (ping_exclude_zCCP == MagickFalse)
9914 Magick_png_write_raw_profile(image_info,ping,ping_info,
9915 (unsigned char *) name,(unsigned char *) name,
9916 GetStringInfoDatum(profile),
9917 (png_uint_32) GetStringInfoLength(profile));
9921 if (logging != MagickFalse)
9922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " Setting up text chunk with %s profile",name);
9925 name=GetNextImageProfile(image);
9930 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9931 if ((mng_info->have_write_global_srgb == 0) &&
9932 ((image->rendering_intent != UndefinedIntent) ||
9933 (image->colorspace == sRGBColorspace)))
9935 if (ping_exclude_sRGB == MagickFalse)
9938 Note image rendering intent.
9940 if (logging != MagickFalse)
9941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " Setting up sRGB chunk");
9944 (void) png_set_sRGB(ping,ping_info,(
9945 Magick_RenderingIntent_to_PNG_RenderingIntent(
9946 image->rendering_intent)));
9950 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9953 if (ping_exclude_gAMA == MagickFalse &&
9954 (ping_exclude_sRGB == MagickFalse ||
9955 (image->gamma < .45 || image->gamma > .46)))
9957 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9961 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9963 if (logging != MagickFalse)
9964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9965 " Setting up gAMA chunk");
9967 png_set_gAMA(ping,ping_info,image->gamma);
9971 if (ping_exclude_cHRM == MagickFalse)
9973 if ((mng_info->have_write_global_chrm == 0) &&
9974 (image->chromaticity.red_primary.x != 0.0))
9977 Note image chromaticity.
9978 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9986 wp=image->chromaticity.white_point;
9987 rp=image->chromaticity.red_primary;
9988 gp=image->chromaticity.green_primary;
9989 bp=image->chromaticity.blue_primary;
9991 if (logging != MagickFalse)
9992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993 " Setting up cHRM chunk");
9995 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10001 ping_interlace_method=image_info->interlace != NoInterlace;
10003 if (mng_info->write_mng)
10004 png_set_sig_bytes(ping,8);
10006 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10008 if (mng_info->write_png_colortype != 0)
10010 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10011 if (ping_have_color != MagickFalse)
10013 ping_color_type = PNG_COLOR_TYPE_RGB;
10015 if (ping_bit_depth < 8)
10019 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10020 if (ping_have_color != MagickFalse)
10021 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10024 if (ping_need_colortype_warning != MagickFalse ||
10025 ((mng_info->write_png_depth &&
10026 (int) mng_info->write_png_depth != ping_bit_depth) ||
10027 (mng_info->write_png_colortype &&
10028 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10029 mng_info->write_png_colortype != 7 &&
10030 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10032 if (logging != MagickFalse)
10034 if (ping_need_colortype_warning != MagickFalse)
10036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10037 " Image has transparency but tRNS chunk was excluded");
10040 if (mng_info->write_png_depth)
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " Defined png:bit-depth=%u, Computed depth=%u",
10044 mng_info->write_png_depth,
10048 if (mng_info->write_png_colortype)
10050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10051 " Defined png:color-type=%u, Computed color type=%u",
10052 mng_info->write_png_colortype-1,
10058 "Cannot write image with defined png:bit-depth or png:color-type.");
10061 if (image_matte != MagickFalse && image->matte == MagickFalse)
10063 /* Add an opaque matte channel */
10064 image->matte = MagickTrue;
10065 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10067 if (logging != MagickFalse)
10068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10069 " Added an opaque matte channel");
10072 if (number_transparent != 0 || number_semitransparent != 0)
10074 if (ping_color_type < 4)
10076 ping_have_tRNS=MagickTrue;
10077 if (logging != MagickFalse)
10078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10079 " Setting ping_have_tRNS=MagickTrue.");
10083 if (logging != MagickFalse)
10084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10085 " Writing PNG header chunks");
10087 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10088 ping_bit_depth,ping_color_type,
10089 ping_interlace_method,ping_compression_method,
10090 ping_filter_method);
10092 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10094 png_set_PLTE(ping,ping_info,palette,number_colors);
10096 if (logging != MagickFalse)
10098 for (i=0; i< (ssize_t) number_colors; i++)
10100 if (i < ping_num_trans)
10101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10102 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10104 (int) palette[i].red,
10105 (int) palette[i].green,
10106 (int) palette[i].blue,
10108 (int) ping_trans_alpha[i]);
10110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10111 " PLTE[%d] = (%d,%d,%d)",
10113 (int) palette[i].red,
10114 (int) palette[i].green,
10115 (int) palette[i].blue);
10120 if (ping_exclude_bKGD == MagickFalse)
10122 if (ping_have_bKGD != MagickFalse)
10124 png_set_bKGD(ping,ping_info,&ping_background);
10127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10128 " Setting up bKGD chunk");
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130 " background color = (%d,%d,%d)",
10131 (int) ping_background.red,
10132 (int) ping_background.green,
10133 (int) ping_background.blue);
10134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10135 " index = %d, gray=%d",
10136 (int) ping_background.index,
10137 (int) ping_background.gray);
10142 if (ping_exclude_pHYs == MagickFalse)
10144 if (ping_have_pHYs != MagickFalse)
10146 png_set_pHYs(ping,ping_info,
10147 ping_pHYs_x_resolution,
10148 ping_pHYs_y_resolution,
10149 ping_pHYs_unit_type);
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 " Setting up pHYs chunk");
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156 " x_resolution=%lu",
10157 (unsigned long) ping_pHYs_x_resolution);
10158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10159 " y_resolution=%lu",
10160 (unsigned long) ping_pHYs_y_resolution);
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10163 (unsigned long) ping_pHYs_unit_type);
10168 #if defined(PNG_oFFs_SUPPORTED)
10169 if (ping_exclude_oFFs == MagickFalse)
10171 if (image->page.x || image->page.y)
10173 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10174 (png_int_32) image->page.y, 0);
10176 if (logging != MagickFalse)
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10179 (int) image->page.x, (int) image->page.y);
10184 if (mng_info->need_blob != MagickFalse)
10186 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10188 png_error(ping,"WriteBlob Failed");
10190 ping_have_blob=MagickTrue;
10193 png_write_info_before_PLTE(ping, ping_info);
10195 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10197 if (logging != MagickFalse)
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10203 if (ping_color_type == 3)
10204 (void) png_set_tRNS(ping, ping_info,
10211 (void) png_set_tRNS(ping, ping_info,
10214 &ping_trans_color);
10216 if (logging != MagickFalse)
10218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10219 " tRNS color =(%d,%d,%d)",
10220 (int) ping_trans_color.red,
10221 (int) ping_trans_color.green,
10222 (int) ping_trans_color.blue);
10227 /* write any png-chunk-b profiles */
10228 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10230 png_write_info(ping,ping_info);
10232 /* write any PNG-chunk-m profiles */
10233 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10235 if (ping_exclude_vpAg == MagickFalse)
10237 if ((image->page.width != 0 && image->page.width != image->columns) ||
10238 (image->page.height != 0 && image->page.height != image->rows))
10243 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10244 PNGType(chunk,mng_vpAg);
10245 LogPNGChunk(logging,mng_vpAg,9L);
10246 PNGLong(chunk+4,(png_uint_32) image->page.width);
10247 PNGLong(chunk+8,(png_uint_32) image->page.height);
10248 chunk[12]=0; /* unit = pixels */
10249 (void) WriteBlob(image,13,chunk);
10250 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10254 #if (PNG_LIBPNG_VER == 10206)
10255 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10256 #define PNG_HAVE_IDAT 0x04
10257 ping->mode |= PNG_HAVE_IDAT;
10258 #undef PNG_HAVE_IDAT
10261 png_set_packing(ping);
10265 rowbytes=image->columns;
10266 if (image_depth > 8)
10268 switch (ping_color_type)
10270 case PNG_COLOR_TYPE_RGB:
10274 case PNG_COLOR_TYPE_GRAY_ALPHA:
10278 case PNG_COLOR_TYPE_RGBA:
10286 if (logging != MagickFalse)
10288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10289 " Writing PNG image data");
10291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10292 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10294 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10295 sizeof(*ping_pixels));
10297 if (ping_pixels == (unsigned char *) NULL)
10298 png_error(ping,"Allocation of memory for pixels failed");
10301 Initialize image scanlines.
10303 quantum_info=AcquireQuantumInfo(image_info,image);
10304 if (quantum_info == (QuantumInfo *) NULL)
10305 png_error(ping,"Memory allocation for quantum_info failed");
10306 quantum_info->format=UndefinedQuantumFormat;
10307 quantum_info->depth=image_depth;
10308 num_passes=png_set_interlace_handling(ping);
10310 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10311 !mng_info->write_png32) &&
10312 (mng_info->IsPalette ||
10313 (image_info->type == BilevelType)) &&
10314 image_matte == MagickFalse &&
10315 ping_have_non_bw == MagickFalse)
10317 /* Palette, Bilevel, or Opaque Monochrome */
10318 register const Quantum
10321 quantum_info->depth=8;
10322 for (pass=0; pass < num_passes; pass++)
10325 Convert PseudoClass image to a PNG monochrome image.
10327 for (y=0; y < (ssize_t) image->rows; y++)
10329 if (logging != MagickFalse && y == 0)
10330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10331 " Writing row of pixels (0)");
10333 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10335 if (p == (const Quantum *) NULL)
10338 if (mng_info->IsPalette)
10340 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10341 quantum_info,GrayQuantum,ping_pixels,exception);
10342 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10343 mng_info->write_png_depth &&
10344 mng_info->write_png_depth != old_bit_depth)
10346 /* Undo pixel scaling */
10347 for (i=0; i < (ssize_t) image->columns; i++)
10348 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10349 >> (8-old_bit_depth));
10355 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10356 quantum_info,RedQuantum,ping_pixels,exception);
10359 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10360 for (i=0; i < (ssize_t) image->columns; i++)
10361 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10364 if (logging != MagickFalse && y == 0)
10365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10366 " Writing row of pixels (1)");
10368 png_write_row(ping,ping_pixels);
10370 if (image->previous == (Image *) NULL)
10372 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10373 if (status == MagickFalse)
10379 else /* Not Palette, Bilevel, or Opaque Monochrome */
10381 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10382 !mng_info->write_png32) &&
10383 (image_matte != MagickFalse ||
10384 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10385 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10387 register const Quantum
10390 for (pass=0; pass < num_passes; pass++)
10393 for (y=0; y < (ssize_t) image->rows; y++)
10395 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10397 if (p == (const Quantum *) NULL)
10400 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10402 if (mng_info->IsPalette)
10403 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10404 quantum_info,GrayQuantum,ping_pixels,exception);
10407 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10408 quantum_info,RedQuantum,ping_pixels,exception);
10410 if (logging != MagickFalse && y == 0)
10411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10412 " Writing GRAY PNG pixels (2)");
10415 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10417 if (logging != MagickFalse && y == 0)
10418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10419 " Writing GRAY_ALPHA PNG pixels (2)");
10421 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10422 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10425 if (logging != MagickFalse && y == 0)
10426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10427 " Writing row of pixels (2)");
10429 png_write_row(ping,ping_pixels);
10432 if (image->previous == (Image *) NULL)
10434 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10435 if (status == MagickFalse)
10443 register const Quantum
10446 for (pass=0; pass < num_passes; pass++)
10448 if ((image_depth > 8) || (mng_info->write_png24 ||
10449 mng_info->write_png32 ||
10450 (!mng_info->write_png8 && !mng_info->IsPalette)))
10452 for (y=0; y < (ssize_t) image->rows; y++)
10454 p=GetVirtualPixels(image,0,y,image->columns,1,
10457 if (p == (const Quantum *) NULL)
10460 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10462 if (image->storage_class == DirectClass)
10463 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10464 quantum_info,RedQuantum,ping_pixels,exception);
10467 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10468 quantum_info,GrayQuantum,ping_pixels,exception);
10471 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10473 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10474 quantum_info,GrayAlphaQuantum,ping_pixels,
10477 if (logging != MagickFalse && y == 0)
10478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10479 " Writing GRAY_ALPHA PNG pixels (3)");
10482 else if (image_matte != MagickFalse)
10483 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10484 quantum_info,RGBAQuantum,ping_pixels,exception);
10487 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10488 quantum_info,RGBQuantum,ping_pixels,exception);
10490 if (logging != MagickFalse && y == 0)
10491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10492 " Writing row of pixels (3)");
10494 png_write_row(ping,ping_pixels);
10499 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10500 mng_info->write_png32 ||
10501 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10503 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10504 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10506 if (logging != MagickFalse)
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10510 quantum_info->depth=8;
10514 for (y=0; y < (ssize_t) image->rows; y++)
10516 if (logging != MagickFalse && y == 0)
10517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10518 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10520 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10522 if (p == (const Quantum *) NULL)
10525 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10527 quantum_info->depth=image->depth;
10529 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10530 quantum_info,GrayQuantum,ping_pixels,exception);
10533 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10535 if (logging != MagickFalse && y == 0)
10536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " Writing GRAY_ALPHA PNG pixels (4)");
10539 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10540 quantum_info,GrayAlphaQuantum,ping_pixels,
10546 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10547 quantum_info,IndexQuantum,ping_pixels,exception);
10549 if (logging != MagickFalse && y <= 2)
10551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10552 " Writing row of non-gray pixels (4)");
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10556 (int)ping_pixels[0],(int)ping_pixels[1]);
10559 png_write_row(ping,ping_pixels);
10563 if (image->previous == (Image *) NULL)
10565 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10566 if (status == MagickFalse)
10573 if (quantum_info != (QuantumInfo *) NULL)
10574 quantum_info=DestroyQuantumInfo(quantum_info);
10576 if (logging != MagickFalse)
10578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10579 " Wrote PNG image data");
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10582 " Width: %.20g",(double) ping_width);
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " Height: %.20g",(double) ping_height);
10587 if (mng_info->write_png_depth)
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594 " PNG bit-depth written: %d",ping_bit_depth);
10596 if (mng_info->write_png_colortype)
10598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10603 " PNG color-type written: %d",ping_color_type);
10605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606 " PNG Interlace method: %d",ping_interlace_method);
10609 Generate text chunks after IDAT.
10611 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10613 ResetImagePropertyIterator(image);
10614 property=GetNextImageProperty(image);
10615 while (property != (const char *) NULL)
10620 value=GetImageProperty(image,property,exception);
10622 /* Don't write any "png:" properties; those are just for "identify" */
10623 if (LocaleNCompare(property,"png:",4) != 0 &&
10625 /* Suppress density and units if we wrote a pHYs chunk */
10626 (ping_exclude_pHYs != MagickFalse ||
10627 LocaleCompare(property,"density") != 0 ||
10628 LocaleCompare(property,"units") != 0) &&
10630 /* Suppress the IM-generated Date:create and Date:modify */
10631 (ping_exclude_date == MagickFalse ||
10632 LocaleNCompare(property, "Date:",5) != 0))
10634 if (value != (const char *) NULL)
10636 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10637 text[0].key=(char *) property;
10638 text[0].text=(char *) value;
10639 text[0].text_length=strlen(value);
10641 if (ping_exclude_tEXt != MagickFalse)
10642 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10644 else if (ping_exclude_zTXt != MagickFalse)
10645 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10649 text[0].compression=image_info->compression == NoCompression ||
10650 (image_info->compression == UndefinedCompression &&
10651 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10652 PNG_TEXT_COMPRESSION_zTXt ;
10655 if (logging != MagickFalse)
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " Setting up text chunk");
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " keyword: %s",text[0].key);
10664 png_set_text(ping,ping_info,text,1);
10665 png_free(ping,text);
10668 property=GetNextImageProperty(image);
10672 /* write any PNG-chunk-e profiles */
10673 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10675 if (logging != MagickFalse)
10676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677 " Writing PNG end info");
10679 png_write_end(ping,ping_info);
10681 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10683 if (mng_info->page.x || mng_info->page.y ||
10684 (ping_width != mng_info->page.width) ||
10685 (ping_height != mng_info->page.height))
10691 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10693 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10694 PNGType(chunk,mng_FRAM);
10695 LogPNGChunk(logging,mng_FRAM,27L);
10697 chunk[5]=0; /* frame name separator (no name) */
10698 chunk[6]=1; /* flag for changing delay, for next frame only */
10699 chunk[7]=0; /* flag for changing frame timeout */
10700 chunk[8]=1; /* flag for changing frame clipping for next frame */
10701 chunk[9]=0; /* flag for changing frame sync_id */
10702 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10703 chunk[14]=0; /* clipping boundaries delta type */
10704 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10706 (png_uint_32) (mng_info->page.x + ping_width));
10707 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10709 (png_uint_32) (mng_info->page.y + ping_height));
10710 (void) WriteBlob(image,31,chunk);
10711 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10712 mng_info->old_framing_mode=4;
10713 mng_info->framing_mode=1;
10717 mng_info->framing_mode=3;
10719 if (mng_info->write_mng && !mng_info->need_fram &&
10720 ((int) image->dispose == 3))
10721 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10724 Free PNG resources.
10727 png_destroy_write_struct(&ping,&ping_info);
10729 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10731 if (ping_have_blob != MagickFalse)
10732 (void) CloseBlob(image);
10734 image_info=DestroyImageInfo(image_info);
10735 image=DestroyImage(image);
10737 /* Store bit depth actually written */
10738 s[0]=(char) ping_bit_depth;
10741 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10743 if (logging != MagickFalse)
10744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10745 " exit WriteOnePNGImage()");
10747 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10748 UnlockSemaphoreInfo(ping_semaphore);
10751 /* } for navigation to beginning of SETJMP-protected block. Revert to
10752 * Throwing an Exception when an error occurs.
10755 return(MagickTrue);
10756 /* End write one PNG image */
10761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10765 % W r i t e P N G I m a g e %
10769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10771 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10772 % Multiple-image Network Graphics (MNG) image file.
10774 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10776 % The format of the WritePNGImage method is:
10778 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10779 % Image *image,ExceptionInfo *exception)
10781 % A description of each parameter follows:
10783 % o image_info: the image info.
10785 % o image: The image.
10787 % o exception: return any errors or warnings in this structure.
10789 % Returns MagickTrue on success, MagickFalse on failure.
10791 % Communicating with the PNG encoder:
10793 % While the datastream written is always in PNG format and normally would
10794 % be given the "png" file extension, this method also writes the following
10795 % pseudo-formats which are subsets of png:
10797 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10798 % a depth greater than 8, the depth is reduced. If transparency
10799 % is present, the tRNS chunk must only have values 0 and 255
10800 % (i.e., transparency is binary: fully opaque or fully
10801 % transparent). If other values are present they will be
10802 % 50%-thresholded to binary transparency. If more than 256
10803 % colors are present, they will be quantized to the 4-4-4-1,
10804 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10805 % of any resulting fully-transparent pixels is changed to
10806 % the image's background color.
10808 % If you want better quantization or dithering of the colors
10809 % or alpha than that, you need to do it before calling the
10810 % PNG encoder. The pixels contain 8-bit indices even if
10811 % they could be represented with 1, 2, or 4 bits. Grayscale
10812 % images will be written as indexed PNG files even though the
10813 % PNG grayscale type might be slightly more efficient. Please
10814 % note that writing to the PNG8 format may result in loss
10815 % of color and alpha data.
10817 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10818 % chunk can be present to convey binary transparency by naming
10819 % one of the colors as transparent. The only loss incurred
10820 % is reduction of sample depth to 8. If the image has more
10821 % than one transparent color, has semitransparent pixels, or
10822 % has an opaque pixel with the same RGB components as the
10823 % transparent color, an image is not written.
10825 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10826 % transparency is permitted, i.e., the alpha sample for
10827 % each pixel can have any value from 0 to 255. The alpha
10828 % channel is present even if the image is fully opaque.
10829 % The only loss in data is the reduction of the sample depth
10832 % o -define: For more precise control of the PNG output, you can use the
10833 % Image options "png:bit-depth" and "png:color-type". These
10834 % can be set from the commandline with "-define" and also
10835 % from the application programming interfaces. The options
10836 % are case-independent and are converted to lowercase before
10837 % being passed to this encoder.
10839 % png:color-type can be 0, 2, 3, 4, or 6.
10841 % When png:color-type is 0 (Grayscale), png:bit-depth can
10842 % be 1, 2, 4, 8, or 16.
10844 % When png:color-type is 2 (RGB), png:bit-depth can
10847 % When png:color-type is 3 (Indexed), png:bit-depth can
10848 % be 1, 2, 4, or 8. This refers to the number of bits
10849 % used to store the index. The color samples always have
10850 % bit-depth 8 in indexed PNG files.
10852 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10853 % png:bit-depth can be 8 or 16.
10855 % If the image cannot be written without loss with the requested bit-depth
10856 % and color-type, a PNG file will not be written, and the encoder will
10857 % return MagickFalse.
10859 % Since image encoders should not be responsible for the "heavy lifting",
10860 % the user should make sure that ImageMagick has already reduced the
10861 % image depth and number of colors and limit transparency to binary
10862 % transparency prior to attempting to write the image with depth, color,
10863 % or transparency limitations.
10865 % Note that another definition, "png:bit-depth-written" exists, but it
10866 % is not intended for external use. It is only used internally by the
10867 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10869 % It is possible to request that the PNG encoder write previously-formatted
10870 % ancillary chunks in the output PNG file, using the "-profile" commandline
10871 % option as shown below or by setting the profile via a programming
10874 % -profile PNG-chunk-x:<file>
10876 % where x is a location flag and <file> is a file containing the chunk
10877 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10878 % This encoder will compute the chunk length and CRC, so those must not
10879 % be included in the file.
10881 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10882 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10883 % of the same type, then add a short unique string after the "x" to prevent
10884 % subsequent profiles from overwriting the preceding ones, e.g.,
10886 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10888 % As of version 6.6.6 the following optimizations are always done:
10890 % o 32-bit depth is reduced to 16.
10891 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10892 % high byte and low byte are identical.
10893 % o Palette is sorted to remove unused entries and to put a
10894 % transparent color first, if BUILD_PNG_PALETTE is defined.
10895 % o Opaque matte channel is removed when writing an indexed PNG.
10896 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10897 % this can be done without loss and a larger bit depth N was not
10898 % requested via the "-define png:bit-depth=N" option.
10899 % o If matte channel is present but only one transparent color is
10900 % present, RGB+tRNS is written instead of RGBA
10901 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10902 % was requested when converting an opaque image).
10904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10906 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10907 Image *image,ExceptionInfo *exception)
10912 have_mng_structure,
10928 assert(image_info != (const ImageInfo *) NULL);
10929 assert(image_info->signature == MagickSignature);
10930 assert(image != (Image *) NULL);
10931 assert(image->signature == MagickSignature);
10932 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10933 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10935 Allocate a MngInfo structure.
10937 have_mng_structure=MagickFalse;
10938 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10940 if (mng_info == (MngInfo *) NULL)
10941 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10944 Initialize members of the MngInfo structure.
10946 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10947 mng_info->image=image;
10948 mng_info->equal_backgrounds=MagickTrue;
10949 have_mng_structure=MagickTrue;
10951 /* See if user has requested a specific PNG subformat */
10953 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10954 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10955 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10957 value=GetImageOption(image_info,"png:format");
10959 if (value != (char *) NULL)
10961 if (LocaleCompare(value,"png8") == 0)
10963 mng_info->write_png8 = MagickTrue;
10964 mng_info->write_png24 = MagickFalse;
10965 mng_info->write_png32 = MagickFalse;
10968 else if (LocaleCompare(value,"png24") == 0)
10970 mng_info->write_png8 = MagickFalse;
10971 mng_info->write_png24 = MagickTrue;
10972 mng_info->write_png32 = MagickFalse;
10975 else if (LocaleCompare(value,"png32") == 0)
10977 mng_info->write_png8 = MagickFalse;
10978 mng_info->write_png24 = MagickFalse;
10979 mng_info->write_png32 = MagickTrue;
10982 if (mng_info->write_png8)
10984 mng_info->write_png_colortype = /* 3 */ 4;
10985 mng_info->write_png_depth = 8;
10989 if (mng_info->write_png24)
10991 mng_info->write_png_colortype = /* 2 */ 3;
10992 mng_info->write_png_depth = 8;
10995 if (image->matte == MagickTrue)
10996 (void) SetImageType(image,TrueColorMatteType,exception);
10999 (void) SetImageType(image,TrueColorType,exception);
11001 (void) SyncImage(image,exception);
11004 if (mng_info->write_png32)
11006 mng_info->write_png_colortype = /* 6 */ 7;
11007 mng_info->write_png_depth = 8;
11010 if (image->matte == MagickTrue)
11011 (void) SetImageType(image,TrueColorMatteType,exception);
11014 (void) SetImageType(image,TrueColorType,exception);
11016 (void) SyncImage(image,exception);
11019 value=GetImageOption(image_info,"png:bit-depth");
11021 if (value != (char *) NULL)
11023 if (LocaleCompare(value,"1") == 0)
11024 mng_info->write_png_depth = 1;
11026 else if (LocaleCompare(value,"2") == 0)
11027 mng_info->write_png_depth = 2;
11029 else if (LocaleCompare(value,"4") == 0)
11030 mng_info->write_png_depth = 4;
11032 else if (LocaleCompare(value,"8") == 0)
11033 mng_info->write_png_depth = 8;
11035 else if (LocaleCompare(value,"16") == 0)
11036 mng_info->write_png_depth = 16;
11039 (void) ThrowMagickException(exception,
11040 GetMagickModule(),CoderWarning,
11041 "ignoring invalid defined png:bit-depth",
11044 if (logging != MagickFalse)
11045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11046 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11049 value=GetImageOption(image_info,"png:color-type");
11051 if (value != (char *) NULL)
11053 /* We must store colortype+1 because 0 is a valid colortype */
11054 if (LocaleCompare(value,"0") == 0)
11055 mng_info->write_png_colortype = 1;
11057 else if (LocaleCompare(value,"1") == 0)
11058 mng_info->write_png_colortype = 2;
11060 else if (LocaleCompare(value,"2") == 0)
11061 mng_info->write_png_colortype = 3;
11063 else if (LocaleCompare(value,"3") == 0)
11064 mng_info->write_png_colortype = 4;
11066 else if (LocaleCompare(value,"4") == 0)
11067 mng_info->write_png_colortype = 5;
11069 else if (LocaleCompare(value,"6") == 0)
11070 mng_info->write_png_colortype = 7;
11073 (void) ThrowMagickException(exception,
11074 GetMagickModule(),CoderWarning,
11075 "ignoring invalid defined png:color-type",
11078 if (logging != MagickFalse)
11079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11080 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11083 /* Check for chunks to be excluded:
11085 * The default is to not exclude any known chunks except for any
11086 * listed in the "unused_chunks" array, above.
11088 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11089 * define (in the image properties or in the image artifacts)
11090 * or via a mng_info member. For convenience, in addition
11091 * to or instead of a comma-separated list of chunks, the
11092 * "exclude-chunk" string can be simply "all" or "none".
11094 * The exclude-chunk define takes priority over the mng_info.
11096 * A "png:include-chunk" define takes priority over both the
11097 * mng_info and the "png:exclude-chunk" define. Like the
11098 * "exclude-chunk" string, it can define "all" or "none" as
11099 * well as a comma-separated list. Chunks that are unknown to
11100 * ImageMagick are always excluded, regardless of their "copy-safe"
11101 * status according to the PNG specification, and even if they
11102 * appear in the "include-chunk" list. Such defines appearing among
11103 * the image options take priority over those found among the image
11106 * Finally, all chunks listed in the "unused_chunks" array are
11107 * automatically excluded, regardless of the other instructions
11110 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11111 * will not be written and the gAMA chunk will only be written if it
11112 * is not between .45 and .46, or approximately (1.0/2.2).
11114 * If you exclude tRNS and the image has transparency, the colortype
11115 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11117 * The -strip option causes StripImage() to set the png:include-chunk
11118 * artifact to "none,trns,gama".
11121 mng_info->ping_exclude_bKGD=MagickFalse;
11122 mng_info->ping_exclude_cHRM=MagickFalse;
11123 mng_info->ping_exclude_date=MagickFalse;
11124 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11125 mng_info->ping_exclude_gAMA=MagickFalse;
11126 mng_info->ping_exclude_iCCP=MagickFalse;
11127 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11128 mng_info->ping_exclude_oFFs=MagickFalse;
11129 mng_info->ping_exclude_pHYs=MagickFalse;
11130 mng_info->ping_exclude_sRGB=MagickFalse;
11131 mng_info->ping_exclude_tEXt=MagickFalse;
11132 mng_info->ping_exclude_tRNS=MagickFalse;
11133 mng_info->ping_exclude_vpAg=MagickFalse;
11134 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11135 mng_info->ping_exclude_zTXt=MagickFalse;
11137 mng_info->ping_preserve_colormap=MagickFalse;
11139 value=GetImageArtifact(image,"png:preserve-colormap");
11141 value=GetImageOption(image_info,"png:preserve-colormap");
11143 mng_info->ping_preserve_colormap=MagickTrue;
11145 /* Thes compression-level, compression-strategy, and compression-filter
11146 * defines take precedence over values from the -quality option.
11148 value=GetImageArtifact(image,"png:compression-level");
11150 value=GetImageOption(image_info,"png:compression-level");
11153 /* We have to add 1 to everything because 0 is a valid input,
11154 * and we want to use 0 (the default) to mean undefined.
11156 if (LocaleCompare(value,"0") == 0)
11157 mng_info->write_png_compression_level = 1;
11159 else if (LocaleCompare(value,"1") == 0)
11160 mng_info->write_png_compression_level = 2;
11162 else if (LocaleCompare(value,"2") == 0)
11163 mng_info->write_png_compression_level = 3;
11165 else if (LocaleCompare(value,"3") == 0)
11166 mng_info->write_png_compression_level = 4;
11168 else if (LocaleCompare(value,"4") == 0)
11169 mng_info->write_png_compression_level = 5;
11171 else if (LocaleCompare(value,"5") == 0)
11172 mng_info->write_png_compression_level = 6;
11174 else if (LocaleCompare(value,"6") == 0)
11175 mng_info->write_png_compression_level = 7;
11177 else if (LocaleCompare(value,"7") == 0)
11178 mng_info->write_png_compression_level = 8;
11180 else if (LocaleCompare(value,"8") == 0)
11181 mng_info->write_png_compression_level = 9;
11183 else if (LocaleCompare(value,"9") == 0)
11184 mng_info->write_png_compression_level = 10;
11187 (void) ThrowMagickException(exception,
11188 GetMagickModule(),CoderWarning,
11189 "ignoring invalid defined png:compression-level",
11193 value=GetImageArtifact(image,"png:compression-strategy");
11195 value=GetImageOption(image_info,"png:compression-strategy");
11199 if (LocaleCompare(value,"0") == 0)
11200 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11202 else if (LocaleCompare(value,"1") == 0)
11203 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11205 else if (LocaleCompare(value,"2") == 0)
11206 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11208 else if (LocaleCompare(value,"3") == 0)
11209 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11210 mng_info->write_png_compression_strategy = Z_RLE+1;
11212 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11215 else if (LocaleCompare(value,"4") == 0)
11216 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11217 mng_info->write_png_compression_strategy = Z_FIXED+1;
11219 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11223 (void) ThrowMagickException(exception,
11224 GetMagickModule(),CoderWarning,
11225 "ignoring invalid defined png:compression-strategy",
11229 value=GetImageArtifact(image,"png:compression-filter");
11231 value=GetImageOption(image_info,"png:compression-filter");
11235 /* To do: combinations of filters allowed by libpng
11236 * masks 0x08 through 0xf8
11238 * Implement this as a comma-separated list of 0,1,2,3,4,5
11239 * where 5 is a special case meaning PNG_ALL_FILTERS.
11242 if (LocaleCompare(value,"0") == 0)
11243 mng_info->write_png_compression_filter = 1;
11245 if (LocaleCompare(value,"1") == 0)
11246 mng_info->write_png_compression_filter = 2;
11248 else if (LocaleCompare(value,"2") == 0)
11249 mng_info->write_png_compression_filter = 3;
11251 else if (LocaleCompare(value,"3") == 0)
11252 mng_info->write_png_compression_filter = 4;
11254 else if (LocaleCompare(value,"4") == 0)
11255 mng_info->write_png_compression_filter = 5;
11257 else if (LocaleCompare(value,"5") == 0)
11258 mng_info->write_png_compression_filter = 6;
11261 (void) ThrowMagickException(exception,
11262 GetMagickModule(),CoderWarning,
11263 "ignoring invalid defined png:compression-filter",
11267 excluding=MagickFalse;
11269 for (source=0; source<1; source++)
11273 value=GetImageArtifact(image,"png:exclude-chunk");
11276 value=GetImageArtifact(image,"png:exclude-chunks");
11280 value=GetImageOption(image_info,"png:exclude-chunk");
11283 value=GetImageOption(image_info,"png:exclude-chunks");
11292 excluding=MagickTrue;
11294 if (logging != MagickFalse)
11297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11298 " png:exclude-chunk=%s found in image artifacts.\n", value);
11300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11301 " png:exclude-chunk=%s found in image properties.\n", value);
11304 last=strlen(value);
11306 for (i=0; i<(int) last; i+=5)
11309 if (LocaleNCompare(value+i,"all",3) == 0)
11311 mng_info->ping_exclude_bKGD=MagickTrue;
11312 mng_info->ping_exclude_cHRM=MagickTrue;
11313 mng_info->ping_exclude_date=MagickTrue;
11314 mng_info->ping_exclude_EXIF=MagickTrue;
11315 mng_info->ping_exclude_gAMA=MagickTrue;
11316 mng_info->ping_exclude_iCCP=MagickTrue;
11317 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11318 mng_info->ping_exclude_oFFs=MagickTrue;
11319 mng_info->ping_exclude_pHYs=MagickTrue;
11320 mng_info->ping_exclude_sRGB=MagickTrue;
11321 mng_info->ping_exclude_tEXt=MagickTrue;
11322 mng_info->ping_exclude_tRNS=MagickTrue;
11323 mng_info->ping_exclude_vpAg=MagickTrue;
11324 mng_info->ping_exclude_zCCP=MagickTrue;
11325 mng_info->ping_exclude_zTXt=MagickTrue;
11329 if (LocaleNCompare(value+i,"none",4) == 0)
11331 mng_info->ping_exclude_bKGD=MagickFalse;
11332 mng_info->ping_exclude_cHRM=MagickFalse;
11333 mng_info->ping_exclude_date=MagickFalse;
11334 mng_info->ping_exclude_EXIF=MagickFalse;
11335 mng_info->ping_exclude_gAMA=MagickFalse;
11336 mng_info->ping_exclude_iCCP=MagickFalse;
11337 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11338 mng_info->ping_exclude_oFFs=MagickFalse;
11339 mng_info->ping_exclude_pHYs=MagickFalse;
11340 mng_info->ping_exclude_sRGB=MagickFalse;
11341 mng_info->ping_exclude_tEXt=MagickFalse;
11342 mng_info->ping_exclude_tRNS=MagickFalse;
11343 mng_info->ping_exclude_vpAg=MagickFalse;
11344 mng_info->ping_exclude_zCCP=MagickFalse;
11345 mng_info->ping_exclude_zTXt=MagickFalse;
11348 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11349 mng_info->ping_exclude_bKGD=MagickTrue;
11351 if (LocaleNCompare(value+i,"chrm",4) == 0)
11352 mng_info->ping_exclude_cHRM=MagickTrue;
11354 if (LocaleNCompare(value+i,"date",4) == 0)
11355 mng_info->ping_exclude_date=MagickTrue;
11357 if (LocaleNCompare(value+i,"exif",4) == 0)
11358 mng_info->ping_exclude_EXIF=MagickTrue;
11360 if (LocaleNCompare(value+i,"gama",4) == 0)
11361 mng_info->ping_exclude_gAMA=MagickTrue;
11363 if (LocaleNCompare(value+i,"iccp",4) == 0)
11364 mng_info->ping_exclude_iCCP=MagickTrue;
11367 if (LocaleNCompare(value+i,"itxt",4) == 0)
11368 mng_info->ping_exclude_iTXt=MagickTrue;
11371 if (LocaleNCompare(value+i,"gama",4) == 0)
11372 mng_info->ping_exclude_gAMA=MagickTrue;
11374 if (LocaleNCompare(value+i,"offs",4) == 0)
11375 mng_info->ping_exclude_oFFs=MagickTrue;
11377 if (LocaleNCompare(value+i,"phys",4) == 0)
11378 mng_info->ping_exclude_pHYs=MagickTrue;
11380 if (LocaleNCompare(value+i,"srgb",4) == 0)
11381 mng_info->ping_exclude_sRGB=MagickTrue;
11383 if (LocaleNCompare(value+i,"text",4) == 0)
11384 mng_info->ping_exclude_tEXt=MagickTrue;
11386 if (LocaleNCompare(value+i,"trns",4) == 0)
11387 mng_info->ping_exclude_tRNS=MagickTrue;
11389 if (LocaleNCompare(value+i,"vpag",4) == 0)
11390 mng_info->ping_exclude_vpAg=MagickTrue;
11392 if (LocaleNCompare(value+i,"zccp",4) == 0)
11393 mng_info->ping_exclude_zCCP=MagickTrue;
11395 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11396 mng_info->ping_exclude_zTXt=MagickTrue;
11402 for (source=0; source<1; source++)
11406 value=GetImageArtifact(image,"png:include-chunk");
11409 value=GetImageArtifact(image,"png:include-chunks");
11413 value=GetImageOption(image_info,"png:include-chunk");
11416 value=GetImageOption(image_info,"png:include-chunks");
11424 excluding=MagickTrue;
11426 if (logging != MagickFalse)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " png:include-chunk=%s found in image artifacts.\n", value);
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11433 " png:include-chunk=%s found in image properties.\n", value);
11436 last=strlen(value);
11438 for (i=0; i<(int) last; i+=5)
11440 if (LocaleNCompare(value+i,"all",3) == 0)
11442 mng_info->ping_exclude_bKGD=MagickFalse;
11443 mng_info->ping_exclude_cHRM=MagickFalse;
11444 mng_info->ping_exclude_date=MagickFalse;
11445 mng_info->ping_exclude_EXIF=MagickFalse;
11446 mng_info->ping_exclude_gAMA=MagickFalse;
11447 mng_info->ping_exclude_iCCP=MagickFalse;
11448 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11449 mng_info->ping_exclude_oFFs=MagickFalse;
11450 mng_info->ping_exclude_pHYs=MagickFalse;
11451 mng_info->ping_exclude_sRGB=MagickFalse;
11452 mng_info->ping_exclude_tEXt=MagickFalse;
11453 mng_info->ping_exclude_tRNS=MagickFalse;
11454 mng_info->ping_exclude_vpAg=MagickFalse;
11455 mng_info->ping_exclude_zCCP=MagickFalse;
11456 mng_info->ping_exclude_zTXt=MagickFalse;
11460 if (LocaleNCompare(value+i,"none",4) == 0)
11462 mng_info->ping_exclude_bKGD=MagickTrue;
11463 mng_info->ping_exclude_cHRM=MagickTrue;
11464 mng_info->ping_exclude_date=MagickTrue;
11465 mng_info->ping_exclude_EXIF=MagickTrue;
11466 mng_info->ping_exclude_gAMA=MagickTrue;
11467 mng_info->ping_exclude_iCCP=MagickTrue;
11468 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11469 mng_info->ping_exclude_oFFs=MagickTrue;
11470 mng_info->ping_exclude_pHYs=MagickTrue;
11471 mng_info->ping_exclude_sRGB=MagickTrue;
11472 mng_info->ping_exclude_tEXt=MagickTrue;
11473 mng_info->ping_exclude_tRNS=MagickTrue;
11474 mng_info->ping_exclude_vpAg=MagickTrue;
11475 mng_info->ping_exclude_zCCP=MagickTrue;
11476 mng_info->ping_exclude_zTXt=MagickTrue;
11479 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11480 mng_info->ping_exclude_bKGD=MagickFalse;
11482 if (LocaleNCompare(value+i,"chrm",4) == 0)
11483 mng_info->ping_exclude_cHRM=MagickFalse;
11485 if (LocaleNCompare(value+i,"date",4) == 0)
11486 mng_info->ping_exclude_date=MagickFalse;
11488 if (LocaleNCompare(value+i,"exif",4) == 0)
11489 mng_info->ping_exclude_EXIF=MagickFalse;
11491 if (LocaleNCompare(value+i,"gama",4) == 0)
11492 mng_info->ping_exclude_gAMA=MagickFalse;
11494 if (LocaleNCompare(value+i,"iccp",4) == 0)
11495 mng_info->ping_exclude_iCCP=MagickFalse;
11498 if (LocaleNCompare(value+i,"itxt",4) == 0)
11499 mng_info->ping_exclude_iTXt=MagickFalse;
11502 if (LocaleNCompare(value+i,"gama",4) == 0)
11503 mng_info->ping_exclude_gAMA=MagickFalse;
11505 if (LocaleNCompare(value+i,"offs",4) == 0)
11506 mng_info->ping_exclude_oFFs=MagickFalse;
11508 if (LocaleNCompare(value+i,"phys",4) == 0)
11509 mng_info->ping_exclude_pHYs=MagickFalse;
11511 if (LocaleNCompare(value+i,"srgb",4) == 0)
11512 mng_info->ping_exclude_sRGB=MagickFalse;
11514 if (LocaleNCompare(value+i,"text",4) == 0)
11515 mng_info->ping_exclude_tEXt=MagickFalse;
11517 if (LocaleNCompare(value+i,"trns",4) == 0)
11518 mng_info->ping_exclude_tRNS=MagickFalse;
11520 if (LocaleNCompare(value+i,"vpag",4) == 0)
11521 mng_info->ping_exclude_vpAg=MagickFalse;
11523 if (LocaleNCompare(value+i,"zccp",4) == 0)
11524 mng_info->ping_exclude_zCCP=MagickFalse;
11526 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11527 mng_info->ping_exclude_zTXt=MagickFalse;
11533 if (excluding != MagickFalse && logging != MagickFalse)
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11536 " Chunks to be excluded from the output png:");
11537 if (mng_info->ping_exclude_bKGD != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11540 if (mng_info->ping_exclude_cHRM != MagickFalse)
11541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543 if (mng_info->ping_exclude_date != MagickFalse)
11544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11546 if (mng_info->ping_exclude_EXIF != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11549 if (mng_info->ping_exclude_gAMA != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11552 if (mng_info->ping_exclude_iCCP != MagickFalse)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 if (mng_info->ping_exclude_iTXt != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11560 if (mng_info->ping_exclude_oFFs != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11563 if (mng_info->ping_exclude_pHYs != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11566 if (mng_info->ping_exclude_sRGB != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 if (mng_info->ping_exclude_tEXt != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11572 if (mng_info->ping_exclude_tRNS != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 if (mng_info->ping_exclude_vpAg != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 if (mng_info->ping_exclude_zCCP != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 if (mng_info->ping_exclude_zTXt != MagickFalse)
11582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11586 mng_info->need_blob = MagickTrue;
11588 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11590 MngInfoFreeStruct(mng_info,&have_mng_structure);
11592 if (logging != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11598 #if defined(JNG_SUPPORTED)
11600 /* Write one JNG image */
11601 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11602 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11623 jng_alpha_compression_method,
11624 jng_alpha_sample_depth,
11632 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11633 " Enter WriteOneJNGImage()");
11635 blob=(unsigned char *) NULL;
11636 jpeg_image=(Image *) NULL;
11637 jpeg_image_info=(ImageInfo *) NULL;
11640 transparent=image_info->type==GrayscaleMatteType ||
11641 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
11643 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11645 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11647 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11648 image_info->quality;
11650 if (jng_alpha_quality >= 1000)
11651 jng_alpha_quality /= 1000;
11657 /* Create JPEG blob, image, and image_info */
11658 if (logging != MagickFalse)
11659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11660 " Creating jpeg_image_info for alpha.");
11662 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11664 if (jpeg_image_info == (ImageInfo *) NULL)
11665 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11667 if (logging != MagickFalse)
11668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11669 " Creating jpeg_image.");
11671 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11672 if (jpeg_image == (Image *) NULL)
11673 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11674 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11675 jpeg_image->matte=MagickFalse;
11676 jpeg_image->quality=jng_alpha_quality;
11677 jpeg_image_info->type=GrayscaleType;
11678 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11679 (void) AcquireUniqueFilename(jpeg_image->filename);
11680 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11681 "%s",jpeg_image->filename);
11685 jng_alpha_compression_method=0;
11687 jng_alpha_sample_depth=0;
11690 /* To do: check bit depth of PNG alpha channel */
11692 /* Check if image is grayscale. */
11693 if (image_info->type != TrueColorMatteType && image_info->type !=
11694 TrueColorType && ImageIsGray(image,exception))
11697 if (logging != MagickFalse)
11699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11700 " JNG Quality = %d",(int) jng_quality);
11701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11702 " JNG Color Type = %d",jng_color_type);
11705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11706 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11710 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11716 if (jng_alpha_compression_method==0)
11721 /* Encode alpha as a grayscale PNG blob */
11722 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11724 if (logging != MagickFalse)
11725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11726 " Creating PNG blob.");
11729 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11730 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11731 jpeg_image_info->interlace=NoInterlace;
11733 /* Exclude all ancillary chunks */
11734 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11736 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11739 /* Retrieve sample depth used */
11740 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11741 if (value != (char *) NULL)
11742 jng_alpha_sample_depth= (unsigned int) value[0];
11746 /* Encode alpha as a grayscale JPEG blob */
11748 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11751 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11752 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11753 jpeg_image_info->interlace=NoInterlace;
11754 if (logging != MagickFalse)
11755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11756 " Creating blob.");
11757 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11759 jng_alpha_sample_depth=8;
11761 if (logging != MagickFalse)
11762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11763 " Successfully read jpeg_image into a blob, length=%.20g.",
11767 /* Destroy JPEG image and image_info */
11768 jpeg_image=DestroyImage(jpeg_image);
11769 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11770 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11773 /* Write JHDR chunk */
11774 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11775 PNGType(chunk,mng_JHDR);
11776 LogPNGChunk(logging,mng_JHDR,16L);
11777 PNGLong(chunk+4,(png_uint_32) image->columns);
11778 PNGLong(chunk+8,(png_uint_32) image->rows);
11779 chunk[12]=jng_color_type;
11780 chunk[13]=8; /* sample depth */
11781 chunk[14]=8; /*jng_image_compression_method */
11782 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11783 chunk[16]=jng_alpha_sample_depth;
11784 chunk[17]=jng_alpha_compression_method;
11785 chunk[18]=0; /*jng_alpha_filter_method */
11786 chunk[19]=0; /*jng_alpha_interlace_method */
11787 (void) WriteBlob(image,20,chunk);
11788 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11789 if (logging != MagickFalse)
11791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792 " JNG width:%15lu",(unsigned long) image->columns);
11794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG height:%14lu",(unsigned long) image->rows);
11797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG color type:%10d",jng_color_type);
11800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG sample depth:%8d",8);
11803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG compression:%9d",8);
11806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG interlace:%11d",0);
11809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG alpha compression:%3d",jng_alpha_compression_method);
11815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " JNG alpha filter:%8d",0);
11818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG alpha interlace:%5d",0);
11822 /* Write any JNG-chunk-b profiles */
11823 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11826 Write leading ancillary chunks
11832 Write JNG bKGD chunk
11843 if (jng_color_type == 8 || jng_color_type == 12)
11847 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11848 PNGType(chunk,mng_bKGD);
11849 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11850 red=ScaleQuantumToChar(image->background_color.red);
11851 green=ScaleQuantumToChar(image->background_color.green);
11852 blue=ScaleQuantumToChar(image->background_color.blue);
11859 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11860 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11863 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11866 Write JNG sRGB chunk
11868 (void) WriteBlobMSBULong(image,1L);
11869 PNGType(chunk,mng_sRGB);
11870 LogPNGChunk(logging,mng_sRGB,1L);
11872 if (image->rendering_intent != UndefinedIntent)
11873 chunk[4]=(unsigned char)
11874 Magick_RenderingIntent_to_PNG_RenderingIntent(
11875 (image->rendering_intent));
11878 chunk[4]=(unsigned char)
11879 Magick_RenderingIntent_to_PNG_RenderingIntent(
11880 (PerceptualIntent));
11882 (void) WriteBlob(image,5,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11887 if (image->gamma != 0.0)
11890 Write JNG gAMA chunk
11892 (void) WriteBlobMSBULong(image,4L);
11893 PNGType(chunk,mng_gAMA);
11894 LogPNGChunk(logging,mng_gAMA,4L);
11895 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11896 (void) WriteBlob(image,8,chunk);
11897 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11900 if ((mng_info->equal_chrms == MagickFalse) &&
11901 (image->chromaticity.red_primary.x != 0.0))
11907 Write JNG cHRM chunk
11909 (void) WriteBlobMSBULong(image,32L);
11910 PNGType(chunk,mng_cHRM);
11911 LogPNGChunk(logging,mng_cHRM,32L);
11912 primary=image->chromaticity.white_point;
11913 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11914 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11915 primary=image->chromaticity.red_primary;
11916 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11917 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11918 primary=image->chromaticity.green_primary;
11919 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11920 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11921 primary=image->chromaticity.blue_primary;
11922 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11923 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11924 (void) WriteBlob(image,36,chunk);
11925 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11929 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
11932 Write JNG pHYs chunk
11934 (void) WriteBlobMSBULong(image,9L);
11935 PNGType(chunk,mng_pHYs);
11936 LogPNGChunk(logging,mng_pHYs,9L);
11937 if (image->units == PixelsPerInchResolution)
11939 PNGLong(chunk+4,(png_uint_32)
11940 (image->resolution.x*100.0/2.54+0.5));
11942 PNGLong(chunk+8,(png_uint_32)
11943 (image->resolution.y*100.0/2.54+0.5));
11950 if (image->units == PixelsPerCentimeterResolution)
11952 PNGLong(chunk+4,(png_uint_32)
11953 (image->resolution.x*100.0+0.5));
11955 PNGLong(chunk+8,(png_uint_32)
11956 (image->resolution.y*100.0+0.5));
11963 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11964 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
11968 (void) WriteBlob(image,13,chunk);
11969 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11972 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11975 Write JNG oFFs chunk
11977 (void) WriteBlobMSBULong(image,9L);
11978 PNGType(chunk,mng_oFFs);
11979 LogPNGChunk(logging,mng_oFFs,9L);
11980 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11981 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11983 (void) WriteBlob(image,13,chunk);
11984 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11986 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11988 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11989 PNGType(chunk,mng_vpAg);
11990 LogPNGChunk(logging,mng_vpAg,9L);
11991 PNGLong(chunk+4,(png_uint_32) image->page.width);
11992 PNGLong(chunk+8,(png_uint_32) image->page.height);
11993 chunk[12]=0; /* unit = pixels */
11994 (void) WriteBlob(image,13,chunk);
11995 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12001 if (jng_alpha_compression_method==0)
12009 /* Write IDAT chunk header */
12010 if (logging != MagickFalse)
12011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12012 " Write IDAT chunks from blob, length=%.20g.",(double)
12015 /* Copy IDAT chunks */
12018 for (i=8; i<(ssize_t) length; i+=len+12)
12020 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12023 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12025 /* Found an IDAT chunk. */
12026 (void) WriteBlobMSBULong(image,(size_t) len);
12027 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12028 (void) WriteBlob(image,(size_t) len+4,p);
12029 (void) WriteBlobMSBULong(image,
12030 crc32(0,p,(uInt) len+4));
12035 if (logging != MagickFalse)
12036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12037 " Skipping %c%c%c%c chunk, length=%.20g.",
12038 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12045 /* Write JDAA chunk header */
12046 if (logging != MagickFalse)
12047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12048 " Write JDAA chunk, length=%.20g.",(double) length);
12049 (void) WriteBlobMSBULong(image,(size_t) length);
12050 PNGType(chunk,mng_JDAA);
12051 LogPNGChunk(logging,mng_JDAA,length);
12052 /* Write JDAT chunk(s) data */
12053 (void) WriteBlob(image,4,chunk);
12054 (void) WriteBlob(image,length,blob);
12055 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12058 blob=(unsigned char *) RelinquishMagickMemory(blob);
12061 /* Encode image as a JPEG blob */
12062 if (logging != MagickFalse)
12063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12064 " Creating jpeg_image_info.");
12065 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12066 if (jpeg_image_info == (ImageInfo *) NULL)
12067 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12069 if (logging != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071 " Creating jpeg_image.");
12073 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12074 if (jpeg_image == (Image *) NULL)
12075 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12076 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12078 (void) AcquireUniqueFilename(jpeg_image->filename);
12079 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12080 jpeg_image->filename);
12082 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12088 (double) jpeg_image->rows);
12090 if (jng_color_type == 8 || jng_color_type == 12)
12091 jpeg_image_info->type=GrayscaleType;
12093 jpeg_image_info->quality=jng_quality;
12094 jpeg_image->quality=jng_quality;
12095 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12096 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12098 if (logging != MagickFalse)
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " Creating blob.");
12102 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12104 if (logging != MagickFalse)
12106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12107 " Successfully read jpeg_image into a blob, length=%.20g.",
12110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12111 " Write JDAT chunk, length=%.20g.",(double) length);
12114 /* Write JDAT chunk(s) */
12115 (void) WriteBlobMSBULong(image,(size_t) length);
12116 PNGType(chunk,mng_JDAT);
12117 LogPNGChunk(logging,mng_JDAT,length);
12118 (void) WriteBlob(image,4,chunk);
12119 (void) WriteBlob(image,length,blob);
12120 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12122 jpeg_image=DestroyImage(jpeg_image);
12123 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12124 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12125 blob=(unsigned char *) RelinquishMagickMemory(blob);
12127 /* Write any JNG-chunk-e profiles */
12128 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12130 /* Write IEND chunk */
12131 (void) WriteBlobMSBULong(image,0L);
12132 PNGType(chunk,mng_IEND);
12133 LogPNGChunk(logging,mng_IEND,0);
12134 (void) WriteBlob(image,4,chunk);
12135 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12137 if (logging != MagickFalse)
12138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12139 " exit WriteOneJNGImage()");
12146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12150 % W r i t e J N G I m a g e %
12154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12156 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12158 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12160 % The format of the WriteJNGImage method is:
12162 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12163 % Image *image,ExceptionInfo *exception)
12165 % A description of each parameter follows:
12167 % o image_info: the image info.
12169 % o image: The image.
12171 % o exception: return any errors or warnings in this structure.
12173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12175 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12176 ExceptionInfo *exception)
12179 have_mng_structure,
12189 assert(image_info != (const ImageInfo *) NULL);
12190 assert(image_info->signature == MagickSignature);
12191 assert(image != (Image *) NULL);
12192 assert(image->signature == MagickSignature);
12193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12194 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12195 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12196 if (status == MagickFalse)
12200 Allocate a MngInfo structure.
12202 have_mng_structure=MagickFalse;
12203 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12204 if (mng_info == (MngInfo *) NULL)
12205 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12207 Initialize members of the MngInfo structure.
12209 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12210 mng_info->image=image;
12211 have_mng_structure=MagickTrue;
12213 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12215 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12216 (void) CloseBlob(image);
12218 (void) CatchImageException(image);
12219 MngInfoFreeStruct(mng_info,&have_mng_structure);
12220 if (logging != MagickFalse)
12221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12226 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12227 ExceptionInfo *exception)
12236 have_mng_structure,
12239 volatile MagickBooleanType
12251 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12252 defined(PNG_MNG_FEATURES_SUPPORTED)
12255 all_images_are_gray,
12265 volatile unsigned int
12276 #if (PNG_LIBPNG_VER < 10200)
12277 if (image_info->verbose)
12278 printf("Your PNG library (libpng-%s) is rather old.\n",
12279 PNG_LIBPNG_VER_STRING);
12285 assert(image_info != (const ImageInfo *) NULL);
12286 assert(image_info->signature == MagickSignature);
12287 assert(image != (Image *) NULL);
12288 assert(image->signature == MagickSignature);
12289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12290 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12291 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12292 if (status == MagickFalse)
12296 Allocate a MngInfo structure.
12298 have_mng_structure=MagickFalse;
12299 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12300 if (mng_info == (MngInfo *) NULL)
12301 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12303 Initialize members of the MngInfo structure.
12305 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12306 mng_info->image=image;
12307 have_mng_structure=MagickTrue;
12308 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12311 * See if user has requested a specific PNG subformat to be used
12312 * for all of the PNGs in the MNG being written, e.g.,
12314 * convert *.png png8:animation.mng
12316 * To do: check -define png:bit_depth and png:color_type as well,
12317 * or perhaps use mng:bit_depth and mng:color_type instead for
12321 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12322 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12323 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12325 write_jng=MagickFalse;
12326 if (image_info->compression == JPEGCompression)
12327 write_jng=MagickTrue;
12329 mng_info->adjoin=image_info->adjoin &&
12330 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12332 if (logging != MagickFalse)
12334 /* Log some info about the input */
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " Checking input image(s)");
12341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12342 " Image_info depth: %.20g",(double) image_info->depth);
12344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12345 " Type: %d",image_info->type);
12348 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " Scene: %.20g",(double) scene++);
12353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354 " Image depth: %.20g",(double) p->depth);
12357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12364 if (p->storage_class == PseudoClass)
12365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12366 " Storage class: PseudoClass");
12369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12370 " Storage class: DirectClass");
12373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12374 " Number of colors: %.20g",(double) p->colors);
12377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12378 " Number of colors: unspecified");
12380 if (mng_info->adjoin == MagickFalse)
12385 use_global_plte=MagickFalse;
12386 all_images_are_gray=MagickFalse;
12387 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12388 need_local_plte=MagickTrue;
12390 need_defi=MagickFalse;
12391 need_matte=MagickFalse;
12392 mng_info->framing_mode=1;
12393 mng_info->old_framing_mode=1;
12396 if (image_info->page != (char *) NULL)
12399 Determine image bounding box.
12401 SetGeometry(image,&mng_info->page);
12402 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12403 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12415 mng_info->page=image->page;
12416 need_geom=MagickTrue;
12417 if (mng_info->page.width || mng_info->page.height)
12418 need_geom=MagickFalse;
12420 Check all the scenes.
12422 initial_delay=image->delay;
12423 need_iterations=MagickFalse;
12424 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12425 mng_info->equal_physs=MagickTrue,
12426 mng_info->equal_gammas=MagickTrue;
12427 mng_info->equal_srgbs=MagickTrue;
12428 mng_info->equal_backgrounds=MagickTrue;
12430 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12431 defined(PNG_MNG_FEATURES_SUPPORTED)
12432 all_images_are_gray=MagickTrue;
12433 mng_info->equal_palettes=MagickFalse;
12434 need_local_plte=MagickFalse;
12436 for (next_image=image; next_image != (Image *) NULL; )
12440 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12441 mng_info->page.width=next_image->columns+next_image->page.x;
12443 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12444 mng_info->page.height=next_image->rows+next_image->page.y;
12447 if (next_image->page.x || next_image->page.y)
12448 need_defi=MagickTrue;
12450 if (next_image->matte)
12451 need_matte=MagickTrue;
12453 if ((int) next_image->dispose >= BackgroundDispose)
12454 if (next_image->matte || next_image->page.x || next_image->page.y ||
12455 ((next_image->columns < mng_info->page.width) &&
12456 (next_image->rows < mng_info->page.height)))
12457 mng_info->need_fram=MagickTrue;
12459 if (next_image->iterations)
12460 need_iterations=MagickTrue;
12462 final_delay=next_image->delay;
12464 if (final_delay != initial_delay || final_delay > 1UL*
12465 next_image->ticks_per_second)
12466 mng_info->need_fram=1;
12468 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12469 defined(PNG_MNG_FEATURES_SUPPORTED)
12471 check for global palette possibility.
12473 if (image->matte != MagickFalse)
12474 need_local_plte=MagickTrue;
12476 if (need_local_plte == 0)
12478 if (ImageIsGray(image,exception) == MagickFalse)
12479 all_images_are_gray=MagickFalse;
12480 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12481 if (use_global_plte == 0)
12482 use_global_plte=mng_info->equal_palettes;
12483 need_local_plte=!mng_info->equal_palettes;
12486 if (GetNextImageInList(next_image) != (Image *) NULL)
12488 if (next_image->background_color.red !=
12489 next_image->next->background_color.red ||
12490 next_image->background_color.green !=
12491 next_image->next->background_color.green ||
12492 next_image->background_color.blue !=
12493 next_image->next->background_color.blue)
12494 mng_info->equal_backgrounds=MagickFalse;
12496 if (next_image->gamma != next_image->next->gamma)
12497 mng_info->equal_gammas=MagickFalse;
12499 if (next_image->rendering_intent !=
12500 next_image->next->rendering_intent)
12501 mng_info->equal_srgbs=MagickFalse;
12503 if ((next_image->units != next_image->next->units) ||
12504 (next_image->resolution.x != next_image->next->resolution.x) ||
12505 (next_image->resolution.y != next_image->next->resolution.y))
12506 mng_info->equal_physs=MagickFalse;
12508 if (mng_info->equal_chrms)
12510 if (next_image->chromaticity.red_primary.x !=
12511 next_image->next->chromaticity.red_primary.x ||
12512 next_image->chromaticity.red_primary.y !=
12513 next_image->next->chromaticity.red_primary.y ||
12514 next_image->chromaticity.green_primary.x !=
12515 next_image->next->chromaticity.green_primary.x ||
12516 next_image->chromaticity.green_primary.y !=
12517 next_image->next->chromaticity.green_primary.y ||
12518 next_image->chromaticity.blue_primary.x !=
12519 next_image->next->chromaticity.blue_primary.x ||
12520 next_image->chromaticity.blue_primary.y !=
12521 next_image->next->chromaticity.blue_primary.y ||
12522 next_image->chromaticity.white_point.x !=
12523 next_image->next->chromaticity.white_point.x ||
12524 next_image->chromaticity.white_point.y !=
12525 next_image->next->chromaticity.white_point.y)
12526 mng_info->equal_chrms=MagickFalse;
12530 next_image=GetNextImageInList(next_image);
12532 if (image_count < 2)
12534 mng_info->equal_backgrounds=MagickFalse;
12535 mng_info->equal_chrms=MagickFalse;
12536 mng_info->equal_gammas=MagickFalse;
12537 mng_info->equal_srgbs=MagickFalse;
12538 mng_info->equal_physs=MagickFalse;
12539 use_global_plte=MagickFalse;
12540 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12541 need_local_plte=MagickTrue;
12543 need_iterations=MagickFalse;
12546 if (mng_info->need_fram == MagickFalse)
12549 Only certain framing rates 100/n are exactly representable without
12550 the FRAM chunk but we'll allow some slop in VLC files
12552 if (final_delay == 0)
12554 if (need_iterations != MagickFalse)
12557 It's probably a GIF with loop; don't run it *too* fast.
12559 if (mng_info->adjoin)
12562 (void) ThrowMagickException(exception,GetMagickModule(),
12564 "input has zero delay between all frames; assuming",
12569 mng_info->ticks_per_second=0;
12571 if (final_delay != 0)
12572 mng_info->ticks_per_second=(png_uint_32)
12573 (image->ticks_per_second/final_delay);
12574 if (final_delay > 50)
12575 mng_info->ticks_per_second=2;
12577 if (final_delay > 75)
12578 mng_info->ticks_per_second=1;
12580 if (final_delay > 125)
12581 mng_info->need_fram=MagickTrue;
12583 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12584 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12585 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12586 1UL*image->ticks_per_second))
12587 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12590 if (mng_info->need_fram != MagickFalse)
12591 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12593 If pseudocolor, we should also check to see if all the
12594 palettes are identical and write a global PLTE if they are.
12598 Write the MNG version 1.0 signature and MHDR chunk.
12600 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12601 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12602 PNGType(chunk,mng_MHDR);
12603 LogPNGChunk(logging,mng_MHDR,28L);
12604 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12605 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12606 PNGLong(chunk+12,mng_info->ticks_per_second);
12607 PNGLong(chunk+16,0L); /* layer count=unknown */
12608 PNGLong(chunk+20,0L); /* frame count=unknown */
12609 PNGLong(chunk+24,0L); /* play time=unknown */
12614 if (need_defi || mng_info->need_fram || use_global_plte)
12615 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12618 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12623 if (need_defi || mng_info->need_fram || use_global_plte)
12624 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12627 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12635 if (need_defi || mng_info->need_fram || use_global_plte)
12636 PNGLong(chunk+28,11L); /* simplicity=LC */
12639 PNGLong(chunk+28,9L); /* simplicity=VLC */
12644 if (need_defi || mng_info->need_fram || use_global_plte)
12645 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12648 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12651 (void) WriteBlob(image,32,chunk);
12652 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12653 option=GetImageOption(image_info,"mng:need-cacheoff");
12654 if (option != (const char *) NULL)
12660 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12662 PNGType(chunk,mng_nEED);
12663 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12664 (void) WriteBlobMSBULong(image,(size_t) length);
12665 LogPNGChunk(logging,mng_nEED,(size_t) length);
12667 (void) WriteBlob(image,length,chunk);
12668 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12670 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12671 (GetNextImageInList(image) != (Image *) NULL) &&
12672 (image->iterations != 1))
12675 Write MNG TERM chunk
12677 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12678 PNGType(chunk,mng_TERM);
12679 LogPNGChunk(logging,mng_TERM,10L);
12680 chunk[4]=3; /* repeat animation */
12681 chunk[5]=0; /* show last frame when done */
12682 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12683 final_delay/MagickMax(image->ticks_per_second,1)));
12685 if (image->iterations == 0)
12686 PNGLong(chunk+10,PNG_UINT_31_MAX);
12689 PNGLong(chunk+10,(png_uint_32) image->iterations);
12691 if (logging != MagickFalse)
12693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12694 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12695 final_delay/MagickMax(image->ticks_per_second,1)));
12697 if (image->iterations == 0)
12698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12699 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12703 " Image iterations: %.20g",(double) image->iterations);
12705 (void) WriteBlob(image,14,chunk);
12706 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12709 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12711 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12712 mng_info->equal_srgbs)
12715 Write MNG sRGB chunk
12717 (void) WriteBlobMSBULong(image,1L);
12718 PNGType(chunk,mng_sRGB);
12719 LogPNGChunk(logging,mng_sRGB,1L);
12721 if (image->rendering_intent != UndefinedIntent)
12722 chunk[4]=(unsigned char)
12723 Magick_RenderingIntent_to_PNG_RenderingIntent(
12724 (image->rendering_intent));
12727 chunk[4]=(unsigned char)
12728 Magick_RenderingIntent_to_PNG_RenderingIntent(
12729 (PerceptualIntent));
12731 (void) WriteBlob(image,5,chunk);
12732 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12733 mng_info->have_write_global_srgb=MagickTrue;
12738 if (image->gamma && mng_info->equal_gammas)
12741 Write MNG gAMA chunk
12743 (void) WriteBlobMSBULong(image,4L);
12744 PNGType(chunk,mng_gAMA);
12745 LogPNGChunk(logging,mng_gAMA,4L);
12746 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12747 (void) WriteBlob(image,8,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12749 mng_info->have_write_global_gama=MagickTrue;
12751 if (mng_info->equal_chrms)
12757 Write MNG cHRM chunk
12759 (void) WriteBlobMSBULong(image,32L);
12760 PNGType(chunk,mng_cHRM);
12761 LogPNGChunk(logging,mng_cHRM,32L);
12762 primary=image->chromaticity.white_point;
12763 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12764 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12765 primary=image->chromaticity.red_primary;
12766 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12767 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12768 primary=image->chromaticity.green_primary;
12769 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12770 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12771 primary=image->chromaticity.blue_primary;
12772 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12773 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12774 (void) WriteBlob(image,36,chunk);
12775 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12776 mng_info->have_write_global_chrm=MagickTrue;
12779 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12782 Write MNG pHYs chunk
12784 (void) WriteBlobMSBULong(image,9L);
12785 PNGType(chunk,mng_pHYs);
12786 LogPNGChunk(logging,mng_pHYs,9L);
12788 if (image->units == PixelsPerInchResolution)
12790 PNGLong(chunk+4,(png_uint_32)
12791 (image->resolution.x*100.0/2.54+0.5));
12793 PNGLong(chunk+8,(png_uint_32)
12794 (image->resolution.y*100.0/2.54+0.5));
12801 if (image->units == PixelsPerCentimeterResolution)
12803 PNGLong(chunk+4,(png_uint_32)
12804 (image->resolution.x*100.0+0.5));
12806 PNGLong(chunk+8,(png_uint_32)
12807 (image->resolution.y*100.0+0.5));
12814 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12815 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12819 (void) WriteBlob(image,13,chunk);
12820 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12823 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12824 or does not cover the entire frame.
12826 if (write_mng && (image->matte || image->page.x > 0 ||
12827 image->page.y > 0 || (image->page.width &&
12828 (image->page.width+image->page.x < mng_info->page.width))
12829 || (image->page.height && (image->page.height+image->page.y
12830 < mng_info->page.height))))
12832 (void) WriteBlobMSBULong(image,6L);
12833 PNGType(chunk,mng_BACK);
12834 LogPNGChunk(logging,mng_BACK,6L);
12835 red=ScaleQuantumToShort(image->background_color.red);
12836 green=ScaleQuantumToShort(image->background_color.green);
12837 blue=ScaleQuantumToShort(image->background_color.blue);
12838 PNGShort(chunk+4,red);
12839 PNGShort(chunk+6,green);
12840 PNGShort(chunk+8,blue);
12841 (void) WriteBlob(image,10,chunk);
12842 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12843 if (mng_info->equal_backgrounds)
12845 (void) WriteBlobMSBULong(image,6L);
12846 PNGType(chunk,mng_bKGD);
12847 LogPNGChunk(logging,mng_bKGD,6L);
12848 (void) WriteBlob(image,10,chunk);
12849 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12853 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12854 if ((need_local_plte == MagickFalse) &&
12855 (image->storage_class == PseudoClass) &&
12856 (all_images_are_gray == MagickFalse))
12862 Write MNG PLTE chunk
12864 data_length=3*image->colors;
12865 (void) WriteBlobMSBULong(image,data_length);
12866 PNGType(chunk,mng_PLTE);
12867 LogPNGChunk(logging,mng_PLTE,data_length);
12869 for (i=0; i < (ssize_t) image->colors; i++)
12871 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12872 image->colormap[i].red) & 0xff);
12873 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12874 image->colormap[i].green) & 0xff);
12875 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12876 image->colormap[i].blue) & 0xff);
12879 (void) WriteBlob(image,data_length+4,chunk);
12880 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12881 mng_info->have_write_global_plte=MagickTrue;
12887 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12888 defined(PNG_MNG_FEATURES_SUPPORTED)
12889 mng_info->equal_palettes=MagickFalse;
12893 if (mng_info->adjoin)
12895 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12896 defined(PNG_MNG_FEATURES_SUPPORTED)
12898 If we aren't using a global palette for the entire MNG, check to
12899 see if we can use one for two or more consecutive images.
12901 if (need_local_plte && use_global_plte && !all_images_are_gray)
12903 if (mng_info->IsPalette)
12906 When equal_palettes is true, this image has the same palette
12907 as the previous PseudoClass image
12909 mng_info->have_write_global_plte=mng_info->equal_palettes;
12910 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12911 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12914 Write MNG PLTE chunk
12919 data_length=3*image->colors;
12920 (void) WriteBlobMSBULong(image,data_length);
12921 PNGType(chunk,mng_PLTE);
12922 LogPNGChunk(logging,mng_PLTE,data_length);
12924 for (i=0; i < (ssize_t) image->colors; i++)
12926 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12927 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12928 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12931 (void) WriteBlob(image,data_length+4,chunk);
12932 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12933 (uInt) (data_length+4)));
12934 mng_info->have_write_global_plte=MagickTrue;
12938 mng_info->have_write_global_plte=MagickFalse;
12949 previous_x=mng_info->page.x;
12950 previous_y=mng_info->page.y;
12957 mng_info->page=image->page;
12958 if ((mng_info->page.x != previous_x) ||
12959 (mng_info->page.y != previous_y))
12961 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12962 PNGType(chunk,mng_DEFI);
12963 LogPNGChunk(logging,mng_DEFI,12L);
12964 chunk[4]=0; /* object 0 MSB */
12965 chunk[5]=0; /* object 0 LSB */
12966 chunk[6]=0; /* visible */
12967 chunk[7]=0; /* abstract */
12968 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12969 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12970 (void) WriteBlob(image,16,chunk);
12971 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12976 mng_info->write_mng=write_mng;
12978 if ((int) image->dispose >= 3)
12979 mng_info->framing_mode=3;
12981 if (mng_info->need_fram && mng_info->adjoin &&
12982 ((image->delay != mng_info->delay) ||
12983 (mng_info->framing_mode != mng_info->old_framing_mode)))
12985 if (image->delay == mng_info->delay)
12988 Write a MNG FRAM chunk with the new framing mode.
12990 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12991 PNGType(chunk,mng_FRAM);
12992 LogPNGChunk(logging,mng_FRAM,1L);
12993 chunk[4]=(unsigned char) mng_info->framing_mode;
12994 (void) WriteBlob(image,5,chunk);
12995 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13000 Write a MNG FRAM chunk with the delay.
13002 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13003 PNGType(chunk,mng_FRAM);
13004 LogPNGChunk(logging,mng_FRAM,10L);
13005 chunk[4]=(unsigned char) mng_info->framing_mode;
13006 chunk[5]=0; /* frame name separator (no name) */
13007 chunk[6]=2; /* flag for changing default delay */
13008 chunk[7]=0; /* flag for changing frame timeout */
13009 chunk[8]=0; /* flag for changing frame clipping */
13010 chunk[9]=0; /* flag for changing frame sync_id */
13011 PNGLong(chunk+10,(png_uint_32)
13012 ((mng_info->ticks_per_second*
13013 image->delay)/MagickMax(image->ticks_per_second,1)));
13014 (void) WriteBlob(image,14,chunk);
13015 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13016 mng_info->delay=(png_uint_32) image->delay;
13018 mng_info->old_framing_mode=mng_info->framing_mode;
13021 #if defined(JNG_SUPPORTED)
13022 if (image_info->compression == JPEGCompression)
13027 if (logging != MagickFalse)
13028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13029 " Writing JNG object.");
13030 /* To do: specify the desired alpha compression method. */
13031 write_info=CloneImageInfo(image_info);
13032 write_info->compression=UndefinedCompression;
13033 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13034 write_info=DestroyImageInfo(write_info);
13039 if (logging != MagickFalse)
13040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13041 " Writing PNG object.");
13043 mng_info->need_blob = MagickFalse;
13044 mng_info->ping_preserve_colormap = MagickFalse;
13046 /* We don't want any ancillary chunks written */
13047 mng_info->ping_exclude_bKGD=MagickTrue;
13048 mng_info->ping_exclude_cHRM=MagickTrue;
13049 mng_info->ping_exclude_date=MagickTrue;
13050 mng_info->ping_exclude_EXIF=MagickTrue;
13051 mng_info->ping_exclude_gAMA=MagickTrue;
13052 mng_info->ping_exclude_iCCP=MagickTrue;
13053 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13054 mng_info->ping_exclude_oFFs=MagickTrue;
13055 mng_info->ping_exclude_pHYs=MagickTrue;
13056 mng_info->ping_exclude_sRGB=MagickTrue;
13057 mng_info->ping_exclude_tEXt=MagickTrue;
13058 mng_info->ping_exclude_tRNS=MagickTrue;
13059 mng_info->ping_exclude_vpAg=MagickTrue;
13060 mng_info->ping_exclude_zCCP=MagickTrue;
13061 mng_info->ping_exclude_zTXt=MagickTrue;
13063 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13066 if (status == MagickFalse)
13068 MngInfoFreeStruct(mng_info,&have_mng_structure);
13069 (void) CloseBlob(image);
13070 return(MagickFalse);
13072 (void) CatchImageException(image);
13073 if (GetNextImageInList(image) == (Image *) NULL)
13075 image=SyncNextImageInList(image);
13076 status=SetImageProgress(image,SaveImagesTag,scene++,
13077 GetImageListLength(image));
13079 if (status == MagickFalse)
13082 } while (mng_info->adjoin);
13086 while (GetPreviousImageInList(image) != (Image *) NULL)
13087 image=GetPreviousImageInList(image);
13089 Write the MEND chunk.
13091 (void) WriteBlobMSBULong(image,0x00000000L);
13092 PNGType(chunk,mng_MEND);
13093 LogPNGChunk(logging,mng_MEND,0L);
13094 (void) WriteBlob(image,4,chunk);
13095 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13098 Relinquish resources.
13100 (void) CloseBlob(image);
13101 MngInfoFreeStruct(mng_info,&have_mng_structure);
13103 if (logging != MagickFalse)
13104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13106 return(MagickTrue);
13108 #else /* PNG_LIBPNG_VER > 10011 */
13110 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13113 printf("Your PNG library is too old: You have libpng-%s\n",
13114 PNG_LIBPNG_VER_STRING);
13116 ThrowBinaryException(CoderError,"PNG library is too old",
13117 image_info->filename);
13120 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13122 return(WritePNGImage(image_info,image));
13124 #endif /* PNG_LIBPNG_VER > 10011 */