2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
106 /* ImageMagick differences */
107 #define first_scene scene
109 #if PNG_LIBPNG_VER > 10011
111 Optional declarations. Define or undefine them as you like.
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116 Features under construction. Define these to work on them.
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
132 /* Macros for left-bit-replication to ensure that pixels
133 * and PixelInfos all have the same image->depth, and for use
134 * in PNG8 quantization.
138 /* LBR01: Replicate top bit */
140 #define LBR01PacketRed(pixelpacket) \
141 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
144 #define LBR01PacketGreen(pixelpacket) \
145 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
148 #define LBR01PacketBlue(pixelpacket) \
149 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
152 #define LBR01PacketAlpha(pixelpacket) \
153 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
156 #define LBR01PacketRGB(pixelpacket) \
158 LBR01PacketRed((pixelpacket)); \
159 LBR01PacketGreen((pixelpacket)); \
160 LBR01PacketBlue((pixelpacket)); \
163 #define LBR01PacketRGBO(pixelpacket) \
165 LBR01PacketRGB((pixelpacket)); \
166 LBR01PacketAlpha((pixelpacket)); \
169 #define LBR01PixelRed(pixel) \
170 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
173 #define LBR01PixelGreen(pixel) \
174 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177 #define LBR01PixelBlue(pixel) \
178 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
181 #define LBR01PixelAlpha(pixel) \
182 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
185 #define LBR01PixelRGB(pixel) \
187 LBR01PixelRed((pixel)); \
188 LBR01PixelGreen((pixel)); \
189 LBR01PixelBlue((pixel)); \
192 #define LBR01PixelRGBA(pixel) \
194 LBR01PixelRGB((pixel)); \
195 LBR01PixelAlpha((pixel)); \
198 /* LBR02: Replicate top 2 bits */
200 #define LBR02PacketRed(pixelpacket) \
202 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
203 (pixelpacket).red=ScaleCharToQuantum( \
204 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
206 #define LBR02PacketGreen(pixelpacket) \
208 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
209 (pixelpacket).green=ScaleCharToQuantum( \
210 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
212 #define LBR02PacketBlue(pixelpacket) \
214 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
215 (pixelpacket).blue=ScaleCharToQuantum( \
216 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
218 #define LBR02PacketAlpha(pixelpacket) \
220 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
221 (pixelpacket).alpha=ScaleCharToQuantum( \
222 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
225 #define LBR02PacketRGB(pixelpacket) \
227 LBR02PacketRed((pixelpacket)); \
228 LBR02PacketGreen((pixelpacket)); \
229 LBR02PacketBlue((pixelpacket)); \
232 #define LBR02PacketRGBO(pixelpacket) \
234 LBR02PacketRGB((pixelpacket)); \
235 LBR02PacketAlpha((pixelpacket)); \
238 #define LBR02PixelRed(pixel) \
240 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
242 SetPixelRed(image, ScaleCharToQuantum( \
243 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
246 #define LBR02PixelGreen(pixel) \
248 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
250 SetPixelGreen(image, ScaleCharToQuantum( \
251 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
254 #define LBR02PixelBlue(pixel) \
256 unsigned char lbr_bits= \
257 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
258 SetPixelBlue(image, ScaleCharToQuantum( \
259 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
262 #define LBR02PixelAlpha(pixel) \
264 unsigned char lbr_bits= \
265 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
266 SetPixelAlpha(image, ScaleCharToQuantum( \
267 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
271 #define LBR02PixelRGB(pixel) \
273 LBR02PixelRed((pixel)); \
274 LBR02PixelGreen((pixel)); \
275 LBR02PixelBlue((pixel)); \
278 #define LBR02PixelRGBA(pixel) \
280 LBR02PixelRGB((pixel)); \
281 LBR02PixelAlpha((pixel)); \
284 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
285 PNG8 quantization) */
287 #define LBR03PacketRed(pixelpacket) \
289 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
290 (pixelpacket).red=ScaleCharToQuantum( \
291 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
293 #define LBR03PacketGreen(pixelpacket) \
295 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
296 (pixelpacket).green=ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
299 #define LBR03PacketBlue(pixelpacket) \
301 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
302 (pixelpacket).blue=ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
306 #define LBR03PacketRGB(pixelpacket) \
308 LBR03PacketRed((pixelpacket)); \
309 LBR03PacketGreen((pixelpacket)); \
310 LBR03PacketBlue((pixelpacket)); \
313 #define LBR03PixelRed(pixel) \
315 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
317 SetPixelRed(image, ScaleCharToQuantum( \
318 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
320 #define LBR03Green(pixel) \
322 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
324 SetPixelGreen(image, ScaleCharToQuantum( \
325 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
327 #define LBR03Blue(pixel) \
329 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
331 SetPixelBlue(image, ScaleCharToQuantum( \
332 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
335 #define LBR03RGB(pixel) \
337 LBR03PixelRed((pixel)); \
338 LBR03Green((pixel)); \
339 LBR03Blue((pixel)); \
342 /* LBR04: Replicate top 4 bits */
344 #define LBR04PacketRed(pixelpacket) \
346 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
347 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
349 #define LBR04PacketGreen(pixelpacket) \
351 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
352 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
354 #define LBR04PacketBlue(pixelpacket) \
356 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
357 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
359 #define LBR04PacketAlpha(pixelpacket) \
361 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
362 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
365 #define LBR04PacketRGB(pixelpacket) \
367 LBR04PacketRed((pixelpacket)); \
368 LBR04PacketGreen((pixelpacket)); \
369 LBR04PacketBlue((pixelpacket)); \
372 #define LBR04PacketRGBO(pixelpacket) \
374 LBR04PacketRGB((pixelpacket)); \
375 LBR04PacketAlpha((pixelpacket)); \
378 #define LBR04PixelRed(pixel) \
380 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
383 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
385 #define LBR04PixelGreen(pixel) \
387 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
389 SetPixelGreen(image,\
390 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
392 #define LBR04PixelBlue(pixel) \
394 unsigned char lbr_bits= \
395 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
397 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
399 #define LBR04PixelAlpha(pixel) \
401 unsigned char lbr_bits= \
402 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
403 SetPixelAlpha(image,\
404 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
407 #define LBR04PixelRGB(pixel) \
409 LBR04PixelRed((pixel)); \
410 LBR04PixelGreen((pixel)); \
411 LBR04PixelBlue((pixel)); \
414 #define LBR04PixelRGBA(pixel) \
416 LBR04PixelRGB((pixel)); \
417 LBR04PixelAlpha((pixel)); \
421 /* LBR08: Replicate top 8 bits */
423 #define LBR08PacketRed(pixelpacket) \
425 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
426 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
428 #define LBR08PacketGreen(pixelpacket) \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
431 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
433 #define LBR08PacketBlue(pixelpacket) \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
436 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
438 #define LBR08PacketAlpha(pixelpacket) \
440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
441 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
444 #define LBR08PacketRGB(pixelpacket) \
446 LBR08PacketRed((pixelpacket)); \
447 LBR08PacketGreen((pixelpacket)); \
448 LBR08PacketBlue((pixelpacket)); \
451 #define LBR08PacketRGBO(pixelpacket) \
453 LBR08PacketRGB((pixelpacket)); \
454 LBR08PacketAlpha((pixelpacket)); \
457 #define LBR08PixelRed(pixel) \
459 unsigned char lbr_bits= \
460 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
462 ScaleCharToQuantum((lbr_bits)), (pixel)); \
464 #define LBR08PixelGreen(pixel) \
466 unsigned char lbr_bits= \
467 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
468 SetPixelGreen(image,\
469 ScaleCharToQuantum((lbr_bits)), (pixel)); \
471 #define LBR08PixelBlue(pixel) \
473 unsigned char lbr_bits= \
474 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
476 ScaleCharToQuantum((lbr_bits)), (pixel)); \
478 #define LBR08PixelAlpha(pixel) \
480 unsigned char lbr_bits= \
481 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
482 SetPixelAlpha(image,\
483 ScaleCharToQuantum((lbr_bits)), (pixel)); \
486 #define LBR08PixelRGB(pixel) \
488 LBR08PixelRed((pixel)); \
489 LBR08PixelGreen((pixel)); \
490 LBR08PixelBlue((pixel)); \
493 #define LBR08PixelRGBA(pixel) \
495 LBR08PixelRGB((pixel)); \
496 LBR08PixelAlpha((pixel)); \
500 /* LBR16: Replicate top 16 bits */
502 #define LBR16PacketRed(pixelpacket) \
504 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
505 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
507 #define LBR16PacketGreen(pixelpacket) \
509 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
510 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
512 #define LBR16PacketBlue(pixelpacket) \
514 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
515 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
517 #define LBR16PacketAlpha(pixelpacket) \
519 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
520 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
523 #define LBR16PacketRGB(pixelpacket) \
525 LBR16PacketRed((pixelpacket)); \
526 LBR16PacketGreen((pixelpacket)); \
527 LBR16PacketBlue((pixelpacket)); \
530 #define LBR16PacketRGBO(pixelpacket) \
532 LBR16PacketRGB((pixelpacket)); \
533 LBR16PacketAlpha((pixelpacket)); \
536 #define LBR16PixelRed(pixel) \
538 unsigned short lbr_bits= \
539 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
541 ScaleShortToQuantum((lbr_bits)),(pixel)); \
543 #define LBR16PixelGreen(pixel) \
545 unsigned short lbr_bits= \
546 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
547 SetPixelGreen(image,\
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
550 #define LBR16PixelBlue(pixel) \
552 unsigned short lbr_bits= \
553 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
557 #define LBR16PixelAlpha(pixel) \
559 unsigned short lbr_bits= \
560 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
561 SetPixelAlpha(image,\
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
565 #define LBR16PixelRGB(pixel) \
567 LBR16PixelRed((pixel)); \
568 LBR16PixelGreen((pixel)); \
569 LBR16PixelBlue((pixel)); \
572 #define LBR16PixelRGBA(pixel) \
574 LBR16PixelRGB((pixel)); \
575 LBR16PixelAlpha((pixel)); \
579 Establish thread safety.
580 setjmp/longjmp is claimed to be safe on these platforms:
581 setjmp/longjmp is alleged to be unsafe on these platforms:
583 #ifndef SETJMP_IS_THREAD_SAFE
584 #define PNG_SETJMP_NOT_THREAD_SAFE
587 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
589 *ping_semaphore = (SemaphoreInfo *) NULL;
593 This temporary until I set up malloc'ed object attributes array.
594 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
597 #define MNG_MAX_OBJECTS 256
600 If this not defined, spec is interpreted strictly. If it is
601 defined, an attempt will be made to recover from some errors,
603 o global PLTE too short
608 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
609 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
610 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
611 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
612 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
613 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
614 will be enabled by default in libpng-1.2.0.
616 #ifdef PNG_MNG_FEATURES_SUPPORTED
617 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
618 # define PNG_READ_EMPTY_PLTE_SUPPORTED
620 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
621 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
626 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
627 This macro is only defined in libpng-1.0.3 and later.
628 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
630 #ifndef PNG_UINT_31_MAX
631 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
635 Constant strings for known chunk types. If you need to add a chunk,
636 add a string holding the name here. To make the code more
637 portable, we use ASCII numbers like this, not characters.
640 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
641 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
642 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
643 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
644 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
645 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
646 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
647 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
648 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
649 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
650 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
651 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
652 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
653 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
654 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
655 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
656 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
657 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
658 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
659 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
660 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
661 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
662 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
663 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
664 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
665 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
666 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
667 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
668 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
669 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
670 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
671 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
672 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
673 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
675 #if defined(JNG_SUPPORTED)
676 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
677 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
678 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
679 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
680 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
681 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
685 Other known chunks that are not yet supported by ImageMagick:
686 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
687 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
688 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
689 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
690 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
691 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
692 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
693 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
696 typedef struct _MngBox
705 typedef struct _MngPair
712 #ifdef MNG_OBJECT_BUFFERS
713 typedef struct _MngBuffer
745 typedef struct _MngInfo
748 #ifdef MNG_OBJECT_BUFFERS
750 *ob[MNG_MAX_OBJECTS];
761 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
762 bytes_in_read_buffer,
768 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
769 defined(PNG_MNG_FEATURES_SUPPORTED)
781 have_saved_bkgd_index,
782 have_write_global_chrm,
783 have_write_global_gama,
784 have_write_global_plte,
785 have_write_global_srgb,
799 x_off[MNG_MAX_OBJECTS],
800 y_off[MNG_MAX_OBJECTS];
806 object_clip[MNG_MAX_OBJECTS];
809 /* These flags could be combined into one byte */
810 exists[MNG_MAX_OBJECTS],
811 frozen[MNG_MAX_OBJECTS],
813 invisible[MNG_MAX_OBJECTS],
814 viewable[MNG_MAX_OBJECTS];
826 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
844 global_x_pixels_per_unit,
845 global_y_pixels_per_unit,
855 global_phys_unit_type,
870 write_png_compression_level,
871 write_png_compression_strategy,
872 write_png_compression_filter,
877 #ifdef MNG_BASI_SUPPORTED
885 basi_compression_method,
887 basi_interlace_method,
910 /* Added at version 6.6.6-7 */
918 /* ping_exclude_iTXt, */
925 ping_exclude_zCCP, /* hex-encoded iCCP */
927 ping_preserve_colormap;
933 Forward declarations.
935 static MagickBooleanType
936 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
938 static MagickBooleanType
939 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
941 #if defined(JNG_SUPPORTED)
942 static MagickBooleanType
943 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
946 #if PNG_LIBPNG_VER > 10011
949 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
950 static MagickBooleanType
951 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
953 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
955 * This is true if the high byte and the next highest byte of
956 * each sample of the image, the colormap, and the background color
957 * are equal to each other. We check this by seeing if the samples
958 * are unchanged when we scale them down to 8 and back up to Quantum.
960 * We don't use the method GetImageDepth() because it doesn't check
961 * background and doesn't handle PseudoClass specially.
964 #define QuantumToCharToQuantumEqQuantum(quantum) \
965 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
968 ok_to_reduce=MagickFalse;
970 if (image->depth >= 16)
977 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
979 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
980 MagickTrue : MagickFalse;
982 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
986 for (indx=0; indx < (ssize_t) image->colors; indx++)
989 QuantumToCharToQuantumEqQuantum(
990 image->colormap[indx].red) &&
991 QuantumToCharToQuantumEqQuantum(
992 image->colormap[indx].green) &&
993 QuantumToCharToQuantumEqQuantum(
994 image->colormap[indx].blue)) ?
995 MagickTrue : MagickFalse;
997 if (ok_to_reduce == MagickFalse)
1002 if ((ok_to_reduce != MagickFalse) &&
1003 (image->storage_class != PseudoClass))
1011 for (y=0; y < (ssize_t) image->rows; y++)
1013 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1015 if (p == (const Quantum *) NULL)
1017 ok_to_reduce = MagickFalse;
1021 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1024 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1026 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1027 MagickTrue : MagickFalse;
1029 if (ok_to_reduce == MagickFalse)
1032 p+=GetPixelChannels(image);
1039 if (ok_to_reduce != MagickFalse)
1041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1042 " OK to reduce PNG bit depth to 8 without loss of info");
1046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1047 " Not OK to reduce PNG bit depth to 8 without loss of info");
1051 return ok_to_reduce;
1053 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1055 static const char* PngColorTypeToString(const unsigned int color_type)
1058 *result = "Unknown";
1062 case PNG_COLOR_TYPE_GRAY:
1065 case PNG_COLOR_TYPE_GRAY_ALPHA:
1066 result = "Gray+Alpha";
1068 case PNG_COLOR_TYPE_PALETTE:
1071 case PNG_COLOR_TYPE_RGB:
1074 case PNG_COLOR_TYPE_RGB_ALPHA:
1075 result = "RGB+Alpha";
1083 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1087 case PerceptualIntent:
1090 case RelativeIntent:
1093 case SaturationIntent:
1096 case AbsoluteIntent:
1104 static RenderingIntent
1105 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1107 switch (ping_intent)
1110 return PerceptualIntent;
1113 return RelativeIntent;
1116 return SaturationIntent;
1119 return AbsoluteIntent;
1122 return UndefinedIntent;
1127 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1129 switch (ping_intent)
1132 return "Perceptual Intent";
1135 return "Relative Intent";
1138 return "Saturation Intent";
1141 return "Absolute Intent";
1144 return "Undefined Intent";
1148 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1157 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1159 switch (ping_colortype)
1177 return "UndefinedColorType";
1182 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1189 #endif /* PNG_LIBPNG_VER > 10011 */
1190 #endif /* MAGICKCORE_PNG_DELEGATE */
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1203 % IsMNG() returns MagickTrue if the image format type, identified by the
1204 % magick string, is MNG.
1206 % The format of the IsMNG method is:
1208 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1210 % A description of each parameter follows:
1212 % o magick: compare image format pattern against these bytes.
1214 % o length: Specifies the length of the magick string.
1218 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1221 return(MagickFalse);
1223 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1226 return(MagickFalse);
1230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240 % IsJNG() returns MagickTrue if the image format type, identified by the
1241 % magick string, is JNG.
1243 % The format of the IsJNG method is:
1245 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1247 % A description of each parameter follows:
1249 % o magick: compare image format pattern against these bytes.
1251 % o length: Specifies the length of the magick string.
1255 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1258 return(MagickFalse);
1260 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1263 return(MagickFalse);
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % IsPNG() returns MagickTrue if the image format type, identified by the
1278 % magick string, is PNG.
1280 % The format of the IsPNG method is:
1282 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1284 % A description of each parameter follows:
1286 % o magick: compare image format pattern against these bytes.
1288 % o length: Specifies the length of the magick string.
1291 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1294 return(MagickFalse);
1296 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1299 return(MagickFalse);
1302 #if defined(MAGICKCORE_PNG_DELEGATE)
1303 #if defined(__cplusplus) || defined(c_plusplus)
1307 #if (PNG_LIBPNG_VER > 10011)
1308 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1313 assert(image != (Image *) NULL);
1314 assert(image->signature == MagickSignature);
1315 buffer[0]=(unsigned char) (value >> 24);
1316 buffer[1]=(unsigned char) (value >> 16);
1317 buffer[2]=(unsigned char) (value >> 8);
1318 buffer[3]=(unsigned char) value;
1319 return((size_t) WriteBlob(image,4,buffer));
1322 static void PNGLong(png_bytep p,png_uint_32 value)
1324 *p++=(png_byte) ((value >> 24) & 0xff);
1325 *p++=(png_byte) ((value >> 16) & 0xff);
1326 *p++=(png_byte) ((value >> 8) & 0xff);
1327 *p++=(png_byte) (value & 0xff);
1330 #if defined(JNG_SUPPORTED)
1331 static void PNGsLong(png_bytep p,png_int_32 value)
1333 *p++=(png_byte) ((value >> 24) & 0xff);
1334 *p++=(png_byte) ((value >> 16) & 0xff);
1335 *p++=(png_byte) ((value >> 8) & 0xff);
1336 *p++=(png_byte) (value & 0xff);
1340 static void PNGShort(png_bytep p,png_uint_16 value)
1342 *p++=(png_byte) ((value >> 8) & 0xff);
1343 *p++=(png_byte) (value & 0xff);
1346 static void PNGType(png_bytep p,png_bytep type)
1348 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1351 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1354 if (logging != MagickFalse)
1355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1356 " Writing %c%c%c%c chunk, length: %.20g",
1357 type[0],type[1],type[2],type[3],(double) length);
1359 #endif /* PNG_LIBPNG_VER > 10011 */
1361 #if defined(__cplusplus) || defined(c_plusplus)
1365 #if PNG_LIBPNG_VER > 10011
1367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1371 % R e a d P N G I m a g e %
1375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1378 % Multiple-image Network Graphics (MNG) image file and returns it. It
1379 % allocates the memory necessary for the new Image structure and returns a
1380 % pointer to the new image or set of images.
1382 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1384 % The format of the ReadPNGImage method is:
1386 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1388 % A description of each parameter follows:
1390 % o image_info: the image info.
1392 % o exception: return any errors or warnings in this structure.
1394 % To do, more or less in chronological order (as of version 5.5.2,
1395 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1397 % Get 16-bit cheap transparency working.
1399 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1401 % Preserve all unknown and not-yet-handled known chunks found in input
1402 % PNG file and copy them into output PNG files according to the PNG
1405 % (At this point, PNG encoding should be in full MNG compliance)
1407 % Provide options for choice of background to use when the MNG BACK
1408 % chunk is not present or is not mandatory (i.e., leave transparent,
1409 % user specified, MNG BACK, PNG bKGD)
1411 % Implement LOOP/ENDL [done, but could do discretionary loops more
1412 % efficiently by linking in the duplicate frames.].
1414 % Decode and act on the MHDR simplicity profile (offer option to reject
1415 % files or attempt to process them anyway when the profile isn't LC or VLC).
1417 % Upgrade to full MNG without Delta-PNG.
1419 % o BACK [done a while ago except for background image ID]
1420 % o MOVE [done 15 May 1999]
1421 % o CLIP [done 15 May 1999]
1422 % o DISC [done 19 May 1999]
1423 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1424 % o SEEK [partially done 19 May 1999 (discard function only)]
1428 % o MNG-level tEXt/iTXt/zTXt
1433 % o iTXt (wait for libpng implementation).
1435 % Use the scene signature to discover when an identical scene is
1436 % being reused, and just point to the original image->exception instead
1437 % of storing another set of pixels. This not specific to MNG
1438 % but could be applied generally.
1440 % Upgrade to full MNG with Delta-PNG.
1442 % JNG tEXt/iTXt/zTXt
1444 % We will not attempt to read files containing the CgBI chunk.
1445 % They are really Xcode files meant for display on the iPhone.
1446 % These are not valid PNG files and it is impossible to recover
1447 % the original PNG from files that have been converted to Xcode-PNG,
1448 % since irretrievable loss of color data has occurred due to the
1449 % use of premultiplied alpha.
1452 #if defined(__cplusplus) || defined(c_plusplus)
1457 This the function that does the actual reading of data. It is
1458 the same as the one supplied in libpng, except that it receives the
1459 datastream from the ReadBlob() function instead of standard input.
1461 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1466 image=(Image *) png_get_io_ptr(png_ptr);
1472 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1473 if (check != length)
1478 (void) FormatLocaleString(msg,MaxTextExtent,
1479 "Expected %.20g bytes; found %.20g bytes",(double) length,
1481 png_warning(png_ptr,msg);
1482 png_error(png_ptr,"Read Exception");
1487 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1488 !defined(PNG_MNG_FEATURES_SUPPORTED)
1489 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1490 * older than libpng-1.0.3a, which was the first to allow the empty
1491 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1492 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1493 * encountered after an empty PLTE, so we have to look ahead for bKGD
1494 * chunks and remove them from the datastream that is passed to libpng,
1495 * and store their contents for later use.
1497 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1512 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1513 image=(Image *) mng_info->image;
1514 while (mng_info->bytes_in_read_buffer && length)
1516 data[i]=mng_info->read_buffer[i];
1517 mng_info->bytes_in_read_buffer--;
1523 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1525 if (check != length)
1526 png_error(png_ptr,"Read Exception");
1530 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1533 check=(png_size_t) ReadBlob(image,(size_t) length,
1534 (char *) mng_info->read_buffer);
1535 mng_info->read_buffer[4]=0;
1536 mng_info->bytes_in_read_buffer=4;
1537 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1538 mng_info->found_empty_plte=MagickTrue;
1539 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1541 mng_info->found_empty_plte=MagickFalse;
1542 mng_info->have_saved_bkgd_index=MagickFalse;
1546 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1549 check=(png_size_t) ReadBlob(image,(size_t) length,
1550 (char *) mng_info->read_buffer);
1551 mng_info->read_buffer[4]=0;
1552 mng_info->bytes_in_read_buffer=4;
1553 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1554 if (mng_info->found_empty_plte)
1557 Skip the bKGD data byte and CRC.
1560 ReadBlob(image,5,(char *) mng_info->read_buffer);
1561 check=(png_size_t) ReadBlob(image,(size_t) length,
1562 (char *) mng_info->read_buffer);
1563 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1564 mng_info->have_saved_bkgd_index=MagickTrue;
1565 mng_info->bytes_in_read_buffer=0;
1573 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1578 image=(Image *) png_get_io_ptr(png_ptr);
1584 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1586 if (check != length)
1587 png_error(png_ptr,"WriteBlob Failed");
1591 static void png_flush_data(png_structp png_ptr)
1596 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1597 static int PalettesAreEqual(Image *a,Image *b)
1602 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1603 return((int) MagickFalse);
1605 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1606 return((int) MagickFalse);
1608 if (a->colors != b->colors)
1609 return((int) MagickFalse);
1611 for (i=0; i < (ssize_t) a->colors; i++)
1613 if ((a->colormap[i].red != b->colormap[i].red) ||
1614 (a->colormap[i].green != b->colormap[i].green) ||
1615 (a->colormap[i].blue != b->colormap[i].blue))
1616 return((int) MagickFalse);
1619 return((int) MagickTrue);
1623 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1625 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1626 mng_info->exists[i] && !mng_info->frozen[i])
1628 #ifdef MNG_OBJECT_BUFFERS
1629 if (mng_info->ob[i] != (MngBuffer *) NULL)
1631 if (mng_info->ob[i]->reference_count > 0)
1632 mng_info->ob[i]->reference_count--;
1634 if (mng_info->ob[i]->reference_count == 0)
1636 if (mng_info->ob[i]->image != (Image *) NULL)
1637 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1639 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1642 mng_info->ob[i]=(MngBuffer *) NULL;
1644 mng_info->exists[i]=MagickFalse;
1645 mng_info->invisible[i]=MagickFalse;
1646 mng_info->viewable[i]=MagickFalse;
1647 mng_info->frozen[i]=MagickFalse;
1648 mng_info->x_off[i]=0;
1649 mng_info->y_off[i]=0;
1650 mng_info->object_clip[i].left=0;
1651 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1652 mng_info->object_clip[i].top=0;
1653 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1657 static void MngInfoFreeStruct(MngInfo *mng_info,
1658 MagickBooleanType *have_mng_structure)
1660 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1665 for (i=1; i < MNG_MAX_OBJECTS; i++)
1666 MngInfoDiscardObject(mng_info,i);
1668 if (mng_info->global_plte != (png_colorp) NULL)
1669 mng_info->global_plte=(png_colorp)
1670 RelinquishMagickMemory(mng_info->global_plte);
1672 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1673 *have_mng_structure=MagickFalse;
1677 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1683 if (box.left < box2.left)
1686 if (box.top < box2.top)
1689 if (box.right > box2.right)
1690 box.right=box2.right;
1692 if (box.bottom > box2.bottom)
1693 box.bottom=box2.bottom;
1698 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1704 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1706 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1708 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1709 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1710 if (delta_type != 0)
1712 box.left+=previous_box.left;
1713 box.right+=previous_box.right;
1714 box.top+=previous_box.top;
1715 box.bottom+=previous_box.bottom;
1721 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1727 Read two ssize_ts from CLON, MOVE or PAST chunk
1729 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1730 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1732 if (delta_type != 0)
1734 pair.a+=previous_pair.a;
1735 pair.b+=previous_pair.b;
1741 static long mng_get_long(unsigned char *p)
1743 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1746 typedef struct _PNGErrorInfo
1755 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1766 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1767 image=error_info->image;
1768 exception=error_info->exception;
1770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1771 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1773 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1774 "`%s'",image->filename);
1776 #if (PNG_LIBPNG_VER < 10500)
1777 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1778 * are building with libpng-1.4.x and can be ignored.
1780 longjmp(ping->jmpbuf,1);
1782 png_longjmp(ping,1);
1786 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1797 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1798 png_error(ping, message);
1800 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1801 image=error_info->image;
1802 exception=error_info->exception;
1803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1804 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1806 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1807 message,"`%s'",image->filename);
1810 #ifdef PNG_USER_MEM_SUPPORTED
1811 #if PNG_LIBPNG_VER >= 14000
1812 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1814 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1818 return((png_voidp) AcquireMagickMemory((size_t) size));
1822 Free a pointer. It is removed from the list at the same time.
1824 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1827 ptr=RelinquishMagickMemory(ptr);
1828 return((png_free_ptr) NULL);
1832 #if defined(__cplusplus) || defined(c_plusplus)
1837 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1838 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1843 register unsigned char
1857 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1858 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1859 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1860 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1861 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1865 /* look for newline */
1869 /* look for length */
1870 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1873 length=(png_uint_32) StringToLong(sp);
1875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1876 " length: %lu",(unsigned long) length);
1878 while (*sp != ' ' && *sp != '\n')
1881 /* allocate space */
1884 png_warning(ping,"invalid profile length");
1885 return(MagickFalse);
1888 profile=BlobToStringInfo((const void *) NULL,length);
1890 if (profile == (StringInfo *) NULL)
1892 png_warning(ping, "unable to copy profile");
1893 return(MagickFalse);
1896 /* copy profile, skipping white space and column 1 "=" signs */
1897 dp=GetStringInfoDatum(profile);
1900 for (i=0; i < (ssize_t) nibbles; i++)
1902 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1906 png_warning(ping, "ran out of profile data");
1907 profile=DestroyStringInfo(profile);
1908 return(MagickFalse);
1914 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1917 (*dp++)+=unhex[(int) *sp++];
1920 We have already read "Raw profile type.
1922 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1923 profile=DestroyStringInfo(profile);
1925 if (image_info->verbose)
1926 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1931 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1932 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1938 /* The unknown chunk structure contains the chunk data:
1943 Note that libpng has already taken care of the CRC handling.
1947 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1948 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1949 return(0); /* Did not recognize */
1951 /* recognized vpAg */
1953 if (chunk->size != 9)
1954 return(-1); /* Error return */
1956 if (chunk->data[8] != 0)
1957 return(0); /* ImageMagick requires pixel units */
1959 image=(Image *) png_get_user_chunk_ptr(ping);
1961 image->page.width=(size_t) ((chunk->data[0] << 24) |
1962 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1964 image->page.height=(size_t) ((chunk->data[4] << 24) |
1965 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1967 /* Return one of the following: */
1968 /* return(-n); chunk had an error */
1969 /* return(0); did not recognize */
1970 /* return(n); success */
1978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1982 % R e a d O n e P N G I m a g e %
1986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1988 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1989 % (minus the 8-byte signature) and returns it. It allocates the memory
1990 % necessary for the new Image structure and returns a pointer to the new
1993 % The format of the ReadOnePNGImage method is:
1995 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1996 % ExceptionInfo *exception)
1998 % A description of each parameter follows:
2000 % o mng_info: Specifies a pointer to a MngInfo structure.
2002 % o image_info: the image info.
2004 % o exception: return any errors or warnings in this structure.
2007 static Image *ReadOnePNGImage(MngInfo *mng_info,
2008 const ImageInfo *image_info, ExceptionInfo *exception)
2010 /* Read one PNG image */
2012 /* To do: Read the tIME chunk into the date:modify property */
2013 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2019 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2028 ping_interlace_method,
2029 ping_compression_method,
2081 register unsigned char
2099 *volatile ping_pixels;
2101 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2102 png_byte unused_chunks[]=
2104 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2105 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2106 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2107 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2108 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2109 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2110 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2111 /* ignore the APNG chunks */
2112 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2113 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2114 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2119 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2120 " Enter ReadOnePNGImage()");
2122 #if (PNG_LIBPNG_VER < 10200)
2123 if (image_info->verbose)
2124 printf("Your PNG library (libpng-%s) is rather old.\n",
2125 PNG_LIBPNG_VER_STRING);
2128 #if (PNG_LIBPNG_VER >= 10400)
2129 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2130 if (image_info->verbose)
2132 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2133 PNG_LIBPNG_VER_STRING);
2134 printf("Please update it.\n");
2140 quantum_info = (QuantumInfo *) NULL;
2141 image=mng_info->image;
2143 if (logging != MagickFalse)
2145 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2146 " image->alpha_trait=%d",(int) image->alpha_trait);
2148 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2149 " image->rendering_intent=%d",(int) image->rendering_intent);
2151 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2152 " image->colorspace=%d",(int) image->colorspace);
2154 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2156 /* Set to an out-of-range color unless tRNS chunk is present */
2157 transparent_color.red=65537;
2158 transparent_color.green=65537;
2159 transparent_color.blue=65537;
2160 transparent_color.alpha=65537;
2165 num_raw_profiles = 0;
2167 ping_found_cHRM = MagickFalse;
2168 ping_found_gAMA = MagickFalse;
2169 ping_found_iCCP = MagickFalse;
2170 ping_found_sRGB = MagickFalse;
2173 Allocate the PNG structures
2175 #ifdef PNG_USER_MEM_SUPPORTED
2176 error_info.image=image;
2177 error_info.exception=exception;
2178 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2179 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2180 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2182 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2183 MagickPNGErrorHandler,MagickPNGWarningHandler);
2185 if (ping == (png_struct *) NULL)
2186 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2188 ping_info=png_create_info_struct(ping);
2190 if (ping_info == (png_info *) NULL)
2192 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2193 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2196 end_info=png_create_info_struct(ping);
2198 if (end_info == (png_info *) NULL)
2200 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2201 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2204 ping_pixels=(unsigned char *) NULL;
2206 if (setjmp(png_jmpbuf(ping)))
2209 PNG image is corrupt.
2211 png_destroy_read_struct(&ping,&ping_info,&end_info);
2213 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2214 UnlockSemaphoreInfo(ping_semaphore);
2217 if (ping_pixels != (unsigned char *) NULL)
2218 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2220 if (logging != MagickFalse)
2221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2222 " exit ReadOnePNGImage() with error.");
2224 if (image != (Image *) NULL)
2226 InheritException(exception,exception);
2230 return(GetFirstImageInList(image));
2233 /* { For navigation to end of SETJMP-protected block. Within this
2234 * block, use png_error() instead of Throwing an Exception, to ensure
2235 * that libpng is able to clean up, and that the semaphore is unlocked.
2238 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2239 LockSemaphoreInfo(ping_semaphore);
2243 Prepare PNG for reading.
2246 mng_info->image_found++;
2247 png_set_sig_bytes(ping,8);
2249 if (LocaleCompare(image_info->magick,"MNG") == 0)
2251 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2252 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2253 png_set_read_fn(ping,image,png_get_data);
2255 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2256 png_permit_empty_plte(ping,MagickTrue);
2257 png_set_read_fn(ping,image,png_get_data);
2259 mng_info->image=image;
2260 mng_info->bytes_in_read_buffer=0;
2261 mng_info->found_empty_plte=MagickFalse;
2262 mng_info->have_saved_bkgd_index=MagickFalse;
2263 png_set_read_fn(ping,mng_info,mng_get_data);
2269 png_set_read_fn(ping,image,png_get_data);
2271 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2272 /* Ignore unused chunks and all unknown chunks except for vpAg */
2273 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2274 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2275 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2276 (int)sizeof(unused_chunks)/5);
2277 /* Callback for other unknown chunks */
2278 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2281 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2282 /* Disable new libpng-1.5.10 feature */
2283 png_set_check_for_invalid_index (ping, 0);
2286 #if (PNG_LIBPNG_VER < 10400)
2287 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2288 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2289 /* Disable thread-unsafe features of pnggccrd */
2290 if (png_access_version_number() >= 10200)
2292 png_uint_32 mmx_disable_mask=0;
2293 png_uint_32 asm_flags;
2295 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2296 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2297 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2298 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2299 asm_flags=png_get_asm_flags(ping);
2300 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2305 png_read_info(ping,ping_info);
2307 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2308 &ping_bit_depth,&ping_color_type,
2309 &ping_interlace_method,&ping_compression_method,
2310 &ping_filter_method);
2312 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2315 (void) png_get_bKGD(ping, ping_info, &ping_background);
2317 if (ping_bit_depth < 8)
2319 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2321 png_set_packing(ping);
2326 image->depth=ping_bit_depth;
2327 image->depth=GetImageQuantumDepth(image,MagickFalse);
2328 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2330 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2331 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2333 image->rendering_intent=UndefinedIntent;
2334 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2336 (void) ResetMagickMemory(&image->chromaticity,0,
2337 sizeof(image->chromaticity));
2340 if (logging != MagickFalse)
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " PNG width: %.20g, height: %.20g",
2344 (double) ping_width, (double) ping_height);
2346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2347 " PNG color_type: %d, bit_depth: %d",
2348 ping_color_type, ping_bit_depth);
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " PNG compression_method: %d",
2352 ping_compression_method);
2354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 " PNG interlace_method: %d, filter_method: %d",
2356 ping_interlace_method,ping_filter_method);
2359 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2361 ping_found_gAMA=MagickTrue;
2362 if (logging != MagickFalse)
2363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2364 " Found PNG gAMA chunk.");
2367 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2369 ping_found_cHRM=MagickTrue;
2370 if (logging != MagickFalse)
2371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2372 " Found PNG cHRM chunk.");
2375 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2377 ping_found_iCCP=MagickTrue;
2378 if (logging != MagickFalse)
2379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380 " Found PNG iCCP chunk.");
2383 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2385 ping_found_sRGB=MagickTrue;
2386 if (logging != MagickFalse)
2387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2388 " Found PNG sRGB chunk.");
2391 #ifdef PNG_READ_iCCP_SUPPORTED
2392 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2397 #if (PNG_LIBPNG_VER < 10500)
2411 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2414 if (profile_length != 0)
2419 if (logging != MagickFalse)
2420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2421 " Reading PNG iCCP chunk.");
2422 profile=BlobToStringInfo(info,profile_length);
2423 if (profile == (StringInfo *) NULL)
2425 png_warning(ping, "ICC profile is NULL");
2426 profile=DestroyStringInfo(profile);
2430 (void) SetImageProfile(image,"icc",profile,exception);
2431 profile=DestroyStringInfo(profile);
2436 #if defined(PNG_READ_sRGB_SUPPORTED)
2438 if (mng_info->have_global_srgb)
2440 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2441 (mng_info->global_srgb_intent);
2444 if (png_get_sRGB(ping,ping_info,&intent))
2446 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2449 if (logging != MagickFalse)
2450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2451 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2456 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2457 if (mng_info->have_global_gama)
2458 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2460 if (png_get_gAMA(ping,ping_info,&file_gamma))
2462 image->gamma=(float) file_gamma;
2463 if (logging != MagickFalse)
2464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2465 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2469 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2471 if (mng_info->have_global_chrm != MagickFalse)
2473 (void) png_set_cHRM(ping,ping_info,
2474 mng_info->global_chrm.white_point.x,
2475 mng_info->global_chrm.white_point.y,
2476 mng_info->global_chrm.red_primary.x,
2477 mng_info->global_chrm.red_primary.y,
2478 mng_info->global_chrm.green_primary.x,
2479 mng_info->global_chrm.green_primary.y,
2480 mng_info->global_chrm.blue_primary.x,
2481 mng_info->global_chrm.blue_primary.y);
2485 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2487 (void) png_get_cHRM(ping,ping_info,
2488 &image->chromaticity.white_point.x,
2489 &image->chromaticity.white_point.y,
2490 &image->chromaticity.red_primary.x,
2491 &image->chromaticity.red_primary.y,
2492 &image->chromaticity.green_primary.x,
2493 &image->chromaticity.green_primary.y,
2494 &image->chromaticity.blue_primary.x,
2495 &image->chromaticity.blue_primary.y);
2499 if (image->rendering_intent != UndefinedIntent)
2501 png_set_sRGB(ping,ping_info,
2502 Magick_RenderingIntent_to_PNG_RenderingIntent
2503 (image->rendering_intent));
2504 png_set_gAMA(ping,ping_info,1.000f/2.200f);
2505 png_set_cHRM(ping,ping_info,
2506 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2507 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2509 #if defined(PNG_oFFs_SUPPORTED)
2510 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2512 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2513 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2515 if (logging != MagickFalse)
2516 if (image->page.x || image->page.y)
2517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2518 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2519 image->page.x,(double) image->page.y);
2522 #if defined(PNG_pHYs_SUPPORTED)
2523 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2525 if (mng_info->have_global_phys)
2527 png_set_pHYs(ping,ping_info,
2528 mng_info->global_x_pixels_per_unit,
2529 mng_info->global_y_pixels_per_unit,
2530 mng_info->global_phys_unit_type);
2534 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2537 Set image resolution.
2539 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2541 image->resolution.x=(double) x_resolution;
2542 image->resolution.y=(double) y_resolution;
2544 if (unit_type == PNG_RESOLUTION_METER)
2546 image->units=PixelsPerCentimeterResolution;
2547 image->resolution.x=(double) x_resolution/100.0;
2548 image->resolution.y=(double) y_resolution/100.0;
2551 if (logging != MagickFalse)
2552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2553 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2554 (double) x_resolution,(double) y_resolution,unit_type);
2558 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2563 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2565 if ((number_colors == 0) &&
2566 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2568 if (mng_info->global_plte_length)
2570 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2571 (int) mng_info->global_plte_length);
2573 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2575 if (mng_info->global_trns_length)
2578 "global tRNS has more entries than global PLTE");
2582 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2583 (int) mng_info->global_trns_length,NULL);
2586 #ifdef PNG_READ_bKGD_SUPPORTED
2588 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2589 mng_info->have_saved_bkgd_index ||
2591 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2596 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2597 if (mng_info->have_saved_bkgd_index)
2598 background.index=mng_info->saved_bkgd_index;
2600 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2601 background.index=ping_background->index;
2603 background.red=(png_uint_16)
2604 mng_info->global_plte[background.index].red;
2606 background.green=(png_uint_16)
2607 mng_info->global_plte[background.index].green;
2609 background.blue=(png_uint_16)
2610 mng_info->global_plte[background.index].blue;
2612 background.gray=(png_uint_16)
2613 mng_info->global_plte[background.index].green;
2615 png_set_bKGD(ping,ping_info,&background);
2620 png_error(ping,"No global PLTE in file");
2624 #ifdef PNG_READ_bKGD_SUPPORTED
2625 if (mng_info->have_global_bkgd &&
2626 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2627 image->background_color=mng_info->mng_global_bkgd;
2629 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2635 Set image background color.
2637 if (logging != MagickFalse)
2638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2639 " Reading PNG bKGD chunk.");
2641 /* Scale background components to 16-bit, then scale
2644 if (logging != MagickFalse)
2645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2646 " raw ping_background=(%d,%d,%d).",ping_background->red,
2647 ping_background->green,ping_background->blue);
2651 if (ping_bit_depth == 1)
2654 else if (ping_bit_depth == 2)
2657 else if (ping_bit_depth == 4)
2660 if (ping_bit_depth <= 8)
2663 ping_background->red *= bkgd_scale;
2664 ping_background->green *= bkgd_scale;
2665 ping_background->blue *= bkgd_scale;
2667 if (logging != MagickFalse)
2669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670 " bkgd_scale=%d.",bkgd_scale);
2672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673 " ping_background=(%d,%d,%d).",ping_background->red,
2674 ping_background->green,ping_background->blue);
2677 image->background_color.red=
2678 ScaleShortToQuantum(ping_background->red);
2680 image->background_color.green=
2681 ScaleShortToQuantum(ping_background->green);
2683 image->background_color.blue=
2684 ScaleShortToQuantum(ping_background->blue);
2686 image->background_color.alpha=OpaqueAlpha;
2688 if (logging != MagickFalse)
2689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2690 " image->background_color=(%.20g,%.20g,%.20g).",
2691 (double) image->background_color.red,
2692 (double) image->background_color.green,
2693 (double) image->background_color.blue);
2695 #endif /* PNG_READ_bKGD_SUPPORTED */
2697 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2700 Image has a tRNS chunk.
2708 if (logging != MagickFalse)
2709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2710 " Reading PNG tRNS chunk.");
2712 max_sample = (int) ((one << ping_bit_depth) - 1);
2714 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2715 (int)ping_trans_color->gray > max_sample) ||
2716 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2717 ((int)ping_trans_color->red > max_sample ||
2718 (int)ping_trans_color->green > max_sample ||
2719 (int)ping_trans_color->blue > max_sample)))
2721 if (logging != MagickFalse)
2722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2723 " Ignoring PNG tRNS chunk with out-of-range sample.");
2724 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2725 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2726 image->alpha_trait=UndefinedPixelTrait;
2733 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2735 /* Scale transparent_color to short */
2736 transparent_color.red= scale_to_short*ping_trans_color->red;
2737 transparent_color.green= scale_to_short*ping_trans_color->green;
2738 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2739 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2741 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2743 if (logging != MagickFalse)
2745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2746 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2749 " scaled graylevel is %.20g.",transparent_color.alpha);
2751 transparent_color.red=transparent_color.alpha;
2752 transparent_color.green=transparent_color.alpha;
2753 transparent_color.blue=transparent_color.alpha;
2757 #if defined(PNG_READ_sBIT_SUPPORTED)
2758 if (mng_info->have_global_sbit)
2760 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2761 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2764 num_passes=png_set_interlace_handling(ping);
2766 png_read_update_info(ping,ping_info);
2768 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2771 Initialize image structure.
2773 mng_info->image_box.left=0;
2774 mng_info->image_box.right=(ssize_t) ping_width;
2775 mng_info->image_box.top=0;
2776 mng_info->image_box.bottom=(ssize_t) ping_height;
2777 if (mng_info->mng_type == 0)
2779 mng_info->mng_width=ping_width;
2780 mng_info->mng_height=ping_height;
2781 mng_info->frame=mng_info->image_box;
2782 mng_info->clip=mng_info->image_box;
2787 image->page.y=mng_info->y_off[mng_info->object_id];
2790 image->compression=ZipCompression;
2791 image->columns=ping_width;
2792 image->rows=ping_height;
2794 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2795 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2797 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2798 image->gamma == 1.0) &&
2799 !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
2800 !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2802 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2803 * image->colorspace to GRAY, and reset image->chromaticity.
2805 SetImageColorspace(image,GRAYColorspace,exception);
2809 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2810 " image->colorspace=%d",(int) image->colorspace);
2812 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2813 ((int) ping_bit_depth < 16 &&
2814 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2819 image->storage_class=PseudoClass;
2821 image->colors=one << ping_bit_depth;
2822 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2823 if (image->colors > 256)
2826 if (image->colors > 65536L)
2827 image->colors=65536L;
2829 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2834 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2835 image->colors=(size_t) number_colors;
2837 if (logging != MagickFalse)
2838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2839 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2843 if (image->storage_class == PseudoClass)
2846 Initialize image colormap.
2848 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2849 png_error(ping,"Memory allocation failed");
2851 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2856 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2858 for (i=0; i < (ssize_t) number_colors; i++)
2860 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2861 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2862 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2865 for ( ; i < (ssize_t) image->colors; i++)
2867 image->colormap[i].red=0;
2868 image->colormap[i].green=0;
2869 image->colormap[i].blue=0;
2878 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2883 for (i=0; i < (ssize_t) image->colors; i++)
2885 image->colormap[i].red=(Quantum) (i*scale);
2886 image->colormap[i].green=(Quantum) (i*scale);
2887 image->colormap[i].blue=(Quantum) (i*scale);
2892 /* Set some properties for reporting by "identify" */
2897 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2898 ping_interlace_method in value */
2900 (void) FormatLocaleString(msg,MaxTextExtent,
2901 "%d, %d",(int) ping_width, (int) ping_height);
2902 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2904 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2905 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2907 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2908 (int) ping_color_type,
2909 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
2910 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2912 if (ping_interlace_method == 0)
2914 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2915 (int) ping_interlace_method);
2917 else if (ping_interlace_method == 1)
2919 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2920 (int) ping_interlace_method);
2924 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2925 (int) ping_interlace_method);
2927 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2929 if (number_colors != 0)
2931 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2932 (int) number_colors);
2933 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
2939 Read image scanlines.
2941 if (image->delay != 0)
2942 mng_info->scenes_found++;
2944 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2945 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2946 (image_info->first_scene+image_info->number_scenes))))
2948 /* This happens later in non-ping decodes */
2949 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2950 image->storage_class=DirectClass;
2952 if (logging != MagickFalse)
2953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2954 " Skipping PNG image data for scene %.20g",(double)
2955 mng_info->scenes_found-1);
2956 png_destroy_read_struct(&ping,&ping_info,&end_info);
2958 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2959 UnlockSemaphoreInfo(ping_semaphore);
2962 if (logging != MagickFalse)
2963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2964 " exit ReadOnePNGImage().");
2969 if (logging != MagickFalse)
2970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2971 " Reading PNG IDAT chunk(s)");
2974 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2975 ping_rowbytes*sizeof(*ping_pixels));
2978 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2979 sizeof(*ping_pixels));
2981 if (ping_pixels == (unsigned char *) NULL)
2982 png_error(ping,"Memory allocation failed");
2984 if (logging != MagickFalse)
2985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2986 " Converting PNG pixels to pixel packets");
2988 Convert PNG pixels to pixel packets.
2990 quantum_info=AcquireQuantumInfo(image_info,image);
2992 if (quantum_info == (QuantumInfo *) NULL)
2993 png_error(ping,"Failed to allocate quantum_info");
2998 found_transparent_pixel;
3000 found_transparent_pixel=MagickFalse;
3002 if (image->storage_class == DirectClass)
3004 for (pass=0; pass < num_passes; pass++)
3007 Convert image to DirectClass pixel packets.
3009 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3010 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3011 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3012 BlendPixelTrait : UndefinedPixelTrait;
3014 for (y=0; y < (ssize_t) image->rows; y++)
3017 row_offset=ping_rowbytes*y;
3022 png_read_row(ping,ping_pixels+row_offset,NULL);
3023 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3025 if (q == (Quantum *) NULL)
3028 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3029 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3030 GrayQuantum,ping_pixels+row_offset,exception);
3032 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3033 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3034 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3036 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3037 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3038 RGBAQuantum,ping_pixels+row_offset,exception);
3040 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3041 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3042 IndexQuantum,ping_pixels+row_offset,exception);
3044 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3045 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3046 RGBQuantum,ping_pixels+row_offset,exception);
3048 if (found_transparent_pixel == MagickFalse)
3050 /* Is there a transparent pixel in the row? */
3051 if (y== 0 && logging != MagickFalse)
3052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3053 " Looking for cheap transparent pixel");
3055 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3057 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3058 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3059 (GetPixelAlpha(image,q) != OpaqueAlpha))
3061 if (logging != MagickFalse)
3062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3065 found_transparent_pixel = MagickTrue;
3068 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3069 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3070 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3071 transparent_color.red &&
3072 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3073 transparent_color.green &&
3074 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3075 transparent_color.blue))
3077 if (logging != MagickFalse)
3078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3080 found_transparent_pixel = MagickTrue;
3083 q+=GetPixelChannels(image);
3087 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3089 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3092 if (status == MagickFalse)
3095 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3099 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3101 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3102 if (status == MagickFalse)
3108 else /* image->storage_class != DirectClass */
3110 for (pass=0; pass < num_passes; pass++)
3119 Convert grayscale image to PseudoClass pixel packets.
3121 if (logging != MagickFalse)
3122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3123 " Converting grayscale pixels to pixel packets");
3125 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3126 BlendPixelTrait : UndefinedPixelTrait;
3128 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3129 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3130 sizeof(*quantum_scanline));
3132 if (quantum_scanline == (Quantum *) NULL)
3133 png_error(ping,"Memory allocation failed");
3135 for (y=0; y < (ssize_t) image->rows; y++)
3138 row_offset=ping_rowbytes*y;
3143 png_read_row(ping,ping_pixels+row_offset,NULL);
3144 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3146 if (q == (Quantum *) NULL)
3149 p=ping_pixels+row_offset;
3152 switch (ping_bit_depth)
3159 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3161 for (bit=7; bit >= 0; bit--)
3162 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3166 if ((image->columns % 8) != 0)
3168 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3169 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3177 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3179 *r++=(*p >> 6) & 0x03;
3180 *r++=(*p >> 4) & 0x03;
3181 *r++=(*p >> 2) & 0x03;
3185 if ((image->columns % 4) != 0)
3187 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3188 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3196 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3198 *r++=(*p >> 4) & 0x0f;
3202 if ((image->columns % 2) != 0)
3203 *r++=(*p++ >> 4) & 0x0f;
3210 if (ping_color_type == 4)
3211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3214 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3215 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3216 found_transparent_pixel = MagickTrue;
3217 q+=GetPixelChannels(image);
3221 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3229 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3231 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3235 if (image->colors > 256)
3236 quantum=((*p++) << 8);
3242 *r=ScaleShortToQuantum(quantum);
3245 if (ping_color_type == 4)
3247 if (image->colors > 256)
3248 quantum=((*p++) << 8);
3253 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3254 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3255 found_transparent_pixel = MagickTrue;
3256 q+=GetPixelChannels(image);
3259 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3261 p++; /* strip low byte */
3263 if (ping_color_type == 4)
3265 SetPixelAlpha(image,*p++,q);
3266 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3267 found_transparent_pixel = MagickTrue;
3269 q+=GetPixelChannels(image);
3282 Transfer image scanline.
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3288 if (q == (Quantum *) NULL)
3290 for (x=0; x < (ssize_t) image->columns; x++)
3292 SetPixelIndex(image,*r++,q);
3293 q+=GetPixelChannels(image);
3296 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3299 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3301 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3304 if (status == MagickFalse)
3309 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3311 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3313 if (status == MagickFalse)
3317 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3320 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3321 UndefinedPixelTrait;
3323 if (logging != MagickFalse)
3325 if (found_transparent_pixel != MagickFalse)
3326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3327 " Found transparent pixel");
3330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3331 " No transparent pixel was found");
3333 ping_color_type&=0x03;
3338 if (quantum_info != (QuantumInfo *) NULL)
3339 quantum_info=DestroyQuantumInfo(quantum_info);
3341 if (image->storage_class == PseudoClass)
3346 alpha_trait=image->alpha_trait;
3347 image->alpha_trait=UndefinedPixelTrait;
3348 (void) SyncImage(image,exception);
3349 image->alpha_trait=alpha_trait;
3352 png_read_end(ping,end_info);
3354 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3355 (ssize_t) image_info->first_scene && image->delay != 0)
3357 png_destroy_read_struct(&ping,&ping_info,&end_info);
3358 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3360 (void) SetImageBackgroundColor(image,exception);
3361 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3362 UnlockSemaphoreInfo(ping_semaphore);
3364 if (logging != MagickFalse)
3365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3366 " exit ReadOnePNGImage() early.");
3370 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3376 Image has a transparent background.
3378 storage_class=image->storage_class;
3379 image->alpha_trait=BlendPixelTrait;
3381 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3383 if (storage_class == PseudoClass)
3385 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3387 for (x=0; x < ping_num_trans; x++)
3389 image->colormap[x].alpha_trait=BlendPixelTrait;
3390 image->colormap[x].alpha =
3391 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3395 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3397 for (x=0; x < (int) image->colors; x++)
3399 if (ScaleQuantumToShort(image->colormap[x].red) ==
3400 transparent_color.alpha)
3402 image->colormap[x].alpha_trait=BlendPixelTrait;
3403 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3407 (void) SyncImage(image,exception);
3410 #if 1 /* Should have already been done above, but glennrp problem P10
3415 for (y=0; y < (ssize_t) image->rows; y++)
3417 image->storage_class=storage_class;
3418 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3420 if (q == (Quantum *) NULL)
3424 /* Caution: on a Q8 build, this does not distinguish between
3425 * 16-bit colors that differ only in the low byte
3427 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3429 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3430 transparent_color.red &&
3431 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3432 transparent_color.green &&
3433 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3434 transparent_color.blue)
3436 SetPixelAlpha(image,TransparentAlpha,q);
3439 #if 0 /* I have not found a case where this is needed. */
3442 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3446 q+=GetPixelChannels(image);
3449 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3455 image->storage_class=DirectClass;
3458 for (j = 0; j < 2; j++)
3461 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3462 MagickTrue : MagickFalse;
3464 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3465 MagickTrue : MagickFalse;
3467 if (status != MagickFalse)
3468 for (i=0; i < (ssize_t) num_text; i++)
3470 /* Check for a profile */
3472 if (logging != MagickFalse)
3473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3474 " Reading PNG text chunk");
3476 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3478 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3488 length=text[i].text_length;
3489 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3491 if (value == (char *) NULL)
3493 png_error(ping,"Memory allocation failed");
3497 (void) ConcatenateMagickString(value,text[i].text,length+2);
3499 /* Don't save "density" or "units" property if we have a pHYs
3502 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3503 (LocaleCompare(text[i].key,"density") != 0 &&
3504 LocaleCompare(text[i].key,"units") != 0))
3505 (void) SetImageProperty(image,text[i].key,value,exception);
3507 if (logging != MagickFalse)
3509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3510 " length: %lu",(unsigned long) length);
3511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3512 " Keyword: %s",text[i].key);
3515 value=DestroyString(value);
3518 num_text_total += num_text;
3521 #ifdef MNG_OBJECT_BUFFERS
3523 Store the object if necessary.
3525 if (object_id && !mng_info->frozen[object_id])
3527 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3530 create a new object buffer.
3532 mng_info->ob[object_id]=(MngBuffer *)
3533 AcquireMagickMemory(sizeof(MngBuffer));
3535 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3537 mng_info->ob[object_id]->image=(Image *) NULL;
3538 mng_info->ob[object_id]->reference_count=1;
3542 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3543 mng_info->ob[object_id]->frozen)
3545 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3546 png_error(ping,"Memory allocation failed");
3548 if (mng_info->ob[object_id]->frozen)
3549 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3555 if (mng_info->ob[object_id]->image != (Image *) NULL)
3556 mng_info->ob[object_id]->image=DestroyImage
3557 (mng_info->ob[object_id]->image);
3559 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3562 if (mng_info->ob[object_id]->image != (Image *) NULL)
3563 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3566 png_error(ping, "Cloning image for object buffer failed");
3568 if (ping_width > 250000L || ping_height > 250000L)
3569 png_error(ping,"PNG Image dimensions are too large.");
3571 mng_info->ob[object_id]->width=ping_width;
3572 mng_info->ob[object_id]->height=ping_height;
3573 mng_info->ob[object_id]->color_type=ping_color_type;
3574 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3575 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3576 mng_info->ob[object_id]->compression_method=
3577 ping_compression_method;
3578 mng_info->ob[object_id]->filter_method=ping_filter_method;
3580 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3586 Copy the PLTE to the object buffer.
3588 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3589 mng_info->ob[object_id]->plte_length=number_colors;
3591 for (i=0; i < number_colors; i++)
3593 mng_info->ob[object_id]->plte[i]=plte[i];
3598 mng_info->ob[object_id]->plte_length=0;
3603 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3604 * alpha or if a valid tRNS chunk is present, no matter whether there
3605 * is actual transparency present.
3607 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3608 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3609 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3610 BlendPixelTrait : UndefinedPixelTrait;
3612 /* Set more properties for identify to retrieve */
3617 if (num_text_total != 0)
3619 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3620 (void) FormatLocaleString(msg,MaxTextExtent,
3621 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3622 (void) SetImageProperty(image,"png:text ",msg,
3626 if (num_raw_profiles != 0)
3628 (void) FormatLocaleString(msg,MaxTextExtent,
3629 "%d were found", num_raw_profiles);
3630 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3634 if (ping_found_cHRM != MagickFalse)
3636 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3637 "chunk was found (see Chromaticity, above)");
3638 (void) SetImageProperty(image,"png:cHRM ",msg,
3642 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3644 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3645 "chunk was found (see Background color, above)");
3646 (void) SetImageProperty(image,"png:bKGD ",msg,
3650 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3653 #if defined(PNG_iCCP_SUPPORTED)
3654 if (ping_found_iCCP != MagickFalse)
3655 (void) SetImageProperty(image,"png:iCCP ",msg,
3659 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3660 (void) SetImageProperty(image,"png:tRNS ",msg,
3663 #if defined(PNG_sRGB_SUPPORTED)
3664 if (ping_found_sRGB != MagickFalse)
3666 (void) FormatLocaleString(msg,MaxTextExtent,
3669 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3670 (void) SetImageProperty(image,"png:sRGB ",msg,
3675 if (ping_found_gAMA != MagickFalse)
3677 (void) FormatLocaleString(msg,MaxTextExtent,
3678 "gamma=%.8g (See Gamma, above)",
3680 (void) SetImageProperty(image,"png:gAMA ",msg,
3684 #if defined(PNG_pHYs_SUPPORTED)
3685 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3687 (void) FormatLocaleString(msg,MaxTextExtent,
3688 "x_res=%.10g, y_res=%.10g, units=%d",
3689 (double) x_resolution,(double) y_resolution, unit_type);
3690 (void) SetImageProperty(image,"png:pHYs ",msg,
3695 #if defined(PNG_oFFs_SUPPORTED)
3696 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3698 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3699 (double) image->page.x,(double) image->page.y);
3700 (void) SetImageProperty(image,"png:oFFs ",msg,
3705 if ((image->page.width != 0 && image->page.width != image->columns) ||
3706 (image->page.height != 0 && image->page.height != image->rows))
3708 (void) FormatLocaleString(msg,MaxTextExtent,
3709 "width=%.20g, height=%.20g",
3710 (double) image->page.width,(double) image->page.height);
3711 (void) SetImageProperty(image,"png:vpAg ",msg,
3717 Relinquish resources.
3719 png_destroy_read_struct(&ping,&ping_info,&end_info);
3721 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3723 if (logging != MagickFalse)
3724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725 " exit ReadOnePNGImage()");
3727 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3728 UnlockSemaphoreInfo(ping_semaphore);
3731 /* } for navigation to beginning of SETJMP-protected block, revert to
3732 * Throwing an Exception when an error occurs.
3737 /* end of reading one PNG image */
3740 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3755 magic_number[MaxTextExtent];
3763 assert(image_info != (const ImageInfo *) NULL);
3764 assert(image_info->signature == MagickSignature);
3766 if (image_info->debug != MagickFalse)
3767 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3768 image_info->filename);
3770 assert(exception != (ExceptionInfo *) NULL);
3771 assert(exception->signature == MagickSignature);
3772 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3773 image=AcquireImage(image_info,exception);
3774 mng_info=(MngInfo *) NULL;
3775 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3777 if (status == MagickFalse)
3778 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3781 Verify PNG signature.
3783 count=ReadBlob(image,8,(unsigned char *) magic_number);
3785 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3786 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3789 Allocate a MngInfo structure.
3791 have_mng_structure=MagickFalse;
3792 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3794 if (mng_info == (MngInfo *) NULL)
3795 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3798 Initialize members of the MngInfo structure.
3800 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3801 mng_info->image=image;
3802 have_mng_structure=MagickTrue;
3805 image=ReadOnePNGImage(mng_info,image_info,exception);
3806 MngInfoFreeStruct(mng_info,&have_mng_structure);
3808 if (image == (Image *) NULL)
3810 if (previous != (Image *) NULL)
3812 if (previous->signature != MagickSignature)
3813 ThrowReaderException(CorruptImageError,"CorruptImage");
3815 (void) CloseBlob(previous);
3816 (void) DestroyImageList(previous);
3819 if (logging != MagickFalse)
3820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3821 "exit ReadPNGImage() with error");
3823 return((Image *) NULL);
3826 (void) CloseBlob(image);
3828 if ((image->columns == 0) || (image->rows == 0))
3830 if (logging != MagickFalse)
3831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3832 "exit ReadPNGImage() with error.");
3834 ThrowReaderException(CorruptImageError,"CorruptImage");
3837 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3838 (image->gamma == 1.0))
3839 SetImageColorspace(image,RGBColorspace,exception);
3841 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3843 (void) SetImageType(image,TrueColorType,exception);
3844 image->alpha_trait=UndefinedPixelTrait;
3847 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3848 (void) SetImageType(image,TrueColorMatteType,exception);
3850 if (logging != MagickFalse)
3851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3852 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3853 (double) image->page.width,(double) image->page.height,
3854 (double) image->page.x,(double) image->page.y);
3856 if (logging != MagickFalse)
3857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3864 #if defined(JNG_SUPPORTED)
3866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3870 % R e a d O n e J N G I m a g e %
3874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3876 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3877 % (minus the 8-byte signature) and returns it. It allocates the memory
3878 % necessary for the new Image structure and returns a pointer to the new
3881 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3883 % The format of the ReadOneJNGImage method is:
3885 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3886 % ExceptionInfo *exception)
3888 % A description of each parameter follows:
3890 % o mng_info: Specifies a pointer to a MngInfo structure.
3892 % o image_info: the image info.
3894 % o exception: return any errors or warnings in this structure.
3897 static Image *ReadOneJNGImage(MngInfo *mng_info,
3898 const ImageInfo *image_info, ExceptionInfo *exception)
3925 jng_image_sample_depth,
3926 jng_image_compression_method,
3927 jng_image_interlace_method,
3928 jng_alpha_sample_depth,
3929 jng_alpha_compression_method,
3930 jng_alpha_filter_method,
3931 jng_alpha_interlace_method;
3933 register const Quantum
3943 register unsigned char
3954 jng_alpha_compression_method=0;
3955 jng_alpha_sample_depth=8;
3959 alpha_image=(Image *) NULL;
3960 color_image=(Image *) NULL;
3961 alpha_image_info=(ImageInfo *) NULL;
3962 color_image_info=(ImageInfo *) NULL;
3964 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " Enter ReadOneJNGImage()");
3967 image=mng_info->image;
3969 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3972 Allocate next image structure.
3974 if (logging != MagickFalse)
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3976 " AcquireNextImage()");
3978 AcquireNextImage(image_info,image,exception);
3980 if (GetNextImageInList(image) == (Image *) NULL)
3981 return((Image *) NULL);
3983 image=SyncNextImageInList(image);
3985 mng_info->image=image;
3988 Signature bytes have already been read.
3991 read_JSEP=MagickFalse;
3992 reading_idat=MagickFalse;
3993 skip_to_iend=MagickFalse;
3997 type[MaxTextExtent];
4006 Read a new JNG chunk.
4008 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4009 2*GetBlobSize(image));
4011 if (status == MagickFalse)
4015 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4016 length=ReadBlobMSBLong(image);
4017 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4019 if (logging != MagickFalse)
4020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4021 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4022 type[0],type[1],type[2],type[3],(double) length);
4024 if (length > PNG_UINT_31_MAX || count == 0)
4025 ThrowReaderException(CorruptImageError,"CorruptImage");
4028 chunk=(unsigned char *) NULL;
4032 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4034 if (chunk == (unsigned char *) NULL)
4035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4037 for (i=0; i < (ssize_t) length; i++)
4038 chunk[i]=(unsigned char) ReadBlobByte(image);
4043 (void) ReadBlobMSBLong(image); /* read crc word */
4048 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4053 if (memcmp(type,mng_JHDR,4) == 0)
4057 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4058 (p[2] << 8) | p[3]);
4059 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4060 (p[6] << 8) | p[7]);
4061 jng_color_type=p[8];
4062 jng_image_sample_depth=p[9];
4063 jng_image_compression_method=p[10];
4064 jng_image_interlace_method=p[11];
4066 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4069 jng_alpha_sample_depth=p[12];
4070 jng_alpha_compression_method=p[13];
4071 jng_alpha_filter_method=p[14];
4072 jng_alpha_interlace_method=p[15];
4074 if (logging != MagickFalse)
4076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4077 " jng_width: %16lu",(unsigned long) jng_width);
4079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4080 " jng_width: %16lu",(unsigned long) jng_height);
4082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4083 " jng_color_type: %16d",jng_color_type);
4085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4086 " jng_image_sample_depth: %3d",
4087 jng_image_sample_depth);
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " jng_image_compression_method:%3d",
4091 jng_image_compression_method);
4093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4094 " jng_image_interlace_method: %3d",
4095 jng_image_interlace_method);
4097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4098 " jng_alpha_sample_depth: %3d",
4099 jng_alpha_sample_depth);
4101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4102 " jng_alpha_compression_method:%3d",
4103 jng_alpha_compression_method);
4105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4106 " jng_alpha_filter_method: %3d",
4107 jng_alpha_filter_method);
4109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4110 " jng_alpha_interlace_method: %3d",
4111 jng_alpha_interlace_method);
4116 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4122 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4123 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4124 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4127 o create color_image
4128 o open color_blob, attached to color_image
4129 o if (color type has alpha)
4130 open alpha_blob, attached to alpha_image
4133 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4135 if (color_image_info == (ImageInfo *) NULL)
4136 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4138 GetImageInfo(color_image_info);
4139 color_image=AcquireImage(color_image_info,exception);
4141 if (color_image == (Image *) NULL)
4142 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4144 if (logging != MagickFalse)
4145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4146 " Creating color_blob.");
4148 (void) AcquireUniqueFilename(color_image->filename);
4149 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4152 if (status == MagickFalse)
4153 return((Image *) NULL);
4155 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4157 alpha_image_info=(ImageInfo *)
4158 AcquireMagickMemory(sizeof(ImageInfo));
4160 if (alpha_image_info == (ImageInfo *) NULL)
4161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4163 GetImageInfo(alpha_image_info);
4164 alpha_image=AcquireImage(alpha_image_info,exception);
4166 if (alpha_image == (Image *) NULL)
4168 alpha_image=DestroyImage(alpha_image);
4169 ThrowReaderException(ResourceLimitError,
4170 "MemoryAllocationFailed");
4173 if (logging != MagickFalse)
4174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4175 " Creating alpha_blob.");
4177 (void) AcquireUniqueFilename(alpha_image->filename);
4178 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4181 if (status == MagickFalse)
4182 return((Image *) NULL);
4184 if (jng_alpha_compression_method == 0)
4189 if (logging != MagickFalse)
4190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4191 " Writing IHDR chunk to alpha_blob.");
4193 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4194 "\211PNG\r\n\032\n");
4196 (void) WriteBlobMSBULong(alpha_image,13L);
4197 PNGType(data,mng_IHDR);
4198 LogPNGChunk(logging,mng_IHDR,13L);
4199 PNGLong(data+4,jng_width);
4200 PNGLong(data+8,jng_height);
4201 data[12]=jng_alpha_sample_depth;
4202 data[13]=0; /* color_type gray */
4203 data[14]=0; /* compression method 0 */
4204 data[15]=0; /* filter_method 0 */
4205 data[16]=0; /* interlace_method 0 */
4206 (void) WriteBlob(alpha_image,17,data);
4207 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4210 reading_idat=MagickTrue;
4213 if (memcmp(type,mng_JDAT,4) == 0)
4215 /* Copy chunk to color_image->blob */
4217 if (logging != MagickFalse)
4218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4219 " Copying JDAT chunk data to color_blob.");
4221 (void) WriteBlob(color_image,length,chunk);
4224 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4229 if (memcmp(type,mng_IDAT,4) == 0)
4234 /* Copy IDAT header and chunk data to alpha_image->blob */
4236 if (image_info->ping == MagickFalse)
4238 if (logging != MagickFalse)
4239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4240 " Copying IDAT chunk data to alpha_blob.");
4242 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4243 PNGType(data,mng_IDAT);
4244 LogPNGChunk(logging,mng_IDAT,length);
4245 (void) WriteBlob(alpha_image,4,data);
4246 (void) WriteBlob(alpha_image,length,chunk);
4247 (void) WriteBlobMSBULong(alpha_image,
4248 crc32(crc32(0,data,4),chunk,(uInt) length));
4252 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4257 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4259 /* Copy chunk data to alpha_image->blob */
4261 if (image_info->ping == MagickFalse)
4263 if (logging != MagickFalse)
4264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4265 " Copying JDAA chunk data to alpha_blob.");
4267 (void) WriteBlob(alpha_image,length,chunk);
4271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4276 if (memcmp(type,mng_JSEP,4) == 0)
4278 read_JSEP=MagickTrue;
4281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4286 if (memcmp(type,mng_bKGD,4) == 0)
4290 image->background_color.red=ScaleCharToQuantum(p[1]);
4291 image->background_color.green=image->background_color.red;
4292 image->background_color.blue=image->background_color.red;
4297 image->background_color.red=ScaleCharToQuantum(p[1]);
4298 image->background_color.green=ScaleCharToQuantum(p[3]);
4299 image->background_color.blue=ScaleCharToQuantum(p[5]);
4302 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4306 if (memcmp(type,mng_gAMA,4) == 0)
4309 image->gamma=((float) mng_get_long(p))*0.00001;
4311 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4315 if (memcmp(type,mng_cHRM,4) == 0)
4319 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4320 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4321 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4322 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4323 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4324 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4325 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4326 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4329 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4333 if (memcmp(type,mng_sRGB,4) == 0)
4337 image->rendering_intent=
4338 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4339 image->gamma=1.000f/2.200f;
4340 image->chromaticity.red_primary.x=0.6400f;
4341 image->chromaticity.red_primary.y=0.3300f;
4342 image->chromaticity.green_primary.x=0.3000f;
4343 image->chromaticity.green_primary.y=0.6000f;
4344 image->chromaticity.blue_primary.x=0.1500f;
4345 image->chromaticity.blue_primary.y=0.0600f;
4346 image->chromaticity.white_point.x=0.3127f;
4347 image->chromaticity.white_point.y=0.3290f;
4350 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4354 if (memcmp(type,mng_oFFs,4) == 0)
4358 image->page.x=(ssize_t) mng_get_long(p);
4359 image->page.y=(ssize_t) mng_get_long(&p[4]);
4361 if ((int) p[8] != 0)
4363 image->page.x/=10000;
4364 image->page.y/=10000;
4369 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4374 if (memcmp(type,mng_pHYs,4) == 0)
4378 image->resolution.x=(double) mng_get_long(p);
4379 image->resolution.y=(double) mng_get_long(&p[4]);
4380 if ((int) p[8] == PNG_RESOLUTION_METER)
4382 image->units=PixelsPerCentimeterResolution;
4383 image->resolution.x=image->resolution.x/100.0f;
4384 image->resolution.y=image->resolution.y/100.0f;
4388 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4393 if (memcmp(type,mng_iCCP,4) == 0)
4397 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4404 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4406 if (memcmp(type,mng_IEND,4))
4416 Finish up reading image data:
4418 o read main image from color_blob.
4422 o if (color_type has alpha)
4423 if alpha_encoding is PNG
4424 read secondary image from alpha_blob via ReadPNG
4425 if alpha_encoding is JPEG
4426 read secondary image from alpha_blob via ReadJPEG
4430 o copy intensity of secondary image into
4431 alpha samples of main image.
4433 o destroy the secondary image.
4436 (void) CloseBlob(color_image);
4438 if (logging != MagickFalse)
4439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4440 " Reading jng_image from color_blob.");
4442 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4443 color_image->filename);
4445 color_image_info->ping=MagickFalse; /* To do: avoid this */
4446 jng_image=ReadImage(color_image_info,exception);
4448 if (jng_image == (Image *) NULL)
4449 return((Image *) NULL);
4451 (void) RelinquishUniqueFileResource(color_image->filename);
4452 color_image=DestroyImage(color_image);
4453 color_image_info=DestroyImageInfo(color_image_info);
4455 if (jng_image == (Image *) NULL)
4456 return((Image *) NULL);
4458 if (logging != MagickFalse)
4459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4460 " Copying jng_image pixels to main image.");
4462 image->rows=jng_height;
4463 image->columns=jng_width;
4465 for (y=0; y < (ssize_t) image->rows; y++)
4467 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4468 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4469 for (x=(ssize_t) image->columns; x != 0; x--)
4471 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4472 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4473 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4474 q+=GetPixelChannels(image);
4475 s+=GetPixelChannels(jng_image);
4478 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4482 jng_image=DestroyImage(jng_image);
4484 if (image_info->ping == MagickFalse)
4486 if (jng_color_type >= 12)
4488 if (jng_alpha_compression_method == 0)
4492 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4493 PNGType(data,mng_IEND);
4494 LogPNGChunk(logging,mng_IEND,0L);
4495 (void) WriteBlob(alpha_image,4,data);
4496 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4499 (void) CloseBlob(alpha_image);
4501 if (logging != MagickFalse)
4502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4503 " Reading alpha from alpha_blob.");
4505 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4506 "%s",alpha_image->filename);
4508 jng_image=ReadImage(alpha_image_info,exception);
4510 if (jng_image != (Image *) NULL)
4511 for (y=0; y < (ssize_t) image->rows; y++)
4513 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4515 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4517 if (image->alpha_trait == BlendPixelTrait)
4518 for (x=(ssize_t) image->columns; x != 0; x--)
4520 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4521 q+=GetPixelChannels(image);
4522 s+=GetPixelChannels(jng_image);
4526 for (x=(ssize_t) image->columns; x != 0; x--)
4528 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4529 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4530 image->alpha_trait=BlendPixelTrait;
4531 q+=GetPixelChannels(image);
4532 s+=GetPixelChannels(jng_image);
4535 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4538 (void) RelinquishUniqueFileResource(alpha_image->filename);
4539 alpha_image=DestroyImage(alpha_image);
4540 alpha_image_info=DestroyImageInfo(alpha_image_info);
4541 if (jng_image != (Image *) NULL)
4542 jng_image=DestroyImage(jng_image);
4546 /* Read the JNG image. */
4548 if (mng_info->mng_type == 0)
4550 mng_info->mng_width=jng_width;
4551 mng_info->mng_height=jng_height;
4554 if (image->page.width == 0 && image->page.height == 0)
4556 image->page.width=jng_width;
4557 image->page.height=jng_height;
4560 if (image->page.x == 0 && image->page.y == 0)
4562 image->page.x=mng_info->x_off[mng_info->object_id];
4563 image->page.y=mng_info->y_off[mng_info->object_id];
4568 image->page.y=mng_info->y_off[mng_info->object_id];
4571 mng_info->image_found++;
4572 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4573 2*GetBlobSize(image));
4575 if (logging != MagickFalse)
4576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4577 " exit ReadOneJNGImage()");
4583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4587 % R e a d J N G I m a g e %
4591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4593 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4594 % (including the 8-byte signature) and returns it. It allocates the memory
4595 % necessary for the new Image structure and returns a pointer to the new
4598 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4600 % The format of the ReadJNGImage method is:
4602 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4605 % A description of each parameter follows:
4607 % o image_info: the image info.
4609 % o exception: return any errors or warnings in this structure.
4613 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4628 magic_number[MaxTextExtent];
4636 assert(image_info != (const ImageInfo *) NULL);
4637 assert(image_info->signature == MagickSignature);
4638 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4639 assert(exception != (ExceptionInfo *) NULL);
4640 assert(exception->signature == MagickSignature);
4641 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4642 image=AcquireImage(image_info,exception);
4643 mng_info=(MngInfo *) NULL;
4644 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4646 if (status == MagickFalse)
4647 return((Image *) NULL);
4649 if (LocaleCompare(image_info->magick,"JNG") != 0)
4650 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4652 /* Verify JNG signature. */
4654 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4656 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4657 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4659 /* Allocate a MngInfo structure. */
4661 have_mng_structure=MagickFalse;
4662 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4664 if (mng_info == (MngInfo *) NULL)
4665 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4667 /* Initialize members of the MngInfo structure. */
4669 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4670 have_mng_structure=MagickTrue;
4672 mng_info->image=image;
4674 image=ReadOneJNGImage(mng_info,image_info,exception);
4675 MngInfoFreeStruct(mng_info,&have_mng_structure);
4677 if (image == (Image *) NULL)
4679 if (IsImageObject(previous) != MagickFalse)
4681 (void) CloseBlob(previous);
4682 (void) DestroyImageList(previous);
4685 if (logging != MagickFalse)
4686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4687 "exit ReadJNGImage() with error");
4689 return((Image *) NULL);
4691 (void) CloseBlob(image);
4693 if (image->columns == 0 || image->rows == 0)
4695 if (logging != MagickFalse)
4696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4697 "exit ReadJNGImage() with error");
4699 ThrowReaderException(CorruptImageError,"CorruptImage");
4702 if (logging != MagickFalse)
4703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4709 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4712 page_geometry[MaxTextExtent];
4745 #if defined(MNG_INSERT_LAYERS)
4747 mng_background_color;
4750 register unsigned char
4765 #if defined(MNG_INSERT_LAYERS)
4770 volatile unsigned int
4771 #ifdef MNG_OBJECT_BUFFERS
4772 mng_background_object=0,
4774 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4777 default_frame_timeout,
4779 #if defined(MNG_INSERT_LAYERS)
4785 /* These delays are all measured in image ticks_per_second,
4786 * not in MNG ticks_per_second
4789 default_frame_delay,
4793 #if defined(MNG_INSERT_LAYERS)
4802 previous_fb.bottom=0;
4804 previous_fb.right=0;
4806 default_fb.bottom=0;
4810 /* Open image file. */
4812 assert(image_info != (const ImageInfo *) NULL);
4813 assert(image_info->signature == MagickSignature);
4814 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4815 assert(exception != (ExceptionInfo *) NULL);
4816 assert(exception->signature == MagickSignature);
4817 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4818 image=AcquireImage(image_info,exception);
4819 mng_info=(MngInfo *) NULL;
4820 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4822 if (status == MagickFalse)
4823 return((Image *) NULL);
4825 first_mng_object=MagickFalse;
4827 have_mng_structure=MagickFalse;
4829 /* Allocate a MngInfo structure. */
4831 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4833 if (mng_info == (MngInfo *) NULL)
4834 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4836 /* Initialize members of the MngInfo structure. */
4838 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4839 mng_info->image=image;
4840 have_mng_structure=MagickTrue;
4842 if (LocaleCompare(image_info->magick,"MNG") == 0)
4845 magic_number[MaxTextExtent];
4847 /* Verify MNG signature. */
4848 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4849 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4850 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4852 /* Initialize some nonzero members of the MngInfo structure. */
4853 for (i=0; i < MNG_MAX_OBJECTS; i++)
4855 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4856 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4858 mng_info->exists[0]=MagickTrue;
4861 first_mng_object=MagickTrue;
4863 #if defined(MNG_INSERT_LAYERS)
4864 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4866 default_frame_delay=0;
4867 default_frame_timeout=0;
4870 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4872 skip_to_iend=MagickFalse;
4873 term_chunk_found=MagickFalse;
4874 mng_info->framing_mode=1;
4875 #if defined(MNG_INSERT_LAYERS)
4876 mandatory_back=MagickFalse;
4878 #if defined(MNG_INSERT_LAYERS)
4879 mng_background_color=image->background_color;
4881 default_fb=mng_info->frame;
4882 previous_fb=mng_info->frame;
4886 type[MaxTextExtent];
4888 if (LocaleCompare(image_info->magick,"MNG") == 0)
4897 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4898 length=ReadBlobMSBLong(image);
4899 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4901 if (logging != MagickFalse)
4902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4903 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4904 type[0],type[1],type[2],type[3],(double) length);
4906 if (length > PNG_UINT_31_MAX)
4910 ThrowReaderException(CorruptImageError,"CorruptImage");
4913 chunk=(unsigned char *) NULL;
4917 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4919 if (chunk == (unsigned char *) NULL)
4920 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4922 for (i=0; i < (ssize_t) length; i++)
4923 chunk[i]=(unsigned char) ReadBlobByte(image);
4928 (void) ReadBlobMSBLong(image); /* read crc word */
4930 #if !defined(JNG_SUPPORTED)
4931 if (memcmp(type,mng_JHDR,4) == 0)
4933 skip_to_iend=MagickTrue;
4935 if (mng_info->jhdr_warning == 0)
4936 (void) ThrowMagickException(exception,GetMagickModule(),
4937 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4939 mng_info->jhdr_warning++;
4942 if (memcmp(type,mng_DHDR,4) == 0)
4944 skip_to_iend=MagickTrue;
4946 if (mng_info->dhdr_warning == 0)
4947 (void) ThrowMagickException(exception,GetMagickModule(),
4948 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4950 mng_info->dhdr_warning++;
4952 if (memcmp(type,mng_MEND,4) == 0)
4957 if (memcmp(type,mng_IEND,4) == 0)
4958 skip_to_iend=MagickFalse;
4961 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4963 if (logging != MagickFalse)
4964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4970 if (memcmp(type,mng_MHDR,4) == 0)
4972 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4973 (p[2] << 8) | p[3]);
4975 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4976 (p[6] << 8) | p[7]);
4978 if (logging != MagickFalse)
4980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4981 " MNG width: %.20g",(double) mng_info->mng_width);
4982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4983 " MNG height: %.20g",(double) mng_info->mng_height);
4987 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4989 if (mng_info->ticks_per_second == 0)
4990 default_frame_delay=0;
4993 default_frame_delay=1UL*image->ticks_per_second/
4994 mng_info->ticks_per_second;
4996 frame_delay=default_frame_delay;
5002 simplicity=(size_t) mng_get_long(p);
5005 mng_type=1; /* Full MNG */
5007 if ((simplicity != 0) && ((simplicity | 11) == 11))
5008 mng_type=2; /* LC */
5010 if ((simplicity != 0) && ((simplicity | 9) == 9))
5011 mng_type=3; /* VLC */
5013 #if defined(MNG_INSERT_LAYERS)
5015 insert_layers=MagickTrue;
5017 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5019 /* Allocate next image structure. */
5020 AcquireNextImage(image_info,image,exception);
5022 if (GetNextImageInList(image) == (Image *) NULL)
5023 return((Image *) NULL);
5025 image=SyncNextImageInList(image);
5026 mng_info->image=image;
5029 if ((mng_info->mng_width > 65535L) ||
5030 (mng_info->mng_height > 65535L))
5031 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5033 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5034 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5035 mng_info->mng_height);
5037 mng_info->frame.left=0;
5038 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5039 mng_info->frame.top=0;
5040 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5041 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5043 for (i=0; i < MNG_MAX_OBJECTS; i++)
5044 mng_info->object_clip[i]=mng_info->frame;
5046 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5050 if (memcmp(type,mng_TERM,4) == 0)
5061 final_delay=(png_uint_32) mng_get_long(&p[2]);
5062 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5064 if (mng_iterations == PNG_UINT_31_MAX)
5067 image->iterations=mng_iterations;
5068 term_chunk_found=MagickTrue;
5071 if (logging != MagickFalse)
5073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5074 " repeat=%d",repeat);
5076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5077 " final_delay=%.20g",(double) final_delay);
5079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5080 " image->iterations=%.20g",(double) image->iterations);
5083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5086 if (memcmp(type,mng_DEFI,4) == 0)
5089 (void) ThrowMagickException(exception,GetMagickModule(),
5090 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5093 object_id=(p[0] << 8) | p[1];
5095 if (mng_type == 2 && object_id != 0)
5096 (void) ThrowMagickException(exception,GetMagickModule(),
5097 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5100 if (object_id > MNG_MAX_OBJECTS)
5103 Instead of using a warning we should allocate a larger
5104 MngInfo structure and continue.
5106 (void) ThrowMagickException(exception,GetMagickModule(),
5107 CoderError,"object id too large","`%s'",image->filename);
5108 object_id=MNG_MAX_OBJECTS;
5111 if (mng_info->exists[object_id])
5112 if (mng_info->frozen[object_id])
5114 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5115 (void) ThrowMagickException(exception,
5116 GetMagickModule(),CoderError,
5117 "DEFI cannot redefine a frozen MNG object","`%s'",
5122 mng_info->exists[object_id]=MagickTrue;
5125 mng_info->invisible[object_id]=p[2];
5128 Extract object offset info.
5132 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5133 (p[5] << 16) | (p[6] << 8) | p[7]);
5135 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5136 (p[9] << 16) | (p[10] << 8) | p[11]);
5138 if (logging != MagickFalse)
5140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5141 " x_off[%d]: %.20g",object_id,(double)
5142 mng_info->x_off[object_id]);
5144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5145 " y_off[%d]: %.20g",object_id,(double)
5146 mng_info->y_off[object_id]);
5151 Extract object clipping info.
5154 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5157 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5160 if (memcmp(type,mng_bKGD,4) == 0)
5162 mng_info->have_global_bkgd=MagickFalse;
5166 mng_info->mng_global_bkgd.red=
5167 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5169 mng_info->mng_global_bkgd.green=
5170 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5172 mng_info->mng_global_bkgd.blue=
5173 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5175 mng_info->have_global_bkgd=MagickTrue;
5178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5181 if (memcmp(type,mng_BACK,4) == 0)
5183 #if defined(MNG_INSERT_LAYERS)
5185 mandatory_back=p[6];
5190 if (mandatory_back && length > 5)
5192 mng_background_color.red=
5193 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5195 mng_background_color.green=
5196 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5198 mng_background_color.blue=
5199 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5201 mng_background_color.alpha=OpaqueAlpha;
5204 #ifdef MNG_OBJECT_BUFFERS
5206 mng_background_object=(p[7] << 8) | p[8];
5209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5213 if (memcmp(type,mng_PLTE,4) == 0)
5215 /* Read global PLTE. */
5217 if (length && (length < 769))
5219 if (mng_info->global_plte == (png_colorp) NULL)
5220 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5221 sizeof(*mng_info->global_plte));
5223 for (i=0; i < (ssize_t) (length/3); i++)
5225 mng_info->global_plte[i].red=p[3*i];
5226 mng_info->global_plte[i].green=p[3*i+1];
5227 mng_info->global_plte[i].blue=p[3*i+2];
5230 mng_info->global_plte_length=(unsigned int) (length/3);
5233 for ( ; i < 256; i++)
5235 mng_info->global_plte[i].red=i;
5236 mng_info->global_plte[i].green=i;
5237 mng_info->global_plte[i].blue=i;
5241 mng_info->global_plte_length=256;
5244 mng_info->global_plte_length=0;
5246 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5250 if (memcmp(type,mng_tRNS,4) == 0)
5252 /* read global tRNS */
5255 for (i=0; i < (ssize_t) length; i++)
5256 mng_info->global_trns[i]=p[i];
5259 for ( ; i < 256; i++)
5260 mng_info->global_trns[i]=255;
5262 mng_info->global_trns_length=(unsigned int) length;
5263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5266 if (memcmp(type,mng_gAMA,4) == 0)
5273 igamma=mng_get_long(p);
5274 mng_info->global_gamma=((float) igamma)*0.00001;
5275 mng_info->have_global_gama=MagickTrue;
5279 mng_info->have_global_gama=MagickFalse;
5281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5285 if (memcmp(type,mng_cHRM,4) == 0)
5287 /* Read global cHRM */
5291 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5292 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5293 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5294 mng_info->global_chrm.red_primary.y=0.00001*
5295 mng_get_long(&p[12]);
5296 mng_info->global_chrm.green_primary.x=0.00001*
5297 mng_get_long(&p[16]);
5298 mng_info->global_chrm.green_primary.y=0.00001*
5299 mng_get_long(&p[20]);
5300 mng_info->global_chrm.blue_primary.x=0.00001*
5301 mng_get_long(&p[24]);
5302 mng_info->global_chrm.blue_primary.y=0.00001*
5303 mng_get_long(&p[28]);
5304 mng_info->have_global_chrm=MagickTrue;
5307 mng_info->have_global_chrm=MagickFalse;
5309 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5313 if (memcmp(type,mng_sRGB,4) == 0)
5320 mng_info->global_srgb_intent=
5321 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5322 mng_info->have_global_srgb=MagickTrue;
5325 mng_info->have_global_srgb=MagickFalse;
5327 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5331 if (memcmp(type,mng_iCCP,4) == 0)
5339 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5344 if (memcmp(type,mng_FRAM,4) == 0)
5347 (void) ThrowMagickException(exception,GetMagickModule(),
5348 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5351 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5352 image->delay=frame_delay;
5354 frame_delay=default_frame_delay;
5355 frame_timeout=default_frame_timeout;
5360 mng_info->framing_mode=p[0];
5362 if (logging != MagickFalse)
5363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5364 " Framing_mode=%d",mng_info->framing_mode);
5368 /* Note the delay and frame clipping boundaries. */
5370 p++; /* framing mode */
5372 while (*p && ((p-chunk) < (ssize_t) length))
5373 p++; /* frame name */
5375 p++; /* frame name terminator */
5377 if ((p-chunk) < (ssize_t) (length-4))
5384 change_delay=(*p++);
5385 change_timeout=(*p++);
5386 change_clipping=(*p++);
5387 p++; /* change_sync */
5391 frame_delay=1UL*image->ticks_per_second*
5394 if (mng_info->ticks_per_second != 0)
5395 frame_delay/=mng_info->ticks_per_second;
5398 frame_delay=PNG_UINT_31_MAX;
5400 if (change_delay == 2)
5401 default_frame_delay=frame_delay;
5405 if (logging != MagickFalse)
5406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5407 " Framing_delay=%.20g",(double) frame_delay);
5412 frame_timeout=1UL*image->ticks_per_second*
5415 if (mng_info->ticks_per_second != 0)
5416 frame_timeout/=mng_info->ticks_per_second;
5419 frame_timeout=PNG_UINT_31_MAX;
5421 if (change_delay == 2)
5422 default_frame_timeout=frame_timeout;
5426 if (logging != MagickFalse)
5427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5428 " Framing_timeout=%.20g",(double) frame_timeout);
5431 if (change_clipping)
5433 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5437 if (logging != MagickFalse)
5438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5439 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5440 (double) fb.left,(double) fb.right,(double) fb.top,
5441 (double) fb.bottom);
5443 if (change_clipping == 2)
5449 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5451 subframe_width=(size_t) (mng_info->clip.right
5452 -mng_info->clip.left);
5454 subframe_height=(size_t) (mng_info->clip.bottom
5455 -mng_info->clip.top);
5457 Insert a background layer behind the frame if framing_mode is 4.
5459 #if defined(MNG_INSERT_LAYERS)
5460 if (logging != MagickFalse)
5461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5462 " subframe_width=%.20g, subframe_height=%.20g",(double)
5463 subframe_width,(double) subframe_height);
5465 if (insert_layers && (mng_info->framing_mode == 4) &&
5466 (subframe_width) && (subframe_height))
5468 /* Allocate next image structure. */
5469 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5471 AcquireNextImage(image_info,image,exception);
5473 if (GetNextImageInList(image) == (Image *) NULL)
5475 image=DestroyImageList(image);
5476 MngInfoFreeStruct(mng_info,&have_mng_structure);
5477 return((Image *) NULL);
5480 image=SyncNextImageInList(image);
5483 mng_info->image=image;
5485 if (term_chunk_found)
5487 image->start_loop=MagickTrue;
5488 image->iterations=mng_iterations;
5489 term_chunk_found=MagickFalse;
5493 image->start_loop=MagickFalse;
5495 image->columns=subframe_width;
5496 image->rows=subframe_height;
5497 image->page.width=subframe_width;
5498 image->page.height=subframe_height;
5499 image->page.x=mng_info->clip.left;
5500 image->page.y=mng_info->clip.top;
5501 image->background_color=mng_background_color;
5502 image->alpha_trait=UndefinedPixelTrait;
5504 (void) SetImageBackgroundColor(image,exception);
5506 if (logging != MagickFalse)
5507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5508 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5509 (double) mng_info->clip.left,(double) mng_info->clip.right,
5510 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5513 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5516 if (memcmp(type,mng_CLIP,4) == 0)
5525 first_object=(p[0] << 8) | p[1];
5526 last_object=(p[2] << 8) | p[3];
5528 for (i=(int) first_object; i <= (int) last_object; i++)
5530 if (mng_info->exists[i] && !mng_info->frozen[i])
5535 box=mng_info->object_clip[i];
5536 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5540 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5543 if (memcmp(type,mng_SAVE,4) == 0)
5545 for (i=1; i < MNG_MAX_OBJECTS; i++)
5546 if (mng_info->exists[i])
5548 mng_info->frozen[i]=MagickTrue;
5549 #ifdef MNG_OBJECT_BUFFERS
5550 if (mng_info->ob[i] != (MngBuffer *) NULL)
5551 mng_info->ob[i]->frozen=MagickTrue;
5556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5561 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5563 /* Read DISC or SEEK. */
5565 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5567 for (i=1; i < MNG_MAX_OBJECTS; i++)
5568 MngInfoDiscardObject(mng_info,i);
5576 for (j=0; j < (ssize_t) length; j+=2)
5578 i=p[j] << 8 | p[j+1];
5579 MngInfoDiscardObject(mng_info,i);
5584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5589 if (memcmp(type,mng_MOVE,4) == 0)
5597 first_object=(p[0] << 8) | p[1];
5598 last_object=(p[2] << 8) | p[3];
5599 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5601 if (mng_info->exists[i] && !mng_info->frozen[i])
5609 old_pair.a=mng_info->x_off[i];
5610 old_pair.b=mng_info->y_off[i];
5611 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5612 mng_info->x_off[i]=new_pair.a;
5613 mng_info->y_off[i]=new_pair.b;
5617 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5621 if (memcmp(type,mng_LOOP,4) == 0)
5623 ssize_t loop_iters=1;
5624 loop_level=chunk[0];
5625 mng_info->loop_active[loop_level]=1; /* mark loop active */
5627 /* Record starting point. */
5628 loop_iters=mng_get_long(&chunk[1]);
5630 if (logging != MagickFalse)
5631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5632 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5633 (double) loop_iters);
5635 if (loop_iters == 0)
5636 skipping_loop=loop_level;
5640 mng_info->loop_jump[loop_level]=TellBlob(image);
5641 mng_info->loop_count[loop_level]=loop_iters;
5644 mng_info->loop_iteration[loop_level]=0;
5645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5649 if (memcmp(type,mng_ENDL,4) == 0)
5651 loop_level=chunk[0];
5653 if (skipping_loop > 0)
5655 if (skipping_loop == loop_level)
5658 Found end of zero-iteration loop.
5661 mng_info->loop_active[loop_level]=0;
5667 if (mng_info->loop_active[loop_level] == 1)
5669 mng_info->loop_count[loop_level]--;
5670 mng_info->loop_iteration[loop_level]++;
5672 if (logging != MagickFalse)
5673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5674 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5675 (double) loop_level,(double)
5676 mng_info->loop_count[loop_level]);
5678 if (mng_info->loop_count[loop_level] != 0)
5680 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5684 ThrowReaderException(CorruptImageError,
5685 "ImproperImageHeader");
5696 mng_info->loop_active[loop_level]=0;
5698 for (i=0; i < loop_level; i++)
5699 if (mng_info->loop_active[i] == 1)
5700 last_level=(short) i;
5701 loop_level=last_level;
5706 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5710 if (memcmp(type,mng_CLON,4) == 0)
5712 if (mng_info->clon_warning == 0)
5713 (void) ThrowMagickException(exception,GetMagickModule(),
5714 CoderError,"CLON is not implemented yet","`%s'",
5717 mng_info->clon_warning++;
5720 if (memcmp(type,mng_MAGN,4) == 0)
5735 magn_first=(p[0] << 8) | p[1];
5741 magn_last=(p[2] << 8) | p[3];
5744 magn_last=magn_first;
5745 #ifndef MNG_OBJECT_BUFFERS
5746 if (magn_first || magn_last)
5747 if (mng_info->magn_warning == 0)
5749 (void) ThrowMagickException(exception,
5750 GetMagickModule(),CoderError,
5751 "MAGN is not implemented yet for nonzero objects",
5752 "`%s'",image->filename);
5754 mng_info->magn_warning++;
5764 magn_mx=(p[5] << 8) | p[6];
5773 magn_my=(p[7] << 8) | p[8];
5782 magn_ml=(p[9] << 8) | p[10];
5791 magn_mr=(p[11] << 8) | p[12];
5800 magn_mt=(p[13] << 8) | p[14];
5809 magn_mb=(p[15] << 8) | p[16];
5821 magn_methy=magn_methx;
5824 if (magn_methx > 5 || magn_methy > 5)
5825 if (mng_info->magn_warning == 0)
5827 (void) ThrowMagickException(exception,
5828 GetMagickModule(),CoderError,
5829 "Unknown MAGN method in MNG datastream","`%s'",
5832 mng_info->magn_warning++;
5834 #ifdef MNG_OBJECT_BUFFERS
5835 /* Magnify existing objects in the range magn_first to magn_last */
5837 if (magn_first == 0 || magn_last == 0)
5839 /* Save the magnification factors for object 0 */
5840 mng_info->magn_mb=magn_mb;
5841 mng_info->magn_ml=magn_ml;
5842 mng_info->magn_mr=magn_mr;
5843 mng_info->magn_mt=magn_mt;
5844 mng_info->magn_mx=magn_mx;
5845 mng_info->magn_my=magn_my;
5846 mng_info->magn_methx=magn_methx;
5847 mng_info->magn_methy=magn_methy;
5851 if (memcmp(type,mng_PAST,4) == 0)
5853 if (mng_info->past_warning == 0)
5854 (void) ThrowMagickException(exception,GetMagickModule(),
5855 CoderError,"PAST is not implemented yet","`%s'",
5858 mng_info->past_warning++;
5861 if (memcmp(type,mng_SHOW,4) == 0)
5863 if (mng_info->show_warning == 0)
5864 (void) ThrowMagickException(exception,GetMagickModule(),
5865 CoderError,"SHOW is not implemented yet","`%s'",
5868 mng_info->show_warning++;
5871 if (memcmp(type,mng_sBIT,4) == 0)
5874 mng_info->have_global_sbit=MagickFalse;
5878 mng_info->global_sbit.gray=p[0];
5879 mng_info->global_sbit.red=p[0];
5880 mng_info->global_sbit.green=p[1];
5881 mng_info->global_sbit.blue=p[2];
5882 mng_info->global_sbit.alpha=p[3];
5883 mng_info->have_global_sbit=MagickTrue;
5886 if (memcmp(type,mng_pHYs,4) == 0)
5890 mng_info->global_x_pixels_per_unit=
5891 (size_t) mng_get_long(p);
5892 mng_info->global_y_pixels_per_unit=
5893 (size_t) mng_get_long(&p[4]);
5894 mng_info->global_phys_unit_type=p[8];
5895 mng_info->have_global_phys=MagickTrue;
5899 mng_info->have_global_phys=MagickFalse;
5901 if (memcmp(type,mng_pHYg,4) == 0)
5903 if (mng_info->phyg_warning == 0)
5904 (void) ThrowMagickException(exception,GetMagickModule(),
5905 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5907 mng_info->phyg_warning++;
5909 if (memcmp(type,mng_BASI,4) == 0)
5911 skip_to_iend=MagickTrue;
5913 if (mng_info->basi_warning == 0)
5914 (void) ThrowMagickException(exception,GetMagickModule(),
5915 CoderError,"BASI is not implemented yet","`%s'",
5918 mng_info->basi_warning++;
5919 #ifdef MNG_BASI_SUPPORTED
5920 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5921 (p[2] << 8) | p[3]);
5922 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5923 (p[6] << 8) | p[7]);
5924 basi_color_type=p[8];
5925 basi_compression_method=p[9];
5926 basi_filter_type=p[10];
5927 basi_interlace_method=p[11];
5929 basi_red=(p[12] << 8) & p[13];
5935 basi_green=(p[14] << 8) & p[15];
5941 basi_blue=(p[16] << 8) & p[17];
5947 basi_alpha=(p[18] << 8) & p[19];
5951 if (basi_sample_depth == 16)
5958 basi_viewable=p[20];
5964 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5968 if (memcmp(type,mng_IHDR,4)
5969 #if defined(JNG_SUPPORTED)
5970 && memcmp(type,mng_JHDR,4)
5974 /* Not an IHDR or JHDR chunk */
5976 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5981 if (logging != MagickFalse)
5982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5983 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5985 mng_info->exists[object_id]=MagickTrue;
5986 mng_info->viewable[object_id]=MagickTrue;
5988 if (mng_info->invisible[object_id])
5990 if (logging != MagickFalse)
5991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5992 " Skipping invisible object");
5994 skip_to_iend=MagickTrue;
5995 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5998 #if defined(MNG_INSERT_LAYERS)
6000 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6002 image_width=(size_t) mng_get_long(p);
6003 image_height=(size_t) mng_get_long(&p[4]);
6005 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6008 Insert a transparent background layer behind the entire animation
6009 if it is not full screen.
6011 #if defined(MNG_INSERT_LAYERS)
6012 if (insert_layers && mng_type && first_mng_object)
6014 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6015 (image_width < mng_info->mng_width) ||
6016 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6017 (image_height < mng_info->mng_height) ||
6018 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6020 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6023 Allocate next image structure.
6025 AcquireNextImage(image_info,image,exception);
6027 if (GetNextImageInList(image) == (Image *) NULL)
6029 image=DestroyImageList(image);
6030 MngInfoFreeStruct(mng_info,&have_mng_structure);
6031 return((Image *) NULL);
6034 image=SyncNextImageInList(image);
6036 mng_info->image=image;
6038 if (term_chunk_found)
6040 image->start_loop=MagickTrue;
6041 image->iterations=mng_iterations;
6042 term_chunk_found=MagickFalse;
6046 image->start_loop=MagickFalse;
6048 /* Make a background rectangle. */
6051 image->columns=mng_info->mng_width;
6052 image->rows=mng_info->mng_height;
6053 image->page.width=mng_info->mng_width;
6054 image->page.height=mng_info->mng_height;
6057 image->background_color=mng_background_color;
6058 (void) SetImageBackgroundColor(image,exception);
6059 if (logging != MagickFalse)
6060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6061 " Inserted transparent background layer, W=%.20g, H=%.20g",
6062 (double) mng_info->mng_width,(double) mng_info->mng_height);
6066 Insert a background layer behind the upcoming image if
6067 framing_mode is 3, and we haven't already inserted one.
6069 if (insert_layers && (mng_info->framing_mode == 3) &&
6070 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6071 (simplicity & 0x08)))
6073 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6076 Allocate next image structure.
6078 AcquireNextImage(image_info,image,exception);
6080 if (GetNextImageInList(image) == (Image *) NULL)
6082 image=DestroyImageList(image);
6083 MngInfoFreeStruct(mng_info,&have_mng_structure);
6084 return((Image *) NULL);
6087 image=SyncNextImageInList(image);
6090 mng_info->image=image;
6092 if (term_chunk_found)
6094 image->start_loop=MagickTrue;
6095 image->iterations=mng_iterations;
6096 term_chunk_found=MagickFalse;
6100 image->start_loop=MagickFalse;
6103 image->columns=subframe_width;
6104 image->rows=subframe_height;
6105 image->page.width=subframe_width;
6106 image->page.height=subframe_height;
6107 image->page.x=mng_info->clip.left;
6108 image->page.y=mng_info->clip.top;
6109 image->background_color=mng_background_color;
6110 image->alpha_trait=UndefinedPixelTrait;
6111 (void) SetImageBackgroundColor(image,exception);
6113 if (logging != MagickFalse)
6114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6115 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6116 (double) mng_info->clip.left,(double) mng_info->clip.right,
6117 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6119 #endif /* MNG_INSERT_LAYERS */
6120 first_mng_object=MagickFalse;
6122 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6125 Allocate next image structure.
6127 AcquireNextImage(image_info,image,exception);
6129 if (GetNextImageInList(image) == (Image *) NULL)
6131 image=DestroyImageList(image);
6132 MngInfoFreeStruct(mng_info,&have_mng_structure);
6133 return((Image *) NULL);
6136 image=SyncNextImageInList(image);
6138 mng_info->image=image;
6139 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6140 GetBlobSize(image));
6142 if (status == MagickFalse)
6145 if (term_chunk_found)
6147 image->start_loop=MagickTrue;
6148 term_chunk_found=MagickFalse;
6152 image->start_loop=MagickFalse;
6154 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6156 image->delay=frame_delay;
6157 frame_delay=default_frame_delay;
6163 image->page.width=mng_info->mng_width;
6164 image->page.height=mng_info->mng_height;
6165 image->page.x=mng_info->x_off[object_id];
6166 image->page.y=mng_info->y_off[object_id];
6167 image->iterations=mng_iterations;
6170 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6173 if (logging != MagickFalse)
6174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6175 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6178 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6181 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6185 mng_info->image=image;
6186 mng_info->mng_type=mng_type;
6187 mng_info->object_id=object_id;
6189 if (memcmp(type,mng_IHDR,4) == 0)
6190 image=ReadOnePNGImage(mng_info,image_info,exception);
6192 #if defined(JNG_SUPPORTED)
6194 image=ReadOneJNGImage(mng_info,image_info,exception);
6197 if (image == (Image *) NULL)
6199 if (IsImageObject(previous) != MagickFalse)
6201 (void) DestroyImageList(previous);
6202 (void) CloseBlob(previous);
6205 MngInfoFreeStruct(mng_info,&have_mng_structure);
6206 return((Image *) NULL);
6209 if (image->columns == 0 || image->rows == 0)
6211 (void) CloseBlob(image);
6212 image=DestroyImageList(image);
6213 MngInfoFreeStruct(mng_info,&have_mng_structure);
6214 return((Image *) NULL);
6217 mng_info->image=image;
6224 if (mng_info->magn_methx || mng_info->magn_methy)
6230 if (logging != MagickFalse)
6231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6232 " Processing MNG MAGN chunk");
6234 if (mng_info->magn_methx == 1)
6236 magnified_width=mng_info->magn_ml;
6238 if (image->columns > 1)
6239 magnified_width += mng_info->magn_mr;
6241 if (image->columns > 2)
6242 magnified_width += (png_uint_32)
6243 ((image->columns-2)*(mng_info->magn_mx));
6248 magnified_width=(png_uint_32) image->columns;
6250 if (image->columns > 1)
6251 magnified_width += mng_info->magn_ml-1;
6253 if (image->columns > 2)
6254 magnified_width += mng_info->magn_mr-1;
6256 if (image->columns > 3)
6257 magnified_width += (png_uint_32)
6258 ((image->columns-3)*(mng_info->magn_mx-1));
6261 if (mng_info->magn_methy == 1)
6263 magnified_height=mng_info->magn_mt;
6265 if (image->rows > 1)
6266 magnified_height += mng_info->magn_mb;
6268 if (image->rows > 2)
6269 magnified_height += (png_uint_32)
6270 ((image->rows-2)*(mng_info->magn_my));
6275 magnified_height=(png_uint_32) image->rows;
6277 if (image->rows > 1)
6278 magnified_height += mng_info->magn_mt-1;
6280 if (image->rows > 2)
6281 magnified_height += mng_info->magn_mb-1;
6283 if (image->rows > 3)
6284 magnified_height += (png_uint_32)
6285 ((image->rows-3)*(mng_info->magn_my-1));
6288 if (magnified_height > image->rows ||
6289 magnified_width > image->columns)
6316 /* Allocate next image structure. */
6318 if (logging != MagickFalse)
6319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6320 " Allocate magnified image");
6322 AcquireNextImage(image_info,image,exception);
6324 if (GetNextImageInList(image) == (Image *) NULL)
6326 image=DestroyImageList(image);
6327 MngInfoFreeStruct(mng_info,&have_mng_structure);
6328 return((Image *) NULL);
6331 large_image=SyncNextImageInList(image);
6333 large_image->columns=magnified_width;
6334 large_image->rows=magnified_height;
6336 magn_methx=mng_info->magn_methx;
6337 magn_methy=mng_info->magn_methy;
6339 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6340 #define QM unsigned short
6341 if (magn_methx != 1 || magn_methy != 1)
6344 Scale pixels to unsigned shorts to prevent
6345 overflow of intermediate values of interpolations
6347 for (y=0; y < (ssize_t) image->rows; y++)
6349 q=GetAuthenticPixels(image,0,y,image->columns,1,
6352 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6354 SetPixelRed(image,ScaleQuantumToShort(
6355 GetPixelRed(image,q)),q);
6356 SetPixelGreen(image,ScaleQuantumToShort(
6357 GetPixelGreen(image,q)),q);
6358 SetPixelBlue(image,ScaleQuantumToShort(
6359 GetPixelBlue(image,q)),q);
6360 SetPixelAlpha(image,ScaleQuantumToShort(
6361 GetPixelAlpha(image,q)),q);
6362 q+=GetPixelChannels(image);
6365 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6373 if (image->alpha_trait == BlendPixelTrait)
6374 (void) SetImageBackgroundColor(large_image,exception);
6378 large_image->background_color.alpha=OpaqueAlpha;
6379 (void) SetImageBackgroundColor(large_image,exception);
6381 if (magn_methx == 4)
6384 if (magn_methx == 5)
6387 if (magn_methy == 4)
6390 if (magn_methy == 5)
6394 /* magnify the rows into the right side of the large image */
6396 if (logging != MagickFalse)
6397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6398 " Magnify the rows to %.20g",(double) large_image->rows);
6399 m=(ssize_t) mng_info->magn_mt;
6401 length=(size_t) image->columns*GetPixelChannels(image);
6402 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6403 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6405 if ((prev == (Quantum *) NULL) ||
6406 (next == (Quantum *) NULL))
6408 image=DestroyImageList(image);
6409 MngInfoFreeStruct(mng_info,&have_mng_structure);
6410 ThrowReaderException(ResourceLimitError,
6411 "MemoryAllocationFailed");
6414 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6415 (void) CopyMagickMemory(next,n,length);
6417 for (y=0; y < (ssize_t) image->rows; y++)
6420 m=(ssize_t) mng_info->magn_mt;
6422 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6423 m=(ssize_t) mng_info->magn_mb;
6425 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6426 m=(ssize_t) mng_info->magn_mb;
6428 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6432 m=(ssize_t) mng_info->magn_my;
6438 if (y < (ssize_t) image->rows-1)
6440 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6442 (void) CopyMagickMemory(next,n,length);
6445 for (i=0; i < m; i++, yy++)
6450 assert(yy < (ssize_t) large_image->rows);
6453 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6455 q+=(large_image->columns-image->columns)*
6456 GetPixelChannels(large_image);
6458 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6460 /* To do: get color as function of indexes[x] */
6462 if (image->storage_class == PseudoClass)
6467 if (magn_methy <= 1)
6469 /* replicate previous */
6470 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6471 SetPixelGreen(large_image,GetPixelGreen(image,
6473 SetPixelBlue(large_image,GetPixelBlue(image,
6475 SetPixelAlpha(large_image,GetPixelAlpha(image,
6479 else if (magn_methy == 2 || magn_methy == 4)
6483 SetPixelRed(large_image,GetPixelRed(image,
6485 SetPixelGreen(large_image,GetPixelGreen(image,
6487 SetPixelBlue(large_image,GetPixelBlue(image,
6489 SetPixelAlpha(large_image,GetPixelAlpha(image,
6496 SetPixelRed(large_image,((QM) (((ssize_t)
6497 (2*i*(GetPixelRed(image,n)
6498 -GetPixelRed(image,pixels)+m))/
6500 +GetPixelRed(image,pixels)))),q);
6501 SetPixelGreen(large_image,((QM) (((ssize_t)
6502 (2*i*(GetPixelGreen(image,n)
6503 -GetPixelGreen(image,pixels)+m))/
6505 +GetPixelGreen(image,pixels)))),q);
6506 SetPixelBlue(large_image,((QM) (((ssize_t)
6507 (2*i*(GetPixelBlue(image,n)
6508 -GetPixelBlue(image,pixels)+m))/
6510 +GetPixelBlue(image,pixels)))),q);
6512 if (image->alpha_trait == BlendPixelTrait)
6513 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6514 (2*i*(GetPixelAlpha(image,n)
6515 -GetPixelAlpha(image,pixels)+m))
6517 GetPixelAlpha(image,pixels)))),q);
6520 if (magn_methy == 4)
6522 /* Replicate nearest */
6523 if (i <= ((m+1) << 1))
6524 SetPixelAlpha(large_image,GetPixelAlpha(image,
6527 SetPixelAlpha(large_image,GetPixelAlpha(image,
6532 else /* if (magn_methy == 3 || magn_methy == 5) */
6534 /* Replicate nearest */
6535 if (i <= ((m+1) << 1))
6537 SetPixelRed(large_image,GetPixelRed(image,
6539 SetPixelGreen(large_image,GetPixelGreen(image,
6541 SetPixelBlue(large_image,GetPixelBlue(image,
6543 SetPixelAlpha(large_image,GetPixelAlpha(image,
6549 SetPixelRed(large_image,GetPixelRed(image,n),q);
6550 SetPixelGreen(large_image,GetPixelGreen(image,n),
6552 SetPixelBlue(large_image,GetPixelBlue(image,n),
6554 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6558 if (magn_methy == 5)
6560 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6561 (GetPixelAlpha(image,n)
6562 -GetPixelAlpha(image,pixels))
6563 +m))/((ssize_t) (m*2))
6564 +GetPixelAlpha(image,pixels)),q);
6567 n+=GetPixelChannels(image);
6568 q+=GetPixelChannels(large_image);
6569 pixels+=GetPixelChannels(image);
6572 if (SyncAuthenticPixels(large_image,exception) == 0)
6578 prev=(Quantum *) RelinquishMagickMemory(prev);
6579 next=(Quantum *) RelinquishMagickMemory(next);
6581 length=image->columns;
6583 if (logging != MagickFalse)
6584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6585 " Delete original image");
6587 DeleteImageFromList(&image);
6591 mng_info->image=image;
6593 /* magnify the columns */
6594 if (logging != MagickFalse)
6595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6596 " Magnify the columns to %.20g",(double) image->columns);
6598 for (y=0; y < (ssize_t) image->rows; y++)
6603 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6604 pixels=q+(image->columns-length)*GetPixelChannels(image);
6605 n=pixels+GetPixelChannels(image);
6607 for (x=(ssize_t) (image->columns-length);
6608 x < (ssize_t) image->columns; x++)
6610 /* To do: Rewrite using Get/Set***PixelChannel() */
6612 if (x == (ssize_t) (image->columns-length))
6613 m=(ssize_t) mng_info->magn_ml;
6615 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6616 m=(ssize_t) mng_info->magn_mr;
6618 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6619 m=(ssize_t) mng_info->magn_mr;
6621 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6625 m=(ssize_t) mng_info->magn_mx;
6627 for (i=0; i < m; i++)
6629 if (magn_methx <= 1)
6631 /* replicate previous */
6632 SetPixelRed(image,GetPixelRed(image,pixels),q);
6633 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6634 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6635 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6638 else if (magn_methx == 2 || magn_methx == 4)
6642 SetPixelRed(image,GetPixelRed(image,pixels),q);
6643 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6644 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6645 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6648 /* To do: Rewrite using Get/Set***PixelChannel() */
6652 SetPixelRed(image,(QM) ((2*i*(
6653 GetPixelRed(image,n)
6654 -GetPixelRed(image,pixels))+m)
6656 GetPixelRed(image,pixels)),q);
6658 SetPixelGreen(image,(QM) ((2*i*(
6659 GetPixelGreen(image,n)
6660 -GetPixelGreen(image,pixels))+m)
6662 GetPixelGreen(image,pixels)),q);
6664 SetPixelBlue(image,(QM) ((2*i*(
6665 GetPixelBlue(image,n)
6666 -GetPixelBlue(image,pixels))+m)
6668 GetPixelBlue(image,pixels)),q);
6669 if (image->alpha_trait == BlendPixelTrait)
6670 SetPixelAlpha(image,(QM) ((2*i*(
6671 GetPixelAlpha(image,n)
6672 -GetPixelAlpha(image,pixels))+m)
6674 GetPixelAlpha(image,pixels)),q);
6677 if (magn_methx == 4)
6679 /* Replicate nearest */
6680 if (i <= ((m+1) << 1))
6682 SetPixelAlpha(image,
6683 GetPixelAlpha(image,pixels)+0,q);
6687 SetPixelAlpha(image,
6688 GetPixelAlpha(image,n)+0,q);
6693 else /* if (magn_methx == 3 || magn_methx == 5) */
6695 /* Replicate nearest */
6696 if (i <= ((m+1) << 1))
6698 SetPixelRed(image,GetPixelRed(image,pixels),q);
6699 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6700 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6701 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6706 SetPixelRed(image,GetPixelRed(image,n),q);
6707 SetPixelGreen(image,GetPixelGreen(image,n),q);
6708 SetPixelBlue(image,GetPixelBlue(image,n),q);
6709 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6712 if (magn_methx == 5)
6715 SetPixelAlpha(image,
6716 (QM) ((2*i*( GetPixelAlpha(image,n)
6717 -GetPixelAlpha(image,pixels))+m)/
6719 +GetPixelAlpha(image,pixels)),q);
6722 q+=GetPixelChannels(image);
6724 n+=GetPixelChannels(image);
6725 p+=GetPixelChannels(image);
6728 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6731 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6732 if (magn_methx != 1 || magn_methy != 1)
6735 Rescale pixels to Quantum
6737 for (y=0; y < (ssize_t) image->rows; y++)
6739 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6741 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6743 SetPixelRed(image,ScaleShortToQuantum(
6744 GetPixelRed(image,q)),q);
6745 SetPixelGreen(image,ScaleShortToQuantum(
6746 GetPixelGreen(image,q)),q);
6747 SetPixelBlue(image,ScaleShortToQuantum(
6748 GetPixelBlue(image,q)),q);
6749 SetPixelAlpha(image,ScaleShortToQuantum(
6750 GetPixelAlpha(image,q)),q);
6751 q+=GetPixelChannels(image);
6754 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6759 if (logging != MagickFalse)
6760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6761 " Finished MAGN processing");
6766 Crop_box is with respect to the upper left corner of the MNG.
6768 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6769 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6770 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6771 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6772 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6773 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6774 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6775 if ((crop_box.left != (mng_info->image_box.left
6776 +mng_info->x_off[object_id])) ||
6777 (crop_box.right != (mng_info->image_box.right
6778 +mng_info->x_off[object_id])) ||
6779 (crop_box.top != (mng_info->image_box.top
6780 +mng_info->y_off[object_id])) ||
6781 (crop_box.bottom != (mng_info->image_box.bottom
6782 +mng_info->y_off[object_id])))
6784 if (logging != MagickFalse)
6785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6786 " Crop the PNG image");
6788 if ((crop_box.left < crop_box.right) &&
6789 (crop_box.top < crop_box.bottom))
6798 Crop_info is with respect to the upper left corner of
6801 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6802 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6803 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6804 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6805 image->page.width=image->columns;
6806 image->page.height=image->rows;
6809 im=CropImage(image,&crop_info,exception);
6811 if (im != (Image *) NULL)
6813 image->columns=im->columns;
6814 image->rows=im->rows;
6815 im=DestroyImage(im);
6816 image->page.width=image->columns;
6817 image->page.height=image->rows;
6818 image->page.x=crop_box.left;
6819 image->page.y=crop_box.top;
6826 No pixels in crop area. The MNG spec still requires
6827 a layer, though, so make a single transparent pixel in
6828 the top left corner.
6833 (void) SetImageBackgroundColor(image,exception);
6834 image->page.width=1;
6835 image->page.height=1;
6840 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6841 image=mng_info->image;
6845 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6846 /* PNG does not handle depths greater than 16 so reduce it even
6849 if (image->depth > 16)
6853 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6854 if (image->depth > 8)
6856 /* To do: fill low byte properly */
6860 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6864 if (image_info->number_scenes != 0)
6866 if (mng_info->scenes_found >
6867 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6871 if (logging != MagickFalse)
6872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6873 " Finished reading image datastream.");
6875 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6877 (void) CloseBlob(image);
6879 if (logging != MagickFalse)
6880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " Finished reading all image datastreams.");
6883 #if defined(MNG_INSERT_LAYERS)
6884 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6885 (mng_info->mng_height))
6888 Insert a background layer if nothing else was found.
6890 if (logging != MagickFalse)
6891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6892 " No images found. Inserting a background layer.");
6894 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6897 Allocate next image structure.
6899 AcquireNextImage(image_info,image,exception);
6900 if (GetNextImageInList(image) == (Image *) NULL)
6902 image=DestroyImageList(image);
6903 MngInfoFreeStruct(mng_info,&have_mng_structure);
6905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6907 " Allocation failed, returning NULL.");
6909 return((Image *) NULL);
6911 image=SyncNextImageInList(image);
6913 image->columns=mng_info->mng_width;
6914 image->rows=mng_info->mng_height;
6915 image->page.width=mng_info->mng_width;
6916 image->page.height=mng_info->mng_height;
6919 image->background_color=mng_background_color;
6920 image->alpha_trait=UndefinedPixelTrait;
6922 if (image_info->ping == MagickFalse)
6923 (void) SetImageBackgroundColor(image,exception);
6925 mng_info->image_found++;
6928 image->iterations=mng_iterations;
6930 if (mng_iterations == 1)
6931 image->start_loop=MagickTrue;
6933 while (GetPreviousImageInList(image) != (Image *) NULL)
6936 if (image_count > 10*mng_info->image_found)
6938 if (logging != MagickFalse)
6939 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6941 (void) ThrowMagickException(exception,GetMagickModule(),
6942 CoderError,"Linked list is corrupted, beginning of list not found",
6943 "`%s'",image_info->filename);
6945 return((Image *) NULL);
6948 image=GetPreviousImageInList(image);
6950 if (GetNextImageInList(image) == (Image *) NULL)
6952 if (logging != MagickFalse)
6953 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6955 (void) ThrowMagickException(exception,GetMagickModule(),
6956 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6957 image_info->filename);
6961 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6962 GetNextImageInList(image) ==
6965 if (logging != MagickFalse)
6966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6967 " First image null");
6969 (void) ThrowMagickException(exception,GetMagickModule(),
6970 CoderError,"image->next for first image is NULL but shouldn't be.",
6971 "`%s'",image_info->filename);
6974 if (mng_info->image_found == 0)
6976 if (logging != MagickFalse)
6977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6978 " No visible images found.");
6980 (void) ThrowMagickException(exception,GetMagickModule(),
6981 CoderError,"No visible images in file","`%s'",image_info->filename);
6983 if (image != (Image *) NULL)
6984 image=DestroyImageList(image);
6986 MngInfoFreeStruct(mng_info,&have_mng_structure);
6987 return((Image *) NULL);
6990 if (mng_info->ticks_per_second)
6991 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6992 final_delay/mng_info->ticks_per_second;
6995 image->start_loop=MagickTrue;
6997 /* Find final nonzero image delay */
6998 final_image_delay=0;
7000 while (GetNextImageInList(image) != (Image *) NULL)
7003 final_image_delay=image->delay;
7005 image=GetNextImageInList(image);
7008 if (final_delay < final_image_delay)
7009 final_delay=final_image_delay;
7011 image->delay=final_delay;
7013 if (logging != MagickFalse)
7014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7015 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7016 (double) final_delay);
7018 if (logging != MagickFalse)
7024 image=GetFirstImageInList(image);
7026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7027 " Before coalesce:");
7029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7030 " scene 0 delay=%.20g",(double) image->delay);
7032 while (GetNextImageInList(image) != (Image *) NULL)
7034 image=GetNextImageInList(image);
7035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7036 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7040 image=GetFirstImageInList(image);
7041 #ifdef MNG_COALESCE_LAYERS
7051 if (logging != MagickFalse)
7052 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7055 next_image=CoalesceImages(image,exception);
7057 if (next_image == (Image *) NULL)
7058 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7060 image=DestroyImageList(image);
7063 for (next=image; next != (Image *) NULL; next=next_image)
7065 next->page.width=mng_info->mng_width;
7066 next->page.height=mng_info->mng_height;
7069 next->scene=scene++;
7070 next_image=GetNextImageInList(next);
7072 if (next_image == (Image *) NULL)
7075 if (next->delay == 0)
7078 next_image->previous=GetPreviousImageInList(next);
7079 if (GetPreviousImageInList(next) == (Image *) NULL)
7082 next->previous->next=next_image;
7083 next=DestroyImage(next);
7089 while (GetNextImageInList(image) != (Image *) NULL)
7090 image=GetNextImageInList(image);
7092 image->dispose=BackgroundDispose;
7094 if (logging != MagickFalse)
7100 image=GetFirstImageInList(image);
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " After coalesce:");
7105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7106 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7107 (double) image->dispose);
7109 while (GetNextImageInList(image) != (Image *) NULL)
7111 image=GetNextImageInList(image);
7113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7114 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7115 (double) image->delay,(double) image->dispose);
7119 image=GetFirstImageInList(image);
7120 MngInfoFreeStruct(mng_info,&have_mng_structure);
7121 have_mng_structure=MagickFalse;
7123 if (logging != MagickFalse)
7124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7126 return(GetFirstImageInList(image));
7128 #else /* PNG_LIBPNG_VER > 10011 */
7129 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7131 printf("Your PNG library is too old: You have libpng-%s\n",
7132 PNG_LIBPNG_VER_STRING);
7134 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7135 "PNG library is too old","`%s'",image_info->filename);
7137 return(Image *) NULL;
7140 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7142 return(ReadPNGImage(image_info,exception));
7144 #endif /* PNG_LIBPNG_VER > 10011 */
7148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7152 % R e g i s t e r P N G I m a g e %
7156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7158 % RegisterPNGImage() adds properties for the PNG image format to
7159 % the list of supported formats. The properties include the image format
7160 % tag, a method to read and/or write the format, whether the format
7161 % supports the saving of more than one frame to the same file or blob,
7162 % whether the format supports native in-memory I/O, and a brief
7163 % description of the format.
7165 % The format of the RegisterPNGImage method is:
7167 % size_t RegisterPNGImage(void)
7170 ModuleExport size_t RegisterPNGImage(void)
7173 version[MaxTextExtent];
7181 "See http://www.libpng.org/ for details about the PNG format."
7186 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7192 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7198 #if defined(PNG_LIBPNG_VER_STRING)
7199 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7200 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7202 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7204 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7205 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7210 entry=SetMagickInfo("MNG");
7211 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7213 #if defined(MAGICKCORE_PNG_DELEGATE)
7214 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7215 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7218 entry->magick=(IsImageFormatHandler *) IsMNG;
7219 entry->description=ConstantString("Multiple-image Network Graphics");
7221 if (*version != '\0')
7222 entry->version=ConstantString(version);
7224 entry->module=ConstantString("PNG");
7225 entry->note=ConstantString(MNGNote);
7226 (void) RegisterMagickInfo(entry);
7228 entry=SetMagickInfo("PNG");
7230 #if defined(MAGICKCORE_PNG_DELEGATE)
7231 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7232 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7235 entry->magick=(IsImageFormatHandler *) IsPNG;
7236 entry->adjoin=MagickFalse;
7237 entry->description=ConstantString("Portable Network Graphics");
7238 entry->module=ConstantString("PNG");
7240 if (*version != '\0')
7241 entry->version=ConstantString(version);
7243 entry->note=ConstantString(PNGNote);
7244 (void) RegisterMagickInfo(entry);
7246 entry=SetMagickInfo("PNG8");
7248 #if defined(MAGICKCORE_PNG_DELEGATE)
7249 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7250 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7253 entry->magick=(IsImageFormatHandler *) IsPNG;
7254 entry->adjoin=MagickFalse;
7255 entry->description=ConstantString(
7256 "8-bit indexed with optional binary transparency");
7257 entry->module=ConstantString("PNG");
7258 (void) RegisterMagickInfo(entry);
7260 entry=SetMagickInfo("PNG24");
7263 #if defined(ZLIB_VERSION)
7264 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7265 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7267 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7269 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7270 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7274 if (*version != '\0')
7275 entry->version=ConstantString(version);
7277 #if defined(MAGICKCORE_PNG_DELEGATE)
7278 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7279 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7282 entry->magick=(IsImageFormatHandler *) IsPNG;
7283 entry->adjoin=MagickFalse;
7284 entry->description=ConstantString("opaque 24-bit RGB");
7285 entry->module=ConstantString("PNG");
7286 (void) RegisterMagickInfo(entry);
7288 entry=SetMagickInfo("PNG32");
7290 #if defined(MAGICKCORE_PNG_DELEGATE)
7291 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7292 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7295 entry->magick=(IsImageFormatHandler *) IsPNG;
7296 entry->adjoin=MagickFalse;
7297 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7298 entry->module=ConstantString("PNG");
7299 (void) RegisterMagickInfo(entry);
7301 entry=SetMagickInfo("JNG");
7303 #if defined(JNG_SUPPORTED)
7304 #if defined(MAGICKCORE_PNG_DELEGATE)
7305 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7306 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7310 entry->magick=(IsImageFormatHandler *) IsJNG;
7311 entry->adjoin=MagickFalse;
7312 entry->description=ConstantString("JPEG Network Graphics");
7313 entry->module=ConstantString("PNG");
7314 entry->note=ConstantString(JNGNote);
7315 (void) RegisterMagickInfo(entry);
7317 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7318 ping_semaphore=AllocateSemaphoreInfo();
7321 return(MagickImageCoderSignature);
7325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7329 % U n r e g i s t e r P N G I m a g e %
7333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7335 % UnregisterPNGImage() removes format registrations made by the
7336 % PNG module from the list of supported formats.
7338 % The format of the UnregisterPNGImage method is:
7340 % UnregisterPNGImage(void)
7343 ModuleExport void UnregisterPNGImage(void)
7345 (void) UnregisterMagickInfo("MNG");
7346 (void) UnregisterMagickInfo("PNG");
7347 (void) UnregisterMagickInfo("PNG8");
7348 (void) UnregisterMagickInfo("PNG24");
7349 (void) UnregisterMagickInfo("PNG32");
7350 (void) UnregisterMagickInfo("JNG");
7352 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7353 if (ping_semaphore != (SemaphoreInfo *) NULL)
7354 DestroySemaphoreInfo(&ping_semaphore);
7358 #if defined(MAGICKCORE_PNG_DELEGATE)
7359 #if PNG_LIBPNG_VER > 10011
7361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7365 % W r i t e M N G I m a g e %
7369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7371 % WriteMNGImage() writes an image in the Portable Network Graphics
7372 % Group's "Multiple-image Network Graphics" encoded image format.
7374 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7376 % The format of the WriteMNGImage method is:
7378 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7379 % Image *image,ExceptionInfo *exception)
7381 % A description of each parameter follows.
7383 % o image_info: the image info.
7385 % o image: The image.
7387 % o exception: return any errors or warnings in this structure.
7389 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7390 % "To do" under ReadPNGImage):
7392 % Preserve all unknown and not-yet-handled known chunks found in input
7393 % PNG file and copy them into output PNG files according to the PNG
7396 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7398 % Improve selection of color type (use indexed-colour or indexed-colour
7399 % with tRNS when 256 or fewer unique RGBA values are present).
7401 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7402 % This will be complicated if we limit ourselves to generating MNG-LC
7403 % files. For now we ignore disposal method 3 and simply overlay the next
7406 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7407 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7408 % [mostly done 15 June 1999 but still need to take care of tRNS]
7410 % Check for identical sRGB and replace with a global sRGB (and remove
7411 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7412 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7413 % local gAMA/cHRM with local sRGB if appropriate).
7415 % Check for identical sBIT chunks and write global ones.
7417 % Provide option to skip writing the signature tEXt chunks.
7419 % Use signatures to detect identical objects and reuse the first
7420 % instance of such objects instead of writing duplicate objects.
7422 % Use a smaller-than-32k value of compression window size when
7425 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7426 % ancillary text chunks and save profiles.
7428 % Provide an option to force LC files (to ensure exact framing rate)
7431 % Provide an option to force VLC files instead of LC, even when offsets
7432 % are present. This will involve expanding the embedded images with a
7433 % transparent region at the top and/or left.
7437 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7438 png_info *ping_info, unsigned char *profile_type, unsigned char
7439 *profile_description, unsigned char *profile_data, png_uint_32 length)
7458 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7460 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7463 if (image_info->verbose)
7465 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7466 (char *) profile_type, (double) length);
7469 #if PNG_LIBPNG_VER >= 14000
7470 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7472 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7474 description_length=(png_uint_32) strlen((const char *) profile_description);
7475 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7476 + description_length);
7477 #if PNG_LIBPNG_VER >= 14000
7478 text[0].text=(png_charp) png_malloc(ping,
7479 (png_alloc_size_t) allocated_length);
7480 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7482 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7483 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7485 text[0].key[0]='\0';
7486 (void) ConcatenateMagickString(text[0].key,
7487 "Raw profile type ",MaxTextExtent);
7488 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7492 (void) CopyMagickString(dp,(const char *) profile_description,
7494 dp+=description_length;
7496 (void) FormatLocaleString(dp,allocated_length-
7497 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7500 for (i=0; i < (ssize_t) length; i++)
7504 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7505 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7510 text[0].text_length=(png_size_t) (dp-text[0].text);
7511 text[0].compression=image_info->compression == NoCompression ||
7512 (image_info->compression == UndefinedCompression &&
7513 text[0].text_length < 128) ? -1 : 0;
7515 if (text[0].text_length <= allocated_length)
7516 png_set_text(ping,ping_info,text,1);
7518 png_free(ping,text[0].text);
7519 png_free(ping,text[0].key);
7520 png_free(ping,text);
7523 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7524 const char *string, MagickBooleanType logging)
7537 ResetImageProfileIterator(image);
7539 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7541 profile=GetImageProfile(image,name);
7543 if (profile != (const StringInfo *) NULL)
7548 if (LocaleNCompare(name,string,11) == 0)
7550 if (logging != MagickFalse)
7551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7552 " Found %s profile",name);
7554 ping_profile=CloneStringInfo(profile);
7555 data=GetStringInfoDatum(ping_profile),
7556 length=(png_uint_32) GetStringInfoLength(ping_profile);
7561 (void) WriteBlobMSBULong(image,length-5); /* data length */
7562 (void) WriteBlob(image,length-1,data+1);
7563 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7564 ping_profile=DestroyStringInfo(ping_profile);
7568 name=GetNextImageProfile(image);
7575 /* Write one PNG image */
7576 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7577 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7601 ping_trans_alpha[256];
7629 ping_have_cheap_transparency,
7640 /* ping_exclude_EXIF, */
7643 /* ping_exclude_iTXt, */
7648 /* ping_exclude_tRNS, */
7650 ping_exclude_zCCP, /* hex-encoded iCCP */
7653 ping_preserve_colormap,
7654 ping_need_colortype_warning,
7672 *volatile ping_pixels;
7678 ping_interlace_method,
7679 ping_compression_method,
7696 number_semitransparent,
7698 ping_pHYs_unit_type;
7701 ping_pHYs_x_resolution,
7702 ping_pHYs_y_resolution;
7704 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7705 " Enter WriteOnePNGImage()");
7707 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7708 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7709 if (image_info == (ImageInfo *) NULL)
7710 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7712 /* Initialize some stuff */
7715 ping_interlace_method=0,
7716 ping_compression_method=0,
7717 ping_filter_method=0,
7720 ping_background.red = 0;
7721 ping_background.green = 0;
7722 ping_background.blue = 0;
7723 ping_background.gray = 0;
7724 ping_background.index = 0;
7726 ping_trans_color.red=0;
7727 ping_trans_color.green=0;
7728 ping_trans_color.blue=0;
7729 ping_trans_color.gray=0;
7731 ping_pHYs_unit_type = 0;
7732 ping_pHYs_x_resolution = 0;
7733 ping_pHYs_y_resolution = 0;
7735 ping_have_blob=MagickFalse;
7736 ping_have_color=MagickTrue;
7737 ping_have_non_bw=MagickTrue;
7738 ping_have_PLTE=MagickFalse;
7739 ping_have_bKGD=MagickFalse;
7740 ping_have_pHYs=MagickFalse;
7741 ping_have_tRNS=MagickFalse;
7743 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7744 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7745 ping_exclude_date=mng_info->ping_exclude_date;
7746 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7747 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7748 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7749 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7750 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7751 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7752 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7753 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7754 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7755 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7756 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7757 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7759 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7760 ping_need_colortype_warning = MagickFalse;
7762 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7763 * i.e., eliminate the ICC profile and set image->rendering_intent.
7764 * Note that this will not involve any changes to the actual pixels
7765 * but merely passes information to applications that read the resulting
7768 if (ping_exclude_sRGB == MagickFalse)
7776 ResetImageProfileIterator(image);
7777 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7779 profile=GetImageProfile(image,name);
7781 if (profile != (StringInfo *) NULL)
7783 if ((LocaleCompare(name,"ICC") == 0) ||
7784 (LocaleCompare(name,"ICM") == 0))
7789 /* 0: not a known sRGB profile
7790 * 1: HP-Microsoft sRGB v2
7791 * 2: ICC sRGB v4 perceptual
7792 * 3: ICC sRGB v2 perceptual no black-compensation
7795 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7796 check_len[4] = {0, 3144, 60960, 3052};
7805 length=(png_uint_32) GetStringInfoLength(profile);
7807 for (icheck=3; icheck > 0; icheck--)
7809 if (length == check_len[icheck])
7811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7812 " Got a %lu-byte ICC profile (potentially sRGB)",
7813 (unsigned long) length);
7815 data=GetStringInfoDatum(profile);
7816 profile_crc=crc32(0,data,length);
7818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7819 " with crc=%8x",(unsigned int) profile_crc);
7821 if (profile_crc == check_crc[icheck])
7823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7825 if (image->rendering_intent==UndefinedIntent)
7826 image->rendering_intent=PerceptualIntent;
7832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7833 " Got a %lu-byte ICC profile",
7834 (unsigned long) length);
7837 name=GetNextImageProfile(image);
7842 number_semitransparent = 0;
7843 number_transparent = 0;
7845 if (logging != MagickFalse)
7847 if (image->storage_class == UndefinedClass)
7848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7849 " storage_class=UndefinedClass");
7850 if (image->storage_class == DirectClass)
7851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7852 " storage_class=DirectClass");
7853 if (image->storage_class == PseudoClass)
7854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7855 " storage_class=PseudoClass");
7858 if (image->storage_class == PseudoClass &&
7859 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7860 (mng_info->write_png_colortype != 0 &&
7861 mng_info->write_png_colortype != 4)))
7863 (void) SyncImage(image,exception);
7864 image->storage_class = DirectClass;
7867 if (ping_preserve_colormap == MagickFalse)
7869 if (image->storage_class != PseudoClass && image->colormap != NULL)
7871 /* Free the bogus colormap; it can cause trouble later */
7872 if (logging != MagickFalse)
7873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7874 " Freeing bogus colormap");
7875 (void) RelinquishMagickMemory(image->colormap);
7876 image->colormap=NULL;
7880 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
7881 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7884 Sometimes we get PseudoClass images whose RGB values don't match
7885 the colors in the colormap. This code syncs the RGB values.
7887 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7888 (void) SyncImage(image,exception);
7890 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7891 if (image->depth > 8)
7893 if (logging != MagickFalse)
7894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7895 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7901 /* Respect the -depth option */
7902 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7907 if (image->depth > 8)
7909 #if MAGICKCORE_QUANTUM_DEPTH > 16
7910 /* Scale to 16-bit */
7911 LBR16PacketRGBO(image->background_color);
7913 for (y=0; y < (ssize_t) image->rows; y++)
7915 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7917 if (r == (Quantum *) NULL)
7920 for (x=0; x < (ssize_t) image->columns; x++)
7923 r+=GetPixelChannels(image);
7926 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7930 if (image->storage_class == PseudoClass && image->colormap != NULL)
7932 for (i=0; i < (ssize_t) image->colors; i++)
7934 LBR16PacketRGBO(image->colormap[i]);
7937 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7940 else if (image->depth > 4)
7942 #if MAGICKCORE_QUANTUM_DEPTH > 8
7943 /* Scale to 8-bit */
7944 LBR08PacketRGBO(image->background_color);
7946 for (y=0; y < (ssize_t) image->rows; y++)
7948 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7950 if (r == (Quantum *) NULL)
7953 for (x=0; x < (ssize_t) image->columns; x++)
7956 r+=GetPixelChannels(image);
7959 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7963 if (image->storage_class == PseudoClass && image->colormap != NULL)
7965 for (i=0; i < (ssize_t) image->colors; i++)
7967 LBR08PacketRGBO(image->colormap[i]);
7970 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7973 if (image->depth > 2)
7975 /* Scale to 4-bit */
7976 LBR04PacketRGBO(image->background_color);
7978 for (y=0; y < (ssize_t) image->rows; y++)
7980 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7982 if (r == (Quantum *) NULL)
7985 for (x=0; x < (ssize_t) image->columns; x++)
7988 r+=GetPixelChannels(image);
7991 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7995 if (image->storage_class == PseudoClass && image->colormap != NULL)
7997 for (i=0; i < (ssize_t) image->colors; i++)
7999 LBR04PacketRGBO(image->colormap[i]);
8004 else if (image->depth > 1)
8006 /* Scale to 2-bit */
8007 LBR02PacketRGBO(image->background_color);
8009 for (y=0; y < (ssize_t) image->rows; y++)
8011 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8013 if (r == (Quantum *) NULL)
8016 for (x=0; x < (ssize_t) image->columns; x++)
8019 r+=GetPixelChannels(image);
8022 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8026 if (image->storage_class == PseudoClass && image->colormap != NULL)
8028 for (i=0; i < (ssize_t) image->colors; i++)
8030 LBR02PacketRGBO(image->colormap[i]);
8036 /* Scale to 1-bit */
8037 LBR01PacketRGBO(image->background_color);
8039 for (y=0; y < (ssize_t) image->rows; y++)
8041 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8043 if (r == (Quantum *) NULL)
8046 for (x=0; x < (ssize_t) image->columns; x++)
8049 r+=GetPixelChannels(image);
8052 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8056 if (image->storage_class == PseudoClass && image->colormap != NULL)
8058 for (i=0; i < (ssize_t) image->colors; i++)
8060 LBR01PacketRGBO(image->colormap[i]);
8066 /* To do: set to next higher multiple of 8 */
8067 if (image->depth < 8)
8070 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8071 /* PNG does not handle depths greater than 16 so reduce it even
8074 if (image->depth > 8)
8078 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8079 if (image->depth > 8)
8081 /* To do: fill low byte properly */
8085 if (image->depth == 16 && mng_info->write_png_depth != 16)
8086 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8090 /* Normally we run this just once, but in the case of writing PNG8
8091 * we reduce the transparency to binary and run again, then if there
8092 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8093 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8094 * palette. Then (To do) we take care of a final reduction that is only
8095 * needed if there are still 256 colors present and one of them has both
8096 * transparent and opaque instances.
8099 tried_332 = MagickFalse;
8100 tried_333 = MagickFalse;
8101 tried_444 = MagickFalse;
8107 * Sometimes we get DirectClass images that have 256 colors or fewer.
8108 * This code will build a colormap.
8110 * Also, sometimes we get PseudoClass images with an out-of-date
8111 * colormap. This code will replace the colormap with a new one.
8112 * Sometimes we get PseudoClass images that have more than 256 colors.
8113 * This code will delete the colormap and change the image to
8116 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8117 * even though it sometimes contains left-over non-opaque values.
8119 * Also we gather some information (number of opaque, transparent,
8120 * and semitransparent pixels, and whether the image has any non-gray
8121 * pixels or only black-and-white pixels) that we might need later.
8123 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8124 * we need to check for bogus non-opaque values, at least.
8132 semitransparent[260],
8135 register const Quantum
8142 if (logging != MagickFalse)
8143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8144 " Enter BUILD_PALETTE:");
8146 if (logging != MagickFalse)
8148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8149 " image->columns=%.20g",(double) image->columns);
8150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8151 " image->rows=%.20g",(double) image->rows);
8152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8153 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8155 " image->depth=%.20g",(double) image->depth);
8157 if (image->storage_class == PseudoClass && image->colormap != NULL)
8159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8160 " Original colormap:");
8161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8162 " i (red,green,blue,alpha)");
8164 for (i=0; i < 256; i++)
8166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8167 " %d (%d,%d,%d,%d)",
8169 (int) image->colormap[i].red,
8170 (int) image->colormap[i].green,
8171 (int) image->colormap[i].blue,
8172 (int) image->colormap[i].alpha);
8175 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8180 " %d (%d,%d,%d,%d)",
8182 (int) image->colormap[i].red,
8183 (int) image->colormap[i].green,
8184 (int) image->colormap[i].blue,
8185 (int) image->colormap[i].alpha);
8190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8191 " image->colors=%d",(int) image->colors);
8193 if (image->colors == 0)
8194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8195 " (zero means unknown)");
8197 if (ping_preserve_colormap == MagickFalse)
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " Regenerate the colormap");
8204 number_semitransparent = 0;
8205 number_transparent = 0;
8207 for (y=0; y < (ssize_t) image->rows; y++)
8209 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8211 if (q == (Quantum *) NULL)
8214 for (x=0; x < (ssize_t) image->columns; x++)
8216 if (image->alpha_trait != BlendPixelTrait ||
8217 GetPixelAlpha(image,q) == OpaqueAlpha)
8219 if (number_opaque < 259)
8221 if (number_opaque == 0)
8223 GetPixelInfoPixel(image, q, opaque);
8224 opaque[0].alpha=OpaqueAlpha;
8228 for (i=0; i< (ssize_t) number_opaque; i++)
8230 if (IsPixelEquivalent(image,q, opaque+i))
8234 if (i == (ssize_t) number_opaque && number_opaque < 259)
8237 GetPixelInfoPixel(image, q, opaque+i);
8238 opaque[i].alpha=OpaqueAlpha;
8242 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8244 if (number_transparent < 259)
8246 if (number_transparent == 0)
8248 GetPixelInfoPixel(image, q, transparent);
8249 ping_trans_color.red=(unsigned short)
8250 GetPixelRed(image,q);
8251 ping_trans_color.green=(unsigned short)
8252 GetPixelGreen(image,q);
8253 ping_trans_color.blue=(unsigned short)
8254 GetPixelBlue(image,q);
8255 ping_trans_color.gray=(unsigned short)
8256 GetPixelGray(image,q);
8257 number_transparent = 1;
8260 for (i=0; i< (ssize_t) number_transparent; i++)
8262 if (IsPixelEquivalent(image,q, transparent+i))
8266 if (i == (ssize_t) number_transparent &&
8267 number_transparent < 259)
8269 number_transparent++;
8270 GetPixelInfoPixel(image,q,transparent+i);
8276 if (number_semitransparent < 259)
8278 if (number_semitransparent == 0)
8280 GetPixelInfoPixel(image,q,semitransparent);
8281 number_semitransparent = 1;
8284 for (i=0; i< (ssize_t) number_semitransparent; i++)
8286 if (IsPixelEquivalent(image,q, semitransparent+i)
8287 && GetPixelAlpha(image,q) ==
8288 semitransparent[i].alpha)
8292 if (i == (ssize_t) number_semitransparent &&
8293 number_semitransparent < 259)
8295 number_semitransparent++;
8296 GetPixelInfoPixel(image, q, semitransparent+i);
8300 q+=GetPixelChannels(image);
8304 if (mng_info->write_png8 == MagickFalse &&
8305 ping_exclude_bKGD == MagickFalse)
8307 /* Add the background color to the palette, if it
8308 * isn't already there.
8310 if (logging != MagickFalse)
8312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8313 " Check colormap for background (%d,%d,%d)",
8314 (int) image->background_color.red,
8315 (int) image->background_color.green,
8316 (int) image->background_color.blue);
8318 for (i=0; i<number_opaque; i++)
8320 if (opaque[i].red == image->background_color.red &&
8321 opaque[i].green == image->background_color.green &&
8322 opaque[i].blue == image->background_color.blue)
8325 if (number_opaque < 259 && i == number_opaque)
8327 opaque[i] = image->background_color;
8328 ping_background.index = i;
8330 if (logging != MagickFalse)
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " background_color index is %d",(int) i);
8337 else if (logging != MagickFalse)
8338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8339 " No room in the colormap to add background color");
8342 image_colors=number_opaque+number_transparent+number_semitransparent;
8344 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8346 /* No room for the background color; remove it. */
8351 if (logging != MagickFalse)
8353 if (image_colors > 256)
8354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8355 " image has more than 256 colors");
8358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " image has %d colors",image_colors);
8362 if (ping_preserve_colormap != MagickFalse)
8365 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8367 ping_have_color=MagickFalse;
8368 ping_have_non_bw=MagickFalse;
8370 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) &&
8371 (IssRGBColorspace(image->colorspace) == MagickFalse))
8373 ping_have_color=MagickTrue;
8374 ping_have_non_bw=MagickFalse;
8377 if(image_colors > 256)
8379 for (y=0; y < (ssize_t) image->rows; y++)
8381 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8383 if (q == (Quantum *) NULL)
8387 for (x=0; x < (ssize_t) image->columns; x++)
8389 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8390 GetPixelRed(image,s) != GetPixelBlue(image,s))
8392 ping_have_color=MagickTrue;
8393 ping_have_non_bw=MagickTrue;
8396 s+=GetPixelChannels(image);
8399 if (ping_have_color != MagickFalse)
8402 /* Worst case is black-and-white; we are looking at every
8406 if (ping_have_non_bw == MagickFalse)
8409 for (x=0; x < (ssize_t) image->columns; x++)
8411 if (GetPixelRed(image,s) != 0 &&
8412 GetPixelRed(image,s) != QuantumRange)
8414 ping_have_non_bw=MagickTrue;
8417 s+=GetPixelChannels(image);
8424 if (image_colors < 257)
8430 * Initialize image colormap.
8433 if (logging != MagickFalse)
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " Sort the new colormap");
8437 /* Sort palette, transparent first */;
8441 for (i=0; i<number_transparent; i++)
8442 colormap[n++] = transparent[i];
8444 for (i=0; i<number_semitransparent; i++)
8445 colormap[n++] = semitransparent[i];
8447 for (i=0; i<number_opaque; i++)
8448 colormap[n++] = opaque[i];
8450 ping_background.index +=
8451 (number_transparent + number_semitransparent);
8453 /* image_colors < 257; search the colormap instead of the pixels
8454 * to get ping_have_color and ping_have_non_bw
8458 if (ping_have_color == MagickFalse)
8460 if (colormap[i].red != colormap[i].green ||
8461 colormap[i].red != colormap[i].blue)
8463 ping_have_color=MagickTrue;
8464 ping_have_non_bw=MagickTrue;
8469 if (ping_have_non_bw == MagickFalse)
8471 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8472 ping_have_non_bw=MagickTrue;
8476 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8477 (number_transparent == 0 && number_semitransparent == 0)) &&
8478 (((mng_info->write_png_colortype-1) ==
8479 PNG_COLOR_TYPE_PALETTE) ||
8480 (mng_info->write_png_colortype == 0)))
8482 if (logging != MagickFalse)
8484 if (n != (ssize_t) image_colors)
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " image_colors (%d) and n (%d) don't match",
8489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8490 " AcquireImageColormap");
8493 image->colors = image_colors;
8495 if (AcquireImageColormap(image,image_colors,exception) ==
8497 ThrowWriterException(ResourceLimitError,
8498 "MemoryAllocationFailed");
8500 for (i=0; i< (ssize_t) image_colors; i++)
8501 image->colormap[i] = colormap[i];
8503 if (logging != MagickFalse)
8505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8506 " image->colors=%d (%d)",
8507 (int) image->colors, image_colors);
8509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510 " Update the pixel indexes");
8513 /* Sync the pixel indices with the new colormap */
8515 for (y=0; y < (ssize_t) image->rows; y++)
8517 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8519 if (q == (Quantum *) NULL)
8522 for (x=0; x < (ssize_t) image->columns; x++)
8524 for (i=0; i< (ssize_t) image_colors; i++)
8526 if ((image->alpha_trait != BlendPixelTrait ||
8527 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8528 image->colormap[i].red == GetPixelRed(image,q) &&
8529 image->colormap[i].green == GetPixelGreen(image,q) &&
8530 image->colormap[i].blue == GetPixelBlue(image,q))
8532 SetPixelIndex(image,i,q);
8536 q+=GetPixelChannels(image);
8539 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8545 if (logging != MagickFalse)
8547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8548 " image->colors=%d", (int) image->colors);
8550 if (image->colormap != NULL)
8552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553 " i (red,green,blue,alpha)");
8555 for (i=0; i < (ssize_t) image->colors; i++)
8557 if (i < 300 || i >= (ssize_t) image->colors - 10)
8559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8560 " %d (%d,%d,%d,%d)",
8562 (int) image->colormap[i].red,
8563 (int) image->colormap[i].green,
8564 (int) image->colormap[i].blue,
8565 (int) image->colormap[i].alpha);
8570 if (number_transparent < 257)
8571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8572 " number_transparent = %d",
8573 number_transparent);
8576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8577 " number_transparent > 256");
8579 if (number_opaque < 257)
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " number_opaque = %d",
8585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8586 " number_opaque > 256");
8588 if (number_semitransparent < 257)
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590 " number_semitransparent = %d",
8591 number_semitransparent);
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595 " number_semitransparent > 256");
8597 if (ping_have_non_bw == MagickFalse)
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8599 " All pixels and the background are black or white");
8601 else if (ping_have_color == MagickFalse)
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " All pixels and the background are gray");
8606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8607 " At least one pixel or the background is non-gray");
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " Exit BUILD_PALETTE:");
8613 if (mng_info->write_png8 == MagickFalse)
8616 /* Make any reductions necessary for the PNG8 format */
8617 if (image_colors <= 256 &&
8618 image_colors != 0 && image->colormap != NULL &&
8619 number_semitransparent == 0 &&
8620 number_transparent <= 1)
8623 /* PNG8 can't have semitransparent colors so we threshold the
8624 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8625 * transparent color so if more than one is transparent we merge
8626 * them into image->background_color.
8628 if (number_semitransparent != 0 || number_transparent > 1)
8630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8631 " Thresholding the alpha channel to binary");
8633 for (y=0; y < (ssize_t) image->rows; y++)
8635 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8637 if (r == (Quantum *) NULL)
8640 for (x=0; x < (ssize_t) image->columns; x++)
8642 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8644 SetPixelInfoPixel(image,&image->background_color,r);
8645 SetPixelAlpha(image,TransparentAlpha,r);
8648 SetPixelAlpha(image,OpaqueAlpha,r);
8649 r+=GetPixelChannels(image);
8652 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8655 if (image_colors != 0 && image_colors <= 256 &&
8656 image->colormap != NULL)
8657 for (i=0; i<image_colors; i++)
8658 image->colormap[i].alpha =
8659 (image->colormap[i].alpha > TransparentAlpha/2 ?
8660 TransparentAlpha : OpaqueAlpha);
8665 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8666 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8667 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8670 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8672 if (logging != MagickFalse)
8673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8674 " Quantizing the background color to 4-4-4");
8676 tried_444 = MagickTrue;
8678 LBR04PacketRGB(image->background_color);
8680 if (logging != MagickFalse)
8681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8682 " Quantizing the pixel colors to 4-4-4");
8684 if (image->colormap == NULL)
8686 for (y=0; y < (ssize_t) image->rows; y++)
8688 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8690 if (r == (Quantum *) NULL)
8693 for (x=0; x < (ssize_t) image->columns; x++)
8695 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8697 r+=GetPixelChannels(image);
8700 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8705 else /* Should not reach this; colormap already exists and
8708 if (logging != MagickFalse)
8709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710 " Quantizing the colormap to 4-4-4");
8712 for (i=0; i<image_colors; i++)
8714 LBR04PacketRGB(image->colormap[i]);
8720 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8722 if (logging != MagickFalse)
8723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8724 " Quantizing the background color to 3-3-3");
8726 tried_333 = MagickTrue;
8728 LBR03PacketRGB(image->background_color);
8730 if (logging != MagickFalse)
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " Quantizing the pixel colors to 3-3-3-1");
8734 if (image->colormap == NULL)
8736 for (y=0; y < (ssize_t) image->rows; y++)
8738 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8740 if (r == (Quantum *) NULL)
8743 for (x=0; x < (ssize_t) image->columns; x++)
8745 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8747 r+=GetPixelChannels(image);
8750 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8755 else /* Should not reach this; colormap already exists and
8758 if (logging != MagickFalse)
8759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8760 " Quantizing the colormap to 3-3-3-1");
8761 for (i=0; i<image_colors; i++)
8763 LBR03PacketRGB(image->colormap[i]);
8769 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8771 if (logging != MagickFalse)
8772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8773 " Quantizing the background color to 3-3-2");
8775 tried_332 = MagickTrue;
8777 /* Red and green were already done so we only quantize the blue
8781 LBR02PacketBlue(image->background_color);
8783 if (logging != MagickFalse)
8784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785 " Quantizing the pixel colors to 3-3-2-1");
8787 if (image->colormap == NULL)
8789 for (y=0; y < (ssize_t) image->rows; y++)
8791 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8793 if (r == (Quantum *) NULL)
8796 for (x=0; x < (ssize_t) image->columns; x++)
8798 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8800 r+=GetPixelChannels(image);
8803 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8808 else /* Should not reach this; colormap already exists and
8811 if (logging != MagickFalse)
8812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8813 " Quantizing the colormap to 3-3-2-1");
8814 for (i=0; i<image_colors; i++)
8816 LBR02PacketBlue(image->colormap[i]);
8823 if (image_colors == 0 || image_colors > 256)
8825 /* Take care of special case with 256 colors + 1 transparent
8826 * color. We don't need to quantize to 2-3-2-1; we only need to
8827 * eliminate one color, so we'll merge the two darkest red
8828 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8830 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8831 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8832 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8834 image->background_color.red=ScaleCharToQuantum(0x24);
8837 if (image->colormap == NULL)
8839 for (y=0; y < (ssize_t) image->rows; y++)
8841 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8843 if (r == (Quantum *) NULL)
8846 for (x=0; x < (ssize_t) image->columns; x++)
8848 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8849 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8850 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8851 GetPixelAlpha(image,r) == OpaqueAlpha)
8853 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8855 r+=GetPixelChannels(image);
8858 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8866 for (i=0; i<image_colors; i++)
8868 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8869 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8870 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8872 image->colormap[i].red=ScaleCharToQuantum(0x24);
8878 /* END OF BUILD_PALETTE */
8880 /* If we are excluding the tRNS chunk and there is transparency,
8881 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8884 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8885 (number_transparent != 0 || number_semitransparent != 0))
8887 unsigned int colortype=mng_info->write_png_colortype;
8889 if (ping_have_color == MagickFalse)
8890 mng_info->write_png_colortype = 5;
8893 mng_info->write_png_colortype = 7;
8895 if (colortype != 0 &&
8896 mng_info->write_png_colortype != colortype)
8897 ping_need_colortype_warning=MagickTrue;
8901 /* See if cheap transparency is possible. It is only possible
8902 * when there is a single transparent color, no semitransparent
8903 * color, and no opaque color that has the same RGB components
8904 * as the transparent color. We only need this information if
8905 * we are writing a PNG with colortype 0 or 2, and we have not
8906 * excluded the tRNS chunk.
8908 if (number_transparent == 1 &&
8909 mng_info->write_png_colortype < 4)
8911 ping_have_cheap_transparency = MagickTrue;
8913 if (number_semitransparent != 0)
8914 ping_have_cheap_transparency = MagickFalse;
8916 else if (image_colors == 0 || image_colors > 256 ||
8917 image->colormap == NULL)
8919 register const Quantum
8922 for (y=0; y < (ssize_t) image->rows; y++)
8924 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8926 if (q == (Quantum *) NULL)
8929 for (x=0; x < (ssize_t) image->columns; x++)
8931 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8932 (unsigned short) GetPixelRed(image,q) ==
8933 ping_trans_color.red &&
8934 (unsigned short) GetPixelGreen(image,q) ==
8935 ping_trans_color.green &&
8936 (unsigned short) GetPixelBlue(image,q) ==
8937 ping_trans_color.blue)
8939 ping_have_cheap_transparency = MagickFalse;
8943 q+=GetPixelChannels(image);
8946 if (ping_have_cheap_transparency == MagickFalse)
8952 /* Assuming that image->colormap[0] is the one transparent color
8953 * and that all others are opaque.
8955 if (image_colors > 1)
8956 for (i=1; i<image_colors; i++)
8957 if (image->colormap[i].red == image->colormap[0].red &&
8958 image->colormap[i].green == image->colormap[0].green &&
8959 image->colormap[i].blue == image->colormap[0].blue)
8961 ping_have_cheap_transparency = MagickFalse;
8966 if (logging != MagickFalse)
8968 if (ping_have_cheap_transparency == MagickFalse)
8969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8970 " Cheap transparency is not possible.");
8973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8974 " Cheap transparency is possible.");
8978 ping_have_cheap_transparency = MagickFalse;
8980 image_depth=image->depth;
8982 quantum_info = (QuantumInfo *) NULL;
8984 image_colors=(int) image->colors;
8985 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
8987 mng_info->IsPalette=image->storage_class == PseudoClass &&
8988 image_colors <= 256 && image->colormap != NULL;
8990 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8991 (image->colors == 0 || image->colormap == NULL))
8993 image_info=DestroyImageInfo(image_info);
8994 image=DestroyImage(image);
8995 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8996 "Cannot write PNG8 or color-type 3; colormap is NULL",
8997 "`%s'",IMimage->filename);
8998 return(MagickFalse);
9002 Allocate the PNG structures
9004 #ifdef PNG_USER_MEM_SUPPORTED
9005 error_info.image=image;
9006 error_info.exception=exception;
9007 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9008 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9009 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9012 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9013 MagickPNGErrorHandler,MagickPNGWarningHandler);
9016 if (ping == (png_struct *) NULL)
9017 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9019 ping_info=png_create_info_struct(ping);
9021 if (ping_info == (png_info *) NULL)
9023 png_destroy_write_struct(&ping,(png_info **) NULL);
9024 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9027 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9028 ping_pixels=(unsigned char *) NULL;
9030 if (setjmp(png_jmpbuf(ping)))
9036 if (image_info->verbose)
9037 (void) printf("PNG write has failed.\n");
9039 png_destroy_write_struct(&ping,&ping_info);
9040 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9041 UnlockSemaphoreInfo(ping_semaphore);
9044 if (ping_pixels != (unsigned char *) NULL)
9045 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9047 if (quantum_info != (QuantumInfo *) NULL)
9048 quantum_info=DestroyQuantumInfo(quantum_info);
9050 if (ping_have_blob != MagickFalse)
9051 (void) CloseBlob(image);
9052 image_info=DestroyImageInfo(image_info);
9053 image=DestroyImage(image);
9054 return(MagickFalse);
9057 /* { For navigation to end of SETJMP-protected block. Within this
9058 * block, use png_error() instead of Throwing an Exception, to ensure
9059 * that libpng is able to clean up, and that the semaphore is unlocked.
9062 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9063 LockSemaphoreInfo(ping_semaphore);
9067 Prepare PNG for writing.
9070 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9071 if (mng_info->write_mng)
9073 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9074 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9075 /* Disable new libpng-1.5.10 feature when writing a MNG because
9076 * zero-length PLTE is OK
9078 png_set_check_for_invalid_index (ping, 0);
9083 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9084 if (mng_info->write_mng)
9085 png_permit_empty_plte(ping,MagickTrue);
9092 ping_width=(png_uint_32) image->columns;
9093 ping_height=(png_uint_32) image->rows;
9095 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9098 if (mng_info->write_png_depth != 0)
9099 image_depth=mng_info->write_png_depth;
9101 /* Adjust requested depth to next higher valid depth if necessary */
9102 if (image_depth > 8)
9105 if ((image_depth > 4) && (image_depth < 8))
9108 if (image_depth == 3)
9111 if (logging != MagickFalse)
9113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9114 " width=%.20g",(double) ping_width);
9115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9116 " height=%.20g",(double) ping_height);
9117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9118 " image_matte=%.20g",(double) image->alpha_trait);
9119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9120 " image->depth=%.20g",(double) image->depth);
9121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9122 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9125 save_image_depth=image_depth;
9126 ping_bit_depth=(png_byte) save_image_depth;
9129 #if defined(PNG_pHYs_SUPPORTED)
9130 if (ping_exclude_pHYs == MagickFalse)
9132 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9133 (!mng_info->write_mng || !mng_info->equal_physs))
9135 if (logging != MagickFalse)
9136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137 " Setting up pHYs chunk");
9139 if (image->units == PixelsPerInchResolution)
9141 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9142 ping_pHYs_x_resolution=
9143 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9144 ping_pHYs_y_resolution=
9145 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9148 else if (image->units == PixelsPerCentimeterResolution)
9150 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9151 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9152 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9157 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9158 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9159 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9162 if (logging != MagickFalse)
9163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9165 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9166 (int) ping_pHYs_unit_type);
9167 ping_have_pHYs = MagickTrue;
9172 if (ping_exclude_bKGD == MagickFalse)
9174 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9180 if (ping_bit_depth == 8)
9183 if (ping_bit_depth == 4)
9186 if (ping_bit_depth == 2)
9189 if (ping_bit_depth == 1)
9192 ping_background.red=(png_uint_16)
9193 (ScaleQuantumToShort(image->background_color.red) & mask);
9195 ping_background.green=(png_uint_16)
9196 (ScaleQuantumToShort(image->background_color.green) & mask);
9198 ping_background.blue=(png_uint_16)
9199 (ScaleQuantumToShort(image->background_color.blue) & mask);
9201 ping_background.gray=(png_uint_16) ping_background.green;
9204 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " Setting up bKGD chunk (1)");
9208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9209 " background_color index is %d",
9210 (int) ping_background.index);
9212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213 " ping_bit_depth=%d",ping_bit_depth);
9216 ping_have_bKGD = MagickTrue;
9220 Select the color type.
9225 if (mng_info->IsPalette && mng_info->write_png8)
9228 /* To do: make this a function cause it's used twice, except
9229 for reducing the sample depth from 8. */
9231 number_colors=image_colors;
9233 ping_have_tRNS=MagickFalse;
9238 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9240 if (logging != MagickFalse)
9241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9242 " Setting up PLTE chunk with %d colors (%d)",
9243 number_colors, image_colors);
9245 for (i=0; i < (ssize_t) number_colors; i++)
9247 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9248 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9249 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9250 if (logging != MagickFalse)
9251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9252 #if MAGICKCORE_QUANTUM_DEPTH == 8
9253 " %3ld (%3d,%3d,%3d)",
9255 " %5ld (%5d,%5d,%5d)",
9257 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9261 ping_have_PLTE=MagickTrue;
9262 image_depth=ping_bit_depth;
9265 if (matte != MagickFalse)
9268 Identify which colormap entry is transparent.
9270 assert(number_colors <= 256);
9271 assert(image->colormap != NULL);
9273 for (i=0; i < (ssize_t) number_transparent; i++)
9274 ping_trans_alpha[i]=0;
9277 ping_num_trans=(unsigned short) (number_transparent +
9278 number_semitransparent);
9280 if (ping_num_trans == 0)
9281 ping_have_tRNS=MagickFalse;
9284 ping_have_tRNS=MagickTrue;
9287 if (ping_exclude_bKGD == MagickFalse)
9290 * Identify which colormap entry is the background color.
9293 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9294 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9297 ping_background.index=(png_byte) i;
9299 if (logging != MagickFalse)
9301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302 " background_color index is %d",
9303 (int) ping_background.index);
9306 } /* end of write_png8 */
9308 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9310 image_matte=MagickFalse;
9311 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9314 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9316 image_matte=MagickTrue;
9317 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9320 else /* mng_info->write_pngNN not specified */
9322 image_depth=ping_bit_depth;
9324 if (mng_info->write_png_colortype != 0)
9326 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9328 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9329 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9330 image_matte=MagickTrue;
9333 image_matte=MagickFalse;
9335 if (logging != MagickFalse)
9336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9337 " PNG colortype %d was specified:",(int) ping_color_type);
9340 else /* write_png_colortype not specified */
9342 if (logging != MagickFalse)
9343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9344 " Selecting PNG colortype:");
9346 ping_color_type=(png_byte) ((matte != MagickFalse)?
9347 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9349 if (image_info->type == TrueColorType)
9351 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9352 image_matte=MagickFalse;
9355 if (image_info->type == TrueColorMatteType)
9357 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9358 image_matte=MagickTrue;
9361 if (image_info->type == PaletteType ||
9362 image_info->type == PaletteMatteType)
9363 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9365 if (mng_info->write_png_colortype == 0 &&
9366 (image_info->type == UndefinedType ||
9367 image_info->type == OptimizeType))
9369 if (ping_have_color == MagickFalse)
9371 if (image_matte == MagickFalse)
9373 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9374 image_matte=MagickFalse;
9379 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9380 image_matte=MagickTrue;
9385 if (image_matte == MagickFalse)
9387 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9388 image_matte=MagickFalse;
9393 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9394 image_matte=MagickTrue;
9401 if (logging != MagickFalse)
9402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " Selected PNG colortype=%d",ping_color_type);
9405 if (ping_bit_depth < 8)
9407 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9408 ping_color_type == PNG_COLOR_TYPE_RGB ||
9409 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9413 old_bit_depth=ping_bit_depth;
9415 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9417 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9421 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9426 if (image->colors == 0)
9429 png_error(ping,"image has 0 colors");
9432 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9433 ping_bit_depth <<= 1;
9436 if (logging != MagickFalse)
9438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9439 " Number of colors: %.20g",(double) image_colors);
9441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9442 " Tentative PNG bit depth: %d",ping_bit_depth);
9445 if (ping_bit_depth < (int) mng_info->write_png_depth)
9446 ping_bit_depth = mng_info->write_png_depth;
9449 image_depth=ping_bit_depth;
9451 if (logging != MagickFalse)
9453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9454 " Tentative PNG color type: %s (%.20g)",
9455 PngColorTypeToString(ping_color_type),
9456 (double) ping_color_type);
9458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9459 " image_info->type: %.20g",(double) image_info->type);
9461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9462 " image_depth: %.20g",(double) image_depth);
9464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9466 " image->depth: %.20g",(double) image->depth);
9468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9469 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9472 if (matte != MagickFalse)
9474 if (mng_info->IsPalette)
9476 if (mng_info->write_png_colortype == 0)
9478 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9480 if (ping_have_color != MagickFalse)
9481 ping_color_type=PNG_COLOR_TYPE_RGBA;
9485 * Determine if there is any transparent color.
9487 if (number_transparent + number_semitransparent == 0)
9490 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9493 image_matte=MagickFalse;
9495 if (mng_info->write_png_colortype == 0)
9496 ping_color_type&=0x03;
9506 if (ping_bit_depth == 8)
9509 if (ping_bit_depth == 4)
9512 if (ping_bit_depth == 2)
9515 if (ping_bit_depth == 1)
9518 ping_trans_color.red=(png_uint_16)
9519 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9521 ping_trans_color.green=(png_uint_16)
9522 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9524 ping_trans_color.blue=(png_uint_16)
9525 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9527 ping_trans_color.gray=(png_uint_16)
9528 (ScaleQuantumToShort(GetPixelInfoIntensity(
9529 image->colormap)) & mask);
9531 ping_trans_color.index=(png_byte) 0;
9533 ping_have_tRNS=MagickTrue;
9536 if (ping_have_tRNS != MagickFalse)
9539 * Determine if there is one and only one transparent color
9540 * and if so if it is fully transparent.
9542 if (ping_have_cheap_transparency == MagickFalse)
9543 ping_have_tRNS=MagickFalse;
9546 if (ping_have_tRNS != MagickFalse)
9548 if (mng_info->write_png_colortype == 0)
9549 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9551 if (image_depth == 8)
9553 ping_trans_color.red&=0xff;
9554 ping_trans_color.green&=0xff;
9555 ping_trans_color.blue&=0xff;
9556 ping_trans_color.gray&=0xff;
9562 if (image_depth == 8)
9564 ping_trans_color.red&=0xff;
9565 ping_trans_color.green&=0xff;
9566 ping_trans_color.blue&=0xff;
9567 ping_trans_color.gray&=0xff;
9574 if (ping_have_tRNS != MagickFalse)
9575 image_matte=MagickFalse;
9577 if ((mng_info->IsPalette) &&
9578 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9579 ping_have_color == MagickFalse &&
9580 (image_matte == MagickFalse || image_depth >= 8))
9584 if (image_matte != MagickFalse)
9585 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9587 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9589 ping_color_type=PNG_COLOR_TYPE_GRAY;
9591 if (save_image_depth == 16 && image_depth == 8)
9593 if (logging != MagickFalse)
9595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9596 " Scaling ping_trans_color (0)");
9598 ping_trans_color.gray*=0x0101;
9602 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9603 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9605 if ((image_colors == 0) ||
9606 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9607 image_colors=(int) (one << image_depth);
9609 if (image_depth > 8)
9615 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9617 if(!mng_info->write_png_depth)
9621 while ((int) (one << ping_bit_depth)
9622 < (ssize_t) image_colors)
9623 ping_bit_depth <<= 1;
9627 else if (ping_color_type ==
9628 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9629 mng_info->IsPalette)
9631 /* Check if grayscale is reducible */
9634 depth_4_ok=MagickTrue,
9635 depth_2_ok=MagickTrue,
9636 depth_1_ok=MagickTrue;
9638 for (i=0; i < (ssize_t) image_colors; i++)
9643 intensity=ScaleQuantumToChar(image->colormap[i].red);
9645 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9646 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9647 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9648 depth_2_ok=depth_1_ok=MagickFalse;
9649 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9650 depth_1_ok=MagickFalse;
9653 if (depth_1_ok && mng_info->write_png_depth <= 1)
9656 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9659 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9664 image_depth=ping_bit_depth;
9669 if (mng_info->IsPalette)
9671 number_colors=image_colors;
9673 if (image_depth <= 8)
9678 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9680 if (mng_info->have_write_global_plte && matte == MagickFalse)
9682 png_set_PLTE(ping,ping_info,NULL,0);
9684 if (logging != MagickFalse)
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " Setting up empty PLTE chunk");
9691 for (i=0; i < (ssize_t) number_colors; i++)
9693 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9694 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9695 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9698 if (logging != MagickFalse)
9699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9700 " Setting up PLTE chunk with %d colors",
9703 ping_have_PLTE=MagickTrue;
9706 /* color_type is PNG_COLOR_TYPE_PALETTE */
9707 if (mng_info->write_png_depth == 0)
9715 while ((one << ping_bit_depth) < (size_t) number_colors)
9716 ping_bit_depth <<= 1;
9721 if (matte != MagickFalse)
9724 * Set up trans_colors array.
9726 assert(number_colors <= 256);
9728 ping_num_trans=(unsigned short) (number_transparent +
9729 number_semitransparent);
9731 if (ping_num_trans == 0)
9732 ping_have_tRNS=MagickFalse;
9736 if (logging != MagickFalse)
9738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9739 " Scaling ping_trans_color (1)");
9741 ping_have_tRNS=MagickTrue;
9743 for (i=0; i < ping_num_trans; i++)
9745 ping_trans_alpha[i]= (png_byte)
9746 ScaleQuantumToChar(image->colormap[i].alpha);
9756 if (image_depth < 8)
9759 if ((save_image_depth == 16) && (image_depth == 8))
9761 if (logging != MagickFalse)
9763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9764 " Scaling ping_trans_color from (%d,%d,%d)",
9765 (int) ping_trans_color.red,
9766 (int) ping_trans_color.green,
9767 (int) ping_trans_color.blue);
9770 ping_trans_color.red*=0x0101;
9771 ping_trans_color.green*=0x0101;
9772 ping_trans_color.blue*=0x0101;
9773 ping_trans_color.gray*=0x0101;
9775 if (logging != MagickFalse)
9777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9779 (int) ping_trans_color.red,
9780 (int) ping_trans_color.green,
9781 (int) ping_trans_color.blue);
9786 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9787 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9790 Adjust background and transparency samples in sub-8-bit grayscale files.
9792 if (ping_bit_depth < 8 && ping_color_type ==
9793 PNG_COLOR_TYPE_GRAY)
9801 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9803 if (ping_exclude_bKGD == MagickFalse)
9806 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9807 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9808 &image->background_color))) +.5)));
9810 if (logging != MagickFalse)
9811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9812 " Setting up bKGD chunk (2)");
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " background_color index is %d",
9815 (int) ping_background.index);
9817 ping_have_bKGD = MagickTrue;
9820 if (logging != MagickFalse)
9821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9822 " Scaling ping_trans_color.gray from %d",
9823 (int)ping_trans_color.gray);
9825 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9826 ping_trans_color.gray)+.5);
9828 if (logging != MagickFalse)
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " to %d", (int)ping_trans_color.gray);
9833 if (ping_exclude_bKGD == MagickFalse)
9835 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9838 Identify which colormap entry is the background color.
9841 number_colors=image_colors;
9843 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9844 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9847 ping_background.index=(png_byte) i;
9849 if (logging != MagickFalse)
9851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9852 " Setting up bKGD chunk with index=%d",(int) i);
9855 if (i < (ssize_t) number_colors)
9857 ping_have_bKGD = MagickTrue;
9859 if (logging != MagickFalse)
9861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9862 " background =(%d,%d,%d)",
9863 (int) ping_background.red,
9864 (int) ping_background.green,
9865 (int) ping_background.blue);
9869 else /* Can't happen */
9871 if (logging != MagickFalse)
9872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9873 " No room in PLTE to add bKGD color");
9874 ping_have_bKGD = MagickFalse;
9879 if (logging != MagickFalse)
9880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9881 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9884 Initialize compression level and filtering.
9886 if (logging != MagickFalse)
9888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9889 " Setting up deflate compression");
9891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9892 " Compression buffer size: 32768");
9895 png_set_compression_buffer_size(ping,32768L);
9897 if (logging != MagickFalse)
9898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899 " Compression mem level: 9");
9901 png_set_compression_mem_level(ping, 9);
9903 /* Untangle the "-quality" setting:
9905 Undefined is 0; the default is used.
9910 0: Use Z_HUFFMAN_ONLY strategy with the
9911 zlib default compression level
9913 1-9: the zlib compression level
9917 0-4: the PNG filter method
9919 5: libpng adaptive filtering if compression level > 5
9920 libpng filter type "none" if compression level <= 5
9921 or if image is grayscale or palette
9923 6: libpng adaptive filtering
9925 7: "LOCO" filtering (intrapixel differing) if writing
9926 a MNG, othewise "none". Did not work in IM-6.7.0-9
9927 and earlier because of a missing "else".
9929 8: Z_RLE strategy, all filters
9930 Unused prior to IM-6.7.0-10, was same as 6
9932 9: Z_RLE strategy, no PNG filters
9933 Unused prior to IM-6.7.0-10, was same as 6
9935 Note that using the -quality option, not all combinations of
9936 PNG filter type, zlib compression level, and zlib compression
9937 strategy are possible. This will be addressed soon in a
9938 release that accomodates "-define png:compression-strategy", etc.
9942 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9947 if (mng_info->write_png_compression_strategy == 0)
9948 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9951 else if (mng_info->write_png_compression_level == 0)
9956 level=(int) MagickMin((ssize_t) quality/10,9);
9958 mng_info->write_png_compression_level = level+1;
9961 if (mng_info->write_png_compression_strategy == 0)
9963 if ((quality %10) == 8 || (quality %10) == 9)
9964 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
9965 mng_info->write_png_compression_strategy=Z_RLE+1;
9967 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
9971 if (mng_info->write_png_compression_filter == 0)
9972 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9974 if (logging != MagickFalse)
9976 if (mng_info->write_png_compression_level)
9977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9978 " Compression level: %d",
9979 (int) mng_info->write_png_compression_level-1);
9981 if (mng_info->write_png_compression_strategy)
9982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9983 " Compression strategy: %d",
9984 (int) mng_info->write_png_compression_strategy-1);
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up filtering");
9989 if (mng_info->write_png_compression_filter == 6)
9990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9991 " Base filter method: ADAPTIVE");
9992 else if (mng_info->write_png_compression_filter == 0 ||
9993 mng_info->write_png_compression_filter == 1)
9994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995 " Base filter method: NONE");
9997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9998 " Base filter method: %d",
9999 (int) mng_info->write_png_compression_filter-1);
10002 if (mng_info->write_png_compression_level != 0)
10003 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10005 if (mng_info->write_png_compression_filter == 6)
10007 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10008 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10010 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10012 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10014 else if (mng_info->write_png_compression_filter == 7 ||
10015 mng_info->write_png_compression_filter == 10)
10016 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10018 else if (mng_info->write_png_compression_filter == 8)
10020 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10021 if (mng_info->write_mng)
10023 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10024 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10025 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10028 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10031 else if (mng_info->write_png_compression_filter == 9)
10032 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10034 else if (mng_info->write_png_compression_filter != 0)
10035 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10036 mng_info->write_png_compression_filter-1);
10038 if (mng_info->write_png_compression_strategy != 0)
10039 png_set_compression_strategy(ping,
10040 mng_info->write_png_compression_strategy-1);
10042 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10043 if (ping_exclude_sRGB != MagickFalse ||
10044 (image->rendering_intent == UndefinedIntent))
10046 if ((ping_exclude_tEXt == MagickFalse ||
10047 ping_exclude_zTXt == MagickFalse) &&
10048 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10050 ResetImageProfileIterator(image);
10051 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10053 profile=GetImageProfile(image,name);
10055 if (profile != (StringInfo *) NULL)
10057 #ifdef PNG_WRITE_iCCP_SUPPORTED
10058 if ((LocaleCompare(name,"ICC") == 0) ||
10059 (LocaleCompare(name,"ICM") == 0))
10062 if (ping_exclude_iCCP == MagickFalse)
10064 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10065 #if (PNG_LIBPNG_VER < 10500)
10066 (png_charp) GetStringInfoDatum(profile),
10068 (png_const_bytep) GetStringInfoDatum(profile),
10070 (png_uint_32) GetStringInfoLength(profile));
10076 if (ping_exclude_zCCP == MagickFalse)
10078 Magick_png_write_raw_profile(image_info,ping,ping_info,
10079 (unsigned char *) name,(unsigned char *) name,
10080 GetStringInfoDatum(profile),
10081 (png_uint_32) GetStringInfoLength(profile));
10085 if (logging != MagickFalse)
10086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10087 " Setting up text chunk with %s profile",name);
10089 name=GetNextImageProfile(image);
10094 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10095 if ((mng_info->have_write_global_srgb == 0) &&
10096 (image->rendering_intent != UndefinedIntent))
10098 if (ping_exclude_sRGB == MagickFalse)
10101 Note image rendering intent.
10103 if (logging != MagickFalse)
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " Setting up sRGB chunk");
10107 (void) png_set_sRGB(ping,ping_info,(
10108 Magick_RenderingIntent_to_PNG_RenderingIntent(
10109 image->rendering_intent)));
10113 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10116 if (ping_exclude_gAMA == MagickFalse &&
10117 (ping_exclude_sRGB == MagickFalse ||
10118 (image->gamma < .45 || image->gamma > .46)))
10120 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10124 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10126 if (logging != MagickFalse)
10127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10128 " Setting up gAMA chunk");
10130 png_set_gAMA(ping,ping_info,image->gamma);
10134 if (ping_exclude_cHRM == MagickFalse)
10136 if ((mng_info->have_write_global_chrm == 0) &&
10137 (image->chromaticity.red_primary.x != 0.0))
10140 Note image chromaticity.
10141 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10149 wp=image->chromaticity.white_point;
10150 rp=image->chromaticity.red_primary;
10151 gp=image->chromaticity.green_primary;
10152 bp=image->chromaticity.blue_primary;
10154 if (logging != MagickFalse)
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156 " Setting up cHRM chunk");
10158 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10164 ping_interlace_method=image_info->interlace != NoInterlace;
10166 if (mng_info->write_mng)
10167 png_set_sig_bytes(ping,8);
10169 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10171 if (mng_info->write_png_colortype != 0)
10173 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10174 if (ping_have_color != MagickFalse)
10176 ping_color_type = PNG_COLOR_TYPE_RGB;
10178 if (ping_bit_depth < 8)
10182 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10183 if (ping_have_color != MagickFalse)
10184 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10187 if (ping_need_colortype_warning != MagickFalse ||
10188 ((mng_info->write_png_depth &&
10189 (int) mng_info->write_png_depth != ping_bit_depth) ||
10190 (mng_info->write_png_colortype &&
10191 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10192 mng_info->write_png_colortype != 7 &&
10193 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10195 if (logging != MagickFalse)
10197 if (ping_need_colortype_warning != MagickFalse)
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " Image has transparency but tRNS chunk was excluded");
10203 if (mng_info->write_png_depth)
10205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10206 " Defined png:bit-depth=%u, Computed depth=%u",
10207 mng_info->write_png_depth,
10211 if (mng_info->write_png_colortype)
10213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10214 " Defined png:color-type=%u, Computed color type=%u",
10215 mng_info->write_png_colortype-1,
10221 "Cannot write image with defined png:bit-depth or png:color-type.");
10224 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10226 /* Add an opaque matte channel */
10227 image->alpha_trait = BlendPixelTrait;
10228 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10230 if (logging != MagickFalse)
10231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10232 " Added an opaque matte channel");
10235 if (number_transparent != 0 || number_semitransparent != 0)
10237 if (ping_color_type < 4)
10239 ping_have_tRNS=MagickTrue;
10240 if (logging != MagickFalse)
10241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10242 " Setting ping_have_tRNS=MagickTrue.");
10246 if (logging != MagickFalse)
10247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10248 " Writing PNG header chunks");
10250 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10251 ping_bit_depth,ping_color_type,
10252 ping_interlace_method,ping_compression_method,
10253 ping_filter_method);
10255 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10257 png_set_PLTE(ping,ping_info,palette,number_colors);
10259 if (logging != MagickFalse)
10261 for (i=0; i< (ssize_t) number_colors; i++)
10263 if (i < ping_num_trans)
10264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10265 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10267 (int) palette[i].red,
10268 (int) palette[i].green,
10269 (int) palette[i].blue,
10271 (int) ping_trans_alpha[i]);
10273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10274 " PLTE[%d] = (%d,%d,%d)",
10276 (int) palette[i].red,
10277 (int) palette[i].green,
10278 (int) palette[i].blue);
10283 if (ping_exclude_bKGD == MagickFalse)
10285 if (ping_have_bKGD != MagickFalse)
10287 png_set_bKGD(ping,ping_info,&ping_background);
10290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291 " Setting up bKGD chunk");
10292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10293 " background color = (%d,%d,%d)",
10294 (int) ping_background.red,
10295 (int) ping_background.green,
10296 (int) ping_background.blue);
10297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10298 " index = %d, gray=%d",
10299 (int) ping_background.index,
10300 (int) ping_background.gray);
10305 if (ping_exclude_pHYs == MagickFalse)
10307 if (ping_have_pHYs != MagickFalse)
10309 png_set_pHYs(ping,ping_info,
10310 ping_pHYs_x_resolution,
10311 ping_pHYs_y_resolution,
10312 ping_pHYs_unit_type);
10316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10317 " Setting up pHYs chunk");
10318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319 " x_resolution=%lu",
10320 (unsigned long) ping_pHYs_x_resolution);
10321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10322 " y_resolution=%lu",
10323 (unsigned long) ping_pHYs_y_resolution);
10324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10326 (unsigned long) ping_pHYs_unit_type);
10331 #if defined(PNG_oFFs_SUPPORTED)
10332 if (ping_exclude_oFFs == MagickFalse)
10334 if (image->page.x || image->page.y)
10336 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10337 (png_int_32) image->page.y, 0);
10339 if (logging != MagickFalse)
10340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10341 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10342 (int) image->page.x, (int) image->page.y);
10347 if (mng_info->need_blob != MagickFalse)
10349 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10351 png_error(ping,"WriteBlob Failed");
10353 ping_have_blob=MagickTrue;
10356 png_write_info_before_PLTE(ping, ping_info);
10358 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10360 if (logging != MagickFalse)
10362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10366 if (ping_color_type == 3)
10367 (void) png_set_tRNS(ping, ping_info,
10374 (void) png_set_tRNS(ping, ping_info,
10377 &ping_trans_color);
10379 if (logging != MagickFalse)
10381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10382 " tRNS color =(%d,%d,%d)",
10383 (int) ping_trans_color.red,
10384 (int) ping_trans_color.green,
10385 (int) ping_trans_color.blue);
10390 /* write any png-chunk-b profiles */
10391 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10393 png_write_info(ping,ping_info);
10395 /* write any PNG-chunk-m profiles */
10396 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10398 if (ping_exclude_vpAg == MagickFalse)
10400 if ((image->page.width != 0 && image->page.width != image->columns) ||
10401 (image->page.height != 0 && image->page.height != image->rows))
10406 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10407 PNGType(chunk,mng_vpAg);
10408 LogPNGChunk(logging,mng_vpAg,9L);
10409 PNGLong(chunk+4,(png_uint_32) image->page.width);
10410 PNGLong(chunk+8,(png_uint_32) image->page.height);
10411 chunk[12]=0; /* unit = pixels */
10412 (void) WriteBlob(image,13,chunk);
10413 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10417 #if (PNG_LIBPNG_VER == 10206)
10418 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10419 #define PNG_HAVE_IDAT 0x04
10420 ping->mode |= PNG_HAVE_IDAT;
10421 #undef PNG_HAVE_IDAT
10424 png_set_packing(ping);
10428 rowbytes=image->columns;
10429 if (image_depth > 8)
10431 switch (ping_color_type)
10433 case PNG_COLOR_TYPE_RGB:
10437 case PNG_COLOR_TYPE_GRAY_ALPHA:
10441 case PNG_COLOR_TYPE_RGBA:
10449 if (logging != MagickFalse)
10451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452 " Writing PNG image data");
10454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10457 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10458 sizeof(*ping_pixels));
10460 if (ping_pixels == (unsigned char *) NULL)
10461 png_error(ping,"Allocation of memory for pixels failed");
10464 Initialize image scanlines.
10466 quantum_info=AcquireQuantumInfo(image_info,image);
10467 if (quantum_info == (QuantumInfo *) NULL)
10468 png_error(ping,"Memory allocation for quantum_info failed");
10469 quantum_info->format=UndefinedQuantumFormat;
10470 quantum_info->depth=image_depth;
10471 num_passes=png_set_interlace_handling(ping);
10473 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10474 !mng_info->write_png32) &&
10475 (mng_info->IsPalette ||
10476 (image_info->type == BilevelType)) &&
10477 image_matte == MagickFalse &&
10478 ping_have_non_bw == MagickFalse)
10480 /* Palette, Bilevel, or Opaque Monochrome */
10481 register const Quantum
10484 quantum_info->depth=8;
10485 for (pass=0; pass < num_passes; pass++)
10488 Convert PseudoClass image to a PNG monochrome image.
10490 for (y=0; y < (ssize_t) image->rows; y++)
10492 if (logging != MagickFalse && y == 0)
10493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10494 " Writing row of pixels (0)");
10496 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10498 if (p == (const Quantum *) NULL)
10501 if (mng_info->IsPalette)
10503 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10504 quantum_info,GrayQuantum,ping_pixels,exception);
10505 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10506 mng_info->write_png_depth &&
10507 mng_info->write_png_depth != old_bit_depth)
10509 /* Undo pixel scaling */
10510 for (i=0; i < (ssize_t) image->columns; i++)
10511 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10512 >> (8-old_bit_depth));
10518 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10519 quantum_info,RedQuantum,ping_pixels,exception);
10522 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10523 for (i=0; i < (ssize_t) image->columns; i++)
10524 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10527 if (logging != MagickFalse && y == 0)
10528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " Writing row of pixels (1)");
10531 png_write_row(ping,ping_pixels);
10533 if (image->previous == (Image *) NULL)
10535 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10536 if (status == MagickFalse)
10542 else /* Not Palette, Bilevel, or Opaque Monochrome */
10544 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10545 !mng_info->write_png32) &&
10546 (image_matte != MagickFalse ||
10547 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10548 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10550 register const Quantum
10553 for (pass=0; pass < num_passes; pass++)
10556 for (y=0; y < (ssize_t) image->rows; y++)
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 if (mng_info->IsPalette)
10566 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10567 quantum_info,GrayQuantum,ping_pixels,exception);
10570 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10571 quantum_info,RedQuantum,ping_pixels,exception);
10573 if (logging != MagickFalse && y == 0)
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " Writing GRAY PNG pixels (2)");
10578 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10580 if (logging != MagickFalse && y == 0)
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10582 " Writing GRAY_ALPHA PNG pixels (2)");
10584 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10585 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10588 if (logging != MagickFalse && y == 0)
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " Writing row of pixels (2)");
10592 png_write_row(ping,ping_pixels);
10595 if (image->previous == (Image *) NULL)
10597 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10598 if (status == MagickFalse)
10606 register const Quantum
10609 for (pass=0; pass < num_passes; pass++)
10611 if ((image_depth > 8) || (mng_info->write_png24 ||
10612 mng_info->write_png32 ||
10613 (!mng_info->write_png8 && !mng_info->IsPalette)))
10615 for (y=0; y < (ssize_t) image->rows; y++)
10617 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10619 if (p == (const Quantum *) NULL)
10622 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10624 if (image->storage_class == DirectClass)
10625 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10626 quantum_info,RedQuantum,ping_pixels,exception);
10629 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10630 quantum_info,GrayQuantum,ping_pixels,exception);
10633 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10635 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10636 quantum_info,GrayAlphaQuantum,ping_pixels,
10639 if (logging != MagickFalse && y == 0)
10640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641 " Writing GRAY_ALPHA PNG pixels (3)");
10644 else if (image_matte != MagickFalse)
10645 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10646 quantum_info,RGBAQuantum,ping_pixels,exception);
10649 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10650 quantum_info,RGBQuantum,ping_pixels,exception);
10652 if (logging != MagickFalse && y == 0)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " Writing row of pixels (3)");
10656 png_write_row(ping,ping_pixels);
10661 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10662 mng_info->write_png32 ||
10663 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10665 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10666 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10668 if (logging != MagickFalse)
10669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10672 quantum_info->depth=8;
10676 for (y=0; y < (ssize_t) image->rows; y++)
10678 if (logging != MagickFalse && y == 0)
10679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10680 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10682 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10684 if (p == (const Quantum *) NULL)
10687 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10689 quantum_info->depth=image->depth;
10691 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10692 quantum_info,GrayQuantum,ping_pixels,exception);
10695 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10697 if (logging != MagickFalse && y == 0)
10698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10699 " Writing GRAY_ALPHA PNG pixels (4)");
10701 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10702 quantum_info,GrayAlphaQuantum,ping_pixels,
10708 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10709 quantum_info,IndexQuantum,ping_pixels,exception);
10711 if (logging != MagickFalse && y <= 2)
10713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10714 " Writing row of non-gray pixels (4)");
10716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10717 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10718 (int)ping_pixels[0],(int)ping_pixels[1]);
10721 png_write_row(ping,ping_pixels);
10725 if (image->previous == (Image *) NULL)
10727 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10728 if (status == MagickFalse)
10735 if (quantum_info != (QuantumInfo *) NULL)
10736 quantum_info=DestroyQuantumInfo(quantum_info);
10738 if (logging != MagickFalse)
10740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741 " Wrote PNG image data");
10743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744 " Width: %.20g",(double) ping_width);
10746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10747 " Height: %.20g",(double) ping_height);
10749 if (mng_info->write_png_depth)
10751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10752 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756 " PNG bit-depth written: %d",ping_bit_depth);
10758 if (mng_info->write_png_colortype)
10760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10761 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10765 " PNG color-type written: %d",ping_color_type);
10767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10768 " PNG Interlace method: %d",ping_interlace_method);
10771 Generate text chunks after IDAT.
10773 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10775 ResetImagePropertyIterator(image);
10776 property=GetNextImageProperty(image);
10777 while (property != (const char *) NULL)
10782 value=GetImageProperty(image,property,exception);
10784 /* Don't write any "png:" properties; those are just for "identify" */
10785 if (LocaleNCompare(property,"png:",4) != 0 &&
10787 /* Suppress density and units if we wrote a pHYs chunk */
10788 (ping_exclude_pHYs != MagickFalse ||
10789 LocaleCompare(property,"density") != 0 ||
10790 LocaleCompare(property,"units") != 0) &&
10792 /* Suppress the IM-generated Date:create and Date:modify */
10793 (ping_exclude_date == MagickFalse ||
10794 LocaleNCompare(property, "Date:",5) != 0))
10796 if (value != (const char *) NULL)
10799 #if PNG_LIBPNG_VER >= 14000
10800 text=(png_textp) png_malloc(ping,
10801 (png_alloc_size_t) sizeof(png_text));
10803 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10805 text[0].key=(char *) property;
10806 text[0].text=(char *) value;
10807 text[0].text_length=strlen(value);
10809 if (ping_exclude_tEXt != MagickFalse)
10810 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10812 else if (ping_exclude_zTXt != MagickFalse)
10813 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10817 text[0].compression=image_info->compression == NoCompression ||
10818 (image_info->compression == UndefinedCompression &&
10819 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10820 PNG_TEXT_COMPRESSION_zTXt ;
10823 if (logging != MagickFalse)
10825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10826 " Setting up text chunk");
10828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10829 " keyword: %s",text[0].key);
10832 png_set_text(ping,ping_info,text,1);
10833 png_free(ping,text);
10836 property=GetNextImageProperty(image);
10840 /* write any PNG-chunk-e profiles */
10841 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10843 if (logging != MagickFalse)
10844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845 " Writing PNG end info");
10847 png_write_end(ping,ping_info);
10849 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10851 if (mng_info->page.x || mng_info->page.y ||
10852 (ping_width != mng_info->page.width) ||
10853 (ping_height != mng_info->page.height))
10859 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10861 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10862 PNGType(chunk,mng_FRAM);
10863 LogPNGChunk(logging,mng_FRAM,27L);
10865 chunk[5]=0; /* frame name separator (no name) */
10866 chunk[6]=1; /* flag for changing delay, for next frame only */
10867 chunk[7]=0; /* flag for changing frame timeout */
10868 chunk[8]=1; /* flag for changing frame clipping for next frame */
10869 chunk[9]=0; /* flag for changing frame sync_id */
10870 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10871 chunk[14]=0; /* clipping boundaries delta type */
10872 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10874 (png_uint_32) (mng_info->page.x + ping_width));
10875 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10877 (png_uint_32) (mng_info->page.y + ping_height));
10878 (void) WriteBlob(image,31,chunk);
10879 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10880 mng_info->old_framing_mode=4;
10881 mng_info->framing_mode=1;
10885 mng_info->framing_mode=3;
10887 if (mng_info->write_mng && !mng_info->need_fram &&
10888 ((int) image->dispose == 3))
10889 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
10892 Free PNG resources.
10895 png_destroy_write_struct(&ping,&ping_info);
10897 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10899 if (ping_have_blob != MagickFalse)
10900 (void) CloseBlob(image);
10902 image_info=DestroyImageInfo(image_info);
10903 image=DestroyImage(image);
10905 /* Store bit depth actually written */
10906 s[0]=(char) ping_bit_depth;
10909 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10911 if (logging != MagickFalse)
10912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913 " exit WriteOnePNGImage()");
10915 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
10916 UnlockSemaphoreInfo(ping_semaphore);
10919 /* } for navigation to beginning of SETJMP-protected block. Revert to
10920 * Throwing an Exception when an error occurs.
10923 return(MagickTrue);
10924 /* End write one PNG image */
10929 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10933 % W r i t e P N G I m a g e %
10937 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10939 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10940 % Multiple-image Network Graphics (MNG) image file.
10942 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10944 % The format of the WritePNGImage method is:
10946 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10947 % Image *image,ExceptionInfo *exception)
10949 % A description of each parameter follows:
10951 % o image_info: the image info.
10953 % o image: The image.
10955 % o exception: return any errors or warnings in this structure.
10957 % Returns MagickTrue on success, MagickFalse on failure.
10959 % Communicating with the PNG encoder:
10961 % While the datastream written is always in PNG format and normally would
10962 % be given the "png" file extension, this method also writes the following
10963 % pseudo-formats which are subsets of png:
10965 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10966 % a depth greater than 8, the depth is reduced. If transparency
10967 % is present, the tRNS chunk must only have values 0 and 255
10968 % (i.e., transparency is binary: fully opaque or fully
10969 % transparent). If other values are present they will be
10970 % 50%-thresholded to binary transparency. If more than 256
10971 % colors are present, they will be quantized to the 4-4-4-1,
10972 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10973 % of any resulting fully-transparent pixels is changed to
10974 % the image's background color.
10976 % If you want better quantization or dithering of the colors
10977 % or alpha than that, you need to do it before calling the
10978 % PNG encoder. The pixels contain 8-bit indices even if
10979 % they could be represented with 1, 2, or 4 bits. Grayscale
10980 % images will be written as indexed PNG files even though the
10981 % PNG grayscale type might be slightly more efficient. Please
10982 % note that writing to the PNG8 format may result in loss
10983 % of color and alpha data.
10985 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10986 % chunk can be present to convey binary transparency by naming
10987 % one of the colors as transparent. The only loss incurred
10988 % is reduction of sample depth to 8. If the image has more
10989 % than one transparent color, has semitransparent pixels, or
10990 % has an opaque pixel with the same RGB components as the
10991 % transparent color, an image is not written.
10993 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10994 % transparency is permitted, i.e., the alpha sample for
10995 % each pixel can have any value from 0 to 255. The alpha
10996 % channel is present even if the image is fully opaque.
10997 % The only loss in data is the reduction of the sample depth
11000 % o -define: For more precise control of the PNG output, you can use the
11001 % Image options "png:bit-depth" and "png:color-type". These
11002 % can be set from the commandline with "-define" and also
11003 % from the application programming interfaces. The options
11004 % are case-independent and are converted to lowercase before
11005 % being passed to this encoder.
11007 % png:color-type can be 0, 2, 3, 4, or 6.
11009 % When png:color-type is 0 (Grayscale), png:bit-depth can
11010 % be 1, 2, 4, 8, or 16.
11012 % When png:color-type is 2 (RGB), png:bit-depth can
11015 % When png:color-type is 3 (Indexed), png:bit-depth can
11016 % be 1, 2, 4, or 8. This refers to the number of bits
11017 % used to store the index. The color samples always have
11018 % bit-depth 8 in indexed PNG files.
11020 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11021 % png:bit-depth can be 8 or 16.
11023 % If the image cannot be written without loss with the requested bit-depth
11024 % and color-type, a PNG file will not be written, and the encoder will
11025 % return MagickFalse.
11027 % Since image encoders should not be responsible for the "heavy lifting",
11028 % the user should make sure that ImageMagick has already reduced the
11029 % image depth and number of colors and limit transparency to binary
11030 % transparency prior to attempting to write the image with depth, color,
11031 % or transparency limitations.
11033 % Note that another definition, "png:bit-depth-written" exists, but it
11034 % is not intended for external use. It is only used internally by the
11035 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11037 % It is possible to request that the PNG encoder write previously-formatted
11038 % ancillary chunks in the output PNG file, using the "-profile" commandline
11039 % option as shown below or by setting the profile via a programming
11042 % -profile PNG-chunk-x:<file>
11044 % where x is a location flag and <file> is a file containing the chunk
11045 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11046 % This encoder will compute the chunk length and CRC, so those must not
11047 % be included in the file.
11049 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11050 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11051 % of the same type, then add a short unique string after the "x" to prevent
11052 % subsequent profiles from overwriting the preceding ones, e.g.,
11054 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11056 % As of version 6.6.6 the following optimizations are always done:
11058 % o 32-bit depth is reduced to 16.
11059 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11060 % high byte and low byte are identical.
11061 % o Palette is sorted to remove unused entries and to put a
11062 % transparent color first, if BUILD_PNG_PALETTE is defined.
11063 % o Opaque matte channel is removed when writing an indexed PNG.
11064 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11065 % this can be done without loss and a larger bit depth N was not
11066 % requested via the "-define png:bit-depth=N" option.
11067 % o If matte channel is present but only one transparent color is
11068 % present, RGB+tRNS is written instead of RGBA
11069 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11070 % was requested when converting an opaque image).
11072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11074 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11075 Image *image,ExceptionInfo *exception)
11080 have_mng_structure,
11096 assert(image_info != (const ImageInfo *) NULL);
11097 assert(image_info->signature == MagickSignature);
11098 assert(image != (Image *) NULL);
11099 assert(image->signature == MagickSignature);
11100 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11101 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11103 Allocate a MngInfo structure.
11105 have_mng_structure=MagickFalse;
11106 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11108 if (mng_info == (MngInfo *) NULL)
11109 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11112 Initialize members of the MngInfo structure.
11114 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11115 mng_info->image=image;
11116 mng_info->equal_backgrounds=MagickTrue;
11117 have_mng_structure=MagickTrue;
11119 /* See if user has requested a specific PNG subformat */
11121 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11122 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11123 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11125 value=GetImageOption(image_info,"png:format");
11127 if (value != (char *) NULL)
11129 if (LocaleCompare(value,"png8") == 0)
11131 mng_info->write_png8 = MagickTrue;
11132 mng_info->write_png24 = MagickFalse;
11133 mng_info->write_png32 = MagickFalse;
11136 else if (LocaleCompare(value,"png24") == 0)
11138 mng_info->write_png8 = MagickFalse;
11139 mng_info->write_png24 = MagickTrue;
11140 mng_info->write_png32 = MagickFalse;
11143 else if (LocaleCompare(value,"png32") == 0)
11145 mng_info->write_png8 = MagickFalse;
11146 mng_info->write_png24 = MagickFalse;
11147 mng_info->write_png32 = MagickTrue;
11150 if (mng_info->write_png8)
11152 mng_info->write_png_colortype = /* 3 */ 4;
11153 mng_info->write_png_depth = 8;
11157 if (mng_info->write_png24)
11159 mng_info->write_png_colortype = /* 2 */ 3;
11160 mng_info->write_png_depth = 8;
11163 if (image->alpha_trait == BlendPixelTrait)
11164 (void) SetImageType(image,TrueColorMatteType,exception);
11167 (void) SetImageType(image,TrueColorType,exception);
11169 (void) SyncImage(image,exception);
11172 if (mng_info->write_png32)
11174 mng_info->write_png_colortype = /* 6 */ 7;
11175 mng_info->write_png_depth = 8;
11178 if (image->alpha_trait == BlendPixelTrait)
11179 (void) SetImageType(image,TrueColorMatteType,exception);
11182 (void) SetImageType(image,TrueColorType,exception);
11184 (void) SyncImage(image,exception);
11187 value=GetImageOption(image_info,"png:bit-depth");
11189 if (value != (char *) NULL)
11191 if (LocaleCompare(value,"1") == 0)
11192 mng_info->write_png_depth = 1;
11194 else if (LocaleCompare(value,"2") == 0)
11195 mng_info->write_png_depth = 2;
11197 else if (LocaleCompare(value,"4") == 0)
11198 mng_info->write_png_depth = 4;
11200 else if (LocaleCompare(value,"8") == 0)
11201 mng_info->write_png_depth = 8;
11203 else if (LocaleCompare(value,"16") == 0)
11204 mng_info->write_png_depth = 16;
11207 (void) ThrowMagickException(exception,
11208 GetMagickModule(),CoderWarning,
11209 "ignoring invalid defined png:bit-depth",
11212 if (logging != MagickFalse)
11213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11214 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11217 value=GetImageOption(image_info,"png:color-type");
11219 if (value != (char *) NULL)
11221 /* We must store colortype+1 because 0 is a valid colortype */
11222 if (LocaleCompare(value,"0") == 0)
11223 mng_info->write_png_colortype = 1;
11225 else if (LocaleCompare(value,"1") == 0)
11226 mng_info->write_png_colortype = 2;
11228 else if (LocaleCompare(value,"2") == 0)
11229 mng_info->write_png_colortype = 3;
11231 else if (LocaleCompare(value,"3") == 0)
11232 mng_info->write_png_colortype = 4;
11234 else if (LocaleCompare(value,"4") == 0)
11235 mng_info->write_png_colortype = 5;
11237 else if (LocaleCompare(value,"6") == 0)
11238 mng_info->write_png_colortype = 7;
11241 (void) ThrowMagickException(exception,
11242 GetMagickModule(),CoderWarning,
11243 "ignoring invalid defined png:color-type",
11246 if (logging != MagickFalse)
11247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11248 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11251 /* Check for chunks to be excluded:
11253 * The default is to not exclude any known chunks except for any
11254 * listed in the "unused_chunks" array, above.
11256 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11257 * define (in the image properties or in the image artifacts)
11258 * or via a mng_info member. For convenience, in addition
11259 * to or instead of a comma-separated list of chunks, the
11260 * "exclude-chunk" string can be simply "all" or "none".
11262 * The exclude-chunk define takes priority over the mng_info.
11264 * A "png:include-chunk" define takes priority over both the
11265 * mng_info and the "png:exclude-chunk" define. Like the
11266 * "exclude-chunk" string, it can define "all" or "none" as
11267 * well as a comma-separated list. Chunks that are unknown to
11268 * ImageMagick are always excluded, regardless of their "copy-safe"
11269 * status according to the PNG specification, and even if they
11270 * appear in the "include-chunk" list. Such defines appearing among
11271 * the image options take priority over those found among the image
11274 * Finally, all chunks listed in the "unused_chunks" array are
11275 * automatically excluded, regardless of the other instructions
11278 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11279 * will not be written and the gAMA chunk will only be written if it
11280 * is not between .45 and .46, or approximately (1.0/2.2).
11282 * If you exclude tRNS and the image has transparency, the colortype
11283 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11285 * The -strip option causes StripImage() to set the png:include-chunk
11286 * artifact to "none,trns,gama".
11289 mng_info->ping_exclude_bKGD=MagickFalse;
11290 mng_info->ping_exclude_cHRM=MagickFalse;
11291 mng_info->ping_exclude_date=MagickFalse;
11292 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11293 mng_info->ping_exclude_gAMA=MagickFalse;
11294 mng_info->ping_exclude_iCCP=MagickFalse;
11295 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11296 mng_info->ping_exclude_oFFs=MagickFalse;
11297 mng_info->ping_exclude_pHYs=MagickFalse;
11298 mng_info->ping_exclude_sRGB=MagickFalse;
11299 mng_info->ping_exclude_tEXt=MagickFalse;
11300 mng_info->ping_exclude_tRNS=MagickFalse;
11301 mng_info->ping_exclude_vpAg=MagickFalse;
11302 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11303 mng_info->ping_exclude_zTXt=MagickFalse;
11305 mng_info->ping_preserve_colormap=MagickFalse;
11307 value=GetImageArtifact(image,"png:preserve-colormap");
11309 value=GetImageOption(image_info,"png:preserve-colormap");
11311 mng_info->ping_preserve_colormap=MagickTrue;
11313 /* Thes compression-level, compression-strategy, and compression-filter
11314 * defines take precedence over values from the -quality option.
11316 value=GetImageArtifact(image,"png:compression-level");
11318 value=GetImageOption(image_info,"png:compression-level");
11321 /* We have to add 1 to everything because 0 is a valid input,
11322 * and we want to use 0 (the default) to mean undefined.
11324 if (LocaleCompare(value,"0") == 0)
11325 mng_info->write_png_compression_level = 1;
11327 else if (LocaleCompare(value,"1") == 0)
11328 mng_info->write_png_compression_level = 2;
11330 else if (LocaleCompare(value,"2") == 0)
11331 mng_info->write_png_compression_level = 3;
11333 else if (LocaleCompare(value,"3") == 0)
11334 mng_info->write_png_compression_level = 4;
11336 else if (LocaleCompare(value,"4") == 0)
11337 mng_info->write_png_compression_level = 5;
11339 else if (LocaleCompare(value,"5") == 0)
11340 mng_info->write_png_compression_level = 6;
11342 else if (LocaleCompare(value,"6") == 0)
11343 mng_info->write_png_compression_level = 7;
11345 else if (LocaleCompare(value,"7") == 0)
11346 mng_info->write_png_compression_level = 8;
11348 else if (LocaleCompare(value,"8") == 0)
11349 mng_info->write_png_compression_level = 9;
11351 else if (LocaleCompare(value,"9") == 0)
11352 mng_info->write_png_compression_level = 10;
11355 (void) ThrowMagickException(exception,
11356 GetMagickModule(),CoderWarning,
11357 "ignoring invalid defined png:compression-level",
11361 value=GetImageArtifact(image,"png:compression-strategy");
11363 value=GetImageOption(image_info,"png:compression-strategy");
11367 if (LocaleCompare(value,"0") == 0)
11368 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11370 else if (LocaleCompare(value,"1") == 0)
11371 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11373 else if (LocaleCompare(value,"2") == 0)
11374 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11376 else if (LocaleCompare(value,"3") == 0)
11377 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11378 mng_info->write_png_compression_strategy = Z_RLE+1;
11380 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11383 else if (LocaleCompare(value,"4") == 0)
11384 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11385 mng_info->write_png_compression_strategy = Z_FIXED+1;
11387 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11391 (void) ThrowMagickException(exception,
11392 GetMagickModule(),CoderWarning,
11393 "ignoring invalid defined png:compression-strategy",
11397 value=GetImageArtifact(image,"png:compression-filter");
11399 value=GetImageOption(image_info,"png:compression-filter");
11403 /* To do: combinations of filters allowed by libpng
11404 * masks 0x08 through 0xf8
11406 * Implement this as a comma-separated list of 0,1,2,3,4,5
11407 * where 5 is a special case meaning PNG_ALL_FILTERS.
11410 if (LocaleCompare(value,"0") == 0)
11411 mng_info->write_png_compression_filter = 1;
11413 if (LocaleCompare(value,"1") == 0)
11414 mng_info->write_png_compression_filter = 2;
11416 else if (LocaleCompare(value,"2") == 0)
11417 mng_info->write_png_compression_filter = 3;
11419 else if (LocaleCompare(value,"3") == 0)
11420 mng_info->write_png_compression_filter = 4;
11422 else if (LocaleCompare(value,"4") == 0)
11423 mng_info->write_png_compression_filter = 5;
11425 else if (LocaleCompare(value,"5") == 0)
11426 mng_info->write_png_compression_filter = 6;
11429 (void) ThrowMagickException(exception,
11430 GetMagickModule(),CoderWarning,
11431 "ignoring invalid defined png:compression-filter",
11435 excluding=MagickFalse;
11437 for (source=0; source<1; source++)
11441 value=GetImageArtifact(image,"png:exclude-chunk");
11444 value=GetImageArtifact(image,"png:exclude-chunks");
11448 value=GetImageOption(image_info,"png:exclude-chunk");
11451 value=GetImageOption(image_info,"png:exclude-chunks");
11460 excluding=MagickTrue;
11462 if (logging != MagickFalse)
11465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466 " png:exclude-chunk=%s found in image artifacts.\n", value);
11468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11469 " png:exclude-chunk=%s found in image properties.\n", value);
11472 last=strlen(value);
11474 for (i=0; i<(int) last; i+=5)
11477 if (LocaleNCompare(value+i,"all",3) == 0)
11479 mng_info->ping_exclude_bKGD=MagickTrue;
11480 mng_info->ping_exclude_cHRM=MagickTrue;
11481 mng_info->ping_exclude_date=MagickTrue;
11482 mng_info->ping_exclude_EXIF=MagickTrue;
11483 mng_info->ping_exclude_gAMA=MagickTrue;
11484 mng_info->ping_exclude_iCCP=MagickTrue;
11485 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11486 mng_info->ping_exclude_oFFs=MagickTrue;
11487 mng_info->ping_exclude_pHYs=MagickTrue;
11488 mng_info->ping_exclude_sRGB=MagickTrue;
11489 mng_info->ping_exclude_tEXt=MagickTrue;
11490 mng_info->ping_exclude_tRNS=MagickTrue;
11491 mng_info->ping_exclude_vpAg=MagickTrue;
11492 mng_info->ping_exclude_zCCP=MagickTrue;
11493 mng_info->ping_exclude_zTXt=MagickTrue;
11497 if (LocaleNCompare(value+i,"none",4) == 0)
11499 mng_info->ping_exclude_bKGD=MagickFalse;
11500 mng_info->ping_exclude_cHRM=MagickFalse;
11501 mng_info->ping_exclude_date=MagickFalse;
11502 mng_info->ping_exclude_EXIF=MagickFalse;
11503 mng_info->ping_exclude_gAMA=MagickFalse;
11504 mng_info->ping_exclude_iCCP=MagickFalse;
11505 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11506 mng_info->ping_exclude_oFFs=MagickFalse;
11507 mng_info->ping_exclude_pHYs=MagickFalse;
11508 mng_info->ping_exclude_sRGB=MagickFalse;
11509 mng_info->ping_exclude_tEXt=MagickFalse;
11510 mng_info->ping_exclude_tRNS=MagickFalse;
11511 mng_info->ping_exclude_vpAg=MagickFalse;
11512 mng_info->ping_exclude_zCCP=MagickFalse;
11513 mng_info->ping_exclude_zTXt=MagickFalse;
11516 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11517 mng_info->ping_exclude_bKGD=MagickTrue;
11519 if (LocaleNCompare(value+i,"chrm",4) == 0)
11520 mng_info->ping_exclude_cHRM=MagickTrue;
11522 if (LocaleNCompare(value+i,"date",4) == 0)
11523 mng_info->ping_exclude_date=MagickTrue;
11525 if (LocaleNCompare(value+i,"exif",4) == 0)
11526 mng_info->ping_exclude_EXIF=MagickTrue;
11528 if (LocaleNCompare(value+i,"gama",4) == 0)
11529 mng_info->ping_exclude_gAMA=MagickTrue;
11531 if (LocaleNCompare(value+i,"iccp",4) == 0)
11532 mng_info->ping_exclude_iCCP=MagickTrue;
11535 if (LocaleNCompare(value+i,"itxt",4) == 0)
11536 mng_info->ping_exclude_iTXt=MagickTrue;
11539 if (LocaleNCompare(value+i,"gama",4) == 0)
11540 mng_info->ping_exclude_gAMA=MagickTrue;
11542 if (LocaleNCompare(value+i,"offs",4) == 0)
11543 mng_info->ping_exclude_oFFs=MagickTrue;
11545 if (LocaleNCompare(value+i,"phys",4) == 0)
11546 mng_info->ping_exclude_pHYs=MagickTrue;
11548 if (LocaleNCompare(value+i,"srgb",4) == 0)
11549 mng_info->ping_exclude_sRGB=MagickTrue;
11551 if (LocaleNCompare(value+i,"text",4) == 0)
11552 mng_info->ping_exclude_tEXt=MagickTrue;
11554 if (LocaleNCompare(value+i,"trns",4) == 0)
11555 mng_info->ping_exclude_tRNS=MagickTrue;
11557 if (LocaleNCompare(value+i,"vpag",4) == 0)
11558 mng_info->ping_exclude_vpAg=MagickTrue;
11560 if (LocaleNCompare(value+i,"zccp",4) == 0)
11561 mng_info->ping_exclude_zCCP=MagickTrue;
11563 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11564 mng_info->ping_exclude_zTXt=MagickTrue;
11570 for (source=0; source<1; source++)
11574 value=GetImageArtifact(image,"png:include-chunk");
11577 value=GetImageArtifact(image,"png:include-chunks");
11581 value=GetImageOption(image_info,"png:include-chunk");
11584 value=GetImageOption(image_info,"png:include-chunks");
11592 excluding=MagickTrue;
11594 if (logging != MagickFalse)
11597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11598 " png:include-chunk=%s found in image artifacts.\n", value);
11600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11601 " png:include-chunk=%s found in image properties.\n", value);
11604 last=strlen(value);
11606 for (i=0; i<(int) last; i+=5)
11608 if (LocaleNCompare(value+i,"all",3) == 0)
11610 mng_info->ping_exclude_bKGD=MagickFalse;
11611 mng_info->ping_exclude_cHRM=MagickFalse;
11612 mng_info->ping_exclude_date=MagickFalse;
11613 mng_info->ping_exclude_EXIF=MagickFalse;
11614 mng_info->ping_exclude_gAMA=MagickFalse;
11615 mng_info->ping_exclude_iCCP=MagickFalse;
11616 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11617 mng_info->ping_exclude_oFFs=MagickFalse;
11618 mng_info->ping_exclude_pHYs=MagickFalse;
11619 mng_info->ping_exclude_sRGB=MagickFalse;
11620 mng_info->ping_exclude_tEXt=MagickFalse;
11621 mng_info->ping_exclude_tRNS=MagickFalse;
11622 mng_info->ping_exclude_vpAg=MagickFalse;
11623 mng_info->ping_exclude_zCCP=MagickFalse;
11624 mng_info->ping_exclude_zTXt=MagickFalse;
11628 if (LocaleNCompare(value+i,"none",4) == 0)
11630 mng_info->ping_exclude_bKGD=MagickTrue;
11631 mng_info->ping_exclude_cHRM=MagickTrue;
11632 mng_info->ping_exclude_date=MagickTrue;
11633 mng_info->ping_exclude_EXIF=MagickTrue;
11634 mng_info->ping_exclude_gAMA=MagickTrue;
11635 mng_info->ping_exclude_iCCP=MagickTrue;
11636 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11637 mng_info->ping_exclude_oFFs=MagickTrue;
11638 mng_info->ping_exclude_pHYs=MagickTrue;
11639 mng_info->ping_exclude_sRGB=MagickTrue;
11640 mng_info->ping_exclude_tEXt=MagickTrue;
11641 mng_info->ping_exclude_tRNS=MagickTrue;
11642 mng_info->ping_exclude_vpAg=MagickTrue;
11643 mng_info->ping_exclude_zCCP=MagickTrue;
11644 mng_info->ping_exclude_zTXt=MagickTrue;
11647 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11648 mng_info->ping_exclude_bKGD=MagickFalse;
11650 if (LocaleNCompare(value+i,"chrm",4) == 0)
11651 mng_info->ping_exclude_cHRM=MagickFalse;
11653 if (LocaleNCompare(value+i,"date",4) == 0)
11654 mng_info->ping_exclude_date=MagickFalse;
11656 if (LocaleNCompare(value+i,"exif",4) == 0)
11657 mng_info->ping_exclude_EXIF=MagickFalse;
11659 if (LocaleNCompare(value+i,"gama",4) == 0)
11660 mng_info->ping_exclude_gAMA=MagickFalse;
11662 if (LocaleNCompare(value+i,"iccp",4) == 0)
11663 mng_info->ping_exclude_iCCP=MagickFalse;
11666 if (LocaleNCompare(value+i,"itxt",4) == 0)
11667 mng_info->ping_exclude_iTXt=MagickFalse;
11670 if (LocaleNCompare(value+i,"gama",4) == 0)
11671 mng_info->ping_exclude_gAMA=MagickFalse;
11673 if (LocaleNCompare(value+i,"offs",4) == 0)
11674 mng_info->ping_exclude_oFFs=MagickFalse;
11676 if (LocaleNCompare(value+i,"phys",4) == 0)
11677 mng_info->ping_exclude_pHYs=MagickFalse;
11679 if (LocaleNCompare(value+i,"srgb",4) == 0)
11680 mng_info->ping_exclude_sRGB=MagickFalse;
11682 if (LocaleNCompare(value+i,"text",4) == 0)
11683 mng_info->ping_exclude_tEXt=MagickFalse;
11685 if (LocaleNCompare(value+i,"trns",4) == 0)
11686 mng_info->ping_exclude_tRNS=MagickFalse;
11688 if (LocaleNCompare(value+i,"vpag",4) == 0)
11689 mng_info->ping_exclude_vpAg=MagickFalse;
11691 if (LocaleNCompare(value+i,"zccp",4) == 0)
11692 mng_info->ping_exclude_zCCP=MagickFalse;
11694 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11695 mng_info->ping_exclude_zTXt=MagickFalse;
11701 if (excluding != MagickFalse && logging != MagickFalse)
11703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11704 " Chunks to be excluded from the output png:");
11705 if (mng_info->ping_exclude_bKGD != MagickFalse)
11706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 if (mng_info->ping_exclude_cHRM != MagickFalse)
11709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11711 if (mng_info->ping_exclude_date != MagickFalse)
11712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11714 if (mng_info->ping_exclude_EXIF != MagickFalse)
11715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11717 if (mng_info->ping_exclude_gAMA != MagickFalse)
11718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11720 if (mng_info->ping_exclude_iCCP != MagickFalse)
11721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 if (mng_info->ping_exclude_iTXt != MagickFalse)
11725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11728 if (mng_info->ping_exclude_oFFs != MagickFalse)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11731 if (mng_info->ping_exclude_pHYs != MagickFalse)
11732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11734 if (mng_info->ping_exclude_sRGB != MagickFalse)
11735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11737 if (mng_info->ping_exclude_tEXt != MagickFalse)
11738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11740 if (mng_info->ping_exclude_tRNS != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11743 if (mng_info->ping_exclude_vpAg != MagickFalse)
11744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11746 if (mng_info->ping_exclude_zCCP != MagickFalse)
11747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11749 if (mng_info->ping_exclude_zTXt != MagickFalse)
11750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11754 mng_info->need_blob = MagickTrue;
11756 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11758 MngInfoFreeStruct(mng_info,&have_mng_structure);
11760 if (logging != MagickFalse)
11761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11766 #if defined(JNG_SUPPORTED)
11768 /* Write one JNG image */
11769 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11770 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11791 jng_alpha_compression_method,
11792 jng_alpha_sample_depth,
11800 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " Enter WriteOneJNGImage()");
11803 blob=(unsigned char *) NULL;
11804 jpeg_image=(Image *) NULL;
11805 jpeg_image_info=(ImageInfo *) NULL;
11808 transparent=image_info->type==GrayscaleMatteType ||
11809 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
11811 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11813 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11815 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11816 image_info->quality;
11818 if (jng_alpha_quality >= 1000)
11819 jng_alpha_quality /= 1000;
11825 /* Create JPEG blob, image, and image_info */
11826 if (logging != MagickFalse)
11827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11828 " Creating jpeg_image_info for alpha.");
11830 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11832 if (jpeg_image_info == (ImageInfo *) NULL)
11833 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11835 if (logging != MagickFalse)
11836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11837 " Creating jpeg_image.");
11839 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11840 if (jpeg_image == (Image *) NULL)
11841 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11842 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11843 jpeg_image->alpha_trait=UndefinedPixelTrait;
11844 jpeg_image->quality=jng_alpha_quality;
11845 jpeg_image_info->type=GrayscaleType;
11846 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11847 (void) AcquireUniqueFilename(jpeg_image->filename);
11848 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11849 "%s",jpeg_image->filename);
11853 jng_alpha_compression_method=0;
11855 jng_alpha_sample_depth=0;
11858 /* To do: check bit depth of PNG alpha channel */
11860 /* Check if image is grayscale. */
11861 if (image_info->type != TrueColorMatteType && image_info->type !=
11862 TrueColorType && IsImageGray(image,exception))
11865 if (logging != MagickFalse)
11867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11868 " JNG Quality = %d",(int) jng_quality);
11869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11870 " JNG Color Type = %d",jng_color_type);
11873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11874 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11876 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11878 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11884 if (jng_alpha_compression_method==0)
11889 /* Encode alpha as a grayscale PNG blob */
11890 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11892 if (logging != MagickFalse)
11893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11894 " Creating PNG blob.");
11897 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11898 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11899 jpeg_image_info->interlace=NoInterlace;
11901 /* Exclude all ancillary chunks */
11902 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11904 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11907 /* Retrieve sample depth used */
11908 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11909 if (value != (char *) NULL)
11910 jng_alpha_sample_depth= (unsigned int) value[0];
11914 /* Encode alpha as a grayscale JPEG blob */
11916 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11919 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11920 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11921 jpeg_image_info->interlace=NoInterlace;
11922 if (logging != MagickFalse)
11923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11924 " Creating blob.");
11925 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11927 jng_alpha_sample_depth=8;
11929 if (logging != MagickFalse)
11930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11931 " Successfully read jpeg_image into a blob, length=%.20g.",
11935 /* Destroy JPEG image and image_info */
11936 jpeg_image=DestroyImage(jpeg_image);
11937 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11938 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11941 /* Write JHDR chunk */
11942 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11943 PNGType(chunk,mng_JHDR);
11944 LogPNGChunk(logging,mng_JHDR,16L);
11945 PNGLong(chunk+4,(png_uint_32) image->columns);
11946 PNGLong(chunk+8,(png_uint_32) image->rows);
11947 chunk[12]=jng_color_type;
11948 chunk[13]=8; /* sample depth */
11949 chunk[14]=8; /*jng_image_compression_method */
11950 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11951 chunk[16]=jng_alpha_sample_depth;
11952 chunk[17]=jng_alpha_compression_method;
11953 chunk[18]=0; /*jng_alpha_filter_method */
11954 chunk[19]=0; /*jng_alpha_interlace_method */
11955 (void) WriteBlob(image,20,chunk);
11956 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11957 if (logging != MagickFalse)
11959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11960 " JNG width:%15lu",(unsigned long) image->columns);
11962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11963 " JNG height:%14lu",(unsigned long) image->rows);
11965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11966 " JNG color type:%10d",jng_color_type);
11968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11969 " JNG sample depth:%8d",8);
11971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11972 " JNG compression:%9d",8);
11974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11975 " JNG interlace:%11d",0);
11977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11978 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11981 " JNG alpha compression:%3d",jng_alpha_compression_method);
11983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11984 " JNG alpha filter:%8d",0);
11986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11987 " JNG alpha interlace:%5d",0);
11990 /* Write any JNG-chunk-b profiles */
11991 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11994 Write leading ancillary chunks
12000 Write JNG bKGD chunk
12011 if (jng_color_type == 8 || jng_color_type == 12)
12015 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12016 PNGType(chunk,mng_bKGD);
12017 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12018 red=ScaleQuantumToChar(image->background_color.red);
12019 green=ScaleQuantumToChar(image->background_color.green);
12020 blue=ScaleQuantumToChar(image->background_color.blue);
12027 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12028 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12031 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12034 Write JNG sRGB chunk
12036 (void) WriteBlobMSBULong(image,1L);
12037 PNGType(chunk,mng_sRGB);
12038 LogPNGChunk(logging,mng_sRGB,1L);
12040 if (image->rendering_intent != UndefinedIntent)
12041 chunk[4]=(unsigned char)
12042 Magick_RenderingIntent_to_PNG_RenderingIntent(
12043 (image->rendering_intent));
12046 chunk[4]=(unsigned char)
12047 Magick_RenderingIntent_to_PNG_RenderingIntent(
12048 (PerceptualIntent));
12050 (void) WriteBlob(image,5,chunk);
12051 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12055 if (image->gamma != 0.0)
12058 Write JNG gAMA chunk
12060 (void) WriteBlobMSBULong(image,4L);
12061 PNGType(chunk,mng_gAMA);
12062 LogPNGChunk(logging,mng_gAMA,4L);
12063 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12064 (void) WriteBlob(image,8,chunk);
12065 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12068 if ((mng_info->equal_chrms == MagickFalse) &&
12069 (image->chromaticity.red_primary.x != 0.0))
12075 Write JNG cHRM chunk
12077 (void) WriteBlobMSBULong(image,32L);
12078 PNGType(chunk,mng_cHRM);
12079 LogPNGChunk(logging,mng_cHRM,32L);
12080 primary=image->chromaticity.white_point;
12081 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12082 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12083 primary=image->chromaticity.red_primary;
12084 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12085 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12086 primary=image->chromaticity.green_primary;
12087 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12088 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12089 primary=image->chromaticity.blue_primary;
12090 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12091 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12092 (void) WriteBlob(image,36,chunk);
12093 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12097 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12100 Write JNG pHYs chunk
12102 (void) WriteBlobMSBULong(image,9L);
12103 PNGType(chunk,mng_pHYs);
12104 LogPNGChunk(logging,mng_pHYs,9L);
12105 if (image->units == PixelsPerInchResolution)
12107 PNGLong(chunk+4,(png_uint_32)
12108 (image->resolution.x*100.0/2.54+0.5));
12110 PNGLong(chunk+8,(png_uint_32)
12111 (image->resolution.y*100.0/2.54+0.5));
12118 if (image->units == PixelsPerCentimeterResolution)
12120 PNGLong(chunk+4,(png_uint_32)
12121 (image->resolution.x*100.0+0.5));
12123 PNGLong(chunk+8,(png_uint_32)
12124 (image->resolution.y*100.0+0.5));
12131 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12132 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12136 (void) WriteBlob(image,13,chunk);
12137 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12140 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12143 Write JNG oFFs chunk
12145 (void) WriteBlobMSBULong(image,9L);
12146 PNGType(chunk,mng_oFFs);
12147 LogPNGChunk(logging,mng_oFFs,9L);
12148 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12149 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12151 (void) WriteBlob(image,13,chunk);
12152 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12154 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12156 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12157 PNGType(chunk,mng_vpAg);
12158 LogPNGChunk(logging,mng_vpAg,9L);
12159 PNGLong(chunk+4,(png_uint_32) image->page.width);
12160 PNGLong(chunk+8,(png_uint_32) image->page.height);
12161 chunk[12]=0; /* unit = pixels */
12162 (void) WriteBlob(image,13,chunk);
12163 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12169 if (jng_alpha_compression_method==0)
12177 /* Write IDAT chunk header */
12178 if (logging != MagickFalse)
12179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180 " Write IDAT chunks from blob, length=%.20g.",(double)
12183 /* Copy IDAT chunks */
12186 for (i=8; i<(ssize_t) length; i+=len+12)
12188 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12191 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12193 /* Found an IDAT chunk. */
12194 (void) WriteBlobMSBULong(image,(size_t) len);
12195 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12196 (void) WriteBlob(image,(size_t) len+4,p);
12197 (void) WriteBlobMSBULong(image,
12198 crc32(0,p,(uInt) len+4));
12203 if (logging != MagickFalse)
12204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12205 " Skipping %c%c%c%c chunk, length=%.20g.",
12206 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12213 /* Write JDAA chunk header */
12214 if (logging != MagickFalse)
12215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12216 " Write JDAA chunk, length=%.20g.",(double) length);
12217 (void) WriteBlobMSBULong(image,(size_t) length);
12218 PNGType(chunk,mng_JDAA);
12219 LogPNGChunk(logging,mng_JDAA,length);
12220 /* Write JDAT chunk(s) data */
12221 (void) WriteBlob(image,4,chunk);
12222 (void) WriteBlob(image,length,blob);
12223 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12226 blob=(unsigned char *) RelinquishMagickMemory(blob);
12229 /* Encode image as a JPEG blob */
12230 if (logging != MagickFalse)
12231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12232 " Creating jpeg_image_info.");
12233 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12234 if (jpeg_image_info == (ImageInfo *) NULL)
12235 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12237 if (logging != MagickFalse)
12238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12239 " Creating jpeg_image.");
12241 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12242 if (jpeg_image == (Image *) NULL)
12243 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12244 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12246 (void) AcquireUniqueFilename(jpeg_image->filename);
12247 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12248 jpeg_image->filename);
12250 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12253 if (logging != MagickFalse)
12254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12255 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12256 (double) jpeg_image->rows);
12258 if (jng_color_type == 8 || jng_color_type == 12)
12259 jpeg_image_info->type=GrayscaleType;
12261 jpeg_image_info->quality=jng_quality;
12262 jpeg_image->quality=jng_quality;
12263 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12264 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12266 if (logging != MagickFalse)
12267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12268 " Creating blob.");
12270 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12272 if (logging != MagickFalse)
12274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12275 " Successfully read jpeg_image into a blob, length=%.20g.",
12278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12279 " Write JDAT chunk, length=%.20g.",(double) length);
12282 /* Write JDAT chunk(s) */
12283 (void) WriteBlobMSBULong(image,(size_t) length);
12284 PNGType(chunk,mng_JDAT);
12285 LogPNGChunk(logging,mng_JDAT,length);
12286 (void) WriteBlob(image,4,chunk);
12287 (void) WriteBlob(image,length,blob);
12288 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12290 jpeg_image=DestroyImage(jpeg_image);
12291 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12292 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12293 blob=(unsigned char *) RelinquishMagickMemory(blob);
12295 /* Write any JNG-chunk-e profiles */
12296 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12298 /* Write IEND chunk */
12299 (void) WriteBlobMSBULong(image,0L);
12300 PNGType(chunk,mng_IEND);
12301 LogPNGChunk(logging,mng_IEND,0);
12302 (void) WriteBlob(image,4,chunk);
12303 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12305 if (logging != MagickFalse)
12306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307 " exit WriteOneJNGImage()");
12314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12318 % W r i t e J N G I m a g e %
12322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12324 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12326 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12328 % The format of the WriteJNGImage method is:
12330 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12331 % Image *image,ExceptionInfo *exception)
12333 % A description of each parameter follows:
12335 % o image_info: the image info.
12337 % o image: The image.
12339 % o exception: return any errors or warnings in this structure.
12341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12343 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12344 ExceptionInfo *exception)
12347 have_mng_structure,
12357 assert(image_info != (const ImageInfo *) NULL);
12358 assert(image_info->signature == MagickSignature);
12359 assert(image != (Image *) NULL);
12360 assert(image->signature == MagickSignature);
12361 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12362 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12363 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12364 if (status == MagickFalse)
12368 Allocate a MngInfo structure.
12370 have_mng_structure=MagickFalse;
12371 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12372 if (mng_info == (MngInfo *) NULL)
12373 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12375 Initialize members of the MngInfo structure.
12377 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12378 mng_info->image=image;
12379 have_mng_structure=MagickTrue;
12381 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12383 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12384 (void) CloseBlob(image);
12386 (void) CatchImageException(image);
12387 MngInfoFreeStruct(mng_info,&have_mng_structure);
12388 if (logging != MagickFalse)
12389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12394 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12395 ExceptionInfo *exception)
12404 have_mng_structure,
12407 volatile MagickBooleanType
12419 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12420 defined(PNG_MNG_FEATURES_SUPPORTED)
12423 all_images_are_gray,
12433 volatile unsigned int
12444 #if (PNG_LIBPNG_VER < 10200)
12445 if (image_info->verbose)
12446 printf("Your PNG library (libpng-%s) is rather old.\n",
12447 PNG_LIBPNG_VER_STRING);
12453 assert(image_info != (const ImageInfo *) NULL);
12454 assert(image_info->signature == MagickSignature);
12455 assert(image != (Image *) NULL);
12456 assert(image->signature == MagickSignature);
12457 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12458 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12459 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12460 if (status == MagickFalse)
12464 Allocate a MngInfo structure.
12466 have_mng_structure=MagickFalse;
12467 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12468 if (mng_info == (MngInfo *) NULL)
12469 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12471 Initialize members of the MngInfo structure.
12473 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12474 mng_info->image=image;
12475 have_mng_structure=MagickTrue;
12476 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12479 * See if user has requested a specific PNG subformat to be used
12480 * for all of the PNGs in the MNG being written, e.g.,
12482 * convert *.png png8:animation.mng
12484 * To do: check -define png:bit_depth and png:color_type as well,
12485 * or perhaps use mng:bit_depth and mng:color_type instead for
12489 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12490 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12491 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12493 write_jng=MagickFalse;
12494 if (image_info->compression == JPEGCompression)
12495 write_jng=MagickTrue;
12497 mng_info->adjoin=image_info->adjoin &&
12498 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12500 if (logging != MagickFalse)
12502 /* Log some info about the input */
12506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12507 " Checking input image(s)");
12509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12510 " Image_info depth: %.20g",(double) image_info->depth);
12512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12513 " Type: %d",image_info->type);
12516 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12519 " Scene: %.20g",(double) scene++);
12521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12522 " Image depth: %.20g",(double) p->depth);
12524 if (p->alpha_trait)
12525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12532 if (p->storage_class == PseudoClass)
12533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12534 " Storage class: PseudoClass");
12537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12538 " Storage class: DirectClass");
12541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12542 " Number of colors: %.20g",(double) p->colors);
12545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546 " Number of colors: unspecified");
12548 if (mng_info->adjoin == MagickFalse)
12553 use_global_plte=MagickFalse;
12554 all_images_are_gray=MagickFalse;
12555 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12556 need_local_plte=MagickTrue;
12558 need_defi=MagickFalse;
12559 need_matte=MagickFalse;
12560 mng_info->framing_mode=1;
12561 mng_info->old_framing_mode=1;
12564 if (image_info->page != (char *) NULL)
12567 Determine image bounding box.
12569 SetGeometry(image,&mng_info->page);
12570 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12571 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12583 mng_info->page=image->page;
12584 need_geom=MagickTrue;
12585 if (mng_info->page.width || mng_info->page.height)
12586 need_geom=MagickFalse;
12588 Check all the scenes.
12590 initial_delay=image->delay;
12591 need_iterations=MagickFalse;
12592 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12593 mng_info->equal_physs=MagickTrue,
12594 mng_info->equal_gammas=MagickTrue;
12595 mng_info->equal_srgbs=MagickTrue;
12596 mng_info->equal_backgrounds=MagickTrue;
12598 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12599 defined(PNG_MNG_FEATURES_SUPPORTED)
12600 all_images_are_gray=MagickTrue;
12601 mng_info->equal_palettes=MagickFalse;
12602 need_local_plte=MagickFalse;
12604 for (next_image=image; next_image != (Image *) NULL; )
12608 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12609 mng_info->page.width=next_image->columns+next_image->page.x;
12611 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12612 mng_info->page.height=next_image->rows+next_image->page.y;
12615 if (next_image->page.x || next_image->page.y)
12616 need_defi=MagickTrue;
12618 if (next_image->alpha_trait)
12619 need_matte=MagickTrue;
12621 if ((int) next_image->dispose >= BackgroundDispose)
12622 if (next_image->alpha_trait || next_image->page.x || next_image->page.y ||
12623 ((next_image->columns < mng_info->page.width) &&
12624 (next_image->rows < mng_info->page.height)))
12625 mng_info->need_fram=MagickTrue;
12627 if (next_image->iterations)
12628 need_iterations=MagickTrue;
12630 final_delay=next_image->delay;
12632 if (final_delay != initial_delay || final_delay > 1UL*
12633 next_image->ticks_per_second)
12634 mng_info->need_fram=1;
12636 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12637 defined(PNG_MNG_FEATURES_SUPPORTED)
12639 check for global palette possibility.
12641 if (image->alpha_trait == BlendPixelTrait)
12642 need_local_plte=MagickTrue;
12644 if (need_local_plte == 0)
12646 if (IsImageGray(image,exception) == MagickFalse)
12647 all_images_are_gray=MagickFalse;
12648 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12649 if (use_global_plte == 0)
12650 use_global_plte=mng_info->equal_palettes;
12651 need_local_plte=!mng_info->equal_palettes;
12654 if (GetNextImageInList(next_image) != (Image *) NULL)
12656 if (next_image->background_color.red !=
12657 next_image->next->background_color.red ||
12658 next_image->background_color.green !=
12659 next_image->next->background_color.green ||
12660 next_image->background_color.blue !=
12661 next_image->next->background_color.blue)
12662 mng_info->equal_backgrounds=MagickFalse;
12664 if (next_image->gamma != next_image->next->gamma)
12665 mng_info->equal_gammas=MagickFalse;
12667 if (next_image->rendering_intent !=
12668 next_image->next->rendering_intent)
12669 mng_info->equal_srgbs=MagickFalse;
12671 if ((next_image->units != next_image->next->units) ||
12672 (next_image->resolution.x != next_image->next->resolution.x) ||
12673 (next_image->resolution.y != next_image->next->resolution.y))
12674 mng_info->equal_physs=MagickFalse;
12676 if (mng_info->equal_chrms)
12678 if (next_image->chromaticity.red_primary.x !=
12679 next_image->next->chromaticity.red_primary.x ||
12680 next_image->chromaticity.red_primary.y !=
12681 next_image->next->chromaticity.red_primary.y ||
12682 next_image->chromaticity.green_primary.x !=
12683 next_image->next->chromaticity.green_primary.x ||
12684 next_image->chromaticity.green_primary.y !=
12685 next_image->next->chromaticity.green_primary.y ||
12686 next_image->chromaticity.blue_primary.x !=
12687 next_image->next->chromaticity.blue_primary.x ||
12688 next_image->chromaticity.blue_primary.y !=
12689 next_image->next->chromaticity.blue_primary.y ||
12690 next_image->chromaticity.white_point.x !=
12691 next_image->next->chromaticity.white_point.x ||
12692 next_image->chromaticity.white_point.y !=
12693 next_image->next->chromaticity.white_point.y)
12694 mng_info->equal_chrms=MagickFalse;
12698 next_image=GetNextImageInList(next_image);
12700 if (image_count < 2)
12702 mng_info->equal_backgrounds=MagickFalse;
12703 mng_info->equal_chrms=MagickFalse;
12704 mng_info->equal_gammas=MagickFalse;
12705 mng_info->equal_srgbs=MagickFalse;
12706 mng_info->equal_physs=MagickFalse;
12707 use_global_plte=MagickFalse;
12708 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12709 need_local_plte=MagickTrue;
12711 need_iterations=MagickFalse;
12714 if (mng_info->need_fram == MagickFalse)
12717 Only certain framing rates 100/n are exactly representable without
12718 the FRAM chunk but we'll allow some slop in VLC files
12720 if (final_delay == 0)
12722 if (need_iterations != MagickFalse)
12725 It's probably a GIF with loop; don't run it *too* fast.
12727 if (mng_info->adjoin)
12730 (void) ThrowMagickException(exception,GetMagickModule(),
12732 "input has zero delay between all frames; assuming",
12737 mng_info->ticks_per_second=0;
12739 if (final_delay != 0)
12740 mng_info->ticks_per_second=(png_uint_32)
12741 (image->ticks_per_second/final_delay);
12742 if (final_delay > 50)
12743 mng_info->ticks_per_second=2;
12745 if (final_delay > 75)
12746 mng_info->ticks_per_second=1;
12748 if (final_delay > 125)
12749 mng_info->need_fram=MagickTrue;
12751 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12752 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12753 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12754 1UL*image->ticks_per_second))
12755 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12758 if (mng_info->need_fram != MagickFalse)
12759 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12761 If pseudocolor, we should also check to see if all the
12762 palettes are identical and write a global PLTE if they are.
12766 Write the MNG version 1.0 signature and MHDR chunk.
12768 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12769 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12770 PNGType(chunk,mng_MHDR);
12771 LogPNGChunk(logging,mng_MHDR,28L);
12772 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12773 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12774 PNGLong(chunk+12,mng_info->ticks_per_second);
12775 PNGLong(chunk+16,0L); /* layer count=unknown */
12776 PNGLong(chunk+20,0L); /* frame count=unknown */
12777 PNGLong(chunk+24,0L); /* play time=unknown */
12782 if (need_defi || mng_info->need_fram || use_global_plte)
12783 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12786 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12791 if (need_defi || mng_info->need_fram || use_global_plte)
12792 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12795 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12803 if (need_defi || mng_info->need_fram || use_global_plte)
12804 PNGLong(chunk+28,11L); /* simplicity=LC */
12807 PNGLong(chunk+28,9L); /* simplicity=VLC */
12812 if (need_defi || mng_info->need_fram || use_global_plte)
12813 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12816 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12819 (void) WriteBlob(image,32,chunk);
12820 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12821 option=GetImageOption(image_info,"mng:need-cacheoff");
12822 if (option != (const char *) NULL)
12828 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12830 PNGType(chunk,mng_nEED);
12831 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12832 (void) WriteBlobMSBULong(image,(size_t) length);
12833 LogPNGChunk(logging,mng_nEED,(size_t) length);
12835 (void) WriteBlob(image,length,chunk);
12836 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12838 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12839 (GetNextImageInList(image) != (Image *) NULL) &&
12840 (image->iterations != 1))
12843 Write MNG TERM chunk
12845 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12846 PNGType(chunk,mng_TERM);
12847 LogPNGChunk(logging,mng_TERM,10L);
12848 chunk[4]=3; /* repeat animation */
12849 chunk[5]=0; /* show last frame when done */
12850 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12851 final_delay/MagickMax(image->ticks_per_second,1)));
12853 if (image->iterations == 0)
12854 PNGLong(chunk+10,PNG_UINT_31_MAX);
12857 PNGLong(chunk+10,(png_uint_32) image->iterations);
12859 if (logging != MagickFalse)
12861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12862 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12863 final_delay/MagickMax(image->ticks_per_second,1)));
12865 if (image->iterations == 0)
12866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12867 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12871 " Image iterations: %.20g",(double) image->iterations);
12873 (void) WriteBlob(image,14,chunk);
12874 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12877 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12879 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12880 mng_info->equal_srgbs)
12883 Write MNG sRGB chunk
12885 (void) WriteBlobMSBULong(image,1L);
12886 PNGType(chunk,mng_sRGB);
12887 LogPNGChunk(logging,mng_sRGB,1L);
12889 if (image->rendering_intent != UndefinedIntent)
12890 chunk[4]=(unsigned char)
12891 Magick_RenderingIntent_to_PNG_RenderingIntent(
12892 (image->rendering_intent));
12895 chunk[4]=(unsigned char)
12896 Magick_RenderingIntent_to_PNG_RenderingIntent(
12897 (PerceptualIntent));
12899 (void) WriteBlob(image,5,chunk);
12900 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12901 mng_info->have_write_global_srgb=MagickTrue;
12906 if (image->gamma && mng_info->equal_gammas)
12909 Write MNG gAMA chunk
12911 (void) WriteBlobMSBULong(image,4L);
12912 PNGType(chunk,mng_gAMA);
12913 LogPNGChunk(logging,mng_gAMA,4L);
12914 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12915 (void) WriteBlob(image,8,chunk);
12916 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12917 mng_info->have_write_global_gama=MagickTrue;
12919 if (mng_info->equal_chrms)
12925 Write MNG cHRM chunk
12927 (void) WriteBlobMSBULong(image,32L);
12928 PNGType(chunk,mng_cHRM);
12929 LogPNGChunk(logging,mng_cHRM,32L);
12930 primary=image->chromaticity.white_point;
12931 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12932 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12933 primary=image->chromaticity.red_primary;
12934 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12935 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12936 primary=image->chromaticity.green_primary;
12937 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12938 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12939 primary=image->chromaticity.blue_primary;
12940 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12941 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12942 (void) WriteBlob(image,36,chunk);
12943 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12944 mng_info->have_write_global_chrm=MagickTrue;
12947 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12950 Write MNG pHYs chunk
12952 (void) WriteBlobMSBULong(image,9L);
12953 PNGType(chunk,mng_pHYs);
12954 LogPNGChunk(logging,mng_pHYs,9L);
12956 if (image->units == PixelsPerInchResolution)
12958 PNGLong(chunk+4,(png_uint_32)
12959 (image->resolution.x*100.0/2.54+0.5));
12961 PNGLong(chunk+8,(png_uint_32)
12962 (image->resolution.y*100.0/2.54+0.5));
12969 if (image->units == PixelsPerCentimeterResolution)
12971 PNGLong(chunk+4,(png_uint_32)
12972 (image->resolution.x*100.0+0.5));
12974 PNGLong(chunk+8,(png_uint_32)
12975 (image->resolution.y*100.0+0.5));
12982 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12983 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12987 (void) WriteBlob(image,13,chunk);
12988 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12991 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12992 or does not cover the entire frame.
12994 if (write_mng && (image->alpha_trait || image->page.x > 0 ||
12995 image->page.y > 0 || (image->page.width &&
12996 (image->page.width+image->page.x < mng_info->page.width))
12997 || (image->page.height && (image->page.height+image->page.y
12998 < mng_info->page.height))))
13000 (void) WriteBlobMSBULong(image,6L);
13001 PNGType(chunk,mng_BACK);
13002 LogPNGChunk(logging,mng_BACK,6L);
13003 red=ScaleQuantumToShort(image->background_color.red);
13004 green=ScaleQuantumToShort(image->background_color.green);
13005 blue=ScaleQuantumToShort(image->background_color.blue);
13006 PNGShort(chunk+4,red);
13007 PNGShort(chunk+6,green);
13008 PNGShort(chunk+8,blue);
13009 (void) WriteBlob(image,10,chunk);
13010 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13011 if (mng_info->equal_backgrounds)
13013 (void) WriteBlobMSBULong(image,6L);
13014 PNGType(chunk,mng_bKGD);
13015 LogPNGChunk(logging,mng_bKGD,6L);
13016 (void) WriteBlob(image,10,chunk);
13017 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13021 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13022 if ((need_local_plte == MagickFalse) &&
13023 (image->storage_class == PseudoClass) &&
13024 (all_images_are_gray == MagickFalse))
13030 Write MNG PLTE chunk
13032 data_length=3*image->colors;
13033 (void) WriteBlobMSBULong(image,data_length);
13034 PNGType(chunk,mng_PLTE);
13035 LogPNGChunk(logging,mng_PLTE,data_length);
13037 for (i=0; i < (ssize_t) image->colors; i++)
13039 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13040 image->colormap[i].red) & 0xff);
13041 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13042 image->colormap[i].green) & 0xff);
13043 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13044 image->colormap[i].blue) & 0xff);
13047 (void) WriteBlob(image,data_length+4,chunk);
13048 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13049 mng_info->have_write_global_plte=MagickTrue;
13055 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13056 defined(PNG_MNG_FEATURES_SUPPORTED)
13057 mng_info->equal_palettes=MagickFalse;
13061 if (mng_info->adjoin)
13063 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13064 defined(PNG_MNG_FEATURES_SUPPORTED)
13066 If we aren't using a global palette for the entire MNG, check to
13067 see if we can use one for two or more consecutive images.
13069 if (need_local_plte && use_global_plte && !all_images_are_gray)
13071 if (mng_info->IsPalette)
13074 When equal_palettes is true, this image has the same palette
13075 as the previous PseudoClass image
13077 mng_info->have_write_global_plte=mng_info->equal_palettes;
13078 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13079 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13082 Write MNG PLTE chunk
13087 data_length=3*image->colors;
13088 (void) WriteBlobMSBULong(image,data_length);
13089 PNGType(chunk,mng_PLTE);
13090 LogPNGChunk(logging,mng_PLTE,data_length);
13092 for (i=0; i < (ssize_t) image->colors; i++)
13094 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13095 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13096 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13099 (void) WriteBlob(image,data_length+4,chunk);
13100 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13101 (uInt) (data_length+4)));
13102 mng_info->have_write_global_plte=MagickTrue;
13106 mng_info->have_write_global_plte=MagickFalse;
13117 previous_x=mng_info->page.x;
13118 previous_y=mng_info->page.y;
13125 mng_info->page=image->page;
13126 if ((mng_info->page.x != previous_x) ||
13127 (mng_info->page.y != previous_y))
13129 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13130 PNGType(chunk,mng_DEFI);
13131 LogPNGChunk(logging,mng_DEFI,12L);
13132 chunk[4]=0; /* object 0 MSB */
13133 chunk[5]=0; /* object 0 LSB */
13134 chunk[6]=0; /* visible */
13135 chunk[7]=0; /* abstract */
13136 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13137 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13138 (void) WriteBlob(image,16,chunk);
13139 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13144 mng_info->write_mng=write_mng;
13146 if ((int) image->dispose >= 3)
13147 mng_info->framing_mode=3;
13149 if (mng_info->need_fram && mng_info->adjoin &&
13150 ((image->delay != mng_info->delay) ||
13151 (mng_info->framing_mode != mng_info->old_framing_mode)))
13153 if (image->delay == mng_info->delay)
13156 Write a MNG FRAM chunk with the new framing mode.
13158 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13159 PNGType(chunk,mng_FRAM);
13160 LogPNGChunk(logging,mng_FRAM,1L);
13161 chunk[4]=(unsigned char) mng_info->framing_mode;
13162 (void) WriteBlob(image,5,chunk);
13163 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13168 Write a MNG FRAM chunk with the delay.
13170 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13171 PNGType(chunk,mng_FRAM);
13172 LogPNGChunk(logging,mng_FRAM,10L);
13173 chunk[4]=(unsigned char) mng_info->framing_mode;
13174 chunk[5]=0; /* frame name separator (no name) */
13175 chunk[6]=2; /* flag for changing default delay */
13176 chunk[7]=0; /* flag for changing frame timeout */
13177 chunk[8]=0; /* flag for changing frame clipping */
13178 chunk[9]=0; /* flag for changing frame sync_id */
13179 PNGLong(chunk+10,(png_uint_32)
13180 ((mng_info->ticks_per_second*
13181 image->delay)/MagickMax(image->ticks_per_second,1)));
13182 (void) WriteBlob(image,14,chunk);
13183 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13184 mng_info->delay=(png_uint_32) image->delay;
13186 mng_info->old_framing_mode=mng_info->framing_mode;
13189 #if defined(JNG_SUPPORTED)
13190 if (image_info->compression == JPEGCompression)
13195 if (logging != MagickFalse)
13196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13197 " Writing JNG object.");
13198 /* To do: specify the desired alpha compression method. */
13199 write_info=CloneImageInfo(image_info);
13200 write_info->compression=UndefinedCompression;
13201 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13202 write_info=DestroyImageInfo(write_info);
13207 if (logging != MagickFalse)
13208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13209 " Writing PNG object.");
13211 mng_info->need_blob = MagickFalse;
13212 mng_info->ping_preserve_colormap = MagickFalse;
13214 /* We don't want any ancillary chunks written */
13215 mng_info->ping_exclude_bKGD=MagickTrue;
13216 mng_info->ping_exclude_cHRM=MagickTrue;
13217 mng_info->ping_exclude_date=MagickTrue;
13218 mng_info->ping_exclude_EXIF=MagickTrue;
13219 mng_info->ping_exclude_gAMA=MagickTrue;
13220 mng_info->ping_exclude_iCCP=MagickTrue;
13221 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13222 mng_info->ping_exclude_oFFs=MagickTrue;
13223 mng_info->ping_exclude_pHYs=MagickTrue;
13224 mng_info->ping_exclude_sRGB=MagickTrue;
13225 mng_info->ping_exclude_tEXt=MagickTrue;
13226 mng_info->ping_exclude_tRNS=MagickTrue;
13227 mng_info->ping_exclude_vpAg=MagickTrue;
13228 mng_info->ping_exclude_zCCP=MagickTrue;
13229 mng_info->ping_exclude_zTXt=MagickTrue;
13231 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13234 if (status == MagickFalse)
13236 MngInfoFreeStruct(mng_info,&have_mng_structure);
13237 (void) CloseBlob(image);
13238 return(MagickFalse);
13240 (void) CatchImageException(image);
13241 if (GetNextImageInList(image) == (Image *) NULL)
13243 image=SyncNextImageInList(image);
13244 status=SetImageProgress(image,SaveImagesTag,scene++,
13245 GetImageListLength(image));
13247 if (status == MagickFalse)
13250 } while (mng_info->adjoin);
13254 while (GetPreviousImageInList(image) != (Image *) NULL)
13255 image=GetPreviousImageInList(image);
13257 Write the MEND chunk.
13259 (void) WriteBlobMSBULong(image,0x00000000L);
13260 PNGType(chunk,mng_MEND);
13261 LogPNGChunk(logging,mng_MEND,0L);
13262 (void) WriteBlob(image,4,chunk);
13263 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13266 Relinquish resources.
13268 (void) CloseBlob(image);
13269 MngInfoFreeStruct(mng_info,&have_mng_structure);
13271 if (logging != MagickFalse)
13272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13274 return(MagickTrue);
13276 #else /* PNG_LIBPNG_VER > 10011 */
13278 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13281 printf("Your PNG library is too old: You have libpng-%s\n",
13282 PNG_LIBPNG_VER_STRING);
13284 ThrowBinaryException(CoderError,"PNG library is too old",
13285 image_info->filename);
13288 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13290 return(WritePNGImage(image_info,image));
13292 #endif /* PNG_LIBPNG_VER > 10011 */