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 #if defined(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(Image *image, const ImageInfo *image_info,
1812 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 (void) ThrowMagickException(exception,GetMagickModule(),
1859 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1860 return(MagickFalse);
1863 profile=BlobToStringInfo((const void *) NULL,length);
1865 if (profile == (StringInfo *) NULL)
1867 (void) ThrowMagickException(exception,GetMagickModule(),
1868 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1869 "unable to copy profile");
1870 return(MagickFalse);
1873 /* copy profile, skipping white space and column 1 "=" signs */
1874 dp=GetStringInfoDatum(profile);
1877 for (i=0; i < (ssize_t) nibbles; i++)
1879 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1883 (void) ThrowMagickException(exception,GetMagickModule(),
1884 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1885 profile=DestroyStringInfo(profile);
1886 return(MagickFalse);
1892 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1895 (*dp++)+=unhex[(int) *sp++];
1898 We have already read "Raw profile type.
1900 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1901 profile=DestroyStringInfo(profile);
1903 if (image_info->verbose)
1904 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1909 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1910 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1916 /* The unknown chunk structure contains the chunk data:
1921 Note that libpng has already taken care of the CRC handling.
1925 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1926 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1927 return(0); /* Did not recognize */
1929 /* recognized vpAg */
1931 if (chunk->size != 9)
1932 return(-1); /* Error return */
1934 if (chunk->data[8] != 0)
1935 return(0); /* ImageMagick requires pixel units */
1937 image=(Image *) png_get_user_chunk_ptr(ping);
1939 image->page.width=(size_t) ((chunk->data[0] << 24) |
1940 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1942 image->page.height=(size_t) ((chunk->data[4] << 24) |
1943 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1945 /* Return one of the following: */
1946 /* return(-n); chunk had an error */
1947 /* return(0); did not recognize */
1948 /* return(n); success */
1956 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1960 % R e a d O n e P N G I m a g e %
1964 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1966 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1967 % (minus the 8-byte signature) and returns it. It allocates the memory
1968 % necessary for the new Image structure and returns a pointer to the new
1971 % The format of the ReadOnePNGImage method is:
1973 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1974 % ExceptionInfo *exception)
1976 % A description of each parameter follows:
1978 % o mng_info: Specifies a pointer to a MngInfo structure.
1980 % o image_info: the image info.
1982 % o exception: return any errors or warnings in this structure.
1985 static Image *ReadOnePNGImage(MngInfo *mng_info,
1986 const ImageInfo *image_info, ExceptionInfo *exception)
1988 /* Read one PNG image */
1990 /* To do: Read the tIME chunk into the date:modify property */
1991 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2005 ping_interlace_method,
2006 ping_compression_method,
2057 register unsigned char
2074 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2075 png_byte unused_chunks[]=
2077 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2078 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2079 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2080 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2081 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2082 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2086 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2087 " Enter ReadOnePNGImage()");
2089 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2090 LockSemaphoreInfo(ping_semaphore);
2093 #if (PNG_LIBPNG_VER < 10200)
2094 if (image_info->verbose)
2095 printf("Your PNG library (libpng-%s) is rather old.\n",
2096 PNG_LIBPNG_VER_STRING);
2099 #if (PNG_LIBPNG_VER >= 10400)
2100 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2101 if (image_info->verbose)
2103 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2104 PNG_LIBPNG_VER_STRING);
2105 printf("Please update it.\n");
2111 quantum_info = (QuantumInfo *) NULL;
2112 image=mng_info->image;
2114 if (logging != MagickFalse)
2115 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2116 " image->matte=%d",(int) image->matte);
2118 /* Set to an out-of-range color unless tRNS chunk is present */
2119 transparent_color.red=65537;
2120 transparent_color.green=65537;
2121 transparent_color.blue=65537;
2122 transparent_color.alpha=65537;
2126 num_raw_profiles = 0;
2129 Allocate the PNG structures
2131 #ifdef PNG_USER_MEM_SUPPORTED
2132 error_info.image=image;
2133 error_info.exception=exception;
2134 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2135 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2136 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2138 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2139 MagickPNGErrorHandler,MagickPNGWarningHandler);
2141 if (ping == (png_struct *) NULL)
2142 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2144 ping_info=png_create_info_struct(ping);
2146 if (ping_info == (png_info *) NULL)
2148 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2149 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152 end_info=png_create_info_struct(ping);
2154 if (end_info == (png_info *) NULL)
2156 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2157 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160 ping_pixels=(unsigned char *) NULL;
2162 if (setjmp(png_jmpbuf(ping)))
2165 PNG image is corrupt.
2167 png_destroy_read_struct(&ping,&ping_info,&end_info);
2168 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2169 UnlockSemaphoreInfo(ping_semaphore);
2171 if (logging != MagickFalse)
2172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2173 " exit ReadOnePNGImage() with error.");
2175 if (image != (Image *) NULL)
2177 InheritException(exception,exception);
2181 return(GetFirstImageInList(image));
2184 Prepare PNG for reading.
2187 mng_info->image_found++;
2188 png_set_sig_bytes(ping,8);
2190 if (LocaleCompare(image_info->magick,"MNG") == 0)
2192 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2193 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2194 png_set_read_fn(ping,image,png_get_data);
2196 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2197 png_permit_empty_plte(ping,MagickTrue);
2198 png_set_read_fn(ping,image,png_get_data);
2200 mng_info->image=image;
2201 mng_info->bytes_in_read_buffer=0;
2202 mng_info->found_empty_plte=MagickFalse;
2203 mng_info->have_saved_bkgd_index=MagickFalse;
2204 png_set_read_fn(ping,mng_info,mng_get_data);
2210 png_set_read_fn(ping,image,png_get_data);
2212 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2213 /* Ignore unused chunks and all unknown chunks except for vpAg */
2214 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2215 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2216 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2217 (int)sizeof(unused_chunks)/5);
2218 /* Callback for other unknown chunks */
2219 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2222 #if (PNG_LIBPNG_VER < 10400)
2223 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2224 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2225 /* Disable thread-unsafe features of pnggccrd */
2226 if (png_access_version_number() >= 10200)
2228 png_uint_32 mmx_disable_mask=0;
2229 png_uint_32 asm_flags;
2231 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2232 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2233 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2234 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2235 asm_flags=png_get_asm_flags(ping);
2236 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2241 png_read_info(ping,ping_info);
2243 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2244 &ping_bit_depth,&ping_color_type,
2245 &ping_interlace_method,&ping_compression_method,
2246 &ping_filter_method);
2248 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2251 (void) png_get_bKGD(ping, ping_info, &ping_background);
2253 if (ping_bit_depth < 8)
2255 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2257 png_set_packing(ping);
2262 image->depth=ping_bit_depth;
2263 image->depth=GetImageQuantumDepth(image,MagickFalse);
2264 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2265 if (logging != MagickFalse)
2267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2268 " PNG width: %.20g, height: %.20g",
2269 (double) ping_width, (double) ping_height);
2271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2272 " PNG color_type: %d, bit_depth: %d",
2273 ping_color_type, ping_bit_depth);
2275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2276 " PNG compression_method: %d",
2277 ping_compression_method);
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2280 " PNG interlace_method: %d, filter_method: %d",
2281 ping_interlace_method,ping_filter_method);
2284 #ifdef PNG_READ_iCCP_SUPPORTED
2285 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2290 #if (PNG_LIBPNG_VER < 10500)
2304 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2307 if (profile_length != 0)
2312 if (logging != MagickFalse)
2313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2314 " Reading PNG iCCP chunk.");
2315 profile=BlobToStringInfo(info,profile_length);
2316 if (profile == (StringInfo *) NULL)
2318 (void) ThrowMagickException(exception,GetMagickModule(),
2319 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2320 "unable to copy profile");
2321 return((Image *) NULL);
2323 SetStringInfoDatum(profile,(const unsigned char *) info);
2324 (void) SetImageProfile(image,"icc",profile,exception);
2325 profile=DestroyStringInfo(profile);
2329 #if defined(PNG_READ_sRGB_SUPPORTED)
2331 if (mng_info->have_global_srgb)
2333 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2334 (mng_info->global_srgb_intent);
2337 if (png_get_sRGB(ping,ping_info,&intent))
2339 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2342 if (logging != MagickFalse)
2343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2344 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2349 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2350 if (mng_info->have_global_gama)
2351 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2353 if (png_get_gAMA(ping,ping_info,&file_gamma))
2355 image->gamma=(float) file_gamma;
2356 if (logging != MagickFalse)
2357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2358 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2361 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2363 if (mng_info->have_global_chrm != MagickFalse)
2365 (void) png_set_cHRM(ping,ping_info,
2366 mng_info->global_chrm.white_point.x,
2367 mng_info->global_chrm.white_point.y,
2368 mng_info->global_chrm.red_primary.x,
2369 mng_info->global_chrm.red_primary.y,
2370 mng_info->global_chrm.green_primary.x,
2371 mng_info->global_chrm.green_primary.y,
2372 mng_info->global_chrm.blue_primary.x,
2373 mng_info->global_chrm.blue_primary.y);
2377 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2379 (void) png_get_cHRM(ping,ping_info,
2380 &image->chromaticity.white_point.x,
2381 &image->chromaticity.white_point.y,
2382 &image->chromaticity.red_primary.x,
2383 &image->chromaticity.red_primary.y,
2384 &image->chromaticity.green_primary.x,
2385 &image->chromaticity.green_primary.y,
2386 &image->chromaticity.blue_primary.x,
2387 &image->chromaticity.blue_primary.y);
2389 if (logging != MagickFalse)
2390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2391 " Reading PNG cHRM chunk.");
2394 if (image->rendering_intent != UndefinedIntent)
2396 png_set_sRGB(ping,ping_info,
2397 Magick_RenderingIntent_to_PNG_RenderingIntent
2398 (image->rendering_intent));
2399 png_set_gAMA(ping,ping_info,0.45455f);
2400 png_set_cHRM(ping,ping_info,
2401 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2402 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2404 #if defined(PNG_oFFs_SUPPORTED)
2405 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2407 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2408 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2410 if (logging != MagickFalse)
2411 if (image->page.x || image->page.y)
2412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2413 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2414 image->page.x,(double) image->page.y);
2417 #if defined(PNG_pHYs_SUPPORTED)
2418 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2420 if (mng_info->have_global_phys)
2422 png_set_pHYs(ping,ping_info,
2423 mng_info->global_x_pixels_per_unit,
2424 mng_info->global_y_pixels_per_unit,
2425 mng_info->global_phys_unit_type);
2429 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2432 Set image resolution.
2434 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2436 image->resolution.x=(double) x_resolution;
2437 image->resolution.y=(double) y_resolution;
2439 if (unit_type == PNG_RESOLUTION_METER)
2441 image->units=PixelsPerCentimeterResolution;
2442 image->resolution.x=(double) x_resolution/100.0;
2443 image->resolution.y=(double) y_resolution/100.0;
2446 if (logging != MagickFalse)
2447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2448 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2449 (double) x_resolution,(double) y_resolution,unit_type);
2453 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2461 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2463 if ((number_colors == 0) &&
2464 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2466 if (mng_info->global_plte_length)
2468 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2469 (int) mng_info->global_plte_length);
2471 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2472 if (mng_info->global_trns_length)
2474 if (mng_info->global_trns_length >
2475 mng_info->global_plte_length)
2476 (void) ThrowMagickException(exception,
2477 GetMagickModule(),CoderError,
2478 "global tRNS has more entries than global PLTE",
2479 "`%s'",image_info->filename);
2480 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2481 (int) mng_info->global_trns_length,NULL);
2483 #ifdef PNG_READ_bKGD_SUPPORTED
2485 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2486 mng_info->have_saved_bkgd_index ||
2488 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2493 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2494 if (mng_info->have_saved_bkgd_index)
2495 background.index=mng_info->saved_bkgd_index;
2497 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2498 background.index=ping_background->index;
2500 background.red=(png_uint_16)
2501 mng_info->global_plte[background.index].red;
2503 background.green=(png_uint_16)
2504 mng_info->global_plte[background.index].green;
2506 background.blue=(png_uint_16)
2507 mng_info->global_plte[background.index].blue;
2509 background.gray=(png_uint_16)
2510 mng_info->global_plte[background.index].green;
2512 png_set_bKGD(ping,ping_info,&background);
2517 (void) ThrowMagickException(exception,GetMagickModule(),
2518 CoderError,"No global PLTE in file","`%s'",
2519 image_info->filename);
2523 #ifdef PNG_READ_bKGD_SUPPORTED
2524 if (mng_info->have_global_bkgd &&
2525 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2526 image->background_color=mng_info->mng_global_bkgd;
2528 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2534 Set image background color.
2536 if (logging != MagickFalse)
2537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538 " Reading PNG bKGD chunk.");
2540 /* Scale background components to 16-bit, then scale
2543 if (logging != MagickFalse)
2544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2545 " raw ping_background=(%d,%d,%d).",ping_background->red,
2546 ping_background->green,ping_background->blue);
2550 if (ping_bit_depth == 1)
2553 else if (ping_bit_depth == 2)
2556 else if (ping_bit_depth == 4)
2559 if (ping_bit_depth <= 8)
2562 ping_background->red *= bkgd_scale;
2563 ping_background->green *= bkgd_scale;
2564 ping_background->blue *= bkgd_scale;
2566 if (logging != MagickFalse)
2568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2569 " bkgd_scale=%d.",bkgd_scale);
2571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2572 " ping_background=(%d,%d,%d).",ping_background->red,
2573 ping_background->green,ping_background->blue);
2576 image->background_color.red=
2577 ScaleShortToQuantum(ping_background->red);
2579 image->background_color.green=
2580 ScaleShortToQuantum(ping_background->green);
2582 image->background_color.blue=
2583 ScaleShortToQuantum(ping_background->blue);
2585 image->background_color.alpha=OpaqueAlpha;
2587 if (logging != MagickFalse)
2588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2589 " image->background_color=(%.20g,%.20g,%.20g).",
2590 (double) image->background_color.red,
2591 (double) image->background_color.green,
2592 (double) image->background_color.blue);
2594 #endif /* PNG_READ_bKGD_SUPPORTED */
2596 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2599 Image has a tRNS chunk.
2607 if (logging != MagickFalse)
2608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2609 " Reading PNG tRNS chunk.");
2611 max_sample = (int) ((one << ping_bit_depth) - 1);
2613 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2614 (int)ping_trans_color->gray > max_sample) ||
2615 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2616 ((int)ping_trans_color->red > max_sample ||
2617 (int)ping_trans_color->green > max_sample ||
2618 (int)ping_trans_color->blue > max_sample)))
2620 if (logging != MagickFalse)
2621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2622 " Ignoring PNG tRNS chunk with out-of-range sample.");
2623 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2624 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2625 image->matte=MagickFalse;
2632 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2634 /* Scale transparent_color to short */
2635 transparent_color.red= scale_to_short*ping_trans_color->red;
2636 transparent_color.green= scale_to_short*ping_trans_color->green;
2637 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2638 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2640 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2642 if (logging != MagickFalse)
2644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2645 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2648 " scaled graylevel is %.20g.",transparent_color.alpha);
2650 transparent_color.red=transparent_color.alpha;
2651 transparent_color.green=transparent_color.alpha;
2652 transparent_color.blue=transparent_color.alpha;
2656 #if defined(PNG_READ_sBIT_SUPPORTED)
2657 if (mng_info->have_global_sbit)
2659 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2660 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2663 num_passes=png_set_interlace_handling(ping);
2665 png_read_update_info(ping,ping_info);
2667 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2670 Initialize image structure.
2672 mng_info->image_box.left=0;
2673 mng_info->image_box.right=(ssize_t) ping_width;
2674 mng_info->image_box.top=0;
2675 mng_info->image_box.bottom=(ssize_t) ping_height;
2676 if (mng_info->mng_type == 0)
2678 mng_info->mng_width=ping_width;
2679 mng_info->mng_height=ping_height;
2680 mng_info->frame=mng_info->image_box;
2681 mng_info->clip=mng_info->image_box;
2686 image->page.y=mng_info->y_off[mng_info->object_id];
2689 image->compression=ZipCompression;
2690 image->columns=ping_width;
2691 image->rows=ping_height;
2692 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2693 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2694 image->colorspace=GRAYColorspace;
2695 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2696 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2701 image->storage_class=PseudoClass;
2703 image->colors=one << ping_bit_depth;
2704 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2705 if (image->colors > 256)
2708 if (image->colors > 65536L)
2709 image->colors=65536L;
2711 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2719 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2720 image->colors=(size_t) number_colors;
2722 if (logging != MagickFalse)
2723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2724 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2728 if (image->storage_class == PseudoClass)
2731 Initialize image colormap.
2733 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2734 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2736 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2744 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2746 for (i=0; i < (ssize_t) number_colors; i++)
2748 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2749 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2750 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2753 for ( ; i < (ssize_t) image->colors; i++)
2755 image->colormap[i].red=0;
2756 image->colormap[i].green=0;
2757 image->colormap[i].blue=0;
2766 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2771 for (i=0; i < (ssize_t) image->colors; i++)
2773 image->colormap[i].red=(Quantum) (i*scale);
2774 image->colormap[i].green=(Quantum) (i*scale);
2775 image->colormap[i].blue=(Quantum) (i*scale);
2780 /* Set some properties for reporting by "identify" */
2785 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2786 ping_interlace_method in value */
2788 (void) FormatLocaleString(msg,MaxTextExtent,
2789 "%d, %d",(int) ping_width, (int) ping_height);
2790 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2792 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2793 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2795 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2796 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2798 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2799 (int) ping_interlace_method);
2800 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2804 Read image scanlines.
2806 if (image->delay != 0)
2807 mng_info->scenes_found++;
2809 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2810 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2811 (image_info->first_scene+image_info->number_scenes))))
2813 /* This happens later in non-ping decodes */
2814 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2815 image->storage_class=DirectClass;
2817 if (logging != MagickFalse)
2818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2819 " Skipping PNG image data for scene %.20g",(double)
2820 mng_info->scenes_found-1);
2821 png_destroy_read_struct(&ping,&ping_info,&end_info);
2822 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2823 UnlockSemaphoreInfo(ping_semaphore);
2825 if (logging != MagickFalse)
2826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2827 " exit ReadOnePNGImage().");
2832 if (logging != MagickFalse)
2833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2834 " Reading PNG IDAT chunk(s)");
2837 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2838 ping_rowbytes*sizeof(*ping_pixels));
2841 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2842 sizeof(*ping_pixels));
2844 if (ping_pixels == (unsigned char *) NULL)
2845 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2847 if (logging != MagickFalse)
2848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2849 " Converting PNG pixels to pixel packets");
2851 Convert PNG pixels to pixel packets.
2853 if (setjmp(png_jmpbuf(ping)))
2856 PNG image is corrupt.
2858 png_destroy_read_struct(&ping,&ping_info,&end_info);
2859 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2860 UnlockSemaphoreInfo(ping_semaphore);
2862 if (quantum_info != (QuantumInfo *) NULL)
2863 quantum_info = DestroyQuantumInfo(quantum_info);
2865 if (ping_pixels != (unsigned char *) NULL)
2866 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2868 if (logging != MagickFalse)
2869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2870 " exit ReadOnePNGImage() with error.");
2872 if (image != (Image *) NULL)
2874 InheritException(exception,exception);
2878 return(GetFirstImageInList(image));
2881 quantum_info=AcquireQuantumInfo(image_info,image);
2883 if (quantum_info == (QuantumInfo *) NULL)
2884 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2889 found_transparent_pixel;
2891 found_transparent_pixel=MagickFalse;
2893 if (image->storage_class == DirectClass)
2895 for (pass=0; pass < num_passes; pass++)
2898 Convert image to DirectClass pixel packets.
2900 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2904 depth=(ssize_t) ping_bit_depth;
2906 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2907 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2908 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2909 MagickTrue : MagickFalse;
2911 for (y=0; y < (ssize_t) image->rows; y++)
2914 row_offset=ping_rowbytes*y;
2919 png_read_row(ping,ping_pixels+row_offset,NULL);
2920 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2922 if (q == (Quantum *) NULL)
2925 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2926 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2927 GrayQuantum,ping_pixels+row_offset,exception);
2929 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2930 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2931 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2933 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2934 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2935 RGBAQuantum,ping_pixels+row_offset,exception);
2937 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2938 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2939 IndexQuantum,ping_pixels+row_offset,exception);
2941 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2942 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2943 RGBQuantum,ping_pixels+row_offset,exception);
2945 if (found_transparent_pixel == MagickFalse)
2947 /* Is there a transparent pixel in the row? */
2948 if (y== 0 && logging != MagickFalse)
2949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2950 " Looking for cheap transparent pixel");
2952 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2954 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2955 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2956 (GetPixelAlpha(image,q) != OpaqueAlpha))
2958 if (logging != MagickFalse)
2959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2962 found_transparent_pixel = MagickTrue;
2965 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2966 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2967 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2968 transparent_color.red &&
2969 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2970 transparent_color.green &&
2971 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2972 transparent_color.blue))
2974 if (logging != MagickFalse)
2975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2977 found_transparent_pixel = MagickTrue;
2980 q+=GetPixelChannels(image);
2984 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2986 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2989 if (status == MagickFalse)
2992 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2996 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2998 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2999 if (status == MagickFalse)
3005 else /* image->storage_class != DirectClass */
3007 for (pass=0; pass < num_passes; pass++)
3016 Convert grayscale image to PseudoClass pixel packets.
3018 if (logging != MagickFalse)
3019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3020 " Converting grayscale pixels to pixel packets");
3022 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3023 MagickTrue : MagickFalse;
3025 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3026 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
3028 if (quantum_scanline == (Quantum *) NULL)
3029 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3031 for (y=0; y < (ssize_t) image->rows; y++)
3034 row_offset=ping_rowbytes*y;
3039 png_read_row(ping,ping_pixels+row_offset,NULL);
3040 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3042 if (q == (Quantum *) NULL)
3045 p=ping_pixels+row_offset;
3048 switch (ping_bit_depth)
3055 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3057 for (bit=7; bit >= 0; bit--)
3058 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3062 if ((image->columns % 8) != 0)
3064 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3065 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3073 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3075 *r++=(*p >> 6) & 0x03;
3076 *r++=(*p >> 4) & 0x03;
3077 *r++=(*p >> 2) & 0x03;
3081 if ((image->columns % 4) != 0)
3083 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3084 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3092 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3094 *r++=(*p >> 4) & 0x0f;
3098 if ((image->columns % 2) != 0)
3099 *r++=(*p++ >> 4) & 0x0f;
3106 if (ping_color_type == 4)
3107 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3110 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3111 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3112 found_transparent_pixel = MagickTrue;
3113 q+=GetPixelChannels(image);
3117 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3125 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3127 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3131 if (image->colors > 256)
3132 quantum=((*p++) << 8);
3138 *r=ScaleShortToQuantum(quantum);
3141 if (ping_color_type == 4)
3143 if (image->colors > 256)
3144 quantum=((*p++) << 8);
3149 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3150 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3151 found_transparent_pixel = MagickTrue;
3152 q+=GetPixelChannels(image);
3155 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3157 p++; /* strip low byte */
3159 if (ping_color_type == 4)
3161 SetPixelAlpha(image,*p++,q);
3162 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3163 found_transparent_pixel = MagickTrue;
3165 q+=GetPixelChannels(image);
3178 Transfer image scanline.
3182 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3184 if (q == (Quantum *) NULL)
3186 for (x=0; x < (ssize_t) image->columns; x++)
3188 SetPixelIndex(image,*r++,q);
3189 q+=GetPixelChannels(image);
3192 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3195 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3197 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3200 if (status == MagickFalse)
3205 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3207 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3209 if (status == MagickFalse)
3213 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3216 image->matte=found_transparent_pixel;
3218 if (logging != MagickFalse)
3220 if (found_transparent_pixel != MagickFalse)
3221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3222 " Found transparent pixel");
3225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3226 " No transparent pixel was found");
3228 ping_color_type&=0x03;
3233 if (quantum_info != (QuantumInfo *) NULL)
3234 quantum_info=DestroyQuantumInfo(quantum_info);
3236 if (image->storage_class == PseudoClass)
3242 image->matte=MagickFalse;
3243 (void) SyncImage(image,exception);
3247 png_read_end(ping,end_info);
3249 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3250 (ssize_t) image_info->first_scene && image->delay != 0)
3252 png_destroy_read_struct(&ping,&ping_info,&end_info);
3253 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3255 (void) SetImageBackgroundColor(image,exception);
3256 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3257 UnlockSemaphoreInfo(ping_semaphore);
3259 if (logging != MagickFalse)
3260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3261 " exit ReadOnePNGImage() early.");
3265 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3271 Image has a transparent background.
3273 storage_class=image->storage_class;
3274 image->matte=MagickTrue;
3276 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3278 if (storage_class == PseudoClass)
3280 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3282 for (x=0; x < ping_num_trans; x++)
3284 image->colormap[x].matte=MagickTrue;
3285 image->colormap[x].alpha =
3286 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3290 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3292 for (x=0; x < (int) image->colors; x++)
3294 if (ScaleQuantumToShort(image->colormap[x].red) ==
3295 transparent_color.alpha)
3297 image->colormap[x].matte=MagickTrue;
3298 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3302 (void) SyncImage(image,exception);
3305 #if 1 /* Should have already been done above, but glennrp problem P10
3310 for (y=0; y < (ssize_t) image->rows; y++)
3312 image->storage_class=storage_class;
3313 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3315 if (q == (Quantum *) NULL)
3319 /* Caution: on a Q8 build, this does not distinguish between
3320 * 16-bit colors that differ only in the low byte
3322 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3324 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3325 transparent_color.red &&
3326 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3327 transparent_color.green &&
3328 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3329 transparent_color.blue)
3331 SetPixelAlpha(image,TransparentAlpha,q);
3334 #if 0 /* I have not found a case where this is needed. */
3337 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3341 q+=GetPixelChannels(image);
3344 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3350 image->storage_class=DirectClass;
3353 for (j = 0; j < 2; j++)
3356 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3357 MagickTrue : MagickFalse;
3359 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3360 MagickTrue : MagickFalse;
3362 if (status != MagickFalse)
3363 for (i=0; i < (ssize_t) num_text; i++)
3365 /* Check for a profile */
3367 if (logging != MagickFalse)
3368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3369 " Reading PNG text chunk");
3371 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3373 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3383 length=text[i].text_length;
3384 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3386 if (value == (char *) NULL)
3388 (void) ThrowMagickException(exception,GetMagickModule(),
3389 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3394 (void) ConcatenateMagickString(value,text[i].text,length+2);
3396 /* Don't save "density" or "units" property if we have a pHYs
3399 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3400 (LocaleCompare(text[i].key,"density") != 0 &&
3401 LocaleCompare(text[i].key,"units") != 0))
3402 (void) SetImageProperty(image,text[i].key,value,exception);
3404 if (logging != MagickFalse)
3406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3407 " length: %lu",(unsigned long) length);
3408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3409 " Keyword: %s",text[i].key);
3412 value=DestroyString(value);
3415 num_text_total += num_text;
3418 #ifdef MNG_OBJECT_BUFFERS
3420 Store the object if necessary.
3422 if (object_id && !mng_info->frozen[object_id])
3424 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3427 create a new object buffer.
3429 mng_info->ob[object_id]=(MngBuffer *)
3430 AcquireMagickMemory(sizeof(MngBuffer));
3432 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3434 mng_info->ob[object_id]->image=(Image *) NULL;
3435 mng_info->ob[object_id]->reference_count=1;
3439 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3440 mng_info->ob[object_id]->frozen)
3442 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3443 (void) ThrowMagickException(exception,GetMagickModule(),
3444 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3447 if (mng_info->ob[object_id]->frozen)
3448 (void) ThrowMagickException(exception,GetMagickModule(),
3449 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3450 "`%s'",image->filename);
3456 if (mng_info->ob[object_id]->image != (Image *) NULL)
3457 mng_info->ob[object_id]->image=DestroyImage
3458 (mng_info->ob[object_id]->image);
3460 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3463 if (mng_info->ob[object_id]->image != (Image *) NULL)
3464 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3467 (void) ThrowMagickException(exception,GetMagickModule(),
3468 ResourceLimitError,"Cloning image for object buffer failed",
3469 "`%s'",image->filename);
3471 if (ping_width > 250000L || ping_height > 250000L)
3472 png_error(ping,"PNG Image dimensions are too large.");
3474 mng_info->ob[object_id]->width=ping_width;
3475 mng_info->ob[object_id]->height=ping_height;
3476 mng_info->ob[object_id]->color_type=ping_color_type;
3477 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3478 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3479 mng_info->ob[object_id]->compression_method=
3480 ping_compression_method;
3481 mng_info->ob[object_id]->filter_method=ping_filter_method;
3483 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3492 Copy the PLTE to the object buffer.
3494 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3495 mng_info->ob[object_id]->plte_length=number_colors;
3497 for (i=0; i < number_colors; i++)
3499 mng_info->ob[object_id]->plte[i]=plte[i];
3504 mng_info->ob[object_id]->plte_length=0;
3509 /* Set image->matte to MagickTrue if the input colortype supports
3510 * alpha or if a valid tRNS chunk is present, no matter whether there
3511 * is actual transparency present.
3513 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3514 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3515 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3516 MagickTrue : MagickFalse;
3518 /* Set more properties for identify to retrieve */
3523 if (num_text_total != 0)
3525 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3526 (void) FormatLocaleString(msg,MaxTextExtent,
3527 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3528 (void) SetImageProperty(image,"png:text ",msg,
3532 if (num_raw_profiles != 0)
3534 (void) FormatLocaleString(msg,MaxTextExtent,
3535 "%d were found", num_raw_profiles);
3536 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3540 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3542 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3543 "chunk was found (see Chromaticity, above)");
3544 (void) SetImageProperty(image,"png:cHRM ",msg,
3548 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3550 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3551 "chunk was found (see Background color, above)");
3552 (void) SetImageProperty(image,"png:bKGD ",msg,
3556 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3559 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3560 (void) SetImageProperty(image,"png:iCCP ",msg,
3563 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3564 (void) SetImageProperty(image,"png:tRNS ",msg,
3567 #if defined(PNG_sRGB_SUPPORTED)
3568 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3570 (void) FormatLocaleString(msg,MaxTextExtent,
3571 "intent=%d (See Rendering intent)", (int) intent);
3572 (void) SetImageProperty(image,"png:sRGB ",msg,
3577 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3579 (void) FormatLocaleString(msg,MaxTextExtent,
3580 "gamma=%.8g (See Gamma, above)",
3582 (void) SetImageProperty(image,"png:gAMA ",msg,
3586 #if defined(PNG_pHYs_SUPPORTED)
3587 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3589 (void) FormatLocaleString(msg,MaxTextExtent,
3590 "x_res=%.10g, y_res=%.10g, units=%d",
3591 (double) x_resolution,(double) y_resolution, unit_type);
3592 (void) SetImageProperty(image,"png:pHYs ",msg,
3597 #if defined(PNG_oFFs_SUPPORTED)
3598 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3600 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3601 (double) image->page.x,(double) image->page.y);
3602 (void) SetImageProperty(image,"png:oFFs ",msg,
3607 if ((image->page.width != 0 && image->page.width != image->columns) ||
3608 (image->page.height != 0 && image->page.height != image->rows))
3610 (void) FormatLocaleString(msg,MaxTextExtent,
3611 "width=%.20g, height=%.20g",
3612 (double) image->page.width,(double) image->page.height);
3613 (void) SetImageProperty(image,"png:vpAg ",msg,
3619 Relinquish resources.
3621 png_destroy_read_struct(&ping,&ping_info,&end_info);
3623 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3624 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3625 UnlockSemaphoreInfo(ping_semaphore);
3628 if (logging != MagickFalse)
3629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3630 " exit ReadOnePNGImage()");
3634 /* end of reading one PNG image */
3637 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3652 magic_number[MaxTextExtent];
3660 assert(image_info != (const ImageInfo *) NULL);
3661 assert(image_info->signature == MagickSignature);
3663 if (image_info->debug != MagickFalse)
3664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3665 image_info->filename);
3667 assert(exception != (ExceptionInfo *) NULL);
3668 assert(exception->signature == MagickSignature);
3669 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3670 image=AcquireImage(image_info,exception);
3671 mng_info=(MngInfo *) NULL;
3672 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3674 if (status == MagickFalse)
3675 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3678 Verify PNG signature.
3680 count=ReadBlob(image,8,(unsigned char *) magic_number);
3682 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3683 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3686 Allocate a MngInfo structure.
3688 have_mng_structure=MagickFalse;
3689 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3691 if (mng_info == (MngInfo *) NULL)
3692 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3695 Initialize members of the MngInfo structure.
3697 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3698 mng_info->image=image;
3699 have_mng_structure=MagickTrue;
3702 image=ReadOnePNGImage(mng_info,image_info,exception);
3703 MngInfoFreeStruct(mng_info,&have_mng_structure);
3705 if (image == (Image *) NULL)
3707 if (previous != (Image *) NULL)
3709 if (previous->signature != MagickSignature)
3710 ThrowReaderException(CorruptImageError,"CorruptImage");
3712 (void) CloseBlob(previous);
3713 (void) DestroyImageList(previous);
3716 if (logging != MagickFalse)
3717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3718 "exit ReadPNGImage() with error");
3720 return((Image *) NULL);
3723 (void) CloseBlob(image);
3725 if ((image->columns == 0) || (image->rows == 0))
3727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3729 "exit ReadPNGImage() with error.");
3731 ThrowReaderException(CorruptImageError,"CorruptImage");
3734 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3736 (void) SetImageType(image,TrueColorType,exception);
3737 image->matte=MagickFalse;
3740 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3741 (void) SetImageType(image,TrueColorMatteType,exception);
3743 if (logging != MagickFalse)
3744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3745 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3746 (double) image->page.width,(double) image->page.height,
3747 (double) image->page.x,(double) image->page.y);
3749 if (logging != MagickFalse)
3750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3757 #if defined(JNG_SUPPORTED)
3759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3763 % R e a d O n e J N G I m a g e %
3767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3769 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3770 % (minus the 8-byte signature) and returns it. It allocates the memory
3771 % necessary for the new Image structure and returns a pointer to the new
3774 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3776 % The format of the ReadOneJNGImage method is:
3778 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3779 % ExceptionInfo *exception)
3781 % A description of each parameter follows:
3783 % o mng_info: Specifies a pointer to a MngInfo structure.
3785 % o image_info: the image info.
3787 % o exception: return any errors or warnings in this structure.
3790 static Image *ReadOneJNGImage(MngInfo *mng_info,
3791 const ImageInfo *image_info, ExceptionInfo *exception)
3818 jng_image_sample_depth,
3819 jng_image_compression_method,
3820 jng_image_interlace_method,
3821 jng_alpha_sample_depth,
3822 jng_alpha_compression_method,
3823 jng_alpha_filter_method,
3824 jng_alpha_interlace_method;
3826 register const Quantum
3836 register unsigned char
3847 jng_alpha_compression_method=0;
3848 jng_alpha_sample_depth=8;
3852 alpha_image=(Image *) NULL;
3853 color_image=(Image *) NULL;
3854 alpha_image_info=(ImageInfo *) NULL;
3855 color_image_info=(ImageInfo *) NULL;
3857 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3858 " Enter ReadOneJNGImage()");
3860 image=mng_info->image;
3862 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3865 Allocate next image structure.
3867 if (logging != MagickFalse)
3868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3869 " AcquireNextImage()");
3871 AcquireNextImage(image_info,image,exception);
3873 if (GetNextImageInList(image) == (Image *) NULL)
3874 return((Image *) NULL);
3876 image=SyncNextImageInList(image);
3878 mng_info->image=image;
3881 Signature bytes have already been read.
3884 read_JSEP=MagickFalse;
3885 reading_idat=MagickFalse;
3886 skip_to_iend=MagickFalse;
3890 type[MaxTextExtent];
3899 Read a new JNG chunk.
3901 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3902 2*GetBlobSize(image));
3904 if (status == MagickFalse)
3908 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3909 length=ReadBlobMSBLong(image);
3910 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3912 if (logging != MagickFalse)
3913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3914 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3915 type[0],type[1],type[2],type[3],(double) length);
3917 if (length > PNG_UINT_31_MAX || count == 0)
3918 ThrowReaderException(CorruptImageError,"CorruptImage");
3921 chunk=(unsigned char *) NULL;
3925 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3927 if (chunk == (unsigned char *) NULL)
3928 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3930 for (i=0; i < (ssize_t) length; i++)
3931 chunk[i]=(unsigned char) ReadBlobByte(image);
3936 (void) ReadBlobMSBLong(image); /* read crc word */
3941 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3946 if (memcmp(type,mng_JHDR,4) == 0)
3950 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3951 (p[2] << 8) | p[3]);
3952 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3953 (p[6] << 8) | p[7]);
3954 jng_color_type=p[8];
3955 jng_image_sample_depth=p[9];
3956 jng_image_compression_method=p[10];
3957 jng_image_interlace_method=p[11];
3959 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3962 jng_alpha_sample_depth=p[12];
3963 jng_alpha_compression_method=p[13];
3964 jng_alpha_filter_method=p[14];
3965 jng_alpha_interlace_method=p[15];
3967 if (logging != MagickFalse)
3969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3970 " jng_width: %16lu",(unsigned long) jng_width);
3972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3973 " jng_width: %16lu",(unsigned long) jng_height);
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3976 " jng_color_type: %16d",jng_color_type);
3978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3979 " jng_image_sample_depth: %3d",
3980 jng_image_sample_depth);
3982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3983 " jng_image_compression_method:%3d",
3984 jng_image_compression_method);
3986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " jng_image_interlace_method: %3d",
3988 jng_image_interlace_method);
3990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3991 " jng_alpha_sample_depth: %3d",
3992 jng_alpha_sample_depth);
3994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3995 " jng_alpha_compression_method:%3d",
3996 jng_alpha_compression_method);
3998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999 " jng_alpha_filter_method: %3d",
4000 jng_alpha_filter_method);
4002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4003 " jng_alpha_interlace_method: %3d",
4004 jng_alpha_interlace_method);
4009 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4015 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4016 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4017 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4020 o create color_image
4021 o open color_blob, attached to color_image
4022 o if (color type has alpha)
4023 open alpha_blob, attached to alpha_image
4026 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4028 if (color_image_info == (ImageInfo *) NULL)
4029 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4031 GetImageInfo(color_image_info);
4032 color_image=AcquireImage(color_image_info,exception);
4034 if (color_image == (Image *) NULL)
4035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4037 if (logging != MagickFalse)
4038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4039 " Creating color_blob.");
4041 (void) AcquireUniqueFilename(color_image->filename);
4042 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4045 if (status == MagickFalse)
4046 return((Image *) NULL);
4048 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4050 alpha_image_info=(ImageInfo *)
4051 AcquireMagickMemory(sizeof(ImageInfo));
4053 if (alpha_image_info == (ImageInfo *) NULL)
4054 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4056 GetImageInfo(alpha_image_info);
4057 alpha_image=AcquireImage(alpha_image_info,exception);
4059 if (alpha_image == (Image *) NULL)
4061 alpha_image=DestroyImage(alpha_image);
4062 ThrowReaderException(ResourceLimitError,
4063 "MemoryAllocationFailed");
4066 if (logging != MagickFalse)
4067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4068 " Creating alpha_blob.");
4070 (void) AcquireUniqueFilename(alpha_image->filename);
4071 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4074 if (status == MagickFalse)
4075 return((Image *) NULL);
4077 if (jng_alpha_compression_method == 0)
4082 if (logging != MagickFalse)
4083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4084 " Writing IHDR chunk to alpha_blob.");
4086 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4087 "\211PNG\r\n\032\n");
4089 (void) WriteBlobMSBULong(alpha_image,13L);
4090 PNGType(data,mng_IHDR);
4091 LogPNGChunk(logging,mng_IHDR,13L);
4092 PNGLong(data+4,jng_width);
4093 PNGLong(data+8,jng_height);
4094 data[12]=jng_alpha_sample_depth;
4095 data[13]=0; /* color_type gray */
4096 data[14]=0; /* compression method 0 */
4097 data[15]=0; /* filter_method 0 */
4098 data[16]=0; /* interlace_method 0 */
4099 (void) WriteBlob(alpha_image,17,data);
4100 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4103 reading_idat=MagickTrue;
4106 if (memcmp(type,mng_JDAT,4) == 0)
4108 /* Copy chunk to color_image->blob */
4110 if (logging != MagickFalse)
4111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4112 " Copying JDAT chunk data to color_blob.");
4114 (void) WriteBlob(color_image,length,chunk);
4117 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4122 if (memcmp(type,mng_IDAT,4) == 0)
4127 /* Copy IDAT header and chunk data to alpha_image->blob */
4129 if (image_info->ping == MagickFalse)
4131 if (logging != MagickFalse)
4132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4133 " Copying IDAT chunk data to alpha_blob.");
4135 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4136 PNGType(data,mng_IDAT);
4137 LogPNGChunk(logging,mng_IDAT,length);
4138 (void) WriteBlob(alpha_image,4,data);
4139 (void) WriteBlob(alpha_image,length,chunk);
4140 (void) WriteBlobMSBULong(alpha_image,
4141 crc32(crc32(0,data,4),chunk,(uInt) length));
4145 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4150 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4152 /* Copy chunk data to alpha_image->blob */
4154 if (image_info->ping == MagickFalse)
4156 if (logging != MagickFalse)
4157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4158 " Copying JDAA chunk data to alpha_blob.");
4160 (void) WriteBlob(alpha_image,length,chunk);
4164 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4169 if (memcmp(type,mng_JSEP,4) == 0)
4171 read_JSEP=MagickTrue;
4174 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4179 if (memcmp(type,mng_bKGD,4) == 0)
4183 image->background_color.red=ScaleCharToQuantum(p[1]);
4184 image->background_color.green=image->background_color.red;
4185 image->background_color.blue=image->background_color.red;
4190 image->background_color.red=ScaleCharToQuantum(p[1]);
4191 image->background_color.green=ScaleCharToQuantum(p[3]);
4192 image->background_color.blue=ScaleCharToQuantum(p[5]);
4195 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4199 if (memcmp(type,mng_gAMA,4) == 0)
4202 image->gamma=((float) mng_get_long(p))*0.00001;
4204 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4208 if (memcmp(type,mng_cHRM,4) == 0)
4212 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4213 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4214 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4215 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4216 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4217 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4218 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4219 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4222 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4226 if (memcmp(type,mng_sRGB,4) == 0)
4230 image->rendering_intent=
4231 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4232 image->gamma=0.45455f;
4233 image->chromaticity.red_primary.x=0.6400f;
4234 image->chromaticity.red_primary.y=0.3300f;
4235 image->chromaticity.green_primary.x=0.3000f;
4236 image->chromaticity.green_primary.y=0.6000f;
4237 image->chromaticity.blue_primary.x=0.1500f;
4238 image->chromaticity.blue_primary.y=0.0600f;
4239 image->chromaticity.white_point.x=0.3127f;
4240 image->chromaticity.white_point.y=0.3290f;
4243 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4247 if (memcmp(type,mng_oFFs,4) == 0)
4251 image->page.x=(ssize_t) mng_get_long(p);
4252 image->page.y=(ssize_t) mng_get_long(&p[4]);
4254 if ((int) p[8] != 0)
4256 image->page.x/=10000;
4257 image->page.y/=10000;
4262 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4267 if (memcmp(type,mng_pHYs,4) == 0)
4271 image->resolution.x=(double) mng_get_long(p);
4272 image->resolution.y=(double) mng_get_long(&p[4]);
4273 if ((int) p[8] == PNG_RESOLUTION_METER)
4275 image->units=PixelsPerCentimeterResolution;
4276 image->resolution.x=image->resolution.x/100.0f;
4277 image->resolution.y=image->resolution.y/100.0f;
4281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4286 if (memcmp(type,mng_iCCP,4) == 0)
4290 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4297 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4299 if (memcmp(type,mng_IEND,4))
4309 Finish up reading image data:
4311 o read main image from color_blob.
4315 o if (color_type has alpha)
4316 if alpha_encoding is PNG
4317 read secondary image from alpha_blob via ReadPNG
4318 if alpha_encoding is JPEG
4319 read secondary image from alpha_blob via ReadJPEG
4323 o copy intensity of secondary image into
4324 alpha samples of main image.
4326 o destroy the secondary image.
4329 (void) CloseBlob(color_image);
4331 if (logging != MagickFalse)
4332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4333 " Reading jng_image from color_blob.");
4335 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4336 color_image->filename);
4338 color_image_info->ping=MagickFalse; /* To do: avoid this */
4339 jng_image=ReadImage(color_image_info,exception);
4341 if (jng_image == (Image *) NULL)
4342 return((Image *) NULL);
4344 (void) RelinquishUniqueFileResource(color_image->filename);
4345 color_image=DestroyImage(color_image);
4346 color_image_info=DestroyImageInfo(color_image_info);
4348 if (jng_image == (Image *) NULL)
4349 return((Image *) NULL);
4351 if (logging != MagickFalse)
4352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4353 " Copying jng_image pixels to main image.");
4355 image->rows=jng_height;
4356 image->columns=jng_width;
4358 for (y=0; y < (ssize_t) image->rows; y++)
4360 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4361 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4362 for (x=(ssize_t) image->columns; x != 0; x--)
4364 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4365 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4366 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4367 q+=GetPixelChannels(image);
4368 s+=GetPixelChannels(jng_image);
4371 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4375 jng_image=DestroyImage(jng_image);
4377 if (image_info->ping == MagickFalse)
4379 if (jng_color_type >= 12)
4381 if (jng_alpha_compression_method == 0)
4385 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4386 PNGType(data,mng_IEND);
4387 LogPNGChunk(logging,mng_IEND,0L);
4388 (void) WriteBlob(alpha_image,4,data);
4389 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4392 (void) CloseBlob(alpha_image);
4394 if (logging != MagickFalse)
4395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396 " Reading alpha from alpha_blob.");
4398 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4399 "%s",alpha_image->filename);
4401 jng_image=ReadImage(alpha_image_info,exception);
4403 if (jng_image != (Image *) NULL)
4404 for (y=0; y < (ssize_t) image->rows; y++)
4406 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4408 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4410 if (image->matte != MagickFalse)
4411 for (x=(ssize_t) image->columns; x != 0; x--)
4413 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4414 q+=GetPixelChannels(image);
4415 s+=GetPixelChannels(jng_image);
4419 for (x=(ssize_t) image->columns; x != 0; x--)
4421 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4422 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4423 image->matte=MagickTrue;
4424 q+=GetPixelChannels(image);
4425 s+=GetPixelChannels(jng_image);
4428 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4431 (void) RelinquishUniqueFileResource(alpha_image->filename);
4432 alpha_image=DestroyImage(alpha_image);
4433 alpha_image_info=DestroyImageInfo(alpha_image_info);
4434 if (jng_image != (Image *) NULL)
4435 jng_image=DestroyImage(jng_image);
4439 /* Read the JNG image. */
4441 if (mng_info->mng_type == 0)
4443 mng_info->mng_width=jng_width;
4444 mng_info->mng_height=jng_height;
4447 if (image->page.width == 0 && image->page.height == 0)
4449 image->page.width=jng_width;
4450 image->page.height=jng_height;
4453 if (image->page.x == 0 && image->page.y == 0)
4455 image->page.x=mng_info->x_off[mng_info->object_id];
4456 image->page.y=mng_info->y_off[mng_info->object_id];
4461 image->page.y=mng_info->y_off[mng_info->object_id];
4464 mng_info->image_found++;
4465 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4466 2*GetBlobSize(image));
4468 if (logging != MagickFalse)
4469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4470 " exit ReadOneJNGImage()");
4476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4480 % R e a d J N G I m a g e %
4484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4486 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4487 % (including the 8-byte signature) and returns it. It allocates the memory
4488 % necessary for the new Image structure and returns a pointer to the new
4491 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4493 % The format of the ReadJNGImage method is:
4495 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4498 % A description of each parameter follows:
4500 % o image_info: the image info.
4502 % o exception: return any errors or warnings in this structure.
4506 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4521 magic_number[MaxTextExtent];
4529 assert(image_info != (const ImageInfo *) NULL);
4530 assert(image_info->signature == MagickSignature);
4531 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4532 assert(exception != (ExceptionInfo *) NULL);
4533 assert(exception->signature == MagickSignature);
4534 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4535 image=AcquireImage(image_info,exception);
4536 mng_info=(MngInfo *) NULL;
4537 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4539 if (status == MagickFalse)
4540 return((Image *) NULL);
4542 if (LocaleCompare(image_info->magick,"JNG") != 0)
4543 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4545 /* Verify JNG signature. */
4547 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4549 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4550 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4552 /* Allocate a MngInfo structure. */
4554 have_mng_structure=MagickFalse;
4555 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4557 if (mng_info == (MngInfo *) NULL)
4558 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4560 /* Initialize members of the MngInfo structure. */
4562 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4563 have_mng_structure=MagickTrue;
4565 mng_info->image=image;
4567 image=ReadOneJNGImage(mng_info,image_info,exception);
4568 MngInfoFreeStruct(mng_info,&have_mng_structure);
4570 if (image == (Image *) NULL)
4572 if (IsImageObject(previous) != MagickFalse)
4574 (void) CloseBlob(previous);
4575 (void) DestroyImageList(previous);
4578 if (logging != MagickFalse)
4579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4580 "exit ReadJNGImage() with error");
4582 return((Image *) NULL);
4584 (void) CloseBlob(image);
4586 if (image->columns == 0 || image->rows == 0)
4588 if (logging != MagickFalse)
4589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4590 "exit ReadJNGImage() with error");
4592 ThrowReaderException(CorruptImageError,"CorruptImage");
4595 if (logging != MagickFalse)
4596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4602 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4605 page_geometry[MaxTextExtent];
4638 #if defined(MNG_INSERT_LAYERS)
4640 mng_background_color;
4643 register unsigned char
4658 #if defined(MNG_INSERT_LAYERS)
4663 volatile unsigned int
4664 #ifdef MNG_OBJECT_BUFFERS
4665 mng_background_object=0,
4667 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4670 default_frame_timeout,
4672 #if defined(MNG_INSERT_LAYERS)
4678 /* These delays are all measured in image ticks_per_second,
4679 * not in MNG ticks_per_second
4682 default_frame_delay,
4686 #if defined(MNG_INSERT_LAYERS)
4695 previous_fb.bottom=0;
4697 previous_fb.right=0;
4699 default_fb.bottom=0;
4703 /* Open image file. */
4705 assert(image_info != (const ImageInfo *) NULL);
4706 assert(image_info->signature == MagickSignature);
4707 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4708 assert(exception != (ExceptionInfo *) NULL);
4709 assert(exception->signature == MagickSignature);
4710 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4711 image=AcquireImage(image_info,exception);
4712 mng_info=(MngInfo *) NULL;
4713 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4715 if (status == MagickFalse)
4716 return((Image *) NULL);
4718 first_mng_object=MagickFalse;
4720 have_mng_structure=MagickFalse;
4722 /* Allocate a MngInfo structure. */
4724 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4726 if (mng_info == (MngInfo *) NULL)
4727 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4729 /* Initialize members of the MngInfo structure. */
4731 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4732 mng_info->image=image;
4733 have_mng_structure=MagickTrue;
4735 if (LocaleCompare(image_info->magick,"MNG") == 0)
4738 magic_number[MaxTextExtent];
4740 /* Verify MNG signature. */
4741 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4742 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4743 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4745 /* Initialize some nonzero members of the MngInfo structure. */
4746 for (i=0; i < MNG_MAX_OBJECTS; i++)
4748 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4749 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4751 mng_info->exists[0]=MagickTrue;
4754 first_mng_object=MagickTrue;
4756 #if defined(MNG_INSERT_LAYERS)
4757 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4759 default_frame_delay=0;
4760 default_frame_timeout=0;
4763 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4765 skip_to_iend=MagickFalse;
4766 term_chunk_found=MagickFalse;
4767 mng_info->framing_mode=1;
4768 #if defined(MNG_INSERT_LAYERS)
4769 mandatory_back=MagickFalse;
4771 #if defined(MNG_INSERT_LAYERS)
4772 mng_background_color=image->background_color;
4774 default_fb=mng_info->frame;
4775 previous_fb=mng_info->frame;
4779 type[MaxTextExtent];
4781 if (LocaleCompare(image_info->magick,"MNG") == 0)
4790 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4791 length=ReadBlobMSBLong(image);
4792 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4794 if (logging != MagickFalse)
4795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4796 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4797 type[0],type[1],type[2],type[3],(double) length);
4799 if (length > PNG_UINT_31_MAX)
4803 ThrowReaderException(CorruptImageError,"CorruptImage");
4806 chunk=(unsigned char *) NULL;
4810 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4812 if (chunk == (unsigned char *) NULL)
4813 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4815 for (i=0; i < (ssize_t) length; i++)
4816 chunk[i]=(unsigned char) ReadBlobByte(image);
4821 (void) ReadBlobMSBLong(image); /* read crc word */
4823 #if !defined(JNG_SUPPORTED)
4824 if (memcmp(type,mng_JHDR,4) == 0)
4826 skip_to_iend=MagickTrue;
4828 if (mng_info->jhdr_warning == 0)
4829 (void) ThrowMagickException(exception,GetMagickModule(),
4830 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4832 mng_info->jhdr_warning++;
4835 if (memcmp(type,mng_DHDR,4) == 0)
4837 skip_to_iend=MagickTrue;
4839 if (mng_info->dhdr_warning == 0)
4840 (void) ThrowMagickException(exception,GetMagickModule(),
4841 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4843 mng_info->dhdr_warning++;
4845 if (memcmp(type,mng_MEND,4) == 0)
4850 if (memcmp(type,mng_IEND,4) == 0)
4851 skip_to_iend=MagickFalse;
4854 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4856 if (logging != MagickFalse)
4857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4863 if (memcmp(type,mng_MHDR,4) == 0)
4865 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4866 (p[2] << 8) | p[3]);
4868 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4869 (p[6] << 8) | p[7]);
4871 if (logging != MagickFalse)
4873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4874 " MNG width: %.20g",(double) mng_info->mng_width);
4875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4876 " MNG height: %.20g",(double) mng_info->mng_height);
4880 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4882 if (mng_info->ticks_per_second == 0)
4883 default_frame_delay=0;
4886 default_frame_delay=1UL*image->ticks_per_second/
4887 mng_info->ticks_per_second;
4889 frame_delay=default_frame_delay;
4895 simplicity=(size_t) mng_get_long(p);
4898 mng_type=1; /* Full MNG */
4900 if ((simplicity != 0) && ((simplicity | 11) == 11))
4901 mng_type=2; /* LC */
4903 if ((simplicity != 0) && ((simplicity | 9) == 9))
4904 mng_type=3; /* VLC */
4906 #if defined(MNG_INSERT_LAYERS)
4908 insert_layers=MagickTrue;
4910 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4912 /* Allocate next image structure. */
4913 AcquireNextImage(image_info,image,exception);
4915 if (GetNextImageInList(image) == (Image *) NULL)
4916 return((Image *) NULL);
4918 image=SyncNextImageInList(image);
4919 mng_info->image=image;
4922 if ((mng_info->mng_width > 65535L) ||
4923 (mng_info->mng_height > 65535L))
4924 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4926 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4927 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4928 mng_info->mng_height);
4930 mng_info->frame.left=0;
4931 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4932 mng_info->frame.top=0;
4933 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4934 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4936 for (i=0; i < MNG_MAX_OBJECTS; i++)
4937 mng_info->object_clip[i]=mng_info->frame;
4939 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4943 if (memcmp(type,mng_TERM,4) == 0)
4954 final_delay=(png_uint_32) mng_get_long(&p[2]);
4955 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4957 if (mng_iterations == PNG_UINT_31_MAX)
4960 image->iterations=mng_iterations;
4961 term_chunk_found=MagickTrue;
4964 if (logging != MagickFalse)
4966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4967 " repeat=%d",repeat);
4969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4970 " final_delay=%.20g",(double) final_delay);
4972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4973 " image->iterations=%.20g",(double) image->iterations);
4976 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4979 if (memcmp(type,mng_DEFI,4) == 0)
4982 (void) ThrowMagickException(exception,GetMagickModule(),
4983 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4986 object_id=(p[0] << 8) | p[1];
4988 if (mng_type == 2 && object_id != 0)
4989 (void) ThrowMagickException(exception,GetMagickModule(),
4990 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4993 if (object_id > MNG_MAX_OBJECTS)
4996 Instead ofsuing a warning we should allocate a larger
4997 MngInfo structure and continue.
4999 (void) ThrowMagickException(exception,GetMagickModule(),
5000 CoderError,"object id too large","`%s'",image->filename);
5001 object_id=MNG_MAX_OBJECTS;
5004 if (mng_info->exists[object_id])
5005 if (mng_info->frozen[object_id])
5007 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5008 (void) ThrowMagickException(exception,
5009 GetMagickModule(),CoderError,
5010 "DEFI cannot redefine a frozen MNG object","`%s'",
5015 mng_info->exists[object_id]=MagickTrue;
5018 mng_info->invisible[object_id]=p[2];
5021 Extract object offset info.
5025 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5026 (p[5] << 16) | (p[6] << 8) | p[7]);
5028 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5029 (p[9] << 16) | (p[10] << 8) | p[11]);
5031 if (logging != MagickFalse)
5033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5034 " x_off[%d]: %.20g",object_id,(double)
5035 mng_info->x_off[object_id]);
5037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5038 " y_off[%d]: %.20g",object_id,(double)
5039 mng_info->y_off[object_id]);
5044 Extract object clipping info.
5047 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5050 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5053 if (memcmp(type,mng_bKGD,4) == 0)
5055 mng_info->have_global_bkgd=MagickFalse;
5059 mng_info->mng_global_bkgd.red=
5060 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5062 mng_info->mng_global_bkgd.green=
5063 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5065 mng_info->mng_global_bkgd.blue=
5066 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5068 mng_info->have_global_bkgd=MagickTrue;
5071 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5074 if (memcmp(type,mng_BACK,4) == 0)
5076 #if defined(MNG_INSERT_LAYERS)
5078 mandatory_back=p[6];
5083 if (mandatory_back && length > 5)
5085 mng_background_color.red=
5086 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5088 mng_background_color.green=
5089 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5091 mng_background_color.blue=
5092 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5094 mng_background_color.alpha=OpaqueAlpha;
5097 #ifdef MNG_OBJECT_BUFFERS
5099 mng_background_object=(p[7] << 8) | p[8];
5102 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5106 if (memcmp(type,mng_PLTE,4) == 0)
5108 /* Read global PLTE. */
5110 if (length && (length < 769))
5112 if (mng_info->global_plte == (png_colorp) NULL)
5113 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5114 sizeof(*mng_info->global_plte));
5116 for (i=0; i < (ssize_t) (length/3); i++)
5118 mng_info->global_plte[i].red=p[3*i];
5119 mng_info->global_plte[i].green=p[3*i+1];
5120 mng_info->global_plte[i].blue=p[3*i+2];
5123 mng_info->global_plte_length=(unsigned int) (length/3);
5126 for ( ; i < 256; i++)
5128 mng_info->global_plte[i].red=i;
5129 mng_info->global_plte[i].green=i;
5130 mng_info->global_plte[i].blue=i;
5134 mng_info->global_plte_length=256;
5137 mng_info->global_plte_length=0;
5139 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5143 if (memcmp(type,mng_tRNS,4) == 0)
5145 /* read global tRNS */
5148 for (i=0; i < (ssize_t) length; i++)
5149 mng_info->global_trns[i]=p[i];
5152 for ( ; i < 256; i++)
5153 mng_info->global_trns[i]=255;
5155 mng_info->global_trns_length=(unsigned int) length;
5156 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5159 if (memcmp(type,mng_gAMA,4) == 0)
5166 igamma=mng_get_long(p);
5167 mng_info->global_gamma=((float) igamma)*0.00001;
5168 mng_info->have_global_gama=MagickTrue;
5172 mng_info->have_global_gama=MagickFalse;
5174 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5178 if (memcmp(type,mng_cHRM,4) == 0)
5180 /* Read global cHRM */
5184 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5185 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5186 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5187 mng_info->global_chrm.red_primary.y=0.00001*
5188 mng_get_long(&p[12]);
5189 mng_info->global_chrm.green_primary.x=0.00001*
5190 mng_get_long(&p[16]);
5191 mng_info->global_chrm.green_primary.y=0.00001*
5192 mng_get_long(&p[20]);
5193 mng_info->global_chrm.blue_primary.x=0.00001*
5194 mng_get_long(&p[24]);
5195 mng_info->global_chrm.blue_primary.y=0.00001*
5196 mng_get_long(&p[28]);
5197 mng_info->have_global_chrm=MagickTrue;
5200 mng_info->have_global_chrm=MagickFalse;
5202 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5206 if (memcmp(type,mng_sRGB,4) == 0)
5213 mng_info->global_srgb_intent=
5214 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5215 mng_info->have_global_srgb=MagickTrue;
5218 mng_info->have_global_srgb=MagickFalse;
5220 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5224 if (memcmp(type,mng_iCCP,4) == 0)
5232 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5237 if (memcmp(type,mng_FRAM,4) == 0)
5240 (void) ThrowMagickException(exception,GetMagickModule(),
5241 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5244 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5245 image->delay=frame_delay;
5247 frame_delay=default_frame_delay;
5248 frame_timeout=default_frame_timeout;
5253 mng_info->framing_mode=p[0];
5255 if (logging != MagickFalse)
5256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5257 " Framing_mode=%d",mng_info->framing_mode);
5261 /* Note the delay and frame clipping boundaries. */
5263 p++; /* framing mode */
5265 while (*p && ((p-chunk) < (ssize_t) length))
5266 p++; /* frame name */
5268 p++; /* frame name terminator */
5270 if ((p-chunk) < (ssize_t) (length-4))
5277 change_delay=(*p++);
5278 change_timeout=(*p++);
5279 change_clipping=(*p++);
5280 p++; /* change_sync */
5284 frame_delay=1UL*image->ticks_per_second*
5287 if (mng_info->ticks_per_second != 0)
5288 frame_delay/=mng_info->ticks_per_second;
5291 frame_delay=PNG_UINT_31_MAX;
5293 if (change_delay == 2)
5294 default_frame_delay=frame_delay;
5298 if (logging != MagickFalse)
5299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5300 " Framing_delay=%.20g",(double) frame_delay);
5305 frame_timeout=1UL*image->ticks_per_second*
5308 if (mng_info->ticks_per_second != 0)
5309 frame_timeout/=mng_info->ticks_per_second;
5312 frame_timeout=PNG_UINT_31_MAX;
5314 if (change_delay == 2)
5315 default_frame_timeout=frame_timeout;
5319 if (logging != MagickFalse)
5320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5321 " Framing_timeout=%.20g",(double) frame_timeout);
5324 if (change_clipping)
5326 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5330 if (logging != MagickFalse)
5331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5332 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5333 (double) fb.left,(double) fb.right,(double) fb.top,
5334 (double) fb.bottom);
5336 if (change_clipping == 2)
5342 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5344 subframe_width=(size_t) (mng_info->clip.right
5345 -mng_info->clip.left);
5347 subframe_height=(size_t) (mng_info->clip.bottom
5348 -mng_info->clip.top);
5350 Insert a background layer behind the frame if framing_mode is 4.
5352 #if defined(MNG_INSERT_LAYERS)
5353 if (logging != MagickFalse)
5354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5355 " subframe_width=%.20g, subframe_height=%.20g",(double)
5356 subframe_width,(double) subframe_height);
5358 if (insert_layers && (mng_info->framing_mode == 4) &&
5359 (subframe_width) && (subframe_height))
5361 /* Allocate next image structure. */
5362 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5364 AcquireNextImage(image_info,image,exception);
5366 if (GetNextImageInList(image) == (Image *) NULL)
5368 image=DestroyImageList(image);
5369 MngInfoFreeStruct(mng_info,&have_mng_structure);
5370 return((Image *) NULL);
5373 image=SyncNextImageInList(image);
5376 mng_info->image=image;
5378 if (term_chunk_found)
5380 image->start_loop=MagickTrue;
5381 image->iterations=mng_iterations;
5382 term_chunk_found=MagickFalse;
5386 image->start_loop=MagickFalse;
5388 image->columns=subframe_width;
5389 image->rows=subframe_height;
5390 image->page.width=subframe_width;
5391 image->page.height=subframe_height;
5392 image->page.x=mng_info->clip.left;
5393 image->page.y=mng_info->clip.top;
5394 image->background_color=mng_background_color;
5395 image->matte=MagickFalse;
5397 (void) SetImageBackgroundColor(image,exception);
5399 if (logging != MagickFalse)
5400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5401 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5402 (double) mng_info->clip.left,(double) mng_info->clip.right,
5403 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5406 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5409 if (memcmp(type,mng_CLIP,4) == 0)
5418 first_object=(p[0] << 8) | p[1];
5419 last_object=(p[2] << 8) | p[3];
5421 for (i=(int) first_object; i <= (int) last_object; i++)
5423 if (mng_info->exists[i] && !mng_info->frozen[i])
5428 box=mng_info->object_clip[i];
5429 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5433 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5436 if (memcmp(type,mng_SAVE,4) == 0)
5438 for (i=1; i < MNG_MAX_OBJECTS; i++)
5439 if (mng_info->exists[i])
5441 mng_info->frozen[i]=MagickTrue;
5442 #ifdef MNG_OBJECT_BUFFERS
5443 if (mng_info->ob[i] != (MngBuffer *) NULL)
5444 mng_info->ob[i]->frozen=MagickTrue;
5449 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5454 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5456 /* Read DISC or SEEK. */
5458 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5460 for (i=1; i < MNG_MAX_OBJECTS; i++)
5461 MngInfoDiscardObject(mng_info,i);
5469 for (j=0; j < (ssize_t) length; j+=2)
5471 i=p[j] << 8 | p[j+1];
5472 MngInfoDiscardObject(mng_info,i);
5477 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5482 if (memcmp(type,mng_MOVE,4) == 0)
5490 first_object=(p[0] << 8) | p[1];
5491 last_object=(p[2] << 8) | p[3];
5492 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5494 if (mng_info->exists[i] && !mng_info->frozen[i])
5502 old_pair.a=mng_info->x_off[i];
5503 old_pair.b=mng_info->y_off[i];
5504 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5505 mng_info->x_off[i]=new_pair.a;
5506 mng_info->y_off[i]=new_pair.b;
5510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5514 if (memcmp(type,mng_LOOP,4) == 0)
5516 ssize_t loop_iters=1;
5517 loop_level=chunk[0];
5518 mng_info->loop_active[loop_level]=1; /* mark loop active */
5520 /* Record starting point. */
5521 loop_iters=mng_get_long(&chunk[1]);
5523 if (logging != MagickFalse)
5524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5525 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5526 (double) loop_iters);
5528 if (loop_iters == 0)
5529 skipping_loop=loop_level;
5533 mng_info->loop_jump[loop_level]=TellBlob(image);
5534 mng_info->loop_count[loop_level]=loop_iters;
5537 mng_info->loop_iteration[loop_level]=0;
5538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5542 if (memcmp(type,mng_ENDL,4) == 0)
5544 loop_level=chunk[0];
5546 if (skipping_loop > 0)
5548 if (skipping_loop == loop_level)
5551 Found end of zero-iteration loop.
5554 mng_info->loop_active[loop_level]=0;
5560 if (mng_info->loop_active[loop_level] == 1)
5562 mng_info->loop_count[loop_level]--;
5563 mng_info->loop_iteration[loop_level]++;
5565 if (logging != MagickFalse)
5566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5567 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5568 (double) loop_level,(double)
5569 mng_info->loop_count[loop_level]);
5571 if (mng_info->loop_count[loop_level] != 0)
5573 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5577 ThrowReaderException(CorruptImageError,
5578 "ImproperImageHeader");
5589 mng_info->loop_active[loop_level]=0;
5591 for (i=0; i < loop_level; i++)
5592 if (mng_info->loop_active[i] == 1)
5593 last_level=(short) i;
5594 loop_level=last_level;
5599 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5603 if (memcmp(type,mng_CLON,4) == 0)
5605 if (mng_info->clon_warning == 0)
5606 (void) ThrowMagickException(exception,GetMagickModule(),
5607 CoderError,"CLON is not implemented yet","`%s'",
5610 mng_info->clon_warning++;
5613 if (memcmp(type,mng_MAGN,4) == 0)
5628 magn_first=(p[0] << 8) | p[1];
5634 magn_last=(p[2] << 8) | p[3];
5637 magn_last=magn_first;
5638 #ifndef MNG_OBJECT_BUFFERS
5639 if (magn_first || magn_last)
5640 if (mng_info->magn_warning == 0)
5642 (void) ThrowMagickException(exception,
5643 GetMagickModule(),CoderError,
5644 "MAGN is not implemented yet for nonzero objects",
5645 "`%s'",image->filename);
5647 mng_info->magn_warning++;
5657 magn_mx=(p[5] << 8) | p[6];
5666 magn_my=(p[7] << 8) | p[8];
5675 magn_ml=(p[9] << 8) | p[10];
5684 magn_mr=(p[11] << 8) | p[12];
5693 magn_mt=(p[13] << 8) | p[14];
5702 magn_mb=(p[15] << 8) | p[16];
5714 magn_methy=magn_methx;
5717 if (magn_methx > 5 || magn_methy > 5)
5718 if (mng_info->magn_warning == 0)
5720 (void) ThrowMagickException(exception,
5721 GetMagickModule(),CoderError,
5722 "Unknown MAGN method in MNG datastream","`%s'",
5725 mng_info->magn_warning++;
5727 #ifdef MNG_OBJECT_BUFFERS
5728 /* Magnify existing objects in the range magn_first to magn_last */
5730 if (magn_first == 0 || magn_last == 0)
5732 /* Save the magnification factors for object 0 */
5733 mng_info->magn_mb=magn_mb;
5734 mng_info->magn_ml=magn_ml;
5735 mng_info->magn_mr=magn_mr;
5736 mng_info->magn_mt=magn_mt;
5737 mng_info->magn_mx=magn_mx;
5738 mng_info->magn_my=magn_my;
5739 mng_info->magn_methx=magn_methx;
5740 mng_info->magn_methy=magn_methy;
5744 if (memcmp(type,mng_PAST,4) == 0)
5746 if (mng_info->past_warning == 0)
5747 (void) ThrowMagickException(exception,GetMagickModule(),
5748 CoderError,"PAST is not implemented yet","`%s'",
5751 mng_info->past_warning++;
5754 if (memcmp(type,mng_SHOW,4) == 0)
5756 if (mng_info->show_warning == 0)
5757 (void) ThrowMagickException(exception,GetMagickModule(),
5758 CoderError,"SHOW is not implemented yet","`%s'",
5761 mng_info->show_warning++;
5764 if (memcmp(type,mng_sBIT,4) == 0)
5767 mng_info->have_global_sbit=MagickFalse;
5771 mng_info->global_sbit.gray=p[0];
5772 mng_info->global_sbit.red=p[0];
5773 mng_info->global_sbit.green=p[1];
5774 mng_info->global_sbit.blue=p[2];
5775 mng_info->global_sbit.alpha=p[3];
5776 mng_info->have_global_sbit=MagickTrue;
5779 if (memcmp(type,mng_pHYs,4) == 0)
5783 mng_info->global_x_pixels_per_unit=
5784 (size_t) mng_get_long(p);
5785 mng_info->global_y_pixels_per_unit=
5786 (size_t) mng_get_long(&p[4]);
5787 mng_info->global_phys_unit_type=p[8];
5788 mng_info->have_global_phys=MagickTrue;
5792 mng_info->have_global_phys=MagickFalse;
5794 if (memcmp(type,mng_pHYg,4) == 0)
5796 if (mng_info->phyg_warning == 0)
5797 (void) ThrowMagickException(exception,GetMagickModule(),
5798 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5800 mng_info->phyg_warning++;
5802 if (memcmp(type,mng_BASI,4) == 0)
5804 skip_to_iend=MagickTrue;
5806 if (mng_info->basi_warning == 0)
5807 (void) ThrowMagickException(exception,GetMagickModule(),
5808 CoderError,"BASI is not implemented yet","`%s'",
5811 mng_info->basi_warning++;
5812 #ifdef MNG_BASI_SUPPORTED
5813 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5814 (p[2] << 8) | p[3]);
5815 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5816 (p[6] << 8) | p[7]);
5817 basi_color_type=p[8];
5818 basi_compression_method=p[9];
5819 basi_filter_type=p[10];
5820 basi_interlace_method=p[11];
5822 basi_red=(p[12] << 8) & p[13];
5828 basi_green=(p[14] << 8) & p[15];
5834 basi_blue=(p[16] << 8) & p[17];
5840 basi_alpha=(p[18] << 8) & p[19];
5844 if (basi_sample_depth == 16)
5851 basi_viewable=p[20];
5857 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5861 if (memcmp(type,mng_IHDR,4)
5862 #if defined(JNG_SUPPORTED)
5863 && memcmp(type,mng_JHDR,4)
5867 /* Not an IHDR or JHDR chunk */
5869 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5874 if (logging != MagickFalse)
5875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5876 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5878 mng_info->exists[object_id]=MagickTrue;
5879 mng_info->viewable[object_id]=MagickTrue;
5881 if (mng_info->invisible[object_id])
5883 if (logging != MagickFalse)
5884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5885 " Skipping invisible object");
5887 skip_to_iend=MagickTrue;
5888 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5891 #if defined(MNG_INSERT_LAYERS)
5893 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5895 image_width=(size_t) mng_get_long(p);
5896 image_height=(size_t) mng_get_long(&p[4]);
5898 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5901 Insert a transparent background layer behind the entire animation
5902 if it is not full screen.
5904 #if defined(MNG_INSERT_LAYERS)
5905 if (insert_layers && mng_type && first_mng_object)
5907 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5908 (image_width < mng_info->mng_width) ||
5909 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5910 (image_height < mng_info->mng_height) ||
5911 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5913 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5916 Allocate next image structure.
5918 AcquireNextImage(image_info,image,exception);
5920 if (GetNextImageInList(image) == (Image *) NULL)
5922 image=DestroyImageList(image);
5923 MngInfoFreeStruct(mng_info,&have_mng_structure);
5924 return((Image *) NULL);
5927 image=SyncNextImageInList(image);
5929 mng_info->image=image;
5931 if (term_chunk_found)
5933 image->start_loop=MagickTrue;
5934 image->iterations=mng_iterations;
5935 term_chunk_found=MagickFalse;
5939 image->start_loop=MagickFalse;
5941 /* Make a background rectangle. */
5944 image->columns=mng_info->mng_width;
5945 image->rows=mng_info->mng_height;
5946 image->page.width=mng_info->mng_width;
5947 image->page.height=mng_info->mng_height;
5950 image->background_color=mng_background_color;
5951 (void) SetImageBackgroundColor(image,exception);
5952 if (logging != MagickFalse)
5953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5954 " Inserted transparent background layer, W=%.20g, H=%.20g",
5955 (double) mng_info->mng_width,(double) mng_info->mng_height);
5959 Insert a background layer behind the upcoming image if
5960 framing_mode is 3, and we haven't already inserted one.
5962 if (insert_layers && (mng_info->framing_mode == 3) &&
5963 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5964 (simplicity & 0x08)))
5966 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5969 Allocate next image structure.
5971 AcquireNextImage(image_info,image,exception);
5973 if (GetNextImageInList(image) == (Image *) NULL)
5975 image=DestroyImageList(image);
5976 MngInfoFreeStruct(mng_info,&have_mng_structure);
5977 return((Image *) NULL);
5980 image=SyncNextImageInList(image);
5983 mng_info->image=image;
5985 if (term_chunk_found)
5987 image->start_loop=MagickTrue;
5988 image->iterations=mng_iterations;
5989 term_chunk_found=MagickFalse;
5993 image->start_loop=MagickFalse;
5996 image->columns=subframe_width;
5997 image->rows=subframe_height;
5998 image->page.width=subframe_width;
5999 image->page.height=subframe_height;
6000 image->page.x=mng_info->clip.left;
6001 image->page.y=mng_info->clip.top;
6002 image->background_color=mng_background_color;
6003 image->matte=MagickFalse;
6004 (void) SetImageBackgroundColor(image,exception);
6006 if (logging != MagickFalse)
6007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6008 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6009 (double) mng_info->clip.left,(double) mng_info->clip.right,
6010 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6012 #endif /* MNG_INSERT_LAYERS */
6013 first_mng_object=MagickFalse;
6015 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6018 Allocate next image structure.
6020 AcquireNextImage(image_info,image,exception);
6022 if (GetNextImageInList(image) == (Image *) NULL)
6024 image=DestroyImageList(image);
6025 MngInfoFreeStruct(mng_info,&have_mng_structure);
6026 return((Image *) NULL);
6029 image=SyncNextImageInList(image);
6031 mng_info->image=image;
6032 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6033 GetBlobSize(image));
6035 if (status == MagickFalse)
6038 if (term_chunk_found)
6040 image->start_loop=MagickTrue;
6041 term_chunk_found=MagickFalse;
6045 image->start_loop=MagickFalse;
6047 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6049 image->delay=frame_delay;
6050 frame_delay=default_frame_delay;
6056 image->page.width=mng_info->mng_width;
6057 image->page.height=mng_info->mng_height;
6058 image->page.x=mng_info->x_off[object_id];
6059 image->page.y=mng_info->y_off[object_id];
6060 image->iterations=mng_iterations;
6063 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6066 if (logging != MagickFalse)
6067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6068 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6071 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6074 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6078 mng_info->image=image;
6079 mng_info->mng_type=mng_type;
6080 mng_info->object_id=object_id;
6082 if (memcmp(type,mng_IHDR,4) == 0)
6083 image=ReadOnePNGImage(mng_info,image_info,exception);
6085 #if defined(JNG_SUPPORTED)
6087 image=ReadOneJNGImage(mng_info,image_info,exception);
6090 if (image == (Image *) NULL)
6092 if (IsImageObject(previous) != MagickFalse)
6094 (void) DestroyImageList(previous);
6095 (void) CloseBlob(previous);
6098 MngInfoFreeStruct(mng_info,&have_mng_structure);
6099 return((Image *) NULL);
6102 if (image->columns == 0 || image->rows == 0)
6104 (void) CloseBlob(image);
6105 image=DestroyImageList(image);
6106 MngInfoFreeStruct(mng_info,&have_mng_structure);
6107 return((Image *) NULL);
6110 mng_info->image=image;
6117 if (mng_info->magn_methx || mng_info->magn_methy)
6123 if (logging != MagickFalse)
6124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6125 " Processing MNG MAGN chunk");
6127 if (mng_info->magn_methx == 1)
6129 magnified_width=mng_info->magn_ml;
6131 if (image->columns > 1)
6132 magnified_width += mng_info->magn_mr;
6134 if (image->columns > 2)
6135 magnified_width += (png_uint_32)
6136 ((image->columns-2)*(mng_info->magn_mx));
6141 magnified_width=(png_uint_32) image->columns;
6143 if (image->columns > 1)
6144 magnified_width += mng_info->magn_ml-1;
6146 if (image->columns > 2)
6147 magnified_width += mng_info->magn_mr-1;
6149 if (image->columns > 3)
6150 magnified_width += (png_uint_32)
6151 ((image->columns-3)*(mng_info->magn_mx-1));
6154 if (mng_info->magn_methy == 1)
6156 magnified_height=mng_info->magn_mt;
6158 if (image->rows > 1)
6159 magnified_height += mng_info->magn_mb;
6161 if (image->rows > 2)
6162 magnified_height += (png_uint_32)
6163 ((image->rows-2)*(mng_info->magn_my));
6168 magnified_height=(png_uint_32) image->rows;
6170 if (image->rows > 1)
6171 magnified_height += mng_info->magn_mt-1;
6173 if (image->rows > 2)
6174 magnified_height += mng_info->magn_mb-1;
6176 if (image->rows > 3)
6177 magnified_height += (png_uint_32)
6178 ((image->rows-3)*(mng_info->magn_my-1));
6181 if (magnified_height > image->rows ||
6182 magnified_width > image->columns)
6209 /* Allocate next image structure. */
6211 if (logging != MagickFalse)
6212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6213 " Allocate magnified image");
6215 AcquireNextImage(image_info,image,exception);
6217 if (GetNextImageInList(image) == (Image *) NULL)
6219 image=DestroyImageList(image);
6220 MngInfoFreeStruct(mng_info,&have_mng_structure);
6221 return((Image *) NULL);
6224 large_image=SyncNextImageInList(image);
6226 large_image->columns=magnified_width;
6227 large_image->rows=magnified_height;
6229 magn_methx=mng_info->magn_methx;
6230 magn_methy=mng_info->magn_methy;
6232 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6233 #define QM unsigned short
6234 if (magn_methx != 1 || magn_methy != 1)
6237 Scale pixels to unsigned shorts to prevent
6238 overflow of intermediate values of interpolations
6240 for (y=0; y < (ssize_t) image->rows; y++)
6242 q=GetAuthenticPixels(image,0,y,image->columns,1,
6245 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6247 SetPixelRed(image,ScaleQuantumToShort(
6248 GetPixelRed(image,q)),q);
6249 SetPixelGreen(image,ScaleQuantumToShort(
6250 GetPixelGreen(image,q)),q);
6251 SetPixelBlue(image,ScaleQuantumToShort(
6252 GetPixelBlue(image,q)),q);
6253 SetPixelAlpha(image,ScaleQuantumToShort(
6254 GetPixelAlpha(image,q)),q);
6255 q+=GetPixelChannels(image);
6258 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6266 if (image->matte != MagickFalse)
6267 (void) SetImageBackgroundColor(large_image,exception);
6271 large_image->background_color.alpha=OpaqueAlpha;
6272 (void) SetImageBackgroundColor(large_image,exception);
6274 if (magn_methx == 4)
6277 if (magn_methx == 5)
6280 if (magn_methy == 4)
6283 if (magn_methy == 5)
6287 /* magnify the rows into the right side of the large image */
6289 if (logging != MagickFalse)
6290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6291 " Magnify the rows to %.20g",(double) large_image->rows);
6292 m=(ssize_t) mng_info->magn_mt;
6294 length=(size_t) image->columns*GetPixelChannels(image);
6295 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6296 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6298 if ((prev == (Quantum *) NULL) ||
6299 (next == (Quantum *) NULL))
6301 image=DestroyImageList(image);
6302 MngInfoFreeStruct(mng_info,&have_mng_structure);
6303 ThrowReaderException(ResourceLimitError,
6304 "MemoryAllocationFailed");
6307 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6308 (void) CopyMagickMemory(next,n,length);
6310 for (y=0; y < (ssize_t) image->rows; y++)
6313 m=(ssize_t) mng_info->magn_mt;
6315 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6316 m=(ssize_t) mng_info->magn_mb;
6318 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6319 m=(ssize_t) mng_info->magn_mb;
6321 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6325 m=(ssize_t) mng_info->magn_my;
6331 if (y < (ssize_t) image->rows-1)
6333 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6335 (void) CopyMagickMemory(next,n,length);
6338 for (i=0; i < m; i++, yy++)
6343 assert(yy < (ssize_t) large_image->rows);
6346 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6348 q+=(large_image->columns-image->columns)*
6349 GetPixelChannels(large_image);
6351 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6353 /* To do: get color as function of indexes[x] */
6355 if (image->storage_class == PseudoClass)
6360 if (magn_methy <= 1)
6362 /* replicate previous */
6363 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6364 SetPixelGreen(large_image,GetPixelGreen(image,
6366 SetPixelBlue(large_image,GetPixelBlue(image,
6368 SetPixelAlpha(large_image,GetPixelAlpha(image,
6372 else if (magn_methy == 2 || magn_methy == 4)
6376 SetPixelRed(large_image,GetPixelRed(image,
6378 SetPixelGreen(large_image,GetPixelGreen(image,
6380 SetPixelBlue(large_image,GetPixelBlue(image,
6382 SetPixelAlpha(large_image,GetPixelAlpha(image,
6389 SetPixelRed(large_image,((QM) (((ssize_t)
6390 (2*i*(GetPixelRed(image,n)
6391 -GetPixelRed(image,pixels)+m))/
6393 +GetPixelRed(image,pixels)))),q);
6394 SetPixelGreen(large_image,((QM) (((ssize_t)
6395 (2*i*(GetPixelGreen(image,n)
6396 -GetPixelGreen(image,pixels)+m))/
6398 +GetPixelGreen(image,pixels)))),q);
6399 SetPixelBlue(large_image,((QM) (((ssize_t)
6400 (2*i*(GetPixelBlue(image,n)
6401 -GetPixelBlue(image,pixels)+m))/
6403 +GetPixelBlue(image,pixels)))),q);
6405 if (image->matte != MagickFalse)
6406 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6407 (2*i*(GetPixelAlpha(image,n)
6408 -GetPixelAlpha(image,pixels)+m))
6410 GetPixelAlpha(image,pixels)))),q);
6413 if (magn_methy == 4)
6415 /* Replicate nearest */
6416 if (i <= ((m+1) << 1))
6417 SetPixelAlpha(large_image,GetPixelAlpha(image,
6420 SetPixelAlpha(large_image,GetPixelAlpha(image,
6425 else /* if (magn_methy == 3 || magn_methy == 5) */
6427 /* Replicate nearest */
6428 if (i <= ((m+1) << 1))
6430 SetPixelRed(large_image,GetPixelRed(image,
6432 SetPixelGreen(large_image,GetPixelGreen(image,
6434 SetPixelBlue(large_image,GetPixelBlue(image,
6436 SetPixelAlpha(large_image,GetPixelAlpha(image,
6442 SetPixelRed(large_image,GetPixelRed(image,n),q);
6443 SetPixelGreen(large_image,GetPixelGreen(image,n),
6445 SetPixelBlue(large_image,GetPixelBlue(image,n),
6447 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6451 if (magn_methy == 5)
6453 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6454 (GetPixelAlpha(image,n)
6455 -GetPixelAlpha(image,pixels))
6456 +m))/((ssize_t) (m*2))
6457 +GetPixelAlpha(image,pixels)),q);
6460 n+=GetPixelChannels(image);
6461 q+=GetPixelChannels(large_image);
6462 pixels+=GetPixelChannels(image);
6465 if (SyncAuthenticPixels(large_image,exception) == 0)
6471 prev=(Quantum *) RelinquishMagickMemory(prev);
6472 next=(Quantum *) RelinquishMagickMemory(next);
6474 length=image->columns;
6476 if (logging != MagickFalse)
6477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6478 " Delete original image");
6480 DeleteImageFromList(&image);
6484 mng_info->image=image;
6486 /* magnify the columns */
6487 if (logging != MagickFalse)
6488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6489 " Magnify the columns to %.20g",(double) image->columns);
6491 for (y=0; y < (ssize_t) image->rows; y++)
6496 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6497 pixels=q+(image->columns-length)*GetPixelChannels(image);
6498 n=pixels+GetPixelChannels(image);
6500 for (x=(ssize_t) (image->columns-length);
6501 x < (ssize_t) image->columns; x++)
6503 /* To do: Rewrite using Get/Set***PixelChannel() */
6505 if (x == (ssize_t) (image->columns-length))
6506 m=(ssize_t) mng_info->magn_ml;
6508 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6509 m=(ssize_t) mng_info->magn_mr;
6511 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6512 m=(ssize_t) mng_info->magn_mr;
6514 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6518 m=(ssize_t) mng_info->magn_mx;
6520 for (i=0; i < m; i++)
6522 if (magn_methx <= 1)
6524 /* replicate previous */
6525 SetPixelRed(image,GetPixelRed(image,pixels),q);
6526 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6527 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6528 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6531 else if (magn_methx == 2 || magn_methx == 4)
6535 SetPixelRed(image,GetPixelRed(image,pixels),q);
6536 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6537 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6538 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6541 /* To do: Rewrite using Get/Set***PixelChannel() */
6545 SetPixelRed(image,(QM) ((2*i*(
6546 GetPixelRed(image,n)
6547 -GetPixelRed(image,pixels))+m)
6549 GetPixelRed(image,pixels)),q);
6551 SetPixelGreen(image,(QM) ((2*i*(
6552 GetPixelGreen(image,n)
6553 -GetPixelGreen(image,pixels))+m)
6555 GetPixelGreen(image,pixels)),q);
6557 SetPixelBlue(image,(QM) ((2*i*(
6558 GetPixelBlue(image,n)
6559 -GetPixelBlue(image,pixels))+m)
6561 GetPixelBlue(image,pixels)),q);
6562 if (image->matte != MagickFalse)
6563 SetPixelAlpha(image,(QM) ((2*i*(
6564 GetPixelAlpha(image,n)
6565 -GetPixelAlpha(image,pixels))+m)
6567 GetPixelAlpha(image,pixels)),q);
6570 if (magn_methx == 4)
6572 /* Replicate nearest */
6573 if (i <= ((m+1) << 1))
6575 SetPixelAlpha(image,
6576 GetPixelAlpha(image,pixels)+0,q);
6580 SetPixelAlpha(image,
6581 GetPixelAlpha(image,n)+0,q);
6586 else /* if (magn_methx == 3 || magn_methx == 5) */
6588 /* Replicate nearest */
6589 if (i <= ((m+1) << 1))
6591 SetPixelRed(image,GetPixelRed(image,pixels),q);
6592 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6593 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6594 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6599 SetPixelRed(image,GetPixelRed(image,n),q);
6600 SetPixelGreen(image,GetPixelGreen(image,n),q);
6601 SetPixelBlue(image,GetPixelBlue(image,n),q);
6602 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6605 if (magn_methx == 5)
6608 SetPixelAlpha(image,
6609 (QM) ((2*i*( GetPixelAlpha(image,n)
6610 -GetPixelAlpha(image,pixels))+m)/
6612 +GetPixelAlpha(image,pixels)),q);
6615 q+=GetPixelChannels(image);
6617 n+=GetPixelChannels(image);
6618 p+=GetPixelChannels(image);
6621 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6624 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6625 if (magn_methx != 1 || magn_methy != 1)
6628 Rescale pixels to Quantum
6630 for (y=0; y < (ssize_t) image->rows; y++)
6632 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6634 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6636 SetPixelRed(image,ScaleShortToQuantum(
6637 GetPixelRed(image,q)),q);
6638 SetPixelGreen(image,ScaleShortToQuantum(
6639 GetPixelGreen(image,q)),q);
6640 SetPixelBlue(image,ScaleShortToQuantum(
6641 GetPixelBlue(image,q)),q);
6642 SetPixelAlpha(image,ScaleShortToQuantum(
6643 GetPixelAlpha(image,q)),q);
6644 q+=GetPixelChannels(image);
6647 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6652 if (logging != MagickFalse)
6653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6654 " Finished MAGN processing");
6659 Crop_box is with respect to the upper left corner of the MNG.
6661 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6662 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6663 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6664 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6665 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6666 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6667 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6668 if ((crop_box.left != (mng_info->image_box.left
6669 +mng_info->x_off[object_id])) ||
6670 (crop_box.right != (mng_info->image_box.right
6671 +mng_info->x_off[object_id])) ||
6672 (crop_box.top != (mng_info->image_box.top
6673 +mng_info->y_off[object_id])) ||
6674 (crop_box.bottom != (mng_info->image_box.bottom
6675 +mng_info->y_off[object_id])))
6677 if (logging != MagickFalse)
6678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6679 " Crop the PNG image");
6681 if ((crop_box.left < crop_box.right) &&
6682 (crop_box.top < crop_box.bottom))
6691 Crop_info is with respect to the upper left corner of
6694 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6695 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6696 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6697 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6698 image->page.width=image->columns;
6699 image->page.height=image->rows;
6702 im=CropImage(image,&crop_info,exception);
6704 if (im != (Image *) NULL)
6706 image->columns=im->columns;
6707 image->rows=im->rows;
6708 im=DestroyImage(im);
6709 image->page.width=image->columns;
6710 image->page.height=image->rows;
6711 image->page.x=crop_box.left;
6712 image->page.y=crop_box.top;
6719 No pixels in crop area. The MNG spec still requires
6720 a layer, though, so make a single transparent pixel in
6721 the top left corner.
6726 (void) SetImageBackgroundColor(image,exception);
6727 image->page.width=1;
6728 image->page.height=1;
6733 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6734 image=mng_info->image;
6738 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6739 /* PNG does not handle depths greater than 16 so reduce it even
6742 if (image->depth > 16)
6746 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6747 if (image->depth > 8)
6749 /* To do: fill low byte properly */
6753 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6757 if (image_info->number_scenes != 0)
6759 if (mng_info->scenes_found >
6760 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6764 if (logging != MagickFalse)
6765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6766 " Finished reading image datastream.");
6768 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6770 (void) CloseBlob(image);
6772 if (logging != MagickFalse)
6773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6774 " Finished reading all image datastreams.");
6776 #if defined(MNG_INSERT_LAYERS)
6777 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6778 (mng_info->mng_height))
6781 Insert a background layer if nothing else was found.
6783 if (logging != MagickFalse)
6784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6785 " No images found. Inserting a background layer.");
6787 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6790 Allocate next image structure.
6792 AcquireNextImage(image_info,image,exception);
6793 if (GetNextImageInList(image) == (Image *) NULL)
6795 image=DestroyImageList(image);
6796 MngInfoFreeStruct(mng_info,&have_mng_structure);
6798 if (logging != MagickFalse)
6799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6800 " Allocation failed, returning NULL.");
6802 return((Image *) NULL);
6804 image=SyncNextImageInList(image);
6806 image->columns=mng_info->mng_width;
6807 image->rows=mng_info->mng_height;
6808 image->page.width=mng_info->mng_width;
6809 image->page.height=mng_info->mng_height;
6812 image->background_color=mng_background_color;
6813 image->matte=MagickFalse;
6815 if (image_info->ping == MagickFalse)
6816 (void) SetImageBackgroundColor(image,exception);
6818 mng_info->image_found++;
6821 image->iterations=mng_iterations;
6823 if (mng_iterations == 1)
6824 image->start_loop=MagickTrue;
6826 while (GetPreviousImageInList(image) != (Image *) NULL)
6829 if (image_count > 10*mng_info->image_found)
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6834 (void) ThrowMagickException(exception,GetMagickModule(),
6835 CoderError,"Linked list is corrupted, beginning of list not found",
6836 "`%s'",image_info->filename);
6838 return((Image *) NULL);
6841 image=GetPreviousImageInList(image);
6843 if (GetNextImageInList(image) == (Image *) NULL)
6845 if (logging != MagickFalse)
6846 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6848 (void) ThrowMagickException(exception,GetMagickModule(),
6849 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6850 image_info->filename);
6854 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6855 GetNextImageInList(image) ==
6858 if (logging != MagickFalse)
6859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6860 " First image null");
6862 (void) ThrowMagickException(exception,GetMagickModule(),
6863 CoderError,"image->next for first image is NULL but shouldn't be.",
6864 "`%s'",image_info->filename);
6867 if (mng_info->image_found == 0)
6869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " No visible images found.");
6873 (void) ThrowMagickException(exception,GetMagickModule(),
6874 CoderError,"No visible images in file","`%s'",image_info->filename);
6876 if (image != (Image *) NULL)
6877 image=DestroyImageList(image);
6879 MngInfoFreeStruct(mng_info,&have_mng_structure);
6880 return((Image *) NULL);
6883 if (mng_info->ticks_per_second)
6884 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6885 final_delay/mng_info->ticks_per_second;
6888 image->start_loop=MagickTrue;
6890 /* Find final nonzero image delay */
6891 final_image_delay=0;
6893 while (GetNextImageInList(image) != (Image *) NULL)
6896 final_image_delay=image->delay;
6898 image=GetNextImageInList(image);
6901 if (final_delay < final_image_delay)
6902 final_delay=final_image_delay;
6904 image->delay=final_delay;
6906 if (logging != MagickFalse)
6907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6908 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6909 (double) final_delay);
6911 if (logging != MagickFalse)
6917 image=GetFirstImageInList(image);
6919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6920 " Before coalesce:");
6922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6923 " scene 0 delay=%.20g",(double) image->delay);
6925 while (GetNextImageInList(image) != (Image *) NULL)
6927 image=GetNextImageInList(image);
6928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6929 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6933 image=GetFirstImageInList(image);
6934 #ifdef MNG_COALESCE_LAYERS
6944 if (logging != MagickFalse)
6945 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6948 next_image=CoalesceImages(image,exception);
6950 if (next_image == (Image *) NULL)
6951 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6953 image=DestroyImageList(image);
6956 for (next=image; next != (Image *) NULL; next=next_image)
6958 next->page.width=mng_info->mng_width;
6959 next->page.height=mng_info->mng_height;
6962 next->scene=scene++;
6963 next_image=GetNextImageInList(next);
6965 if (next_image == (Image *) NULL)
6968 if (next->delay == 0)
6971 next_image->previous=GetPreviousImageInList(next);
6972 if (GetPreviousImageInList(next) == (Image *) NULL)
6975 next->previous->next=next_image;
6976 next=DestroyImage(next);
6982 while (GetNextImageInList(image) != (Image *) NULL)
6983 image=GetNextImageInList(image);
6985 image->dispose=BackgroundDispose;
6987 if (logging != MagickFalse)
6993 image=GetFirstImageInList(image);
6995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996 " After coalesce:");
6998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6999 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7000 (double) image->dispose);
7002 while (GetNextImageInList(image) != (Image *) NULL)
7004 image=GetNextImageInList(image);
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7008 (double) image->delay,(double) image->dispose);
7012 image=GetFirstImageInList(image);
7013 MngInfoFreeStruct(mng_info,&have_mng_structure);
7014 have_mng_structure=MagickFalse;
7016 if (logging != MagickFalse)
7017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7019 return(GetFirstImageInList(image));
7021 #else /* PNG_LIBPNG_VER > 10011 */
7022 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7024 printf("Your PNG library is too old: You have libpng-%s\n",
7025 PNG_LIBPNG_VER_STRING);
7027 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7028 "PNG library is too old","`%s'",image_info->filename);
7030 return(Image *) NULL;
7033 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7035 return(ReadPNGImage(image_info,exception));
7037 #endif /* PNG_LIBPNG_VER > 10011 */
7041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7045 % R e g i s t e r P N G I m a g e %
7049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7051 % RegisterPNGImage() adds properties for the PNG image format to
7052 % the list of supported formats. The properties include the image format
7053 % tag, a method to read and/or write the format, whether the format
7054 % supports the saving of more than one frame to the same file or blob,
7055 % whether the format supports native in-memory I/O, and a brief
7056 % description of the format.
7058 % The format of the RegisterPNGImage method is:
7060 % size_t RegisterPNGImage(void)
7063 ModuleExport size_t RegisterPNGImage(void)
7066 version[MaxTextExtent];
7074 "See http://www.libpng.org/ for details about the PNG format."
7079 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7085 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7091 #if defined(PNG_LIBPNG_VER_STRING)
7092 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7093 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7095 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7097 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7098 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7103 entry=SetMagickInfo("MNG");
7104 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7106 #if defined(MAGICKCORE_PNG_DELEGATE)
7107 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7108 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7111 entry->magick=(IsImageFormatHandler *) IsMNG;
7112 entry->description=ConstantString("Multiple-image Network Graphics");
7114 if (*version != '\0')
7115 entry->version=ConstantString(version);
7117 entry->module=ConstantString("PNG");
7118 entry->note=ConstantString(MNGNote);
7119 (void) RegisterMagickInfo(entry);
7121 entry=SetMagickInfo("PNG");
7123 #if defined(MAGICKCORE_PNG_DELEGATE)
7124 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7125 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7128 entry->magick=(IsImageFormatHandler *) IsPNG;
7129 entry->adjoin=MagickFalse;
7130 entry->description=ConstantString("Portable Network Graphics");
7131 entry->module=ConstantString("PNG");
7133 if (*version != '\0')
7134 entry->version=ConstantString(version);
7136 entry->note=ConstantString(PNGNote);
7137 (void) RegisterMagickInfo(entry);
7139 entry=SetMagickInfo("PNG8");
7141 #if defined(MAGICKCORE_PNG_DELEGATE)
7142 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7143 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7146 entry->magick=(IsImageFormatHandler *) IsPNG;
7147 entry->adjoin=MagickFalse;
7148 entry->description=ConstantString(
7149 "8-bit indexed with optional binary transparency");
7150 entry->module=ConstantString("PNG");
7151 (void) RegisterMagickInfo(entry);
7153 entry=SetMagickInfo("PNG24");
7156 #if defined(ZLIB_VERSION)
7157 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7158 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7160 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7162 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7163 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7167 if (*version != '\0')
7168 entry->version=ConstantString(version);
7170 #if defined(MAGICKCORE_PNG_DELEGATE)
7171 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7172 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7175 entry->magick=(IsImageFormatHandler *) IsPNG;
7176 entry->adjoin=MagickFalse;
7177 entry->description=ConstantString("opaque 24-bit RGB");
7178 entry->module=ConstantString("PNG");
7179 (void) RegisterMagickInfo(entry);
7181 entry=SetMagickInfo("PNG32");
7183 #if defined(MAGICKCORE_PNG_DELEGATE)
7184 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7185 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7188 entry->magick=(IsImageFormatHandler *) IsPNG;
7189 entry->adjoin=MagickFalse;
7190 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7191 entry->module=ConstantString("PNG");
7192 (void) RegisterMagickInfo(entry);
7194 entry=SetMagickInfo("JNG");
7196 #if defined(JNG_SUPPORTED)
7197 #if defined(MAGICKCORE_PNG_DELEGATE)
7198 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7199 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7203 entry->magick=(IsImageFormatHandler *) IsJNG;
7204 entry->adjoin=MagickFalse;
7205 entry->description=ConstantString("JPEG Network Graphics");
7206 entry->module=ConstantString("PNG");
7207 entry->note=ConstantString(JNGNote);
7208 (void) RegisterMagickInfo(entry);
7210 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7211 ping_semaphore=AllocateSemaphoreInfo();
7214 return(MagickImageCoderSignature);
7218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7222 % U n r e g i s t e r P N G I m a g e %
7226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7228 % UnregisterPNGImage() removes format registrations made by the
7229 % PNG module from the list of supported formats.
7231 % The format of the UnregisterPNGImage method is:
7233 % UnregisterPNGImage(void)
7236 ModuleExport void UnregisterPNGImage(void)
7238 (void) UnregisterMagickInfo("MNG");
7239 (void) UnregisterMagickInfo("PNG");
7240 (void) UnregisterMagickInfo("PNG8");
7241 (void) UnregisterMagickInfo("PNG24");
7242 (void) UnregisterMagickInfo("PNG32");
7243 (void) UnregisterMagickInfo("JNG");
7245 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7246 if (ping_semaphore != (SemaphoreInfo *) NULL)
7247 DestroySemaphoreInfo(&ping_semaphore);
7251 #if defined(MAGICKCORE_PNG_DELEGATE)
7252 #if PNG_LIBPNG_VER > 10011
7254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7258 % W r i t e M N G I m a g e %
7262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7264 % WriteMNGImage() writes an image in the Portable Network Graphics
7265 % Group's "Multiple-image Network Graphics" encoded image format.
7267 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7269 % The format of the WriteMNGImage method is:
7271 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7272 % Image *image,ExceptionInfo *exception)
7274 % A description of each parameter follows.
7276 % o image_info: the image info.
7278 % o image: The image.
7280 % o exception: return any errors or warnings in this structure.
7282 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7283 % "To do" under ReadPNGImage):
7285 % Preserve all unknown and not-yet-handled known chunks found in input
7286 % PNG file and copy them into output PNG files according to the PNG
7289 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7291 % Improve selection of color type (use indexed-colour or indexed-colour
7292 % with tRNS when 256 or fewer unique RGBA values are present).
7294 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7295 % This will be complicated if we limit ourselves to generating MNG-LC
7296 % files. For now we ignore disposal method 3 and simply overlay the next
7299 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7300 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7301 % [mostly done 15 June 1999 but still need to take care of tRNS]
7303 % Check for identical sRGB and replace with a global sRGB (and remove
7304 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7305 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7306 % local gAMA/cHRM with local sRGB if appropriate).
7308 % Check for identical sBIT chunks and write global ones.
7310 % Provide option to skip writing the signature tEXt chunks.
7312 % Use signatures to detect identical objects and reuse the first
7313 % instance of such objects instead of writing duplicate objects.
7315 % Use a smaller-than-32k value of compression window size when
7318 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7319 % ancillary text chunks and save profiles.
7321 % Provide an option to force LC files (to ensure exact framing rate)
7324 % Provide an option to force VLC files instead of LC, even when offsets
7325 % are present. This will involve expanding the embedded images with a
7326 % transparent region at the top and/or left.
7330 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7331 png_info *ping_info, unsigned char *profile_type, unsigned char
7332 *profile_description, unsigned char *profile_data, png_uint_32 length)
7351 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7353 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7356 if (image_info->verbose)
7358 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7359 (char *) profile_type, (double) length);
7362 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7363 description_length=(png_uint_32) strlen((const char *) profile_description);
7364 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7365 + description_length);
7366 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7367 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7368 text[0].key[0]='\0';
7369 (void) ConcatenateMagickString(text[0].key,
7370 "Raw profile type ",MaxTextExtent);
7371 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7375 (void) CopyMagickString(dp,(const char *) profile_description,
7377 dp+=description_length;
7379 (void) FormatLocaleString(dp,allocated_length-
7380 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7383 for (i=0; i < (ssize_t) length; i++)
7387 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7388 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7393 text[0].text_length=(png_size_t) (dp-text[0].text);
7394 text[0].compression=image_info->compression == NoCompression ||
7395 (image_info->compression == UndefinedCompression &&
7396 text[0].text_length < 128) ? -1 : 0;
7398 if (text[0].text_length <= allocated_length)
7399 png_set_text(ping,ping_info,text,1);
7401 png_free(ping,text[0].text);
7402 png_free(ping,text[0].key);
7403 png_free(ping,text);
7406 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7407 const char *string, MagickBooleanType logging)
7420 ResetImageProfileIterator(image);
7422 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7424 profile=GetImageProfile(image,name);
7426 if (profile != (const StringInfo *) NULL)
7431 if (LocaleNCompare(name,string,11) == 0)
7433 if (logging != MagickFalse)
7434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435 " Found %s profile",name);
7437 ping_profile=CloneStringInfo(profile);
7438 data=GetStringInfoDatum(ping_profile),
7439 length=(png_uint_32) GetStringInfoLength(ping_profile);
7444 (void) WriteBlobMSBULong(image,length-5); /* data length */
7445 (void) WriteBlob(image,length-1,data+1);
7446 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7447 ping_profile=DestroyStringInfo(ping_profile);
7451 name=GetNextImageProfile(image);
7458 /* Write one PNG image */
7459 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7460 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7484 ping_trans_alpha[256];
7512 ping_have_cheap_transparency,
7523 /* ping_exclude_EXIF, */
7526 /* ping_exclude_iTXt, */
7531 /* ping_exclude_tRNS, */
7533 ping_exclude_zCCP, /* hex-encoded iCCP */
7536 ping_preserve_colormap,
7537 ping_need_colortype_warning,
7561 ping_interlace_method,
7562 ping_compression_method,
7579 number_semitransparent,
7581 ping_pHYs_unit_type;
7584 ping_pHYs_x_resolution,
7585 ping_pHYs_y_resolution;
7587 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7588 " Enter WriteOnePNGImage()");
7590 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7591 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7592 if (image_info == (ImageInfo *) NULL)
7593 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7595 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7596 LockSemaphoreInfo(ping_semaphore);
7599 /* Initialize some stuff */
7602 ping_interlace_method=0,
7603 ping_compression_method=0,
7604 ping_filter_method=0,
7607 ping_background.red = 0;
7608 ping_background.green = 0;
7609 ping_background.blue = 0;
7610 ping_background.gray = 0;
7611 ping_background.index = 0;
7613 ping_trans_color.red=0;
7614 ping_trans_color.green=0;
7615 ping_trans_color.blue=0;
7616 ping_trans_color.gray=0;
7618 ping_pHYs_unit_type = 0;
7619 ping_pHYs_x_resolution = 0;
7620 ping_pHYs_y_resolution = 0;
7622 ping_have_blob=MagickFalse;
7623 ping_have_color=MagickTrue;
7624 ping_have_non_bw=MagickTrue;
7625 ping_have_PLTE=MagickFalse;
7626 ping_have_bKGD=MagickFalse;
7627 ping_have_pHYs=MagickFalse;
7628 ping_have_tRNS=MagickFalse;
7630 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7631 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7632 ping_exclude_date=mng_info->ping_exclude_date;
7633 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7634 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7635 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7636 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7637 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7638 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7639 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7640 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7641 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7642 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7643 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7644 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7646 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7647 ping_need_colortype_warning = MagickFalse;
7649 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7650 * i.e., eliminate the ICC profile and set image->rendering_intent.
7651 * Note that this will not involve any changes to the actual pixels
7652 * but merely passes information to applications that read the resulting
7655 if (ping_exclude_sRGB == MagickFalse)
7663 ResetImageProfileIterator(image);
7664 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7666 profile=GetImageProfile(image,name);
7668 if (profile != (StringInfo *) NULL)
7670 if ((LocaleCompare(name,"ICC") == 0) ||
7671 (LocaleCompare(name,"ICM") == 0))
7676 /* 0: not a known sRGB profile
7677 * 1: HP-Microsoft sRGB v2
7678 * 2: ICC sRGB v4 perceptual
7679 * 3: ICC sRGB v2 perceptual no black-compensation
7682 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7683 check_len[4] = {0, 3144, 60960, 3052};
7692 length=(png_uint_32) GetStringInfoLength(profile);
7694 for (icheck=3; icheck > 0; icheck--)
7696 if (length == check_len[icheck])
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " Got a %lu-byte ICC profile (potentially sRGB)",
7700 (unsigned long) length);
7702 data=GetStringInfoDatum(profile);
7703 profile_crc=crc32(0,data,length);
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " with crc=%8x",(unsigned int) profile_crc);
7708 if (profile_crc == check_crc[icheck])
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 if (image->rendering_intent==UndefinedIntent)
7713 image->rendering_intent=PerceptualIntent;
7719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7720 " Got a %lu-byte ICC profile",
7721 (unsigned long) length);
7724 name=GetNextImageProfile(image);
7729 number_semitransparent = 0;
7730 number_transparent = 0;
7732 if (logging != MagickFalse)
7734 if (image->storage_class == UndefinedClass)
7735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7736 " storage_class=UndefinedClass");
7737 if (image->storage_class == DirectClass)
7738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7739 " storage_class=DirectClass");
7740 if (image->storage_class == PseudoClass)
7741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7742 " storage_class=PseudoClass");
7745 if (image->storage_class == PseudoClass &&
7746 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7747 (mng_info->write_png_colortype != 0 &&
7748 mng_info->write_png_colortype != 4)))
7750 (void) SyncImage(image,exception);
7751 image->storage_class = DirectClass;
7754 if (ping_preserve_colormap == MagickFalse)
7756 if (image->storage_class != PseudoClass && image->colormap != NULL)
7758 /* Free the bogus colormap; it can cause trouble later */
7759 if (logging != MagickFalse)
7760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7761 " Freeing bogus colormap");
7762 (void) RelinquishMagickMemory(image->colormap);
7763 image->colormap=NULL;
7767 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7768 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7771 Sometimes we get PseudoClass images whose RGB values don't match
7772 the colors in the colormap. This code syncs the RGB values.
7774 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7775 (void) SyncImage(image,exception);
7777 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7778 if (image->depth > 8)
7780 if (logging != MagickFalse)
7781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7782 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7788 /* Respect the -depth option */
7789 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7794 if (image->depth > 8)
7796 #if MAGICKCORE_QUANTUM_DEPTH > 16
7797 /* Scale to 16-bit */
7798 LBR16PacketRGBO(image->background_color);
7800 for (y=0; y < (ssize_t) image->rows; y++)
7802 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7804 if (r == (Quantum *) NULL)
7807 for (x=0; x < (ssize_t) image->columns; x++)
7810 r+=GetPixelChannels(image);
7813 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7817 if (image->storage_class == PseudoClass && image->colormap != NULL)
7819 for (i=0; i < (ssize_t) image->colors; i++)
7821 LBR16PacketRGBO(image->colormap[i]);
7824 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7827 else if (image->depth > 4)
7829 #if MAGICKCORE_QUANTUM_DEPTH > 8
7830 /* Scale to 8-bit */
7831 LBR08PacketRGBO(image->background_color);
7833 for (y=0; y < (ssize_t) image->rows; y++)
7835 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7837 if (r == (Quantum *) NULL)
7840 for (x=0; x < (ssize_t) image->columns; x++)
7843 r+=GetPixelChannels(image);
7846 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7850 if (image->storage_class == PseudoClass && image->colormap != NULL)
7852 for (i=0; i < (ssize_t) image->colors; i++)
7854 LBR08PacketRGBO(image->colormap[i]);
7857 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7860 if (image->depth > 2)
7862 /* Scale to 4-bit */
7863 LBR04PacketRGBO(image->background_color);
7865 for (y=0; y < (ssize_t) image->rows; y++)
7867 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7869 if (r == (Quantum *) NULL)
7872 for (x=0; x < (ssize_t) image->columns; x++)
7875 r+=GetPixelChannels(image);
7878 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7882 if (image->storage_class == PseudoClass && image->colormap != NULL)
7884 for (i=0; i < (ssize_t) image->colors; i++)
7886 LBR04PacketRGBO(image->colormap[i]);
7891 else if (image->depth > 1)
7893 /* Scale to 2-bit */
7894 LBR02PacketRGBO(image->background_color);
7896 for (y=0; y < (ssize_t) image->rows; y++)
7898 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7900 if (r == (Quantum *) NULL)
7903 for (x=0; x < (ssize_t) image->columns; x++)
7906 r+=GetPixelChannels(image);
7909 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7913 if (image->storage_class == PseudoClass && image->colormap != NULL)
7915 for (i=0; i < (ssize_t) image->colors; i++)
7917 LBR02PacketRGBO(image->colormap[i]);
7923 /* Scale to 1-bit */
7924 LBR01PacketRGBO(image->background_color);
7926 for (y=0; y < (ssize_t) image->rows; y++)
7928 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7930 if (r == (Quantum *) NULL)
7933 for (x=0; x < (ssize_t) image->columns; x++)
7936 r+=GetPixelChannels(image);
7939 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7943 if (image->storage_class == PseudoClass && image->colormap != NULL)
7945 for (i=0; i < (ssize_t) image->colors; i++)
7947 LBR01PacketRGBO(image->colormap[i]);
7953 /* To do: set to next higher multiple of 8 */
7954 if (image->depth < 8)
7957 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7958 /* PNG does not handle depths greater than 16 so reduce it even
7961 if (image->depth > 8)
7965 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7966 if (image->depth > 8)
7968 /* To do: fill low byte properly */
7972 if (image->depth == 16 && mng_info->write_png_depth != 16)
7973 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
7977 /* Normally we run this just once, but in the case of writing PNG8
7978 * we reduce the transparency to binary and run again, then if there
7979 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7980 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7981 * palette. Then (To do) we take care of a final reduction that is only
7982 * needed if there are still 256 colors present and one of them has both
7983 * transparent and opaque instances.
7986 tried_332 = MagickFalse;
7987 tried_333 = MagickFalse;
7988 tried_444 = MagickFalse;
7994 * Sometimes we get DirectClass images that have 256 colors or fewer.
7995 * This code will build a colormap.
7997 * Also, sometimes we get PseudoClass images with an out-of-date
7998 * colormap. This code will replace the colormap with a new one.
7999 * Sometimes we get PseudoClass images that have more than 256 colors.
8000 * This code will delete the colormap and change the image to
8003 * If image->matte is MagickFalse, we ignore the alpha channel
8004 * even though it sometimes contains left-over non-opaque values.
8006 * Also we gather some information (number of opaque, transparent,
8007 * and semitransparent pixels, and whether the image has any non-gray
8008 * pixels or only black-and-white pixels) that we might need later.
8010 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8011 * we need to check for bogus non-opaque values, at least.
8019 semitransparent[260],
8022 register const Quantum
8029 if (logging != MagickFalse)
8030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " Enter BUILD_PALETTE:");
8033 if (logging != MagickFalse)
8035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8036 " image->columns=%.20g",(double) image->columns);
8037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8038 " image->rows=%.20g",(double) image->rows);
8039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8040 " image->matte=%.20g",(double) image->matte);
8041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8042 " image->depth=%.20g",(double) image->depth);
8044 if (image->storage_class == PseudoClass && image->colormap != NULL)
8046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " Original colormap:");
8048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8049 " i (red,green,blue,alpha)");
8051 for (i=0; i < 256; i++)
8053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8054 " %d (%d,%d,%d,%d)",
8056 (int) image->colormap[i].red,
8057 (int) image->colormap[i].green,
8058 (int) image->colormap[i].blue,
8059 (int) image->colormap[i].alpha);
8062 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8067 " %d (%d,%d,%d,%d)",
8069 (int) image->colormap[i].red,
8070 (int) image->colormap[i].green,
8071 (int) image->colormap[i].blue,
8072 (int) image->colormap[i].alpha);
8077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8078 " image->colors=%d",(int) image->colors);
8080 if (image->colors == 0)
8081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8082 " (zero means unknown)");
8084 if (ping_preserve_colormap == MagickFalse)
8085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8086 " Regenerate the colormap");
8091 number_semitransparent = 0;
8092 number_transparent = 0;
8094 for (y=0; y < (ssize_t) image->rows; y++)
8096 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8098 if (q == (Quantum *) NULL)
8101 for (x=0; x < (ssize_t) image->columns; x++)
8103 if (image->matte == MagickFalse ||
8104 GetPixelAlpha(image,q) == OpaqueAlpha)
8106 if (number_opaque < 259)
8108 if (number_opaque == 0)
8110 GetPixelInfoPixel(image, q, opaque);
8111 opaque[0].alpha=OpaqueAlpha;
8115 for (i=0; i< (ssize_t) number_opaque; i++)
8117 if (IsPixelEquivalent(image,q, opaque+i))
8121 if (i == (ssize_t) number_opaque && number_opaque < 259)
8124 GetPixelInfoPixel(image, q, opaque+i);
8125 opaque[i].alpha=OpaqueAlpha;
8129 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8131 if (number_transparent < 259)
8133 if (number_transparent == 0)
8135 GetPixelInfoPixel(image, q, transparent);
8136 ping_trans_color.red=(unsigned short)
8137 GetPixelRed(image,q);
8138 ping_trans_color.green=(unsigned short)
8139 GetPixelGreen(image,q);
8140 ping_trans_color.blue=(unsigned short)
8141 GetPixelBlue(image,q);
8142 ping_trans_color.gray=(unsigned short)
8143 GetPixelRed(image,q);
8144 number_transparent = 1;
8147 for (i=0; i< (ssize_t) number_transparent; i++)
8149 if (IsPixelEquivalent(image,q, transparent+i))
8153 if (i == (ssize_t) number_transparent &&
8154 number_transparent < 259)
8156 number_transparent++;
8157 GetPixelInfoPixel(image,q,transparent+i);
8163 if (number_semitransparent < 259)
8165 if (number_semitransparent == 0)
8167 GetPixelInfoPixel(image,q,semitransparent);
8168 number_semitransparent = 1;
8171 for (i=0; i< (ssize_t) number_semitransparent; i++)
8173 if (IsPixelEquivalent(image,q, semitransparent+i)
8174 && GetPixelAlpha(image,q) ==
8175 semitransparent[i].alpha)
8179 if (i == (ssize_t) number_semitransparent &&
8180 number_semitransparent < 259)
8182 number_semitransparent++;
8183 GetPixelInfoPixel(image, q, semitransparent+i);
8187 q+=GetPixelChannels(image);
8191 if (mng_info->write_png8 == MagickFalse &&
8192 ping_exclude_bKGD == MagickFalse)
8194 /* Add the background color to the palette, if it
8195 * isn't already there.
8197 if (logging != MagickFalse)
8199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8200 " Check colormap for background (%d,%d,%d)",
8201 (int) image->background_color.red,
8202 (int) image->background_color.green,
8203 (int) image->background_color.blue);
8205 for (i=0; i<number_opaque; i++)
8207 if (opaque[i].red == image->background_color.red &&
8208 opaque[i].green == image->background_color.green &&
8209 opaque[i].blue == image->background_color.blue)
8212 if (number_opaque < 259 && i == number_opaque)
8214 opaque[i] = image->background_color;
8215 ping_background.index = i;
8216 if (logging != MagickFalse)
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " background_color index is %d",(int) i);
8223 else if (logging != MagickFalse)
8224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8225 " No room in the colormap to add background color");
8228 image_colors=number_opaque+number_transparent+number_semitransparent;
8230 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8232 /* No room for the background color; remove it. */
8237 if (logging != MagickFalse)
8239 if (image_colors > 256)
8240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8241 " image has more than 256 colors");
8244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8245 " image has %d colors",image_colors);
8248 if (ping_preserve_colormap != MagickFalse)
8251 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8253 ping_have_color=MagickFalse;
8254 ping_have_non_bw=MagickFalse;
8256 if(image_colors > 256)
8258 for (y=0; y < (ssize_t) image->rows; y++)
8260 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8262 if (q == (Quantum *) NULL)
8266 for (x=0; x < (ssize_t) image->columns; x++)
8268 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8269 GetPixelRed(image,s) != GetPixelBlue(image,s))
8271 ping_have_color=MagickTrue;
8272 ping_have_non_bw=MagickTrue;
8275 s+=GetPixelChannels(image);
8278 if (ping_have_color != MagickFalse)
8281 /* Worst case is black-and-white; we are looking at every
8285 if (ping_have_non_bw == MagickFalse)
8288 for (x=0; x < (ssize_t) image->columns; x++)
8290 if (GetPixelRed(image,s) != 0 &&
8291 GetPixelRed(image,s) != QuantumRange)
8293 ping_have_non_bw=MagickTrue;
8296 s+=GetPixelChannels(image);
8303 if (image_colors < 257)
8309 * Initialize image colormap.
8312 if (logging != MagickFalse)
8313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314 " Sort the new colormap");
8316 /* Sort palette, transparent first */;
8320 for (i=0; i<number_transparent; i++)
8321 colormap[n++] = transparent[i];
8323 for (i=0; i<number_semitransparent; i++)
8324 colormap[n++] = semitransparent[i];
8326 for (i=0; i<number_opaque; i++)
8327 colormap[n++] = opaque[i];
8329 ping_background.index +=
8330 (number_transparent + number_semitransparent);
8332 /* image_colors < 257; search the colormap instead of the pixels
8333 * to get ping_have_color and ping_have_non_bw
8337 if (ping_have_color == MagickFalse)
8339 if (colormap[i].red != colormap[i].green ||
8340 colormap[i].red != colormap[i].blue)
8342 ping_have_color=MagickTrue;
8343 ping_have_non_bw=MagickTrue;
8348 if (ping_have_non_bw == MagickFalse)
8350 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8351 ping_have_non_bw=MagickTrue;
8355 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8356 (number_transparent == 0 && number_semitransparent == 0)) &&
8357 (((mng_info->write_png_colortype-1) ==
8358 PNG_COLOR_TYPE_PALETTE) ||
8359 (mng_info->write_png_colortype == 0)))
8361 if (logging != MagickFalse)
8363 if (n != (ssize_t) image_colors)
8364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365 " image_colors (%d) and n (%d) don't match",
8368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8369 " AcquireImageColormap");
8372 image->colors = image_colors;
8374 if (AcquireImageColormap(image,image_colors,exception) ==
8376 ThrowWriterException(ResourceLimitError,
8377 "MemoryAllocationFailed");
8379 for (i=0; i< (ssize_t) image_colors; i++)
8380 image->colormap[i] = colormap[i];
8382 if (logging != MagickFalse)
8384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8385 " image->colors=%d (%d)",
8386 (int) image->colors, image_colors);
8388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8389 " Update the pixel indexes");
8392 /* Sync the pixel indices with the new colormap */
8394 for (y=0; y < (ssize_t) image->rows; y++)
8396 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8398 if (q == (Quantum *) NULL)
8401 for (x=0; x < (ssize_t) image->columns; x++)
8403 for (i=0; i< (ssize_t) image_colors; i++)
8405 if ((image->matte == MagickFalse ||
8406 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8407 image->colormap[i].red == GetPixelRed(image,q) &&
8408 image->colormap[i].green == GetPixelGreen(image,q) &&
8409 image->colormap[i].blue == GetPixelBlue(image,q))
8411 SetPixelIndex(image,i,q);
8415 q+=GetPixelChannels(image);
8418 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8424 if (logging != MagickFalse)
8426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " image->colors=%d", (int) image->colors);
8429 if (image->colormap != NULL)
8431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8432 " i (red,green,blue,alpha)");
8434 for (i=0; i < (ssize_t) image->colors; i++)
8436 if (i < 300 || i >= (ssize_t) image->colors - 10)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " %d (%d,%d,%d,%d)",
8441 (int) image->colormap[i].red,
8442 (int) image->colormap[i].green,
8443 (int) image->colormap[i].blue,
8444 (int) image->colormap[i].alpha);
8449 if (number_transparent < 257)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " number_transparent = %d",
8452 number_transparent);
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " number_transparent > 256");
8458 if (number_opaque < 257)
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " number_opaque = %d",
8464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8465 " number_opaque > 256");
8467 if (number_semitransparent < 257)
8468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8469 " number_semitransparent = %d",
8470 number_semitransparent);
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " number_semitransparent > 256");
8476 if (ping_have_non_bw == MagickFalse)
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8478 " All pixels and the background are black or white");
8480 else if (ping_have_color == MagickFalse)
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " All pixels and the background are gray");
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " At least one pixel or the background is non-gray");
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Exit BUILD_PALETTE:");
8492 if (mng_info->write_png8 == MagickFalse)
8495 /* Make any reductions necessary for the PNG8 format */
8496 if (image_colors <= 256 &&
8497 image_colors != 0 && image->colormap != NULL &&
8498 number_semitransparent == 0 &&
8499 number_transparent <= 1)
8502 /* PNG8 can't have semitransparent colors so we threshold the
8503 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8504 * transparent color so if more than one is transparent we merge
8505 * them into image->background_color.
8507 if (number_semitransparent != 0 || number_transparent > 1)
8509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510 " Thresholding the alpha channel to binary");
8512 for (y=0; y < (ssize_t) image->rows; y++)
8514 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8516 if (r == (Quantum *) NULL)
8519 for (x=0; x < (ssize_t) image->columns; x++)
8521 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8523 SetPixelInfoPixel(image,&image->background_color,r);
8524 SetPixelAlpha(image,TransparentAlpha,r);
8527 SetPixelAlpha(image,OpaqueAlpha,r);
8528 r+=GetPixelChannels(image);
8531 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8534 if (image_colors != 0 && image_colors <= 256 &&
8535 image->colormap != NULL)
8536 for (i=0; i<image_colors; i++)
8537 image->colormap[i].alpha =
8538 (image->colormap[i].alpha > TransparentAlpha/2 ?
8539 TransparentAlpha : OpaqueAlpha);
8544 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8545 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8546 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8549 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8551 if (logging != MagickFalse)
8552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553 " Quantizing the background color to 4-4-4");
8555 tried_444 = MagickTrue;
8557 LBR04PacketRGB(image->background_color);
8559 if (logging != MagickFalse)
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8561 " Quantizing the pixel colors to 4-4-4");
8563 if (image->colormap == NULL)
8565 for (y=0; y < (ssize_t) image->rows; y++)
8567 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8569 if (r == (Quantum *) NULL)
8572 for (x=0; x < (ssize_t) image->columns; x++)
8574 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8576 r+=GetPixelChannels(image);
8579 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8584 else /* Should not reach this; colormap already exists and
8587 if (logging != MagickFalse)
8588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8589 " Quantizing the colormap to 4-4-4");
8591 for (i=0; i<image_colors; i++)
8593 LBR04PacketRGB(image->colormap[i]);
8599 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8601 if (logging != MagickFalse)
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " Quantizing the background color to 3-3-3");
8605 tried_333 = MagickTrue;
8607 LBR03PacketRGB(image->background_color);
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Quantizing the pixel colors to 3-3-3-1");
8613 if (image->colormap == NULL)
8615 for (y=0; y < (ssize_t) image->rows; y++)
8617 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8619 if (r == (Quantum *) NULL)
8622 for (x=0; x < (ssize_t) image->columns; x++)
8624 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8626 r+=GetPixelChannels(image);
8629 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8634 else /* Should not reach this; colormap already exists and
8637 if (logging != MagickFalse)
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " Quantizing the colormap to 3-3-3-1");
8640 for (i=0; i<image_colors; i++)
8642 LBR03PacketRGB(image->colormap[i]);
8648 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8650 if (logging != MagickFalse)
8651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8652 " Quantizing the background color to 3-3-2");
8654 tried_332 = MagickTrue;
8656 /* Red and green were already done so we only quantize the blue
8660 LBR02PacketBlue(image->background_color);
8662 if (logging != MagickFalse)
8663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8664 " Quantizing the pixel colors to 3-3-2-1");
8666 if (image->colormap == NULL)
8668 for (y=0; y < (ssize_t) image->rows; y++)
8670 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8672 if (r == (Quantum *) NULL)
8675 for (x=0; x < (ssize_t) image->columns; x++)
8677 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8679 r+=GetPixelChannels(image);
8682 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8687 else /* Should not reach this; colormap already exists and
8690 if (logging != MagickFalse)
8691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8692 " Quantizing the colormap to 3-3-2-1");
8693 for (i=0; i<image_colors; i++)
8695 LBR02PacketBlue(image->colormap[i]);
8702 if (image_colors == 0 || image_colors > 256)
8704 /* Take care of special case with 256 colors + 1 transparent
8705 * color. We don't need to quantize to 2-3-2-1; we only need to
8706 * eliminate one color, so we'll merge the two darkest red
8707 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8709 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8710 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8711 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8713 image->background_color.red=ScaleCharToQuantum(0x24);
8716 if (image->colormap == NULL)
8718 for (y=0; y < (ssize_t) image->rows; y++)
8720 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8722 if (r == (Quantum *) NULL)
8725 for (x=0; x < (ssize_t) image->columns; x++)
8727 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8728 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8729 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8730 GetPixelAlpha(image,r) == OpaqueAlpha)
8732 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8734 r+=GetPixelChannels(image);
8737 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8745 for (i=0; i<image_colors; i++)
8747 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8748 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8749 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8751 image->colormap[i].red=ScaleCharToQuantum(0x24);
8757 /* END OF BUILD_PALETTE */
8759 /* If we are excluding the tRNS chunk and there is transparency,
8760 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8763 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8764 (number_transparent != 0 || number_semitransparent != 0))
8766 unsigned int colortype=mng_info->write_png_colortype;
8768 if (ping_have_color == MagickFalse)
8769 mng_info->write_png_colortype = 5;
8772 mng_info->write_png_colortype = 7;
8774 if (colortype != 0 &&
8775 mng_info->write_png_colortype != colortype)
8776 ping_need_colortype_warning=MagickTrue;
8780 /* See if cheap transparency is possible. It is only possible
8781 * when there is a single transparent color, no semitransparent
8782 * color, and no opaque color that has the same RGB components
8783 * as the transparent color. We only need this information if
8784 * we are writing a PNG with colortype 0 or 2, and we have not
8785 * excluded the tRNS chunk.
8787 if (number_transparent == 1 &&
8788 mng_info->write_png_colortype < 4)
8790 ping_have_cheap_transparency = MagickTrue;
8792 if (number_semitransparent != 0)
8793 ping_have_cheap_transparency = MagickFalse;
8795 else if (image_colors == 0 || image_colors > 256 ||
8796 image->colormap == NULL)
8798 register const Quantum
8801 for (y=0; y < (ssize_t) image->rows; y++)
8803 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8805 if (q == (Quantum *) NULL)
8808 for (x=0; x < (ssize_t) image->columns; x++)
8810 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8811 (unsigned short) GetPixelRed(image,q) ==
8812 ping_trans_color.red &&
8813 (unsigned short) GetPixelGreen(image,q) ==
8814 ping_trans_color.green &&
8815 (unsigned short) GetPixelBlue(image,q) ==
8816 ping_trans_color.blue)
8818 ping_have_cheap_transparency = MagickFalse;
8822 q+=GetPixelChannels(image);
8825 if (ping_have_cheap_transparency == MagickFalse)
8831 /* Assuming that image->colormap[0] is the one transparent color
8832 * and that all others are opaque.
8834 if (image_colors > 1)
8835 for (i=1; i<image_colors; i++)
8836 if (image->colormap[i].red == image->colormap[0].red &&
8837 image->colormap[i].green == image->colormap[0].green &&
8838 image->colormap[i].blue == image->colormap[0].blue)
8840 ping_have_cheap_transparency = MagickFalse;
8845 if (logging != MagickFalse)
8847 if (ping_have_cheap_transparency == MagickFalse)
8848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8849 " Cheap transparency is not possible.");
8852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8853 " Cheap transparency is possible.");
8857 ping_have_cheap_transparency = MagickFalse;
8859 image_depth=image->depth;
8861 quantum_info = (QuantumInfo *) NULL;
8863 image_colors=(int) image->colors;
8864 image_matte=image->matte;
8866 mng_info->IsPalette=image->storage_class == PseudoClass &&
8867 image_colors <= 256 && image->colormap != NULL;
8869 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8870 (image->colors == 0 || image->colormap == NULL))
8872 image_info=DestroyImageInfo(image_info);
8873 image=DestroyImage(image);
8874 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8875 "Cannot write PNG8 or color-type 3; colormap is NULL",
8876 "`%s'",IMimage->filename);
8877 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8878 UnlockSemaphoreInfo(ping_semaphore);
8880 return(MagickFalse);
8884 Allocate the PNG structures
8886 #ifdef PNG_USER_MEM_SUPPORTED
8887 error_info.image=image;
8888 error_info.exception=exception;
8889 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
8890 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8891 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8894 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
8895 MagickPNGErrorHandler,MagickPNGWarningHandler);
8898 if (ping == (png_struct *) NULL)
8899 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8901 ping_info=png_create_info_struct(ping);
8903 if (ping_info == (png_info *) NULL)
8905 png_destroy_write_struct(&ping,(png_info **) NULL);
8906 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8909 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8910 ping_pixels=(unsigned char *) NULL;
8912 if (setjmp(png_jmpbuf(ping)))
8918 if (image_info->verbose)
8919 (void) printf("PNG write has failed.\n");
8921 png_destroy_write_struct(&ping,&ping_info);
8922 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8923 UnlockSemaphoreInfo(ping_semaphore);
8925 if (ping_have_blob != MagickFalse)
8926 (void) CloseBlob(image);
8927 image_info=DestroyImageInfo(image_info);
8928 image=DestroyImage(image);
8929 return(MagickFalse);
8932 Prepare PNG for writing.
8934 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8935 if (mng_info->write_mng)
8936 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8939 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8940 if (mng_info->write_mng)
8941 png_permit_empty_plte(ping,MagickTrue);
8948 ping_width=(png_uint_32) image->columns;
8949 ping_height=(png_uint_32) image->rows;
8951 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8954 if (mng_info->write_png_depth != 0)
8955 image_depth=mng_info->write_png_depth;
8957 /* Adjust requested depth to next higher valid depth if necessary */
8958 if (image_depth > 8)
8961 if ((image_depth > 4) && (image_depth < 8))
8964 if (image_depth == 3)
8967 if (logging != MagickFalse)
8969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8970 " width=%.20g",(double) ping_width);
8971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8972 " height=%.20g",(double) ping_height);
8973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8974 " image_matte=%.20g",(double) image->matte);
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " image->depth=%.20g",(double) image->depth);
8977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8978 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8981 save_image_depth=image_depth;
8982 ping_bit_depth=(png_byte) save_image_depth;
8985 #if defined(PNG_pHYs_SUPPORTED)
8986 if (ping_exclude_pHYs == MagickFalse)
8988 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
8989 (!mng_info->write_mng || !mng_info->equal_physs))
8991 if (logging != MagickFalse)
8992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993 " Setting up pHYs chunk");
8995 if (image->units == PixelsPerInchResolution)
8997 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8998 ping_pHYs_x_resolution=
8999 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9000 ping_pHYs_y_resolution=
9001 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9004 else if (image->units == PixelsPerCentimeterResolution)
9006 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9007 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9008 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9013 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9014 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9015 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9018 if (logging != MagickFalse)
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9021 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9022 (int) ping_pHYs_unit_type);
9023 ping_have_pHYs = MagickTrue;
9028 if (ping_exclude_bKGD == MagickFalse)
9030 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9036 if (ping_bit_depth == 8)
9039 if (ping_bit_depth == 4)
9042 if (ping_bit_depth == 2)
9045 if (ping_bit_depth == 1)
9048 ping_background.red=(png_uint_16)
9049 (ScaleQuantumToShort(image->background_color.red) & mask);
9051 ping_background.green=(png_uint_16)
9052 (ScaleQuantumToShort(image->background_color.green) & mask);
9054 ping_background.blue=(png_uint_16)
9055 (ScaleQuantumToShort(image->background_color.blue) & mask);
9057 ping_background.gray=(png_uint_16) ping_background.green;
9060 if (logging != MagickFalse)
9062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063 " Setting up bKGD chunk (1)");
9064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9065 " background_color index is %d",
9066 (int) ping_background.index);
9068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9069 " ping_bit_depth=%d",ping_bit_depth);
9072 ping_have_bKGD = MagickTrue;
9076 Select the color type.
9081 if (mng_info->IsPalette && mng_info->write_png8)
9084 /* To do: make this a function cause it's used twice, except
9085 for reducing the sample depth from 8. */
9087 number_colors=image_colors;
9089 ping_have_tRNS=MagickFalse;
9094 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9096 if (logging != MagickFalse)
9097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9098 " Setting up PLTE chunk with %d colors (%d)",
9099 number_colors, image_colors);
9101 for (i=0; i < (ssize_t) number_colors; i++)
9103 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9104 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9105 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9106 if (logging != MagickFalse)
9107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9108 #if MAGICKCORE_QUANTUM_DEPTH == 8
9109 " %3ld (%3d,%3d,%3d)",
9111 " %5ld (%5d,%5d,%5d)",
9113 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9117 ping_have_PLTE=MagickTrue;
9118 image_depth=ping_bit_depth;
9121 if (matte != MagickFalse)
9124 Identify which colormap entry is transparent.
9126 assert(number_colors <= 256);
9127 assert(image->colormap != NULL);
9129 for (i=0; i < (ssize_t) number_transparent; i++)
9130 ping_trans_alpha[i]=0;
9133 ping_num_trans=(unsigned short) (number_transparent +
9134 number_semitransparent);
9136 if (ping_num_trans == 0)
9137 ping_have_tRNS=MagickFalse;
9140 ping_have_tRNS=MagickTrue;
9143 if (ping_exclude_bKGD == MagickFalse)
9146 * Identify which colormap entry is the background color.
9149 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9150 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9153 ping_background.index=(png_byte) i;
9155 if (logging != MagickFalse)
9157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9158 " background_color index is %d",
9159 (int) ping_background.index);
9162 } /* end of write_png8 */
9164 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9166 image_matte=MagickFalse;
9167 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9170 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9172 image_matte=MagickTrue;
9173 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9176 else /* mng_info->write_pngNN not specified */
9178 image_depth=ping_bit_depth;
9180 if (mng_info->write_png_colortype != 0)
9182 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9184 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9185 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9186 image_matte=MagickTrue;
9189 image_matte=MagickFalse;
9191 if (logging != MagickFalse)
9192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9193 " PNG colortype %d was specified:",(int) ping_color_type);
9196 else /* write_png_colortype not specified */
9198 if (logging != MagickFalse)
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200 " Selecting PNG colortype:");
9202 ping_color_type=(png_byte) ((matte != MagickFalse)?
9203 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9205 if (image_info->type == TrueColorType)
9207 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9208 image_matte=MagickFalse;
9211 if (image_info->type == TrueColorMatteType)
9213 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9214 image_matte=MagickTrue;
9217 if (image_info->type == PaletteType ||
9218 image_info->type == PaletteMatteType)
9219 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9221 if (mng_info->write_png_colortype == 0 &&
9222 (image_info->type == UndefinedType ||
9223 image_info->type == OptimizeType))
9225 if (ping_have_color == MagickFalse)
9227 if (image_matte == MagickFalse)
9229 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9230 image_matte=MagickFalse;
9235 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9236 image_matte=MagickTrue;
9241 if (image_matte == MagickFalse)
9243 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9244 image_matte=MagickFalse;
9249 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9250 image_matte=MagickTrue;
9257 if (logging != MagickFalse)
9258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9259 " Selected PNG colortype=%d",ping_color_type);
9261 if (ping_bit_depth < 8)
9263 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9264 ping_color_type == PNG_COLOR_TYPE_RGB ||
9265 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9269 old_bit_depth=ping_bit_depth;
9271 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9273 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9277 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9282 if (image->colors == 0)
9285 (void) ThrowMagickException(exception,
9286 GetMagickModule(),CoderError,
9287 "image has 0 colors", "`%s'","");
9290 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9291 ping_bit_depth <<= 1;
9294 if (logging != MagickFalse)
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " Number of colors: %.20g",(double) image_colors);
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " Tentative PNG bit depth: %d",ping_bit_depth);
9303 if (ping_bit_depth < (int) mng_info->write_png_depth)
9304 ping_bit_depth = mng_info->write_png_depth;
9307 image_depth=ping_bit_depth;
9309 if (logging != MagickFalse)
9311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312 " Tentative PNG color type: %.20g",(double) ping_color_type);
9314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9315 " image_info->type: %.20g",(double) image_info->type);
9317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9318 " image_depth: %.20g",(double) image_depth);
9320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9322 " image->depth: %.20g",(double) image->depth);
9324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9325 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9328 if (matte != MagickFalse)
9330 if (mng_info->IsPalette)
9332 if (mng_info->write_png_colortype == 0)
9334 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9336 if (ping_have_color != MagickFalse)
9337 ping_color_type=PNG_COLOR_TYPE_RGBA;
9341 * Determine if there is any transparent color.
9343 if (number_transparent + number_semitransparent == 0)
9346 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9349 image_matte=MagickFalse;
9351 if (mng_info->write_png_colortype == 0)
9352 ping_color_type&=0x03;
9362 if (ping_bit_depth == 8)
9365 if (ping_bit_depth == 4)
9368 if (ping_bit_depth == 2)
9371 if (ping_bit_depth == 1)
9374 ping_trans_color.red=(png_uint_16)
9375 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9377 ping_trans_color.green=(png_uint_16)
9378 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9380 ping_trans_color.blue=(png_uint_16)
9381 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9383 ping_trans_color.gray=(png_uint_16)
9384 (ScaleQuantumToShort(GetPixelInfoIntensity(
9385 image->colormap)) & mask);
9387 ping_trans_color.index=(png_byte) 0;
9389 ping_have_tRNS=MagickTrue;
9392 if (ping_have_tRNS != MagickFalse)
9395 * Determine if there is one and only one transparent color
9396 * and if so if it is fully transparent.
9398 if (ping_have_cheap_transparency == MagickFalse)
9399 ping_have_tRNS=MagickFalse;
9402 if (ping_have_tRNS != MagickFalse)
9404 if (mng_info->write_png_colortype == 0)
9405 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9407 if (image_depth == 8)
9409 ping_trans_color.red&=0xff;
9410 ping_trans_color.green&=0xff;
9411 ping_trans_color.blue&=0xff;
9412 ping_trans_color.gray&=0xff;
9418 if (image_depth == 8)
9420 ping_trans_color.red&=0xff;
9421 ping_trans_color.green&=0xff;
9422 ping_trans_color.blue&=0xff;
9423 ping_trans_color.gray&=0xff;
9430 if (ping_have_tRNS != MagickFalse)
9431 image_matte=MagickFalse;
9433 if ((mng_info->IsPalette) &&
9434 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9435 ping_have_color == MagickFalse &&
9436 (image_matte == MagickFalse || image_depth >= 8))
9440 if (image_matte != MagickFalse)
9441 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9443 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9445 ping_color_type=PNG_COLOR_TYPE_GRAY;
9447 if (save_image_depth == 16 && image_depth == 8)
9449 if (logging != MagickFalse)
9451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9452 " Scaling ping_trans_color (0)");
9454 ping_trans_color.gray*=0x0101;
9458 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9459 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9461 if ((image_colors == 0) ||
9462 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9463 image_colors=(int) (one << image_depth);
9465 if (image_depth > 8)
9471 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9473 if(!mng_info->write_png_depth)
9477 while ((int) (one << ping_bit_depth)
9478 < (ssize_t) image_colors)
9479 ping_bit_depth <<= 1;
9483 else if (ping_color_type ==
9484 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9485 mng_info->IsPalette)
9487 /* Check if grayscale is reducible */
9490 depth_4_ok=MagickTrue,
9491 depth_2_ok=MagickTrue,
9492 depth_1_ok=MagickTrue;
9494 for (i=0; i < (ssize_t) image_colors; i++)
9499 intensity=ScaleQuantumToChar(image->colormap[i].red);
9501 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9502 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9503 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9504 depth_2_ok=depth_1_ok=MagickFalse;
9505 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9506 depth_1_ok=MagickFalse;
9509 if (depth_1_ok && mng_info->write_png_depth <= 1)
9512 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9515 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9520 image_depth=ping_bit_depth;
9525 if (mng_info->IsPalette)
9527 number_colors=image_colors;
9529 if (image_depth <= 8)
9534 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9536 if (mng_info->have_write_global_plte && matte == MagickFalse)
9538 png_set_PLTE(ping,ping_info,NULL,0);
9540 if (logging != MagickFalse)
9541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9542 " Setting up empty PLTE chunk");
9547 for (i=0; i < (ssize_t) number_colors; i++)
9549 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9550 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9551 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9554 if (logging != MagickFalse)
9555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556 " Setting up PLTE chunk with %d colors",
9559 ping_have_PLTE=MagickTrue;
9562 /* color_type is PNG_COLOR_TYPE_PALETTE */
9563 if (mng_info->write_png_depth == 0)
9571 while ((one << ping_bit_depth) < (size_t) number_colors)
9572 ping_bit_depth <<= 1;
9577 if (matte != MagickFalse)
9580 * Set up trans_colors array.
9582 assert(number_colors <= 256);
9584 ping_num_trans=(unsigned short) (number_transparent +
9585 number_semitransparent);
9587 if (ping_num_trans == 0)
9588 ping_have_tRNS=MagickFalse;
9592 if (logging != MagickFalse)
9594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9595 " Scaling ping_trans_color (1)");
9597 ping_have_tRNS=MagickTrue;
9599 for (i=0; i < ping_num_trans; i++)
9601 ping_trans_alpha[i]= (png_byte)
9602 ScaleQuantumToChar(image->colormap[i].alpha);
9612 if (image_depth < 8)
9615 if ((save_image_depth == 16) && (image_depth == 8))
9617 if (logging != MagickFalse)
9619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620 " Scaling ping_trans_color from (%d,%d,%d)",
9621 (int) ping_trans_color.red,
9622 (int) ping_trans_color.green,
9623 (int) ping_trans_color.blue);
9626 ping_trans_color.red*=0x0101;
9627 ping_trans_color.green*=0x0101;
9628 ping_trans_color.blue*=0x0101;
9629 ping_trans_color.gray*=0x0101;
9631 if (logging != MagickFalse)
9633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9635 (int) ping_trans_color.red,
9636 (int) ping_trans_color.green,
9637 (int) ping_trans_color.blue);
9642 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9643 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9646 Adjust background and transparency samples in sub-8-bit grayscale files.
9648 if (ping_bit_depth < 8 && ping_color_type ==
9649 PNG_COLOR_TYPE_GRAY)
9657 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9659 if (ping_exclude_bKGD == MagickFalse)
9662 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9663 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9664 &image->background_color))) +.5)));
9666 if (logging != MagickFalse)
9667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9668 " Setting up bKGD chunk (2)");
9669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9670 " background_color index is %d",
9671 (int) ping_background.index);
9673 ping_have_bKGD = MagickTrue;
9676 if (logging != MagickFalse)
9677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9678 " Scaling ping_trans_color.gray from %d",
9679 (int)ping_trans_color.gray);
9681 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9682 ping_trans_color.gray)+.5);
9684 if (logging != MagickFalse)
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " to %d", (int)ping_trans_color.gray);
9689 if (ping_exclude_bKGD == MagickFalse)
9691 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9694 Identify which colormap entry is the background color.
9697 number_colors=image_colors;
9699 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9700 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9703 ping_background.index=(png_byte) i;
9705 if (logging != MagickFalse)
9707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9708 " Setting up bKGD chunk with index=%d",(int) i);
9711 if (i < (ssize_t) number_colors)
9713 ping_have_bKGD = MagickTrue;
9715 if (logging != MagickFalse)
9717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9718 " background =(%d,%d,%d)",
9719 (int) ping_background.red,
9720 (int) ping_background.green,
9721 (int) ping_background.blue);
9725 else /* Can't happen */
9727 if (logging != MagickFalse)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " No room in PLTE to add bKGD color");
9730 ping_have_bKGD = MagickFalse;
9735 if (logging != MagickFalse)
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " PNG color type: %d",ping_color_type);
9739 Initialize compression level and filtering.
9741 if (logging != MagickFalse)
9743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9744 " Setting up deflate compression");
9746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9747 " Compression buffer size: 32768");
9750 png_set_compression_buffer_size(ping,32768L);
9752 if (logging != MagickFalse)
9753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9754 " Compression mem level: 9");
9756 png_set_compression_mem_level(ping, 9);
9758 /* Untangle the "-quality" setting:
9760 Undefined is 0; the default is used.
9765 0: Use Z_HUFFMAN_ONLY strategy with the
9766 zlib default compression level
9768 1-9: the zlib compression level
9772 0-4: the PNG filter method
9774 5: libpng adaptive filtering if compression level > 5
9775 libpng filter type "none" if compression level <= 5
9776 or if image is grayscale or palette
9778 6: libpng adaptive filtering
9780 7: "LOCO" filtering (intrapixel differing) if writing
9781 a MNG, othewise "none". Did not work in IM-6.7.0-9
9782 and earlier because of a missing "else".
9784 8: Z_RLE strategy, all filters
9785 Unused prior to IM-6.7.0-10, was same as 6
9787 9: Z_RLE strategy, no PNG filters
9788 Unused prior to IM-6.7.0-10, was same as 6
9790 Note that using the -quality option, not all combinations of
9791 PNG filter type, zlib compression level, and zlib compression
9792 strategy are possible. This will be addressed soon in a
9793 release that accomodates "-define png:compression-strategy", etc.
9797 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9802 if (mng_info->write_png_compression_strategy == 0)
9803 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9806 else if (mng_info->write_png_compression_level == 0)
9811 level=(int) MagickMin((ssize_t) quality/10,9);
9813 mng_info->write_png_compression_level = level+1;
9816 if (mng_info->write_png_compression_strategy == 0)
9818 if ((quality %10) == 8 || (quality %10) == 9)
9819 mng_info->write_png_compression_strategy=Z_RLE;
9822 if (mng_info->write_png_compression_filter == 0)
9823 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9825 if (logging != MagickFalse)
9827 if (mng_info->write_png_compression_level)
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Compression level: %d",
9830 (int) mng_info->write_png_compression_level-1);
9832 if (mng_info->write_png_compression_strategy)
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Compression strategy: %d",
9835 (int) mng_info->write_png_compression_strategy-1);
9837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9838 " Setting up filtering");
9840 if (mng_info->write_png_compression_filter == 6)
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842 " Base filter method: ADAPTIVE");
9843 else if (mng_info->write_png_compression_filter == 0 ||
9844 mng_info->write_png_compression_filter == 1)
9845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " Base filter method: NONE");
9848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9849 " Base filter method: %d",
9850 (int) mng_info->write_png_compression_filter-1);
9853 if (mng_info->write_png_compression_level != 0)
9854 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9856 if (mng_info->write_png_compression_filter == 6)
9858 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9859 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9861 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9863 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9865 else if (mng_info->write_png_compression_filter == 7 ||
9866 mng_info->write_png_compression_filter == 10)
9867 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9869 else if (mng_info->write_png_compression_filter == 8)
9871 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9872 if (mng_info->write_mng)
9874 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9875 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9876 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9879 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9882 else if (mng_info->write_png_compression_filter == 9)
9883 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9885 else if (mng_info->write_png_compression_filter != 0)
9886 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9887 mng_info->write_png_compression_filter-1);
9889 if (mng_info->write_png_compression_strategy != 0)
9890 png_set_compression_strategy(ping,
9891 mng_info->write_png_compression_strategy-1);
9893 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9894 if (ping_exclude_sRGB != MagickFalse ||
9895 (image->rendering_intent == UndefinedIntent))
9897 if ((ping_exclude_tEXt == MagickFalse ||
9898 ping_exclude_zTXt == MagickFalse) &&
9899 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9901 ResetImageProfileIterator(image);
9902 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9904 profile=GetImageProfile(image,name);
9906 if (profile != (StringInfo *) NULL)
9908 #ifdef PNG_WRITE_iCCP_SUPPORTED
9909 if ((LocaleCompare(name,"ICC") == 0) ||
9910 (LocaleCompare(name,"ICM") == 0))
9913 if (ping_exclude_iCCP == MagickFalse)
9915 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9916 #if (PNG_LIBPNG_VER < 10500)
9917 (png_charp) GetStringInfoDatum(profile),
9919 (png_const_bytep) GetStringInfoDatum(profile),
9921 (png_uint_32) GetStringInfoLength(profile));
9927 if (ping_exclude_zCCP == MagickFalse)
9929 Magick_png_write_raw_profile(image_info,ping,ping_info,
9930 (unsigned char *) name,(unsigned char *) name,
9931 GetStringInfoDatum(profile),
9932 (png_uint_32) GetStringInfoLength(profile));
9936 if (logging != MagickFalse)
9937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9938 " Setting up text chunk with %s profile",name);
9940 name=GetNextImageProfile(image);
9945 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9946 if ((mng_info->have_write_global_srgb == 0) &&
9947 ((image->rendering_intent != UndefinedIntent) ||
9948 (image->colorspace == sRGBColorspace)))
9950 if (ping_exclude_sRGB == MagickFalse)
9953 Note image rendering intent.
9955 if (logging != MagickFalse)
9956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9957 " Setting up sRGB chunk");
9959 (void) png_set_sRGB(ping,ping_info,(
9960 Magick_RenderingIntent_to_PNG_RenderingIntent(
9961 image->rendering_intent)));
9965 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9968 if (ping_exclude_gAMA == MagickFalse &&
9969 (ping_exclude_sRGB == MagickFalse ||
9970 (image->gamma < .45 || image->gamma > .46)))
9972 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9976 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9978 if (logging != MagickFalse)
9979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9980 " Setting up gAMA chunk");
9982 png_set_gAMA(ping,ping_info,image->gamma);
9986 if (ping_exclude_cHRM == MagickFalse)
9988 if ((mng_info->have_write_global_chrm == 0) &&
9989 (image->chromaticity.red_primary.x != 0.0))
9992 Note image chromaticity.
9993 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10001 wp=image->chromaticity.white_point;
10002 rp=image->chromaticity.red_primary;
10003 gp=image->chromaticity.green_primary;
10004 bp=image->chromaticity.blue_primary;
10006 if (logging != MagickFalse)
10007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10008 " Setting up cHRM chunk");
10010 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10016 ping_interlace_method=image_info->interlace != NoInterlace;
10018 if (mng_info->write_mng)
10019 png_set_sig_bytes(ping,8);
10021 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10023 if (mng_info->write_png_colortype != 0)
10025 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10026 if (ping_have_color != MagickFalse)
10028 ping_color_type = PNG_COLOR_TYPE_RGB;
10030 if (ping_bit_depth < 8)
10034 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10035 if (ping_have_color != MagickFalse)
10036 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10039 if (ping_need_colortype_warning != MagickFalse ||
10040 ((mng_info->write_png_depth &&
10041 (int) mng_info->write_png_depth != ping_bit_depth) ||
10042 (mng_info->write_png_colortype &&
10043 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10044 mng_info->write_png_colortype != 7 &&
10045 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10047 if (logging != MagickFalse)
10049 if (ping_need_colortype_warning != MagickFalse)
10051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10052 " Image has transparency but tRNS chunk was excluded");
10055 if (mng_info->write_png_depth)
10057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10058 " Defined png:bit-depth=%u, Computed depth=%u",
10059 mng_info->write_png_depth,
10063 if (mng_info->write_png_colortype)
10065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10066 " Defined png:color-type=%u, Computed color type=%u",
10067 mng_info->write_png_colortype-1,
10073 "Cannot write image with defined png:bit-depth or png:color-type.");
10076 if (image_matte != MagickFalse && image->matte == MagickFalse)
10078 /* Add an opaque matte channel */
10079 image->matte = MagickTrue;
10080 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10082 if (logging != MagickFalse)
10083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10084 " Added an opaque matte channel");
10087 if (number_transparent != 0 || number_semitransparent != 0)
10089 if (ping_color_type < 4)
10091 ping_have_tRNS=MagickTrue;
10092 if (logging != MagickFalse)
10093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10094 " Setting ping_have_tRNS=MagickTrue.");
10098 if (logging != MagickFalse)
10099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10100 " Writing PNG header chunks");
10102 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10103 ping_bit_depth,ping_color_type,
10104 ping_interlace_method,ping_compression_method,
10105 ping_filter_method);
10107 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10109 png_set_PLTE(ping,ping_info,palette,number_colors);
10111 if (logging != MagickFalse)
10113 for (i=0; i< (ssize_t) number_colors; i++)
10115 if (i < ping_num_trans)
10116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10117 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10119 (int) palette[i].red,
10120 (int) palette[i].green,
10121 (int) palette[i].blue,
10123 (int) ping_trans_alpha[i]);
10125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10126 " PLTE[%d] = (%d,%d,%d)",
10128 (int) palette[i].red,
10129 (int) palette[i].green,
10130 (int) palette[i].blue);
10135 if (ping_exclude_bKGD == MagickFalse)
10137 if (ping_have_bKGD != MagickFalse)
10139 png_set_bKGD(ping,ping_info,&ping_background);
10142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10143 " Setting up bKGD chunk");
10144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10145 " background color = (%d,%d,%d)",
10146 (int) ping_background.red,
10147 (int) ping_background.green,
10148 (int) ping_background.blue);
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " index = %d, gray=%d",
10151 (int) ping_background.index,
10152 (int) ping_background.gray);
10157 if (ping_exclude_pHYs == MagickFalse)
10159 if (ping_have_pHYs != MagickFalse)
10161 png_set_pHYs(ping,ping_info,
10162 ping_pHYs_x_resolution,
10163 ping_pHYs_y_resolution,
10164 ping_pHYs_unit_type);
10168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10169 " Setting up pHYs chunk");
10170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10171 " x_resolution=%lu",
10172 (unsigned long) ping_pHYs_x_resolution);
10173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10174 " y_resolution=%lu",
10175 (unsigned long) ping_pHYs_y_resolution);
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 (unsigned long) ping_pHYs_unit_type);
10183 #if defined(PNG_oFFs_SUPPORTED)
10184 if (ping_exclude_oFFs == MagickFalse)
10186 if (image->page.x || image->page.y)
10188 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10189 (png_int_32) image->page.y, 0);
10191 if (logging != MagickFalse)
10192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10193 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10194 (int) image->page.x, (int) image->page.y);
10199 if (mng_info->need_blob != MagickFalse)
10201 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10203 png_error(ping,"WriteBlob Failed");
10205 ping_have_blob=MagickTrue;
10208 png_write_info_before_PLTE(ping, ping_info);
10210 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10212 if (logging != MagickFalse)
10214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10215 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10218 if (ping_color_type == 3)
10219 (void) png_set_tRNS(ping, ping_info,
10226 (void) png_set_tRNS(ping, ping_info,
10229 &ping_trans_color);
10231 if (logging != MagickFalse)
10233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10234 " tRNS color =(%d,%d,%d)",
10235 (int) ping_trans_color.red,
10236 (int) ping_trans_color.green,
10237 (int) ping_trans_color.blue);
10242 /* write any png-chunk-b profiles */
10243 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10245 png_write_info(ping,ping_info);
10247 /* write any PNG-chunk-m profiles */
10248 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10250 if (ping_exclude_vpAg == MagickFalse)
10252 if ((image->page.width != 0 && image->page.width != image->columns) ||
10253 (image->page.height != 0 && image->page.height != image->rows))
10258 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10259 PNGType(chunk,mng_vpAg);
10260 LogPNGChunk(logging,mng_vpAg,9L);
10261 PNGLong(chunk+4,(png_uint_32) image->page.width);
10262 PNGLong(chunk+8,(png_uint_32) image->page.height);
10263 chunk[12]=0; /* unit = pixels */
10264 (void) WriteBlob(image,13,chunk);
10265 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10269 #if (PNG_LIBPNG_VER == 10206)
10270 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10271 #define PNG_HAVE_IDAT 0x04
10272 ping->mode |= PNG_HAVE_IDAT;
10273 #undef PNG_HAVE_IDAT
10276 png_set_packing(ping);
10280 rowbytes=image->columns;
10281 if (image_depth > 8)
10283 switch (ping_color_type)
10285 case PNG_COLOR_TYPE_RGB:
10289 case PNG_COLOR_TYPE_GRAY_ALPHA:
10293 case PNG_COLOR_TYPE_RGBA:
10301 if (logging != MagickFalse)
10303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10304 " Writing PNG image data");
10306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10307 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10309 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10310 sizeof(*ping_pixels));
10312 if (ping_pixels == (unsigned char *) NULL)
10313 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10316 Initialize image scanlines.
10318 if (setjmp(png_jmpbuf(ping)))
10324 if (image_info->verbose)
10325 (void) printf("PNG write has failed.\n");
10327 png_destroy_write_struct(&ping,&ping_info);
10328 if (quantum_info != (QuantumInfo *) NULL)
10329 quantum_info=DestroyQuantumInfo(quantum_info);
10330 if (ping_pixels != (unsigned char *) NULL)
10331 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10332 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10333 UnlockSemaphoreInfo(ping_semaphore);
10335 if (ping_have_blob != MagickFalse)
10336 (void) CloseBlob(image);
10337 image_info=DestroyImageInfo(image_info);
10338 image=DestroyImage(image);
10339 return(MagickFalse);
10341 quantum_info=AcquireQuantumInfo(image_info,image);
10342 if (quantum_info == (QuantumInfo *) NULL)
10343 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10344 quantum_info->format=UndefinedQuantumFormat;
10345 quantum_info->depth=image_depth;
10346 num_passes=png_set_interlace_handling(ping);
10348 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10349 !mng_info->write_png32) &&
10350 (mng_info->IsPalette ||
10351 (image_info->type == BilevelType)) &&
10352 image_matte == MagickFalse &&
10353 ping_have_non_bw == MagickFalse)
10355 /* Palette, Bilevel, or Opaque Monochrome */
10356 register const Quantum
10359 quantum_info->depth=8;
10360 for (pass=0; pass < num_passes; pass++)
10363 Convert PseudoClass image to a PNG monochrome image.
10365 for (y=0; y < (ssize_t) image->rows; y++)
10367 if (logging != MagickFalse && y == 0)
10368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10369 " Writing row of pixels (0)");
10371 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10373 if (p == (const Quantum *) NULL)
10376 if (mng_info->IsPalette)
10378 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10379 quantum_info,GrayQuantum,ping_pixels,exception);
10380 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10381 mng_info->write_png_depth &&
10382 mng_info->write_png_depth != old_bit_depth)
10384 /* Undo pixel scaling */
10385 for (i=0; i < (ssize_t) image->columns; i++)
10386 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10387 >> (8-old_bit_depth));
10393 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10394 quantum_info,RedQuantum,ping_pixels,exception);
10397 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10398 for (i=0; i < (ssize_t) image->columns; i++)
10399 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10402 if (logging != MagickFalse && y == 0)
10403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404 " Writing row of pixels (1)");
10406 png_write_row(ping,ping_pixels);
10408 if (image->previous == (Image *) NULL)
10410 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10411 if (status == MagickFalse)
10417 else /* Not Palette, Bilevel, or Opaque Monochrome */
10419 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10420 !mng_info->write_png32) &&
10421 (image_matte != MagickFalse ||
10422 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10423 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10425 register const Quantum
10428 for (pass=0; pass < num_passes; pass++)
10431 for (y=0; y < (ssize_t) image->rows; y++)
10433 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10435 if (p == (const Quantum *) NULL)
10438 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10440 if (mng_info->IsPalette)
10441 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10442 quantum_info,GrayQuantum,ping_pixels,exception);
10445 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10446 quantum_info,RedQuantum,ping_pixels,exception);
10448 if (logging != MagickFalse && y == 0)
10449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10450 " Writing GRAY PNG pixels (2)");
10453 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10455 if (logging != MagickFalse && y == 0)
10456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10457 " Writing GRAY_ALPHA PNG pixels (2)");
10459 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10460 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10463 if (logging != MagickFalse && y == 0)
10464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10465 " Writing row of pixels (2)");
10467 png_write_row(ping,ping_pixels);
10470 if (image->previous == (Image *) NULL)
10472 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10473 if (status == MagickFalse)
10481 register const Quantum
10484 for (pass=0; pass < num_passes; pass++)
10486 if ((image_depth > 8) || (mng_info->write_png24 ||
10487 mng_info->write_png32 ||
10488 (!mng_info->write_png8 && !mng_info->IsPalette)))
10490 for (y=0; y < (ssize_t) image->rows; y++)
10492 p=GetVirtualPixels(image,0,y,image->columns,1,
10495 if (p == (const Quantum *) NULL)
10498 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10500 if (image->storage_class == DirectClass)
10501 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10502 quantum_info,RedQuantum,ping_pixels,exception);
10505 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10506 quantum_info,GrayQuantum,ping_pixels,exception);
10509 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10511 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10512 quantum_info,GrayAlphaQuantum,ping_pixels,
10515 if (logging != MagickFalse && y == 0)
10516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10517 " Writing GRAY_ALPHA PNG pixels (3)");
10520 else if (image_matte != MagickFalse)
10521 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10522 quantum_info,RGBAQuantum,ping_pixels,exception);
10525 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10526 quantum_info,RGBQuantum,ping_pixels,exception);
10528 if (logging != MagickFalse && y == 0)
10529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10530 " Writing row of pixels (3)");
10532 png_write_row(ping,ping_pixels);
10537 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10538 mng_info->write_png32 ||
10539 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10541 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10542 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10544 if (logging != MagickFalse)
10545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10546 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10548 quantum_info->depth=8;
10552 for (y=0; y < (ssize_t) image->rows; y++)
10554 if (logging != MagickFalse && y == 0)
10555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10556 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10558 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10560 if (p == (const Quantum *) NULL)
10563 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10565 quantum_info->depth=image->depth;
10567 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10568 quantum_info,GrayQuantum,ping_pixels,exception);
10571 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10573 if (logging != MagickFalse && y == 0)
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " Writing GRAY_ALPHA PNG pixels (4)");
10577 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10578 quantum_info,GrayAlphaQuantum,ping_pixels,
10584 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10585 quantum_info,IndexQuantum,ping_pixels,exception);
10587 if (logging != MagickFalse && y <= 2)
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " Writing row of non-gray pixels (4)");
10592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10594 (int)ping_pixels[0],(int)ping_pixels[1]);
10597 png_write_row(ping,ping_pixels);
10601 if (image->previous == (Image *) NULL)
10603 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10604 if (status == MagickFalse)
10611 if (quantum_info != (QuantumInfo *) NULL)
10612 quantum_info=DestroyQuantumInfo(quantum_info);
10614 if (logging != MagickFalse)
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " Wrote PNG image data");
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620 " Width: %.20g",(double) ping_width);
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " Height: %.20g",(double) ping_height);
10625 if (mng_info->write_png_depth)
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " PNG bit-depth written: %d",ping_bit_depth);
10634 if (mng_info->write_png_colortype)
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641 " PNG color-type written: %d",ping_color_type);
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10644 " PNG Interlace method: %d",ping_interlace_method);
10647 Generate text chunks after IDAT.
10649 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10651 ResetImagePropertyIterator(image);
10652 property=GetNextImageProperty(image);
10653 while (property != (const char *) NULL)
10658 value=GetImageProperty(image,property,exception);
10660 /* Don't write any "png:" properties; those are just for "identify" */
10661 if (LocaleNCompare(property,"png:",4) != 0 &&
10663 /* Suppress density and units if we wrote a pHYs chunk */
10664 (ping_exclude_pHYs != MagickFalse ||
10665 LocaleCompare(property,"density") != 0 ||
10666 LocaleCompare(property,"units") != 0) &&
10668 /* Suppress the IM-generated Date:create and Date:modify */
10669 (ping_exclude_date == MagickFalse ||
10670 LocaleNCompare(property, "Date:",5) != 0))
10672 if (value != (const char *) NULL)
10674 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10675 text[0].key=(char *) property;
10676 text[0].text=(char *) value;
10677 text[0].text_length=strlen(value);
10679 if (ping_exclude_tEXt != MagickFalse)
10680 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10682 else if (ping_exclude_zTXt != MagickFalse)
10683 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10687 text[0].compression=image_info->compression == NoCompression ||
10688 (image_info->compression == UndefinedCompression &&
10689 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10690 PNG_TEXT_COMPRESSION_zTXt ;
10693 if (logging != MagickFalse)
10695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696 " Setting up text chunk");
10698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10699 " keyword: %s",text[0].key);
10702 png_set_text(ping,ping_info,text,1);
10703 png_free(ping,text);
10706 property=GetNextImageProperty(image);
10710 /* write any PNG-chunk-e profiles */
10711 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10713 if (logging != MagickFalse)
10714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10715 " Writing PNG end info");
10717 png_write_end(ping,ping_info);
10719 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10721 if (mng_info->page.x || mng_info->page.y ||
10722 (ping_width != mng_info->page.width) ||
10723 (ping_height != mng_info->page.height))
10729 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10731 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10732 PNGType(chunk,mng_FRAM);
10733 LogPNGChunk(logging,mng_FRAM,27L);
10735 chunk[5]=0; /* frame name separator (no name) */
10736 chunk[6]=1; /* flag for changing delay, for next frame only */
10737 chunk[7]=0; /* flag for changing frame timeout */
10738 chunk[8]=1; /* flag for changing frame clipping for next frame */
10739 chunk[9]=0; /* flag for changing frame sync_id */
10740 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10741 chunk[14]=0; /* clipping boundaries delta type */
10742 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10744 (png_uint_32) (mng_info->page.x + ping_width));
10745 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10747 (png_uint_32) (mng_info->page.y + ping_height));
10748 (void) WriteBlob(image,31,chunk);
10749 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10750 mng_info->old_framing_mode=4;
10751 mng_info->framing_mode=1;
10755 mng_info->framing_mode=3;
10757 if (mng_info->write_mng && !mng_info->need_fram &&
10758 ((int) image->dispose == 3))
10759 (void) ThrowMagickException(exception,GetMagickModule(),
10760 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10761 "`%s'",image->filename);
10764 Free PNG resources.
10767 png_destroy_write_struct(&ping,&ping_info);
10769 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10771 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10772 UnlockSemaphoreInfo(ping_semaphore);
10775 if (ping_have_blob != MagickFalse)
10776 (void) CloseBlob(image);
10778 image_info=DestroyImageInfo(image_info);
10779 image=DestroyImage(image);
10781 /* Store bit depth actually written */
10782 s[0]=(char) ping_bit_depth;
10785 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10787 if (logging != MagickFalse)
10788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " exit WriteOnePNGImage()");
10791 return(MagickTrue);
10792 /* End write one PNG image */
10796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10800 % W r i t e P N G I m a g e %
10804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10806 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10807 % Multiple-image Network Graphics (MNG) image file.
10809 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10811 % The format of the WritePNGImage method is:
10813 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10814 % Image *image,ExceptionInfo *exception)
10816 % A description of each parameter follows:
10818 % o image_info: the image info.
10820 % o image: The image.
10822 % o exception: return any errors or warnings in this structure.
10824 % Returns MagickTrue on success, MagickFalse on failure.
10826 % Communicating with the PNG encoder:
10828 % While the datastream written is always in PNG format and normally would
10829 % be given the "png" file extension, this method also writes the following
10830 % pseudo-formats which are subsets of png:
10832 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10833 % a depth greater than 8, the depth is reduced. If transparency
10834 % is present, the tRNS chunk must only have values 0 and 255
10835 % (i.e., transparency is binary: fully opaque or fully
10836 % transparent). If other values are present they will be
10837 % 50%-thresholded to binary transparency. If more than 256
10838 % colors are present, they will be quantized to the 4-4-4-1,
10839 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10840 % of any resulting fully-transparent pixels is changed to
10841 % the image's background color.
10843 % If you want better quantization or dithering of the colors
10844 % or alpha than that, you need to do it before calling the
10845 % PNG encoder. The pixels contain 8-bit indices even if
10846 % they could be represented with 1, 2, or 4 bits. Grayscale
10847 % images will be written as indexed PNG files even though the
10848 % PNG grayscale type might be slightly more efficient. Please
10849 % note that writing to the PNG8 format may result in loss
10850 % of color and alpha data.
10852 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10853 % chunk can be present to convey binary transparency by naming
10854 % one of the colors as transparent. The only loss incurred
10855 % is reduction of sample depth to 8. If the image has more
10856 % than one transparent color, has semitransparent pixels, or
10857 % has an opaque pixel with the same RGB components as the
10858 % transparent color, an image is not written.
10860 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10861 % transparency is permitted, i.e., the alpha sample for
10862 % each pixel can have any value from 0 to 255. The alpha
10863 % channel is present even if the image is fully opaque.
10864 % The only loss in data is the reduction of the sample depth
10867 % o -define: For more precise control of the PNG output, you can use the
10868 % Image options "png:bit-depth" and "png:color-type". These
10869 % can be set from the commandline with "-define" and also
10870 % from the application programming interfaces. The options
10871 % are case-independent and are converted to lowercase before
10872 % being passed to this encoder.
10874 % png:color-type can be 0, 2, 3, 4, or 6.
10876 % When png:color-type is 0 (Grayscale), png:bit-depth can
10877 % be 1, 2, 4, 8, or 16.
10879 % When png:color-type is 2 (RGB), png:bit-depth can
10882 % When png:color-type is 3 (Indexed), png:bit-depth can
10883 % be 1, 2, 4, or 8. This refers to the number of bits
10884 % used to store the index. The color samples always have
10885 % bit-depth 8 in indexed PNG files.
10887 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10888 % png:bit-depth can be 8 or 16.
10890 % If the image cannot be written without loss with the requested bit-depth
10891 % and color-type, a PNG file will not be written, and the encoder will
10892 % return MagickFalse.
10894 % Since image encoders should not be responsible for the "heavy lifting",
10895 % the user should make sure that ImageMagick has already reduced the
10896 % image depth and number of colors and limit transparency to binary
10897 % transparency prior to attempting to write the image with depth, color,
10898 % or transparency limitations.
10900 % Note that another definition, "png:bit-depth-written" exists, but it
10901 % is not intended for external use. It is only used internally by the
10902 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10904 % It is possible to request that the PNG encoder write previously-formatted
10905 % ancillary chunks in the output PNG file, using the "-profile" commandline
10906 % option as shown below or by setting the profile via a programming
10909 % -profile PNG-chunk-x:<file>
10911 % where x is a location flag and <file> is a file containing the chunk
10912 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10913 % This encoder will compute the chunk length and CRC, so those must not
10914 % be included in the file.
10916 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10917 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10918 % of the same type, then add a short unique string after the "x" to prevent
10919 % subsequent profiles from overwriting the preceding ones, e.g.,
10921 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10923 % As of version 6.6.6 the following optimizations are always done:
10925 % o 32-bit depth is reduced to 16.
10926 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10927 % high byte and low byte are identical.
10928 % o Palette is sorted to remove unused entries and to put a
10929 % transparent color first, if BUILD_PNG_PALETTE is defined.
10930 % o Opaque matte channel is removed when writing an indexed PNG.
10931 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10932 % this can be done without loss and a larger bit depth N was not
10933 % requested via the "-define png:bit-depth=N" option.
10934 % o If matte channel is present but only one transparent color is
10935 % present, RGB+tRNS is written instead of RGBA
10936 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10937 % was requested when converting an opaque image).
10939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10941 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10942 Image *image,ExceptionInfo *exception)
10947 have_mng_structure,
10963 assert(image_info != (const ImageInfo *) NULL);
10964 assert(image_info->signature == MagickSignature);
10965 assert(image != (Image *) NULL);
10966 assert(image->signature == MagickSignature);
10967 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10968 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10970 Allocate a MngInfo structure.
10972 have_mng_structure=MagickFalse;
10973 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10975 if (mng_info == (MngInfo *) NULL)
10976 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10979 Initialize members of the MngInfo structure.
10981 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10982 mng_info->image=image;
10983 mng_info->equal_backgrounds=MagickTrue;
10984 have_mng_structure=MagickTrue;
10986 /* See if user has requested a specific PNG subformat */
10988 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10989 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10990 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10992 value=GetImageOption(image_info,"png:format");
10994 if (value != (char *) NULL)
10996 if (LocaleCompare(value,"png8") == 0)
10998 mng_info->write_png8 = MagickTrue;
10999 mng_info->write_png24 = MagickFalse;
11000 mng_info->write_png32 = MagickFalse;
11003 else if (LocaleCompare(value,"png24") == 0)
11005 mng_info->write_png8 = MagickFalse;
11006 mng_info->write_png24 = MagickTrue;
11007 mng_info->write_png32 = MagickFalse;
11010 else if (LocaleCompare(value,"png32") == 0)
11012 mng_info->write_png8 = MagickFalse;
11013 mng_info->write_png24 = MagickFalse;
11014 mng_info->write_png32 = MagickTrue;
11017 if (mng_info->write_png8)
11019 mng_info->write_png_colortype = /* 3 */ 4;
11020 mng_info->write_png_depth = 8;
11024 if (mng_info->write_png24)
11026 mng_info->write_png_colortype = /* 2 */ 3;
11027 mng_info->write_png_depth = 8;
11030 if (image->matte == MagickTrue)
11031 (void) SetImageType(image,TrueColorMatteType,exception);
11034 (void) SetImageType(image,TrueColorType,exception);
11036 (void) SyncImage(image,exception);
11039 if (mng_info->write_png32)
11041 mng_info->write_png_colortype = /* 6 */ 7;
11042 mng_info->write_png_depth = 8;
11045 if (image->matte == MagickTrue)
11046 (void) SetImageType(image,TrueColorMatteType,exception);
11049 (void) SetImageType(image,TrueColorType,exception);
11051 (void) SyncImage(image,exception);
11054 value=GetImageOption(image_info,"png:bit-depth");
11056 if (value != (char *) NULL)
11058 if (LocaleCompare(value,"1") == 0)
11059 mng_info->write_png_depth = 1;
11061 else if (LocaleCompare(value,"2") == 0)
11062 mng_info->write_png_depth = 2;
11064 else if (LocaleCompare(value,"4") == 0)
11065 mng_info->write_png_depth = 4;
11067 else if (LocaleCompare(value,"8") == 0)
11068 mng_info->write_png_depth = 8;
11070 else if (LocaleCompare(value,"16") == 0)
11071 mng_info->write_png_depth = 16;
11074 (void) ThrowMagickException(exception,
11075 GetMagickModule(),CoderWarning,
11076 "ignoring invalid defined png:bit-depth",
11079 if (logging != MagickFalse)
11080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11081 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11084 value=GetImageOption(image_info,"png:color-type");
11086 if (value != (char *) NULL)
11088 /* We must store colortype+1 because 0 is a valid colortype */
11089 if (LocaleCompare(value,"0") == 0)
11090 mng_info->write_png_colortype = 1;
11092 else if (LocaleCompare(value,"1") == 0)
11093 mng_info->write_png_colortype = 2;
11095 else if (LocaleCompare(value,"2") == 0)
11096 mng_info->write_png_colortype = 3;
11098 else if (LocaleCompare(value,"3") == 0)
11099 mng_info->write_png_colortype = 4;
11101 else if (LocaleCompare(value,"4") == 0)
11102 mng_info->write_png_colortype = 5;
11104 else if (LocaleCompare(value,"6") == 0)
11105 mng_info->write_png_colortype = 7;
11108 (void) ThrowMagickException(exception,
11109 GetMagickModule(),CoderWarning,
11110 "ignoring invalid defined png:color-type",
11113 if (logging != MagickFalse)
11114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11115 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11118 /* Check for chunks to be excluded:
11120 * The default is to not exclude any known chunks except for any
11121 * listed in the "unused_chunks" array, above.
11123 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11124 * define (in the image properties or in the image artifacts)
11125 * or via a mng_info member. For convenience, in addition
11126 * to or instead of a comma-separated list of chunks, the
11127 * "exclude-chunk" string can be simply "all" or "none".
11129 * The exclude-chunk define takes priority over the mng_info.
11131 * A "png:include-chunk" define takes priority over both the
11132 * mng_info and the "png:exclude-chunk" define. Like the
11133 * "exclude-chunk" string, it can define "all" or "none" as
11134 * well as a comma-separated list. Chunks that are unknown to
11135 * ImageMagick are always excluded, regardless of their "copy-safe"
11136 * status according to the PNG specification, and even if they
11137 * appear in the "include-chunk" list. Such defines appearing among
11138 * the image options take priority over those found among the image
11141 * Finally, all chunks listed in the "unused_chunks" array are
11142 * automatically excluded, regardless of the other instructions
11145 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11146 * will not be written and the gAMA chunk will only be written if it
11147 * is not between .45 and .46, or approximately (1.0/2.2).
11149 * If you exclude tRNS and the image has transparency, the colortype
11150 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11152 * The -strip option causes StripImage() to set the png:include-chunk
11153 * artifact to "none,trns,gama".
11156 mng_info->ping_exclude_bKGD=MagickFalse;
11157 mng_info->ping_exclude_cHRM=MagickFalse;
11158 mng_info->ping_exclude_date=MagickFalse;
11159 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11160 mng_info->ping_exclude_gAMA=MagickFalse;
11161 mng_info->ping_exclude_iCCP=MagickFalse;
11162 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11163 mng_info->ping_exclude_oFFs=MagickFalse;
11164 mng_info->ping_exclude_pHYs=MagickFalse;
11165 mng_info->ping_exclude_sRGB=MagickFalse;
11166 mng_info->ping_exclude_tEXt=MagickFalse;
11167 mng_info->ping_exclude_tRNS=MagickFalse;
11168 mng_info->ping_exclude_vpAg=MagickFalse;
11169 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11170 mng_info->ping_exclude_zTXt=MagickFalse;
11172 mng_info->ping_preserve_colormap=MagickFalse;
11174 value=GetImageArtifact(image,"png:preserve-colormap");
11176 value=GetImageOption(image_info,"png:preserve-colormap");
11178 mng_info->ping_preserve_colormap=MagickTrue;
11180 /* Thes compression-level, compression-strategy, and compression-filter
11181 * defines take precedence over values from the -quality option.
11183 value=GetImageArtifact(image,"png:compression-level");
11185 value=GetImageOption(image_info,"png:compression-level");
11188 /* We have to add 1 to everything because 0 is a valid input,
11189 * and we want to use 0 (the default) to mean undefined.
11191 if (LocaleCompare(value,"0") == 0)
11192 mng_info->write_png_compression_level = 1;
11194 else if (LocaleCompare(value,"1") == 0)
11195 mng_info->write_png_compression_level = 2;
11197 else if (LocaleCompare(value,"2") == 0)
11198 mng_info->write_png_compression_level = 3;
11200 else if (LocaleCompare(value,"3") == 0)
11201 mng_info->write_png_compression_level = 4;
11203 else if (LocaleCompare(value,"4") == 0)
11204 mng_info->write_png_compression_level = 5;
11206 else if (LocaleCompare(value,"5") == 0)
11207 mng_info->write_png_compression_level = 6;
11209 else if (LocaleCompare(value,"6") == 0)
11210 mng_info->write_png_compression_level = 7;
11212 else if (LocaleCompare(value,"7") == 0)
11213 mng_info->write_png_compression_level = 8;
11215 else if (LocaleCompare(value,"8") == 0)
11216 mng_info->write_png_compression_level = 9;
11218 else if (LocaleCompare(value,"9") == 0)
11219 mng_info->write_png_compression_level = 10;
11222 (void) ThrowMagickException(exception,
11223 GetMagickModule(),CoderWarning,
11224 "ignoring invalid defined png:compression-level",
11228 value=GetImageArtifact(image,"png:compression-strategy");
11230 value=GetImageOption(image_info,"png:compression-strategy");
11234 if (LocaleCompare(value,"0") == 0)
11235 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11237 else if (LocaleCompare(value,"1") == 0)
11238 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11240 else if (LocaleCompare(value,"2") == 0)
11241 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11243 else if (LocaleCompare(value,"3") == 0)
11244 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11245 mng_info->write_png_compression_strategy = Z_RLE+1;
11247 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11250 else if (LocaleCompare(value,"4") == 0)
11251 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11252 mng_info->write_png_compression_strategy = Z_FIXED+1;
11254 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11258 (void) ThrowMagickException(exception,
11259 GetMagickModule(),CoderWarning,
11260 "ignoring invalid defined png:compression-strategy",
11264 value=GetImageArtifact(image,"png:compression-filter");
11266 value=GetImageOption(image_info,"png:compression-filter");
11270 /* To do: combinations of filters allowed by libpng
11271 * masks 0x08 through 0xf8
11273 * Implement this as a comma-separated list of 0,1,2,3,4,5
11274 * where 5 is a special case meaning PNG_ALL_FILTERS.
11277 if (LocaleCompare(value,"0") == 0)
11278 mng_info->write_png_compression_filter = 1;
11280 if (LocaleCompare(value,"1") == 0)
11281 mng_info->write_png_compression_filter = 2;
11283 else if (LocaleCompare(value,"2") == 0)
11284 mng_info->write_png_compression_filter = 3;
11286 else if (LocaleCompare(value,"3") == 0)
11287 mng_info->write_png_compression_filter = 4;
11289 else if (LocaleCompare(value,"4") == 0)
11290 mng_info->write_png_compression_filter = 5;
11292 else if (LocaleCompare(value,"5") == 0)
11293 mng_info->write_png_compression_filter = 6;
11296 (void) ThrowMagickException(exception,
11297 GetMagickModule(),CoderWarning,
11298 "ignoring invalid defined png:compression-filter",
11302 excluding=MagickFalse;
11304 for (source=0; source<1; source++)
11308 value=GetImageArtifact(image,"png:exclude-chunk");
11311 value=GetImageArtifact(image,"png:exclude-chunks");
11315 value=GetImageOption(image_info,"png:exclude-chunk");
11318 value=GetImageOption(image_info,"png:exclude-chunks");
11327 excluding=MagickTrue;
11329 if (logging != MagickFalse)
11332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11333 " png:exclude-chunk=%s found in image artifacts.\n", value);
11335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11336 " png:exclude-chunk=%s found in image properties.\n", value);
11339 last=strlen(value);
11341 for (i=0; i<(int) last; i+=5)
11344 if (LocaleNCompare(value+i,"all",3) == 0)
11346 mng_info->ping_exclude_bKGD=MagickTrue;
11347 mng_info->ping_exclude_cHRM=MagickTrue;
11348 mng_info->ping_exclude_date=MagickTrue;
11349 mng_info->ping_exclude_EXIF=MagickTrue;
11350 mng_info->ping_exclude_gAMA=MagickTrue;
11351 mng_info->ping_exclude_iCCP=MagickTrue;
11352 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11353 mng_info->ping_exclude_oFFs=MagickTrue;
11354 mng_info->ping_exclude_pHYs=MagickTrue;
11355 mng_info->ping_exclude_sRGB=MagickTrue;
11356 mng_info->ping_exclude_tEXt=MagickTrue;
11357 mng_info->ping_exclude_tRNS=MagickTrue;
11358 mng_info->ping_exclude_vpAg=MagickTrue;
11359 mng_info->ping_exclude_zCCP=MagickTrue;
11360 mng_info->ping_exclude_zTXt=MagickTrue;
11364 if (LocaleNCompare(value+i,"none",4) == 0)
11366 mng_info->ping_exclude_bKGD=MagickFalse;
11367 mng_info->ping_exclude_cHRM=MagickFalse;
11368 mng_info->ping_exclude_date=MagickFalse;
11369 mng_info->ping_exclude_EXIF=MagickFalse;
11370 mng_info->ping_exclude_gAMA=MagickFalse;
11371 mng_info->ping_exclude_iCCP=MagickFalse;
11372 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11373 mng_info->ping_exclude_oFFs=MagickFalse;
11374 mng_info->ping_exclude_pHYs=MagickFalse;
11375 mng_info->ping_exclude_sRGB=MagickFalse;
11376 mng_info->ping_exclude_tEXt=MagickFalse;
11377 mng_info->ping_exclude_tRNS=MagickFalse;
11378 mng_info->ping_exclude_vpAg=MagickFalse;
11379 mng_info->ping_exclude_zCCP=MagickFalse;
11380 mng_info->ping_exclude_zTXt=MagickFalse;
11383 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11384 mng_info->ping_exclude_bKGD=MagickTrue;
11386 if (LocaleNCompare(value+i,"chrm",4) == 0)
11387 mng_info->ping_exclude_cHRM=MagickTrue;
11389 if (LocaleNCompare(value+i,"date",4) == 0)
11390 mng_info->ping_exclude_date=MagickTrue;
11392 if (LocaleNCompare(value+i,"exif",4) == 0)
11393 mng_info->ping_exclude_EXIF=MagickTrue;
11395 if (LocaleNCompare(value+i,"gama",4) == 0)
11396 mng_info->ping_exclude_gAMA=MagickTrue;
11398 if (LocaleNCompare(value+i,"iccp",4) == 0)
11399 mng_info->ping_exclude_iCCP=MagickTrue;
11402 if (LocaleNCompare(value+i,"itxt",4) == 0)
11403 mng_info->ping_exclude_iTXt=MagickTrue;
11406 if (LocaleNCompare(value+i,"gama",4) == 0)
11407 mng_info->ping_exclude_gAMA=MagickTrue;
11409 if (LocaleNCompare(value+i,"offs",4) == 0)
11410 mng_info->ping_exclude_oFFs=MagickTrue;
11412 if (LocaleNCompare(value+i,"phys",4) == 0)
11413 mng_info->ping_exclude_pHYs=MagickTrue;
11415 if (LocaleNCompare(value+i,"srgb",4) == 0)
11416 mng_info->ping_exclude_sRGB=MagickTrue;
11418 if (LocaleNCompare(value+i,"text",4) == 0)
11419 mng_info->ping_exclude_tEXt=MagickTrue;
11421 if (LocaleNCompare(value+i,"trns",4) == 0)
11422 mng_info->ping_exclude_tRNS=MagickTrue;
11424 if (LocaleNCompare(value+i,"vpag",4) == 0)
11425 mng_info->ping_exclude_vpAg=MagickTrue;
11427 if (LocaleNCompare(value+i,"zccp",4) == 0)
11428 mng_info->ping_exclude_zCCP=MagickTrue;
11430 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11431 mng_info->ping_exclude_zTXt=MagickTrue;
11437 for (source=0; source<1; source++)
11441 value=GetImageArtifact(image,"png:include-chunk");
11444 value=GetImageArtifact(image,"png:include-chunks");
11448 value=GetImageOption(image_info,"png:include-chunk");
11451 value=GetImageOption(image_info,"png:include-chunks");
11459 excluding=MagickTrue;
11461 if (logging != MagickFalse)
11464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11465 " png:include-chunk=%s found in image artifacts.\n", value);
11467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11468 " png:include-chunk=%s found in image properties.\n", value);
11471 last=strlen(value);
11473 for (i=0; i<(int) last; i+=5)
11475 if (LocaleNCompare(value+i,"all",3) == 0)
11477 mng_info->ping_exclude_bKGD=MagickFalse;
11478 mng_info->ping_exclude_cHRM=MagickFalse;
11479 mng_info->ping_exclude_date=MagickFalse;
11480 mng_info->ping_exclude_EXIF=MagickFalse;
11481 mng_info->ping_exclude_gAMA=MagickFalse;
11482 mng_info->ping_exclude_iCCP=MagickFalse;
11483 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11484 mng_info->ping_exclude_oFFs=MagickFalse;
11485 mng_info->ping_exclude_pHYs=MagickFalse;
11486 mng_info->ping_exclude_sRGB=MagickFalse;
11487 mng_info->ping_exclude_tEXt=MagickFalse;
11488 mng_info->ping_exclude_tRNS=MagickFalse;
11489 mng_info->ping_exclude_vpAg=MagickFalse;
11490 mng_info->ping_exclude_zCCP=MagickFalse;
11491 mng_info->ping_exclude_zTXt=MagickFalse;
11495 if (LocaleNCompare(value+i,"none",4) == 0)
11497 mng_info->ping_exclude_bKGD=MagickTrue;
11498 mng_info->ping_exclude_cHRM=MagickTrue;
11499 mng_info->ping_exclude_date=MagickTrue;
11500 mng_info->ping_exclude_EXIF=MagickTrue;
11501 mng_info->ping_exclude_gAMA=MagickTrue;
11502 mng_info->ping_exclude_iCCP=MagickTrue;
11503 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11504 mng_info->ping_exclude_oFFs=MagickTrue;
11505 mng_info->ping_exclude_pHYs=MagickTrue;
11506 mng_info->ping_exclude_sRGB=MagickTrue;
11507 mng_info->ping_exclude_tEXt=MagickTrue;
11508 mng_info->ping_exclude_tRNS=MagickTrue;
11509 mng_info->ping_exclude_vpAg=MagickTrue;
11510 mng_info->ping_exclude_zCCP=MagickTrue;
11511 mng_info->ping_exclude_zTXt=MagickTrue;
11514 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11515 mng_info->ping_exclude_bKGD=MagickFalse;
11517 if (LocaleNCompare(value+i,"chrm",4) == 0)
11518 mng_info->ping_exclude_cHRM=MagickFalse;
11520 if (LocaleNCompare(value+i,"date",4) == 0)
11521 mng_info->ping_exclude_date=MagickFalse;
11523 if (LocaleNCompare(value+i,"exif",4) == 0)
11524 mng_info->ping_exclude_EXIF=MagickFalse;
11526 if (LocaleNCompare(value+i,"gama",4) == 0)
11527 mng_info->ping_exclude_gAMA=MagickFalse;
11529 if (LocaleNCompare(value+i,"iccp",4) == 0)
11530 mng_info->ping_exclude_iCCP=MagickFalse;
11533 if (LocaleNCompare(value+i,"itxt",4) == 0)
11534 mng_info->ping_exclude_iTXt=MagickFalse;
11537 if (LocaleNCompare(value+i,"gama",4) == 0)
11538 mng_info->ping_exclude_gAMA=MagickFalse;
11540 if (LocaleNCompare(value+i,"offs",4) == 0)
11541 mng_info->ping_exclude_oFFs=MagickFalse;
11543 if (LocaleNCompare(value+i,"phys",4) == 0)
11544 mng_info->ping_exclude_pHYs=MagickFalse;
11546 if (LocaleNCompare(value+i,"srgb",4) == 0)
11547 mng_info->ping_exclude_sRGB=MagickFalse;
11549 if (LocaleNCompare(value+i,"text",4) == 0)
11550 mng_info->ping_exclude_tEXt=MagickFalse;
11552 if (LocaleNCompare(value+i,"trns",4) == 0)
11553 mng_info->ping_exclude_tRNS=MagickFalse;
11555 if (LocaleNCompare(value+i,"vpag",4) == 0)
11556 mng_info->ping_exclude_vpAg=MagickFalse;
11558 if (LocaleNCompare(value+i,"zccp",4) == 0)
11559 mng_info->ping_exclude_zCCP=MagickFalse;
11561 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11562 mng_info->ping_exclude_zTXt=MagickFalse;
11568 if (excluding != MagickFalse && logging != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 " Chunks to be excluded from the output png:");
11572 if (mng_info->ping_exclude_bKGD != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 if (mng_info->ping_exclude_cHRM != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 if (mng_info->ping_exclude_date != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 if (mng_info->ping_exclude_EXIF != MagickFalse)
11582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11584 if (mng_info->ping_exclude_gAMA != MagickFalse)
11585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587 if (mng_info->ping_exclude_iCCP != MagickFalse)
11588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591 if (mng_info->ping_exclude_iTXt != MagickFalse)
11592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11595 if (mng_info->ping_exclude_oFFs != MagickFalse)
11596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11598 if (mng_info->ping_exclude_pHYs != MagickFalse)
11599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11601 if (mng_info->ping_exclude_sRGB != MagickFalse)
11602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11604 if (mng_info->ping_exclude_tEXt != MagickFalse)
11605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11607 if (mng_info->ping_exclude_tRNS != MagickFalse)
11608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11610 if (mng_info->ping_exclude_vpAg != MagickFalse)
11611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11613 if (mng_info->ping_exclude_zCCP != MagickFalse)
11614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11616 if (mng_info->ping_exclude_zTXt != MagickFalse)
11617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11621 mng_info->need_blob = MagickTrue;
11623 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11625 MngInfoFreeStruct(mng_info,&have_mng_structure);
11627 if (logging != MagickFalse)
11628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11633 #if defined(JNG_SUPPORTED)
11635 /* Write one JNG image */
11636 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11637 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11658 jng_alpha_compression_method,
11659 jng_alpha_sample_depth,
11667 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11668 " Enter WriteOneJNGImage()");
11670 blob=(unsigned char *) NULL;
11671 jpeg_image=(Image *) NULL;
11672 jpeg_image_info=(ImageInfo *) NULL;
11675 transparent=image_info->type==GrayscaleMatteType ||
11676 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
11678 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11680 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11682 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11683 image_info->quality;
11685 if (jng_alpha_quality >= 1000)
11686 jng_alpha_quality /= 1000;
11692 /* Create JPEG blob, image, and image_info */
11693 if (logging != MagickFalse)
11694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11695 " Creating jpeg_image_info for alpha.");
11697 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11699 if (jpeg_image_info == (ImageInfo *) NULL)
11700 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11702 if (logging != MagickFalse)
11703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11704 " Creating jpeg_image.");
11706 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11707 if (jpeg_image == (Image *) NULL)
11708 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11709 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11710 jpeg_image->matte=MagickFalse;
11711 jpeg_image->quality=jng_alpha_quality;
11712 jpeg_image_info->type=GrayscaleType;
11713 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11714 (void) AcquireUniqueFilename(jpeg_image->filename);
11715 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11716 "%s",jpeg_image->filename);
11720 jng_alpha_compression_method=0;
11722 jng_alpha_sample_depth=0;
11725 /* To do: check bit depth of PNG alpha channel */
11727 /* Check if image is grayscale. */
11728 if (image_info->type != TrueColorMatteType && image_info->type !=
11729 TrueColorType && ImageIsGray(image,exception))
11732 if (logging != MagickFalse)
11734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11735 " JNG Quality = %d",(int) jng_quality);
11736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11737 " JNG Color Type = %d",jng_color_type);
11740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11741 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11743 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11745 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11751 if (jng_alpha_compression_method==0)
11756 /* Encode alpha as a grayscale PNG blob */
11757 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11759 if (logging != MagickFalse)
11760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11761 " Creating PNG blob.");
11764 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11765 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11766 jpeg_image_info->interlace=NoInterlace;
11768 /* Exclude all ancillary chunks */
11769 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11771 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11774 /* Retrieve sample depth used */
11775 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11776 if (value != (char *) NULL)
11777 jng_alpha_sample_depth= (unsigned int) value[0];
11781 /* Encode alpha as a grayscale JPEG blob */
11783 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11786 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11787 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11788 jpeg_image_info->interlace=NoInterlace;
11789 if (logging != MagickFalse)
11790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11791 " Creating blob.");
11792 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11794 jng_alpha_sample_depth=8;
11796 if (logging != MagickFalse)
11797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " Successfully read jpeg_image into a blob, length=%.20g.",
11802 /* Destroy JPEG image and image_info */
11803 jpeg_image=DestroyImage(jpeg_image);
11804 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11805 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11808 /* Write JHDR chunk */
11809 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11810 PNGType(chunk,mng_JHDR);
11811 LogPNGChunk(logging,mng_JHDR,16L);
11812 PNGLong(chunk+4,(png_uint_32) image->columns);
11813 PNGLong(chunk+8,(png_uint_32) image->rows);
11814 chunk[12]=jng_color_type;
11815 chunk[13]=8; /* sample depth */
11816 chunk[14]=8; /*jng_image_compression_method */
11817 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11818 chunk[16]=jng_alpha_sample_depth;
11819 chunk[17]=jng_alpha_compression_method;
11820 chunk[18]=0; /*jng_alpha_filter_method */
11821 chunk[19]=0; /*jng_alpha_interlace_method */
11822 (void) WriteBlob(image,20,chunk);
11823 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11824 if (logging != MagickFalse)
11826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11827 " JNG width:%15lu",(unsigned long) image->columns);
11829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11830 " JNG height:%14lu",(unsigned long) image->rows);
11832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11833 " JNG color type:%10d",jng_color_type);
11835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836 " JNG sample depth:%8d",8);
11838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11839 " JNG compression:%9d",8);
11841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842 " JNG interlace:%11d",0);
11844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11845 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11848 " JNG alpha compression:%3d",jng_alpha_compression_method);
11850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11851 " JNG alpha filter:%8d",0);
11853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11854 " JNG alpha interlace:%5d",0);
11857 /* Write any JNG-chunk-b profiles */
11858 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11861 Write leading ancillary chunks
11867 Write JNG bKGD chunk
11878 if (jng_color_type == 8 || jng_color_type == 12)
11882 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11883 PNGType(chunk,mng_bKGD);
11884 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11885 red=ScaleQuantumToChar(image->background_color.red);
11886 green=ScaleQuantumToChar(image->background_color.green);
11887 blue=ScaleQuantumToChar(image->background_color.blue);
11894 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11895 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11898 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11901 Write JNG sRGB chunk
11903 (void) WriteBlobMSBULong(image,1L);
11904 PNGType(chunk,mng_sRGB);
11905 LogPNGChunk(logging,mng_sRGB,1L);
11907 if (image->rendering_intent != UndefinedIntent)
11908 chunk[4]=(unsigned char)
11909 Magick_RenderingIntent_to_PNG_RenderingIntent(
11910 (image->rendering_intent));
11913 chunk[4]=(unsigned char)
11914 Magick_RenderingIntent_to_PNG_RenderingIntent(
11915 (PerceptualIntent));
11917 (void) WriteBlob(image,5,chunk);
11918 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11922 if (image->gamma != 0.0)
11925 Write JNG gAMA chunk
11927 (void) WriteBlobMSBULong(image,4L);
11928 PNGType(chunk,mng_gAMA);
11929 LogPNGChunk(logging,mng_gAMA,4L);
11930 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11931 (void) WriteBlob(image,8,chunk);
11932 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11935 if ((mng_info->equal_chrms == MagickFalse) &&
11936 (image->chromaticity.red_primary.x != 0.0))
11942 Write JNG cHRM chunk
11944 (void) WriteBlobMSBULong(image,32L);
11945 PNGType(chunk,mng_cHRM);
11946 LogPNGChunk(logging,mng_cHRM,32L);
11947 primary=image->chromaticity.white_point;
11948 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11949 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11950 primary=image->chromaticity.red_primary;
11951 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11952 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11953 primary=image->chromaticity.green_primary;
11954 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11955 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11956 primary=image->chromaticity.blue_primary;
11957 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11958 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11959 (void) WriteBlob(image,36,chunk);
11960 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11964 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
11967 Write JNG pHYs chunk
11969 (void) WriteBlobMSBULong(image,9L);
11970 PNGType(chunk,mng_pHYs);
11971 LogPNGChunk(logging,mng_pHYs,9L);
11972 if (image->units == PixelsPerInchResolution)
11974 PNGLong(chunk+4,(png_uint_32)
11975 (image->resolution.x*100.0/2.54+0.5));
11977 PNGLong(chunk+8,(png_uint_32)
11978 (image->resolution.y*100.0/2.54+0.5));
11985 if (image->units == PixelsPerCentimeterResolution)
11987 PNGLong(chunk+4,(png_uint_32)
11988 (image->resolution.x*100.0+0.5));
11990 PNGLong(chunk+8,(png_uint_32)
11991 (image->resolution.y*100.0+0.5));
11998 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11999 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12003 (void) WriteBlob(image,13,chunk);
12004 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12007 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12010 Write JNG oFFs chunk
12012 (void) WriteBlobMSBULong(image,9L);
12013 PNGType(chunk,mng_oFFs);
12014 LogPNGChunk(logging,mng_oFFs,9L);
12015 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12016 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12018 (void) WriteBlob(image,13,chunk);
12019 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12021 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12023 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12024 PNGType(chunk,mng_vpAg);
12025 LogPNGChunk(logging,mng_vpAg,9L);
12026 PNGLong(chunk+4,(png_uint_32) image->page.width);
12027 PNGLong(chunk+8,(png_uint_32) image->page.height);
12028 chunk[12]=0; /* unit = pixels */
12029 (void) WriteBlob(image,13,chunk);
12030 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12036 if (jng_alpha_compression_method==0)
12044 /* Write IDAT chunk header */
12045 if (logging != MagickFalse)
12046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12047 " Write IDAT chunks from blob, length=%.20g.",(double)
12050 /* Copy IDAT chunks */
12053 for (i=8; i<(ssize_t) length; i+=len+12)
12055 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12058 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12060 /* Found an IDAT chunk. */
12061 (void) WriteBlobMSBULong(image,(size_t) len);
12062 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12063 (void) WriteBlob(image,(size_t) len+4,p);
12064 (void) WriteBlobMSBULong(image,
12065 crc32(0,p,(uInt) len+4));
12070 if (logging != MagickFalse)
12071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12072 " Skipping %c%c%c%c chunk, length=%.20g.",
12073 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12080 /* Write JDAA chunk header */
12081 if (logging != MagickFalse)
12082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12083 " Write JDAA chunk, length=%.20g.",(double) length);
12084 (void) WriteBlobMSBULong(image,(size_t) length);
12085 PNGType(chunk,mng_JDAA);
12086 LogPNGChunk(logging,mng_JDAA,length);
12087 /* Write JDAT chunk(s) data */
12088 (void) WriteBlob(image,4,chunk);
12089 (void) WriteBlob(image,length,blob);
12090 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12093 blob=(unsigned char *) RelinquishMagickMemory(blob);
12096 /* Encode image as a JPEG blob */
12097 if (logging != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 " Creating jpeg_image_info.");
12100 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12101 if (jpeg_image_info == (ImageInfo *) NULL)
12102 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12104 if (logging != MagickFalse)
12105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12106 " Creating jpeg_image.");
12108 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12109 if (jpeg_image == (Image *) NULL)
12110 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12111 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12113 (void) AcquireUniqueFilename(jpeg_image->filename);
12114 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12115 jpeg_image->filename);
12117 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12120 if (logging != MagickFalse)
12121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12122 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12123 (double) jpeg_image->rows);
12125 if (jng_color_type == 8 || jng_color_type == 12)
12126 jpeg_image_info->type=GrayscaleType;
12128 jpeg_image_info->quality=jng_quality;
12129 jpeg_image->quality=jng_quality;
12130 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12131 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12133 if (logging != MagickFalse)
12134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12135 " Creating blob.");
12137 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12139 if (logging != MagickFalse)
12141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12142 " Successfully read jpeg_image into a blob, length=%.20g.",
12145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12146 " Write JDAT chunk, length=%.20g.",(double) length);
12149 /* Write JDAT chunk(s) */
12150 (void) WriteBlobMSBULong(image,(size_t) length);
12151 PNGType(chunk,mng_JDAT);
12152 LogPNGChunk(logging,mng_JDAT,length);
12153 (void) WriteBlob(image,4,chunk);
12154 (void) WriteBlob(image,length,blob);
12155 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12157 jpeg_image=DestroyImage(jpeg_image);
12158 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12159 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12160 blob=(unsigned char *) RelinquishMagickMemory(blob);
12162 /* Write any JNG-chunk-e profiles */
12163 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12165 /* Write IEND chunk */
12166 (void) WriteBlobMSBULong(image,0L);
12167 PNGType(chunk,mng_IEND);
12168 LogPNGChunk(logging,mng_IEND,0);
12169 (void) WriteBlob(image,4,chunk);
12170 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12172 if (logging != MagickFalse)
12173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12174 " exit WriteOneJNGImage()");
12181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12185 % W r i t e J N G I m a g e %
12189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12191 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12193 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12195 % The format of the WriteJNGImage method is:
12197 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12198 % Image *image,ExceptionInfo *exception)
12200 % A description of each parameter follows:
12202 % o image_info: the image info.
12204 % o image: The image.
12206 % o exception: return any errors or warnings in this structure.
12208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12210 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12211 ExceptionInfo *exception)
12214 have_mng_structure,
12224 assert(image_info != (const ImageInfo *) NULL);
12225 assert(image_info->signature == MagickSignature);
12226 assert(image != (Image *) NULL);
12227 assert(image->signature == MagickSignature);
12228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12229 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12230 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12231 if (status == MagickFalse)
12235 Allocate a MngInfo structure.
12237 have_mng_structure=MagickFalse;
12238 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12239 if (mng_info == (MngInfo *) NULL)
12240 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12242 Initialize members of the MngInfo structure.
12244 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12245 mng_info->image=image;
12246 have_mng_structure=MagickTrue;
12248 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12250 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12251 (void) CloseBlob(image);
12253 (void) CatchImageException(image);
12254 MngInfoFreeStruct(mng_info,&have_mng_structure);
12255 if (logging != MagickFalse)
12256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12261 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12262 ExceptionInfo *exception)
12271 have_mng_structure,
12274 volatile MagickBooleanType
12286 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12287 defined(PNG_MNG_FEATURES_SUPPORTED)
12290 all_images_are_gray,
12300 volatile unsigned int
12311 #if (PNG_LIBPNG_VER < 10200)
12312 if (image_info->verbose)
12313 printf("Your PNG library (libpng-%s) is rather old.\n",
12314 PNG_LIBPNG_VER_STRING);
12320 assert(image_info != (const ImageInfo *) NULL);
12321 assert(image_info->signature == MagickSignature);
12322 assert(image != (Image *) NULL);
12323 assert(image->signature == MagickSignature);
12324 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12325 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12326 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12327 if (status == MagickFalse)
12331 Allocate a MngInfo structure.
12333 have_mng_structure=MagickFalse;
12334 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12335 if (mng_info == (MngInfo *) NULL)
12336 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12338 Initialize members of the MngInfo structure.
12340 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12341 mng_info->image=image;
12342 have_mng_structure=MagickTrue;
12343 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12346 * See if user has requested a specific PNG subformat to be used
12347 * for all of the PNGs in the MNG being written, e.g.,
12349 * convert *.png png8:animation.mng
12351 * To do: check -define png:bit_depth and png:color_type as well,
12352 * or perhaps use mng:bit_depth and mng:color_type instead for
12356 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12357 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12358 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12360 write_jng=MagickFalse;
12361 if (image_info->compression == JPEGCompression)
12362 write_jng=MagickTrue;
12364 mng_info->adjoin=image_info->adjoin &&
12365 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12367 if (logging != MagickFalse)
12369 /* Log some info about the input */
12373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12374 " Checking input image(s)");
12376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12377 " Image_info depth: %.20g",(double) image_info->depth);
12379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12380 " Type: %d",image_info->type);
12383 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12386 " Scene: %.20g",(double) scene++);
12388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12389 " Image depth: %.20g",(double) p->depth);
12392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12399 if (p->storage_class == PseudoClass)
12400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12401 " Storage class: PseudoClass");
12404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12405 " Storage class: DirectClass");
12408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12409 " Number of colors: %.20g",(double) p->colors);
12412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12413 " Number of colors: unspecified");
12415 if (mng_info->adjoin == MagickFalse)
12420 use_global_plte=MagickFalse;
12421 all_images_are_gray=MagickFalse;
12422 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12423 need_local_plte=MagickTrue;
12425 need_defi=MagickFalse;
12426 need_matte=MagickFalse;
12427 mng_info->framing_mode=1;
12428 mng_info->old_framing_mode=1;
12431 if (image_info->page != (char *) NULL)
12434 Determine image bounding box.
12436 SetGeometry(image,&mng_info->page);
12437 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12438 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12450 mng_info->page=image->page;
12451 need_geom=MagickTrue;
12452 if (mng_info->page.width || mng_info->page.height)
12453 need_geom=MagickFalse;
12455 Check all the scenes.
12457 initial_delay=image->delay;
12458 need_iterations=MagickFalse;
12459 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12460 mng_info->equal_physs=MagickTrue,
12461 mng_info->equal_gammas=MagickTrue;
12462 mng_info->equal_srgbs=MagickTrue;
12463 mng_info->equal_backgrounds=MagickTrue;
12465 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12466 defined(PNG_MNG_FEATURES_SUPPORTED)
12467 all_images_are_gray=MagickTrue;
12468 mng_info->equal_palettes=MagickFalse;
12469 need_local_plte=MagickFalse;
12471 for (next_image=image; next_image != (Image *) NULL; )
12475 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12476 mng_info->page.width=next_image->columns+next_image->page.x;
12478 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12479 mng_info->page.height=next_image->rows+next_image->page.y;
12482 if (next_image->page.x || next_image->page.y)
12483 need_defi=MagickTrue;
12485 if (next_image->matte)
12486 need_matte=MagickTrue;
12488 if ((int) next_image->dispose >= BackgroundDispose)
12489 if (next_image->matte || next_image->page.x || next_image->page.y ||
12490 ((next_image->columns < mng_info->page.width) &&
12491 (next_image->rows < mng_info->page.height)))
12492 mng_info->need_fram=MagickTrue;
12494 if (next_image->iterations)
12495 need_iterations=MagickTrue;
12497 final_delay=next_image->delay;
12499 if (final_delay != initial_delay || final_delay > 1UL*
12500 next_image->ticks_per_second)
12501 mng_info->need_fram=1;
12503 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12504 defined(PNG_MNG_FEATURES_SUPPORTED)
12506 check for global palette possibility.
12508 if (image->matte != MagickFalse)
12509 need_local_plte=MagickTrue;
12511 if (need_local_plte == 0)
12513 if (ImageIsGray(image,exception) == MagickFalse)
12514 all_images_are_gray=MagickFalse;
12515 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12516 if (use_global_plte == 0)
12517 use_global_plte=mng_info->equal_palettes;
12518 need_local_plte=!mng_info->equal_palettes;
12521 if (GetNextImageInList(next_image) != (Image *) NULL)
12523 if (next_image->background_color.red !=
12524 next_image->next->background_color.red ||
12525 next_image->background_color.green !=
12526 next_image->next->background_color.green ||
12527 next_image->background_color.blue !=
12528 next_image->next->background_color.blue)
12529 mng_info->equal_backgrounds=MagickFalse;
12531 if (next_image->gamma != next_image->next->gamma)
12532 mng_info->equal_gammas=MagickFalse;
12534 if (next_image->rendering_intent !=
12535 next_image->next->rendering_intent)
12536 mng_info->equal_srgbs=MagickFalse;
12538 if ((next_image->units != next_image->next->units) ||
12539 (next_image->resolution.x != next_image->next->resolution.x) ||
12540 (next_image->resolution.y != next_image->next->resolution.y))
12541 mng_info->equal_physs=MagickFalse;
12543 if (mng_info->equal_chrms)
12545 if (next_image->chromaticity.red_primary.x !=
12546 next_image->next->chromaticity.red_primary.x ||
12547 next_image->chromaticity.red_primary.y !=
12548 next_image->next->chromaticity.red_primary.y ||
12549 next_image->chromaticity.green_primary.x !=
12550 next_image->next->chromaticity.green_primary.x ||
12551 next_image->chromaticity.green_primary.y !=
12552 next_image->next->chromaticity.green_primary.y ||
12553 next_image->chromaticity.blue_primary.x !=
12554 next_image->next->chromaticity.blue_primary.x ||
12555 next_image->chromaticity.blue_primary.y !=
12556 next_image->next->chromaticity.blue_primary.y ||
12557 next_image->chromaticity.white_point.x !=
12558 next_image->next->chromaticity.white_point.x ||
12559 next_image->chromaticity.white_point.y !=
12560 next_image->next->chromaticity.white_point.y)
12561 mng_info->equal_chrms=MagickFalse;
12565 next_image=GetNextImageInList(next_image);
12567 if (image_count < 2)
12569 mng_info->equal_backgrounds=MagickFalse;
12570 mng_info->equal_chrms=MagickFalse;
12571 mng_info->equal_gammas=MagickFalse;
12572 mng_info->equal_srgbs=MagickFalse;
12573 mng_info->equal_physs=MagickFalse;
12574 use_global_plte=MagickFalse;
12575 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12576 need_local_plte=MagickTrue;
12578 need_iterations=MagickFalse;
12581 if (mng_info->need_fram == MagickFalse)
12584 Only certain framing rates 100/n are exactly representable without
12585 the FRAM chunk but we'll allow some slop in VLC files
12587 if (final_delay == 0)
12589 if (need_iterations != MagickFalse)
12592 It's probably a GIF with loop; don't run it *too* fast.
12594 if (mng_info->adjoin)
12597 (void) ThrowMagickException(exception,GetMagickModule(),
12599 "input has zero delay between all frames; assuming",
12604 mng_info->ticks_per_second=0;
12606 if (final_delay != 0)
12607 mng_info->ticks_per_second=(png_uint_32)
12608 (image->ticks_per_second/final_delay);
12609 if (final_delay > 50)
12610 mng_info->ticks_per_second=2;
12612 if (final_delay > 75)
12613 mng_info->ticks_per_second=1;
12615 if (final_delay > 125)
12616 mng_info->need_fram=MagickTrue;
12618 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12619 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12620 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12621 1UL*image->ticks_per_second))
12622 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12625 if (mng_info->need_fram != MagickFalse)
12626 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12628 If pseudocolor, we should also check to see if all the
12629 palettes are identical and write a global PLTE if they are.
12633 Write the MNG version 1.0 signature and MHDR chunk.
12635 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12636 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12637 PNGType(chunk,mng_MHDR);
12638 LogPNGChunk(logging,mng_MHDR,28L);
12639 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12640 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12641 PNGLong(chunk+12,mng_info->ticks_per_second);
12642 PNGLong(chunk+16,0L); /* layer count=unknown */
12643 PNGLong(chunk+20,0L); /* frame count=unknown */
12644 PNGLong(chunk+24,0L); /* play time=unknown */
12649 if (need_defi || mng_info->need_fram || use_global_plte)
12650 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12653 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12658 if (need_defi || mng_info->need_fram || use_global_plte)
12659 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12662 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12670 if (need_defi || mng_info->need_fram || use_global_plte)
12671 PNGLong(chunk+28,11L); /* simplicity=LC */
12674 PNGLong(chunk+28,9L); /* simplicity=VLC */
12679 if (need_defi || mng_info->need_fram || use_global_plte)
12680 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12683 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12686 (void) WriteBlob(image,32,chunk);
12687 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12688 option=GetImageOption(image_info,"mng:need-cacheoff");
12689 if (option != (const char *) NULL)
12695 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12697 PNGType(chunk,mng_nEED);
12698 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12699 (void) WriteBlobMSBULong(image,(size_t) length);
12700 LogPNGChunk(logging,mng_nEED,(size_t) length);
12702 (void) WriteBlob(image,length,chunk);
12703 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12705 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12706 (GetNextImageInList(image) != (Image *) NULL) &&
12707 (image->iterations != 1))
12710 Write MNG TERM chunk
12712 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12713 PNGType(chunk,mng_TERM);
12714 LogPNGChunk(logging,mng_TERM,10L);
12715 chunk[4]=3; /* repeat animation */
12716 chunk[5]=0; /* show last frame when done */
12717 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12718 final_delay/MagickMax(image->ticks_per_second,1)));
12720 if (image->iterations == 0)
12721 PNGLong(chunk+10,PNG_UINT_31_MAX);
12724 PNGLong(chunk+10,(png_uint_32) image->iterations);
12726 if (logging != MagickFalse)
12728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12729 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12730 final_delay/MagickMax(image->ticks_per_second,1)));
12732 if (image->iterations == 0)
12733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12734 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12738 " Image iterations: %.20g",(double) image->iterations);
12740 (void) WriteBlob(image,14,chunk);
12741 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12744 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12746 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12747 mng_info->equal_srgbs)
12750 Write MNG sRGB chunk
12752 (void) WriteBlobMSBULong(image,1L);
12753 PNGType(chunk,mng_sRGB);
12754 LogPNGChunk(logging,mng_sRGB,1L);
12756 if (image->rendering_intent != UndefinedIntent)
12757 chunk[4]=(unsigned char)
12758 Magick_RenderingIntent_to_PNG_RenderingIntent(
12759 (image->rendering_intent));
12762 chunk[4]=(unsigned char)
12763 Magick_RenderingIntent_to_PNG_RenderingIntent(
12764 (PerceptualIntent));
12766 (void) WriteBlob(image,5,chunk);
12767 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12768 mng_info->have_write_global_srgb=MagickTrue;
12773 if (image->gamma && mng_info->equal_gammas)
12776 Write MNG gAMA chunk
12778 (void) WriteBlobMSBULong(image,4L);
12779 PNGType(chunk,mng_gAMA);
12780 LogPNGChunk(logging,mng_gAMA,4L);
12781 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12782 (void) WriteBlob(image,8,chunk);
12783 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12784 mng_info->have_write_global_gama=MagickTrue;
12786 if (mng_info->equal_chrms)
12792 Write MNG cHRM chunk
12794 (void) WriteBlobMSBULong(image,32L);
12795 PNGType(chunk,mng_cHRM);
12796 LogPNGChunk(logging,mng_cHRM,32L);
12797 primary=image->chromaticity.white_point;
12798 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12799 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12800 primary=image->chromaticity.red_primary;
12801 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12802 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12803 primary=image->chromaticity.green_primary;
12804 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12805 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12806 primary=image->chromaticity.blue_primary;
12807 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12808 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12809 (void) WriteBlob(image,36,chunk);
12810 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12811 mng_info->have_write_global_chrm=MagickTrue;
12814 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12817 Write MNG pHYs chunk
12819 (void) WriteBlobMSBULong(image,9L);
12820 PNGType(chunk,mng_pHYs);
12821 LogPNGChunk(logging,mng_pHYs,9L);
12823 if (image->units == PixelsPerInchResolution)
12825 PNGLong(chunk+4,(png_uint_32)
12826 (image->resolution.x*100.0/2.54+0.5));
12828 PNGLong(chunk+8,(png_uint_32)
12829 (image->resolution.y*100.0/2.54+0.5));
12836 if (image->units == PixelsPerCentimeterResolution)
12838 PNGLong(chunk+4,(png_uint_32)
12839 (image->resolution.x*100.0+0.5));
12841 PNGLong(chunk+8,(png_uint_32)
12842 (image->resolution.y*100.0+0.5));
12849 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12850 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12854 (void) WriteBlob(image,13,chunk);
12855 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12858 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12859 or does not cover the entire frame.
12861 if (write_mng && (image->matte || image->page.x > 0 ||
12862 image->page.y > 0 || (image->page.width &&
12863 (image->page.width+image->page.x < mng_info->page.width))
12864 || (image->page.height && (image->page.height+image->page.y
12865 < mng_info->page.height))))
12867 (void) WriteBlobMSBULong(image,6L);
12868 PNGType(chunk,mng_BACK);
12869 LogPNGChunk(logging,mng_BACK,6L);
12870 red=ScaleQuantumToShort(image->background_color.red);
12871 green=ScaleQuantumToShort(image->background_color.green);
12872 blue=ScaleQuantumToShort(image->background_color.blue);
12873 PNGShort(chunk+4,red);
12874 PNGShort(chunk+6,green);
12875 PNGShort(chunk+8,blue);
12876 (void) WriteBlob(image,10,chunk);
12877 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12878 if (mng_info->equal_backgrounds)
12880 (void) WriteBlobMSBULong(image,6L);
12881 PNGType(chunk,mng_bKGD);
12882 LogPNGChunk(logging,mng_bKGD,6L);
12883 (void) WriteBlob(image,10,chunk);
12884 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12888 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12889 if ((need_local_plte == MagickFalse) &&
12890 (image->storage_class == PseudoClass) &&
12891 (all_images_are_gray == MagickFalse))
12897 Write MNG PLTE chunk
12899 data_length=3*image->colors;
12900 (void) WriteBlobMSBULong(image,data_length);
12901 PNGType(chunk,mng_PLTE);
12902 LogPNGChunk(logging,mng_PLTE,data_length);
12904 for (i=0; i < (ssize_t) image->colors; i++)
12906 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12907 image->colormap[i].red) & 0xff);
12908 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12909 image->colormap[i].green) & 0xff);
12910 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12911 image->colormap[i].blue) & 0xff);
12914 (void) WriteBlob(image,data_length+4,chunk);
12915 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12916 mng_info->have_write_global_plte=MagickTrue;
12922 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12923 defined(PNG_MNG_FEATURES_SUPPORTED)
12924 mng_info->equal_palettes=MagickFalse;
12928 if (mng_info->adjoin)
12930 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12931 defined(PNG_MNG_FEATURES_SUPPORTED)
12933 If we aren't using a global palette for the entire MNG, check to
12934 see if we can use one for two or more consecutive images.
12936 if (need_local_plte && use_global_plte && !all_images_are_gray)
12938 if (mng_info->IsPalette)
12941 When equal_palettes is true, this image has the same palette
12942 as the previous PseudoClass image
12944 mng_info->have_write_global_plte=mng_info->equal_palettes;
12945 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12946 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12949 Write MNG PLTE chunk
12954 data_length=3*image->colors;
12955 (void) WriteBlobMSBULong(image,data_length);
12956 PNGType(chunk,mng_PLTE);
12957 LogPNGChunk(logging,mng_PLTE,data_length);
12959 for (i=0; i < (ssize_t) image->colors; i++)
12961 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12962 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12963 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12966 (void) WriteBlob(image,data_length+4,chunk);
12967 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12968 (uInt) (data_length+4)));
12969 mng_info->have_write_global_plte=MagickTrue;
12973 mng_info->have_write_global_plte=MagickFalse;
12984 previous_x=mng_info->page.x;
12985 previous_y=mng_info->page.y;
12992 mng_info->page=image->page;
12993 if ((mng_info->page.x != previous_x) ||
12994 (mng_info->page.y != previous_y))
12996 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12997 PNGType(chunk,mng_DEFI);
12998 LogPNGChunk(logging,mng_DEFI,12L);
12999 chunk[4]=0; /* object 0 MSB */
13000 chunk[5]=0; /* object 0 LSB */
13001 chunk[6]=0; /* visible */
13002 chunk[7]=0; /* abstract */
13003 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13004 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13005 (void) WriteBlob(image,16,chunk);
13006 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13011 mng_info->write_mng=write_mng;
13013 if ((int) image->dispose >= 3)
13014 mng_info->framing_mode=3;
13016 if (mng_info->need_fram && mng_info->adjoin &&
13017 ((image->delay != mng_info->delay) ||
13018 (mng_info->framing_mode != mng_info->old_framing_mode)))
13020 if (image->delay == mng_info->delay)
13023 Write a MNG FRAM chunk with the new framing mode.
13025 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13026 PNGType(chunk,mng_FRAM);
13027 LogPNGChunk(logging,mng_FRAM,1L);
13028 chunk[4]=(unsigned char) mng_info->framing_mode;
13029 (void) WriteBlob(image,5,chunk);
13030 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13035 Write a MNG FRAM chunk with the delay.
13037 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13038 PNGType(chunk,mng_FRAM);
13039 LogPNGChunk(logging,mng_FRAM,10L);
13040 chunk[4]=(unsigned char) mng_info->framing_mode;
13041 chunk[5]=0; /* frame name separator (no name) */
13042 chunk[6]=2; /* flag for changing default delay */
13043 chunk[7]=0; /* flag for changing frame timeout */
13044 chunk[8]=0; /* flag for changing frame clipping */
13045 chunk[9]=0; /* flag for changing frame sync_id */
13046 PNGLong(chunk+10,(png_uint_32)
13047 ((mng_info->ticks_per_second*
13048 image->delay)/MagickMax(image->ticks_per_second,1)));
13049 (void) WriteBlob(image,14,chunk);
13050 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13051 mng_info->delay=(png_uint_32) image->delay;
13053 mng_info->old_framing_mode=mng_info->framing_mode;
13056 #if defined(JNG_SUPPORTED)
13057 if (image_info->compression == JPEGCompression)
13062 if (logging != MagickFalse)
13063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13064 " Writing JNG object.");
13065 /* To do: specify the desired alpha compression method. */
13066 write_info=CloneImageInfo(image_info);
13067 write_info->compression=UndefinedCompression;
13068 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13069 write_info=DestroyImageInfo(write_info);
13074 if (logging != MagickFalse)
13075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13076 " Writing PNG object.");
13078 mng_info->need_blob = MagickFalse;
13079 mng_info->ping_preserve_colormap = MagickFalse;
13081 /* We don't want any ancillary chunks written */
13082 mng_info->ping_exclude_bKGD=MagickTrue;
13083 mng_info->ping_exclude_cHRM=MagickTrue;
13084 mng_info->ping_exclude_date=MagickTrue;
13085 mng_info->ping_exclude_EXIF=MagickTrue;
13086 mng_info->ping_exclude_gAMA=MagickTrue;
13087 mng_info->ping_exclude_iCCP=MagickTrue;
13088 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13089 mng_info->ping_exclude_oFFs=MagickTrue;
13090 mng_info->ping_exclude_pHYs=MagickTrue;
13091 mng_info->ping_exclude_sRGB=MagickTrue;
13092 mng_info->ping_exclude_tEXt=MagickTrue;
13093 mng_info->ping_exclude_tRNS=MagickTrue;
13094 mng_info->ping_exclude_vpAg=MagickTrue;
13095 mng_info->ping_exclude_zCCP=MagickTrue;
13096 mng_info->ping_exclude_zTXt=MagickTrue;
13098 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13101 if (status == MagickFalse)
13103 MngInfoFreeStruct(mng_info,&have_mng_structure);
13104 (void) CloseBlob(image);
13105 return(MagickFalse);
13107 (void) CatchImageException(image);
13108 if (GetNextImageInList(image) == (Image *) NULL)
13110 image=SyncNextImageInList(image);
13111 status=SetImageProgress(image,SaveImagesTag,scene++,
13112 GetImageListLength(image));
13114 if (status == MagickFalse)
13117 } while (mng_info->adjoin);
13121 while (GetPreviousImageInList(image) != (Image *) NULL)
13122 image=GetPreviousImageInList(image);
13124 Write the MEND chunk.
13126 (void) WriteBlobMSBULong(image,0x00000000L);
13127 PNGType(chunk,mng_MEND);
13128 LogPNGChunk(logging,mng_MEND,0L);
13129 (void) WriteBlob(image,4,chunk);
13130 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13133 Relinquish resources.
13135 (void) CloseBlob(image);
13136 MngInfoFreeStruct(mng_info,&have_mng_structure);
13138 if (logging != MagickFalse)
13139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13141 return(MagickTrue);
13143 #else /* PNG_LIBPNG_VER > 10011 */
13145 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13148 printf("Your PNG library is too old: You have libpng-%s\n",
13149 PNG_LIBPNG_VER_STRING);
13151 ThrowBinaryException(CoderError,"PNG library is too old",
13152 image_info->filename);
13155 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13157 return(WritePNGImage(image_info,image));
13159 #endif /* PNG_LIBPNG_VER > 10011 */