2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2011 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/color.h"
51 #include "MagickCore/color-private.h"
52 #include "MagickCore/colormap.h"
53 #include "MagickCore/colorspace.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/constitute.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/histogram.h"
61 #include "MagickCore/image.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/layer.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/MagickCore.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/module.h"
69 #include "MagickCore/monitor.h"
70 #include "MagickCore/monitor-private.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/pixel.h"
73 #include "MagickCore/pixel-accessor.h"
74 #include "MagickCore/profile.h"
75 #include "MagickCore/property.h"
76 #include "MagickCore/quantum-private.h"
77 #include "MagickCore/resource_.h"
78 #include "MagickCore/semaphore.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/static.h"
81 #include "MagickCore/statistic.h"
82 #include "MagickCore/string_.h"
83 #include "MagickCore/string-private.h"
84 #include "MagickCore/transform.h"
85 #include "MagickCore/utility.h"
86 #if defined(MAGICKCORE_PNG_DELEGATE)
88 /* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
90 * migration to libpng-1.5, remove these defines and then
91 * fix any code that generates warnings.
93 /* #define PNG_DEPRECATED Use of this function is deprecated */
94 /* #define PNG_USE_RESULT The result of this function must be checked */
95 /* #define PNG_NORETURN This function does not return */
96 /* #define PNG_ALLOCATED The result of the function is new memory */
97 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
99 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
100 #define PNG_PTR_NORETURN
105 /* ImageMagick differences */
106 #define first_scene scene
108 #if PNG_LIBPNG_VER > 10011
110 Optional declarations. Define or undefine them as you like.
112 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
115 Features under construction. Define these to work on them.
117 #undef MNG_OBJECT_BUFFERS
118 #undef MNG_BASI_SUPPORTED
119 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
121 #if defined(MAGICKCORE_JPEG_DELEGATE)
122 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
124 #if !defined(RGBColorMatchExact)
125 #define IsPNGColorEqual(color,target) \
126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
131 /* Macros for left-bit-replication to ensure that pixels
132 * and PixelPackets all have the image->depth, and for use
133 * in PNG8 quantization.
137 /* LBR01: Replicate top bit */
139 #define LBR01PacketRed(pixelpacket) \
140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
143 #define LBR01PacketGreen(pixelpacket) \
144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
147 #define LBR01PacketBlue(pixelpacket) \
148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
151 #define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
155 #define LBR01PacketRGB(pixelpacket) \
157 LBR01PacketRed((pixelpacket)); \
158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
162 #define LBR01PacketRGBO(pixelpacket) \
164 LBR01PacketRGB((pixelpacket)); \
165 LBR01PacketAlpha((pixelpacket)); \
168 #define LBR01PixelRed(pixel) \
169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
172 #define LBR01PixelGreen(pixel) \
173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
176 #define LBR01PixelBlue(pixel) \
177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
180 #define LBR01PixelAlpha(pixel) \
181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
184 #define LBR01PixelRGB(pixel) \
186 LBR01PixelRed((pixel)); \
187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
191 #define LBR01PixelRGBA(pixel) \
193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
197 /* LBR02: Replicate top 2 bits */
199 #define LBR02PacketRed(pixelpacket) \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
205 #define LBR02PacketGreen(pixelpacket) \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
211 #define LBR02PacketBlue(pixelpacket) \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
217 #define LBR02PacketAlpha(pixelpacket) \
219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
224 #define LBR02PacketRGB(pixelpacket) \
226 LBR02PacketRed((pixelpacket)); \
227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
231 #define LBR02PacketRGBO(pixelpacket) \
233 LBR02PacketRGB((pixelpacket)); \
234 LBR02PacketAlpha((pixelpacket)); \
237 #define LBR02PixelRed(pixel) \
239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
241 SetPixelRed(image, ScaleCharToQuantum( \
242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
245 #define LBR02PixelGreen(pixel) \
247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
249 SetPixelGreen(image, ScaleCharToQuantum( \
250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
253 #define LBR02PixelBlue(pixel) \
255 unsigned char lbr_bits= \
256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
261 #define LBR02PixelAlpha(pixel) \
263 unsigned char lbr_bits= \
264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
270 #define LBR02PixelRGB(pixel) \
272 LBR02PixelRed((pixel)); \
273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
277 #define LBR02PixelRGBA(pixel) \
279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
283 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
286 #define LBR03PacketRed(pixelpacket) \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
292 #define LBR03PacketGreen(pixelpacket) \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
298 #define LBR03PacketBlue(pixelpacket) \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
305 #define LBR03PacketRGB(pixelpacket) \
307 LBR03PacketRed((pixelpacket)); \
308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
312 #define LBR03PixelRed(pixel) \
314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
319 #define LBR03Green(pixel) \
321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
326 #define LBR03Blue(pixel) \
328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
334 #define LBR03RGB(pixel) \
336 LBR03PixelRed((pixel)); \
337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
341 /* LBR04: Replicate top 4 bits */
343 #define LBR04PacketRed(pixelpacket) \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
348 #define LBR04PacketGreen(pixelpacket) \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
353 #define LBR04PacketBlue(pixelpacket) \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
358 #define LBR04PacketAlpha(pixelpacket) \
360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
364 #define LBR04PacketRGB(pixelpacket) \
366 LBR04PacketRed((pixelpacket)); \
367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
371 #define LBR04PacketRGBO(pixelpacket) \
373 LBR04PacketRGB((pixelpacket)); \
374 LBR04PacketAlpha((pixelpacket)); \
377 #define LBR04PixelRed(pixel) \
379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
384 #define LBR04PixelGreen(pixel) \
386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
391 #define LBR04PixelBlue(pixel) \
393 unsigned char lbr_bits= \
394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
398 #define LBR04PixelAlpha(pixel) \
400 unsigned char lbr_bits= \
401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
406 #define LBR04PixelRGB(pixel) \
408 LBR04PixelRed((pixel)); \
409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
413 #define LBR04PixelRGBA(pixel) \
415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
420 /* LBR08: Replicate top 8 bits */
422 #define LBR08PacketRed(pixelpacket) \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
427 #define LBR08PacketGreen(pixelpacket) \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
432 #define LBR08PacketBlue(pixelpacket) \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
437 #define LBR08PacketAlpha(pixelpacket) \
439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
443 #define LBR08PacketRGB(pixelpacket) \
445 LBR08PacketRed((pixelpacket)); \
446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
450 #define LBR08PacketRGBO(pixelpacket) \
452 LBR08PacketRGB((pixelpacket)); \
453 LBR08PacketAlpha((pixelpacket)); \
456 #define LBR08PixelRed(pixel) \
458 unsigned char lbr_bits= \
459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
463 #define LBR08PixelGreen(pixel) \
465 unsigned char lbr_bits= \
466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
470 #define LBR08PixelBlue(pixel) \
472 unsigned char lbr_bits= \
473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
477 #define LBR08PixelAlpha(pixel) \
479 unsigned char lbr_bits= \
480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
485 #define LBR08PixelRGB(pixel) \
487 LBR08PixelRed((pixel)); \
488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
492 #define LBR08PixelRGBA(pixel) \
494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
499 /* LBR16: Replicate top 16 bits */
501 #define LBR16PacketRed(pixelpacket) \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
506 #define LBR16PacketGreen(pixelpacket) \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
511 #define LBR16PacketBlue(pixelpacket) \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
516 #define LBR16PacketAlpha(pixelpacket) \
518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
522 #define LBR16PacketRGB(pixelpacket) \
524 LBR16PacketRed((pixelpacket)); \
525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
529 #define LBR16PacketRGBO(pixelpacket) \
531 LBR16PacketRGB((pixelpacket)); \
532 LBR16PacketAlpha((pixelpacket)); \
535 #define LBR16PixelRed(pixel) \
537 unsigned short lbr_bits= \
538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
542 #define LBR16PixelGreen(pixel) \
544 unsigned short lbr_bits= \
545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
549 #define LBR16PixelBlue(pixel) \
551 unsigned short lbr_bits= \
552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
556 #define LBR16PixelAlpha(pixel) \
558 unsigned short lbr_bits= \
559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
564 #define LBR16PixelRGB(pixel) \
566 LBR16PixelRed((pixel)); \
567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
571 #define LBR16PixelRGBA(pixel) \
573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
582 #ifndef SETJMP_IS_THREAD_SAFE
583 #define PNG_SETJMP_NOT_THREAD_SAFE
586 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
588 *ping_semaphore = (SemaphoreInfo *) NULL;
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
596 #define MNG_MAX_OBJECTS 256
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
602 o global PLTE too short
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
615 #ifdef PNG_MNG_FEATURES_SUPPORTED
616 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617 # define PNG_READ_EMPTY_PLTE_SUPPORTED
619 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
629 #ifndef PNG_UINT_31_MAX
630 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
639 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
674 #if defined(JNG_SUPPORTED)
675 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
684 Other known chunks that are not yet supported by ImageMagick:
685 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
695 typedef struct _MngBox
704 typedef struct _MngPair
711 #ifdef MNG_OBJECT_BUFFERS
712 typedef struct _MngBuffer
744 typedef struct _MngInfo
747 #ifdef MNG_OBJECT_BUFFERS
749 *ob[MNG_MAX_OBJECTS];
760 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
767 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
805 object_clip[MNG_MAX_OBJECTS];
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
825 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
854 global_phys_unit_type,
869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
876 #ifdef MNG_BASI_SUPPORTED
884 basi_compression_method,
886 basi_interlace_method,
909 /* Added at version 6.6.6-7 */
917 /* ping_exclude_iTXt, */
924 ping_exclude_zCCP, /* hex-encoded iCCP */
926 ping_preserve_colormap;
932 Forward declarations.
934 static MagickBooleanType
935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
937 static MagickBooleanType
938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
940 #if defined(JNG_SUPPORTED)
941 static MagickBooleanType
942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
945 #if PNG_LIBPNG_VER > 10011
948 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949 static MagickBooleanType
950 LosslessReduceDepthOK(Image *image)
952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
959 * We don't use the method GetImageDepth() because it doesn't check
960 * background and doesn't handle PseudoClass specially.
963 #define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
967 ok_to_reduce=MagickFalse;
969 if (image->depth >= 16)
976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
996 if (ok_to_reduce == MagickFalse)
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1010 for (y=0; y < (ssize_t) image->rows; y++)
1012 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1014 if (p == (const Quantum *) NULL)
1016 ok_to_reduce = MagickFalse;
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1026 MagickTrue : MagickFalse;
1028 if (ok_to_reduce == MagickFalse)
1031 p+=GetPixelChannels(image);
1038 if (ok_to_reduce != MagickFalse)
1040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1041 " OK to reduce PNG bit depth to 8 without loss of info");
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1046 " Not OK to reduce PNG bit depth to 8 without loss of info");
1050 return ok_to_reduce;
1052 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1055 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1059 case PerceptualIntent:
1062 case RelativeIntent:
1065 case SaturationIntent:
1068 case AbsoluteIntent:
1076 static RenderingIntent
1077 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1079 switch (ping_intent)
1082 return PerceptualIntent;
1085 return RelativeIntent;
1088 return SaturationIntent;
1091 return AbsoluteIntent;
1094 return UndefinedIntent;
1098 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1106 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120 % I m a g e I s G r a y %
1124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126 % Like IsImageGray except does not change DirectClass to PseudoClass %
1128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1130 static MagickBooleanType ImageIsGray(Image *image)
1132 register const Quantum
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1145 if (image->storage_class == PseudoClass)
1147 for (i=0; i < (ssize_t) image->colors; i++)
1148 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
1149 return(MagickFalse);
1152 for (y=0; y < (ssize_t) image->rows; y++)
1154 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1155 if (p == (const Quantum *) NULL)
1156 return(MagickFalse);
1157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1159 if (IsPixelGray(image,p) == MagickFalse)
1160 return(MagickFalse);
1161 p+=GetPixelChannels(image);
1166 #endif /* PNG_LIBPNG_VER > 10011 */
1167 #endif /* MAGICKCORE_PNG_DELEGATE */
1170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 % IsMNG() returns MagickTrue if the image format type, identified by the
1181 % magick string, is MNG.
1183 % The format of the IsMNG method is:
1185 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1187 % A description of each parameter follows:
1189 % o magick: compare image format pattern against these bytes.
1191 % o length: Specifies the length of the magick string.
1195 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1198 return(MagickFalse);
1200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1203 return(MagickFalse);
1207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217 % IsJNG() returns MagickTrue if the image format type, identified by the
1218 % magick string, is JNG.
1220 % The format of the IsJNG method is:
1222 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1224 % A description of each parameter follows:
1226 % o magick: compare image format pattern against these bytes.
1228 % o length: Specifies the length of the magick string.
1232 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1235 return(MagickFalse);
1237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1240 return(MagickFalse);
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 % IsPNG() returns MagickTrue if the image format type, identified by the
1255 % magick string, is PNG.
1257 % The format of the IsPNG method is:
1259 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1261 % A description of each parameter follows:
1263 % o magick: compare image format pattern against these bytes.
1265 % o length: Specifies the length of the magick string.
1268 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1271 return(MagickFalse);
1273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1276 return(MagickFalse);
1279 #if defined(MAGICKCORE_PNG_DELEGATE)
1280 #if defined(__cplusplus) || defined(c_plusplus)
1284 #if (PNG_LIBPNG_VER > 10011)
1285 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1299 static void PNGLong(png_bytep p,png_uint_32 value)
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1307 #if defined(JNG_SUPPORTED)
1308 static void PNGsLong(png_bytep p,png_int_32 value)
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1317 static void PNGShort(png_bytep p,png_uint_16 value)
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1323 static void PNGType(png_bytep p,png_bytep type)
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1328 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
1336 #endif /* PNG_LIBPNG_VER > 10011 */
1338 #if defined(__cplusplus) || defined(c_plusplus)
1342 #if PNG_LIBPNG_VER > 10011
1344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1348 % R e a d P N G I m a g e %
1352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1354 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355 % Multiple-image Network Graphics (MNG) image file and returns it. It
1356 % allocates the memory necessary for the new Image structure and returns a
1357 % pointer to the new image or set of images.
1359 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1361 % The format of the ReadPNGImage method is:
1363 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1365 % A description of each parameter follows:
1367 % o image_info: the image info.
1369 % o exception: return any errors or warnings in this structure.
1371 % To do, more or less in chronological order (as of version 5.5.2,
1372 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1374 % Get 16-bit cheap transparency working.
1376 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1378 % Preserve all unknown and not-yet-handled known chunks found in input
1379 % PNG file and copy them into output PNG files according to the PNG
1382 % (At this point, PNG encoding should be in full MNG compliance)
1384 % Provide options for choice of background to use when the MNG BACK
1385 % chunk is not present or is not mandatory (i.e., leave transparent,
1386 % user specified, MNG BACK, PNG bKGD)
1388 % Implement LOOP/ENDL [done, but could do discretionary loops more
1389 % efficiently by linking in the duplicate frames.].
1391 % Decode and act on the MHDR simplicity profile (offer option to reject
1392 % files or attempt to process them anyway when the profile isn't LC or VLC).
1394 % Upgrade to full MNG without Delta-PNG.
1396 % o BACK [done a while ago except for background image ID]
1397 % o MOVE [done 15 May 1999]
1398 % o CLIP [done 15 May 1999]
1399 % o DISC [done 19 May 1999]
1400 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401 % o SEEK [partially done 19 May 1999 (discard function only)]
1405 % o MNG-level tEXt/iTXt/zTXt
1410 % o iTXt (wait for libpng implementation).
1412 % Use the scene signature to discover when an identical scene is
1413 % being reused, and just point to the original image->exception instead
1414 % of storing another set of pixels. This not specific to MNG
1415 % but could be applied generally.
1417 % Upgrade to full MNG with Delta-PNG.
1419 % JNG tEXt/iTXt/zTXt
1421 % We will not attempt to read files containing the CgBI chunk.
1422 % They are really Xcode files meant for display on the iPhone.
1423 % These are not valid PNG files and it is impossible to recover
1424 % the original PNG from files that have been converted to Xcode-PNG,
1425 % since irretrievable loss of color data has occurred due to the
1426 % use of premultiplied alpha.
1429 #if defined(__cplusplus) || defined(c_plusplus)
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1438 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1443 image=(Image *) png_get_io_ptr(png_ptr);
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1455 (void) FormatLocaleString(msg,MaxTextExtent,
1456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1464 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1474 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1534 Skip the bKGD data byte and CRC.
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1550 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1555 image=(Image *) png_get_io_ptr(png_ptr);
1561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1568 static void png_flush_data(png_structp png_ptr)
1573 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574 static int PalettesAreEqual(Image *a,Image *b)
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
1582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
1585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
1588 for (i=0; i < (ssize_t) a->colors; i++)
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1596 return((int) MagickTrue);
1600 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1605 #ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
1611 if (mng_info->ob[i]->reference_count == 0)
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
1628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1629 mng_info->object_clip[i].top=0;
1630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1634 static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
1637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
1645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
1649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1654 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1660 if (box.left < box2.left)
1663 if (box.top < box2.top)
1666 if (box.right > box2.right)
1667 box.right=box2.right;
1669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
1675 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1687 if (delta_type != 0)
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1698 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1704 Read two ssize_ts from CLON, MOVE or PAST chunk
1706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1709 if (delta_type != 0)
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1718 static long mng_get_long(unsigned char *p)
1720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1723 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1728 image=(Image *) png_get_error_ptr(ping);
1730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1732 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1734 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1735 message,"`%s'",image->filename);
1737 #if (PNG_LIBPNG_VER < 10500)
1738 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1739 * are building with libpng-1.4.x and can be ignored.
1741 longjmp(ping->jmpbuf,1);
1743 png_longjmp(ping,1);
1747 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1752 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1753 png_error(ping, message);
1755 image=(Image *) png_get_error_ptr(ping);
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1758 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1760 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1761 message,"`%s'",image->filename);
1764 #ifdef PNG_USER_MEM_SUPPORTED
1765 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1767 #if (PNG_LIBPNG_VER < 10011)
1772 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1775 png_error("Insufficient memory.");
1780 return((png_voidp) AcquireMagickMemory((size_t) size));
1785 Free a pointer. It is removed from the list at the same time.
1787 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1790 ptr=RelinquishMagickMemory(ptr);
1791 return((png_free_ptr) NULL);
1795 #if defined(__cplusplus) || defined(c_plusplus)
1800 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1801 png_textp text,int ii)
1806 register unsigned char
1820 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1828 /* look for newline */
1832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1836 length=(png_uint_32) StringToLong(sp);
1838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1841 while (*sp != ' ' && *sp != '\n')
1844 /* allocate space */
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1852 profile=BlobToStringInfo((const void *) NULL,length);
1854 if (profile == (StringInfo *) NULL)
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1866 for (i=0; i < (ssize_t) nibbles; i++)
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1884 (*dp++)+=unhex[(int) *sp++];
1887 We have already read "Raw profile type.
1889 (void) SetImageProfile(image,&text[ii].key[17],profile);
1890 profile=DestroyStringInfo(profile);
1892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1898 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1905 /* The unknown chunk structure contains the chunk data:
1910 Note that libpng has already taken care of the CRC handling.
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1918 /* recognized vpAg */
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1928 image->page.width=(size_t) ((chunk->data[0] << 24) |
1929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1931 image->page.height=(size_t) ((chunk->data[4] << 24) |
1932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1945 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1949 % R e a d O n e P N G I m a g e %
1953 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1955 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956 % (minus the 8-byte signature) and returns it. It allocates the memory
1957 % necessary for the new Image structure and returns a pointer to the new
1960 % The format of the ReadOnePNGImage method is:
1962 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963 % ExceptionInfo *exception)
1965 % A description of each parameter follows:
1967 % o mng_info: Specifies a pointer to a MngInfo structure.
1969 % o image_info: the image info.
1971 % o exception: return any errors or warnings in this structure.
1974 static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1977 /* Read one PNG image */
1979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1994 ping_interlace_method,
1995 ping_compression_method,
2043 register unsigned char
2060 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2061 png_byte unused_chunks[]=
2063 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2064 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2065 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2066 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2067 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2068 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2072 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2073 " Enter ReadOnePNGImage()");
2075 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2076 LockSemaphoreInfo(ping_semaphore);
2079 #if (PNG_LIBPNG_VER < 10200)
2080 if (image_info->verbose)
2081 printf("Your PNG library (libpng-%s) is rather old.\n",
2082 PNG_LIBPNG_VER_STRING);
2085 #if (PNG_LIBPNG_VER >= 10400)
2086 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2087 if (image_info->verbose)
2089 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2090 PNG_LIBPNG_VER_STRING);
2091 printf("Please update it.\n");
2097 quantum_info = (QuantumInfo *) NULL;
2098 image=mng_info->image;
2100 if (logging != MagickFalse)
2101 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2102 " image->matte=%d",(int) image->matte);
2104 /* Set to an out-of-range color unless tRNS chunk is present */
2105 transparent_color.red=65537;
2106 transparent_color.green=65537;
2107 transparent_color.blue=65537;
2108 transparent_color.alpha=65537;
2112 num_raw_profiles = 0;
2115 Allocate the PNG structures
2117 #ifdef PNG_USER_MEM_SUPPORTED
2118 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
2119 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2120 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2122 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
2123 MagickPNGErrorHandler,MagickPNGWarningHandler);
2125 if (ping == (png_struct *) NULL)
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2128 ping_info=png_create_info_struct(ping);
2130 if (ping_info == (png_info *) NULL)
2132 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2136 end_info=png_create_info_struct(ping);
2138 if (end_info == (png_info *) NULL)
2140 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2144 ping_pixels=(unsigned char *) NULL;
2146 if (setjmp(png_jmpbuf(ping)))
2149 PNG image is corrupt.
2151 png_destroy_read_struct(&ping,&ping_info,&end_info);
2152 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2153 UnlockSemaphoreInfo(ping_semaphore);
2155 if (logging != MagickFalse)
2156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " exit ReadOnePNGImage() with error.");
2159 if (image != (Image *) NULL)
2161 InheritException(exception,&image->exception);
2165 return(GetFirstImageInList(image));
2168 Prepare PNG for reading.
2171 mng_info->image_found++;
2172 png_set_sig_bytes(ping,8);
2174 if (LocaleCompare(image_info->magick,"MNG") == 0)
2176 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2177 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2178 png_set_read_fn(ping,image,png_get_data);
2180 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2181 png_permit_empty_plte(ping,MagickTrue);
2182 png_set_read_fn(ping,image,png_get_data);
2184 mng_info->image=image;
2185 mng_info->bytes_in_read_buffer=0;
2186 mng_info->found_empty_plte=MagickFalse;
2187 mng_info->have_saved_bkgd_index=MagickFalse;
2188 png_set_read_fn(ping,mng_info,mng_get_data);
2194 png_set_read_fn(ping,image,png_get_data);
2196 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2197 /* Ignore unused chunks and all unknown chunks except for vpAg */
2198 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2199 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2200 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2201 (int)sizeof(unused_chunks)/5);
2202 /* Callback for other unknown chunks */
2203 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2206 #if (PNG_LIBPNG_VER < 10400)
2207 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2208 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2209 /* Disable thread-unsafe features of pnggccrd */
2210 if (png_access_version_number() >= 10200)
2212 png_uint_32 mmx_disable_mask=0;
2213 png_uint_32 asm_flags;
2215 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2218 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2219 asm_flags=png_get_asm_flags(ping);
2220 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2225 png_read_info(ping,ping_info);
2227 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2228 &ping_bit_depth,&ping_color_type,
2229 &ping_interlace_method,&ping_compression_method,
2230 &ping_filter_method);
2232 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2235 (void) png_get_bKGD(ping, ping_info, &ping_background);
2237 if (ping_bit_depth < 8)
2239 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2241 png_set_packing(ping);
2246 image->depth=ping_bit_depth;
2247 image->depth=GetImageQuantumDepth(image,MagickFalse);
2248 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2249 if (logging != MagickFalse)
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2252 " PNG width: %.20g, height: %.20g",
2253 (double) ping_width, (double) ping_height);
2255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG color_type: %d, bit_depth: %d",
2257 ping_color_type, ping_bit_depth);
2259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG compression_method: %d",
2261 ping_compression_method);
2263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " PNG interlace_method: %d, filter_method: %d",
2265 ping_interlace_method,ping_filter_method);
2268 #ifdef PNG_READ_iCCP_SUPPORTED
2269 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2274 #if (PNG_LIBPNG_VER < 10500)
2288 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2291 if (profile_length != 0)
2296 if (logging != MagickFalse)
2297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2298 " Reading PNG iCCP chunk.");
2299 profile=BlobToStringInfo(info,profile_length);
2300 if (profile == (StringInfo *) NULL)
2302 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2303 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2304 "unable to copy profile");
2305 return((Image *) NULL);
2307 SetStringInfoDatum(profile,(const unsigned char *) info);
2308 (void) SetImageProfile(image,"icc",profile);
2309 profile=DestroyStringInfo(profile);
2313 #if defined(PNG_READ_sRGB_SUPPORTED)
2315 if (mng_info->have_global_srgb)
2316 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2317 (mng_info->global_srgb_intent);
2319 if (png_get_sRGB(ping,ping_info,&intent))
2321 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2331 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2332 if (mng_info->have_global_gama)
2333 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2335 if (png_get_gAMA(ping,ping_info,&file_gamma))
2337 image->gamma=(float) file_gamma;
2338 if (logging != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2340 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2343 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2345 if (mng_info->have_global_chrm != MagickFalse)
2347 (void) png_set_cHRM(ping,ping_info,
2348 mng_info->global_chrm.white_point.x,
2349 mng_info->global_chrm.white_point.y,
2350 mng_info->global_chrm.red_primary.x,
2351 mng_info->global_chrm.red_primary.y,
2352 mng_info->global_chrm.green_primary.x,
2353 mng_info->global_chrm.green_primary.y,
2354 mng_info->global_chrm.blue_primary.x,
2355 mng_info->global_chrm.blue_primary.y);
2359 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2361 (void) png_get_cHRM(ping,ping_info,
2362 &image->chromaticity.white_point.x,
2363 &image->chromaticity.white_point.y,
2364 &image->chromaticity.red_primary.x,
2365 &image->chromaticity.red_primary.y,
2366 &image->chromaticity.green_primary.x,
2367 &image->chromaticity.green_primary.y,
2368 &image->chromaticity.blue_primary.x,
2369 &image->chromaticity.blue_primary.y);
2371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " Reading PNG cHRM chunk.");
2376 if (image->rendering_intent != UndefinedIntent)
2378 png_set_sRGB(ping,ping_info,
2379 Magick_RenderingIntent_to_PNG_RenderingIntent
2380 (image->rendering_intent));
2381 png_set_gAMA(ping,ping_info,0.45455f);
2382 png_set_cHRM(ping,ping_info,
2383 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2384 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2386 #if defined(PNG_oFFs_SUPPORTED)
2387 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2389 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2390 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2392 if (logging != MagickFalse)
2393 if (image->page.x || image->page.y)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2395 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2396 image->page.x,(double) image->page.y);
2399 #if defined(PNG_pHYs_SUPPORTED)
2400 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2402 if (mng_info->have_global_phys)
2404 png_set_pHYs(ping,ping_info,
2405 mng_info->global_x_pixels_per_unit,
2406 mng_info->global_y_pixels_per_unit,
2407 mng_info->global_phys_unit_type);
2411 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2414 Set image resolution.
2416 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2418 image->x_resolution=(double) x_resolution;
2419 image->y_resolution=(double) y_resolution;
2421 if (unit_type == PNG_RESOLUTION_METER)
2423 image->units=PixelsPerCentimeterResolution;
2424 image->x_resolution=(double) x_resolution/100.0;
2425 image->y_resolution=(double) y_resolution/100.0;
2428 if (logging != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2430 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2431 (double) x_resolution,(double) y_resolution,unit_type);
2435 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2443 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2445 if ((number_colors == 0) &&
2446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2448 if (mng_info->global_plte_length)
2450 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2451 (int) mng_info->global_plte_length);
2453 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2454 if (mng_info->global_trns_length)
2456 if (mng_info->global_trns_length >
2457 mng_info->global_plte_length)
2458 (void) ThrowMagickException(&image->exception,
2459 GetMagickModule(),CoderError,
2460 "global tRNS has more entries than global PLTE",
2461 "`%s'",image_info->filename);
2462 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2463 (int) mng_info->global_trns_length,NULL);
2465 #ifdef PNG_READ_bKGD_SUPPORTED
2467 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2468 mng_info->have_saved_bkgd_index ||
2470 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2475 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2476 if (mng_info->have_saved_bkgd_index)
2477 background.index=mng_info->saved_bkgd_index;
2479 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2480 background.index=ping_background->index;
2482 background.red=(png_uint_16)
2483 mng_info->global_plte[background.index].red;
2485 background.green=(png_uint_16)
2486 mng_info->global_plte[background.index].green;
2488 background.blue=(png_uint_16)
2489 mng_info->global_plte[background.index].blue;
2491 background.gray=(png_uint_16)
2492 mng_info->global_plte[background.index].green;
2494 png_set_bKGD(ping,ping_info,&background);
2499 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2500 CoderError,"No global PLTE in file","`%s'",
2501 image_info->filename);
2505 #ifdef PNG_READ_bKGD_SUPPORTED
2506 if (mng_info->have_global_bkgd &&
2507 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2508 image->background_color=mng_info->mng_global_bkgd;
2510 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2516 Set image background color.
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " Reading PNG bKGD chunk.");
2522 /* Scale background components to 16-bit, then scale
2525 if (logging != MagickFalse)
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " raw ping_background=(%d,%d,%d).",ping_background->red,
2528 ping_background->green,ping_background->blue);
2532 if (ping_bit_depth == 1)
2535 else if (ping_bit_depth == 2)
2538 else if (ping_bit_depth == 4)
2541 if (ping_bit_depth <= 8)
2544 ping_background->red *= bkgd_scale;
2545 ping_background->green *= bkgd_scale;
2546 ping_background->blue *= bkgd_scale;
2548 if (logging != MagickFalse)
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " bkgd_scale=%d.",bkgd_scale);
2553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 " ping_background=(%d,%d,%d).",ping_background->red,
2555 ping_background->green,ping_background->blue);
2558 image->background_color.red=
2559 ScaleShortToQuantum(ping_background->red);
2561 image->background_color.green=
2562 ScaleShortToQuantum(ping_background->green);
2564 image->background_color.blue=
2565 ScaleShortToQuantum(ping_background->blue);
2567 image->background_color.alpha=OpaqueAlpha;
2569 if (logging != MagickFalse)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2571 " image->background_color=(%.20g,%.20g,%.20g).",
2572 (double) image->background_color.red,
2573 (double) image->background_color.green,
2574 (double) image->background_color.blue);
2576 #endif /* PNG_READ_bKGD_SUPPORTED */
2578 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2581 Image has a tRNS chunk.
2589 if (logging != MagickFalse)
2590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2591 " Reading PNG tRNS chunk.");
2593 max_sample = (int) ((one << ping_bit_depth) - 1);
2595 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2596 (int)ping_trans_color->gray > max_sample) ||
2597 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2598 ((int)ping_trans_color->red > max_sample ||
2599 (int)ping_trans_color->green > max_sample ||
2600 (int)ping_trans_color->blue > max_sample)))
2602 if (logging != MagickFalse)
2603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2604 " Ignoring PNG tRNS chunk with out-of-range sample.");
2605 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2606 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2607 image->matte=MagickFalse;
2614 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2616 /* Scale transparent_color to short */
2617 transparent_color.red= scale_to_short*ping_trans_color->red;
2618 transparent_color.green= scale_to_short*ping_trans_color->green;
2619 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2620 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2622 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2624 if (logging != MagickFalse)
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2630 " scaled graylevel is %d.",transparent_color.alpha);
2632 transparent_color.red=transparent_color.alpha;
2633 transparent_color.green=transparent_color.alpha;
2634 transparent_color.blue=transparent_color.alpha;
2638 #if defined(PNG_READ_sBIT_SUPPORTED)
2639 if (mng_info->have_global_sbit)
2641 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2642 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2645 num_passes=png_set_interlace_handling(ping);
2647 png_read_update_info(ping,ping_info);
2649 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2652 Initialize image structure.
2654 mng_info->image_box.left=0;
2655 mng_info->image_box.right=(ssize_t) ping_width;
2656 mng_info->image_box.top=0;
2657 mng_info->image_box.bottom=(ssize_t) ping_height;
2658 if (mng_info->mng_type == 0)
2660 mng_info->mng_width=ping_width;
2661 mng_info->mng_height=ping_height;
2662 mng_info->frame=mng_info->image_box;
2663 mng_info->clip=mng_info->image_box;
2668 image->page.y=mng_info->y_off[mng_info->object_id];
2671 image->compression=ZipCompression;
2672 image->columns=ping_width;
2673 image->rows=ping_height;
2674 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2675 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2680 image->storage_class=PseudoClass;
2682 image->colors=one << ping_bit_depth;
2683 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2684 if (image->colors > 256)
2687 if (image->colors > 65536L)
2688 image->colors=65536L;
2690 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2698 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2699 image->colors=(size_t) number_colors;
2701 if (logging != MagickFalse)
2702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2703 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2707 if (image->storage_class == PseudoClass)
2710 Initialize image colormap.
2712 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2713 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2715 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2723 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2725 for (i=0; i < (ssize_t) number_colors; i++)
2727 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2728 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2729 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2732 for ( ; i < (ssize_t) image->colors; i++)
2734 image->colormap[i].red=0;
2735 image->colormap[i].green=0;
2736 image->colormap[i].blue=0;
2745 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2750 for (i=0; i < (ssize_t) image->colors; i++)
2752 image->colormap[i].red=(Quantum) (i*scale);
2753 image->colormap[i].green=(Quantum) (i*scale);
2754 image->colormap[i].blue=(Quantum) (i*scale);
2759 /* Set some properties for reporting by "identify" */
2764 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2765 ping_interlace_method in value */
2767 (void) FormatLocaleString(msg,MaxTextExtent,
2768 "%d, %d",(int) ping_width, (int) ping_height);
2769 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
2771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2778 (int) ping_interlace_method);
2779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
2783 Read image scanlines.
2785 if (image->delay != 0)
2786 mng_info->scenes_found++;
2788 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2789 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2790 (image_info->first_scene+image_info->number_scenes))))
2792 if (logging != MagickFalse)
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2794 " Skipping PNG image data for scene %.20g",(double)
2795 mng_info->scenes_found-1);
2796 png_destroy_read_struct(&ping,&ping_info,&end_info);
2797 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2798 UnlockSemaphoreInfo(ping_semaphore);
2800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " exit ReadOnePNGImage().");
2807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " Reading PNG IDAT chunk(s)");
2812 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2813 ping_rowbytes*sizeof(*ping_pixels));
2816 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2817 sizeof(*ping_pixels));
2819 if (ping_pixels == (unsigned char *) NULL)
2820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " Converting PNG pixels to pixel packets");
2826 Convert PNG pixels to pixel packets.
2828 if (setjmp(png_jmpbuf(ping)))
2831 PNG image is corrupt.
2833 png_destroy_read_struct(&ping,&ping_info,&end_info);
2834 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2835 UnlockSemaphoreInfo(ping_semaphore);
2837 if (quantum_info != (QuantumInfo *) NULL)
2838 quantum_info = DestroyQuantumInfo(quantum_info);
2840 if (ping_pixels != (unsigned char *) NULL)
2841 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " exit ReadOnePNGImage() with error.");
2847 if (image != (Image *) NULL)
2849 InheritException(exception,&image->exception);
2853 return(GetFirstImageInList(image));
2856 quantum_info=AcquireQuantumInfo(image_info,image);
2858 if (quantum_info == (QuantumInfo *) NULL)
2859 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2864 found_transparent_pixel;
2866 found_transparent_pixel=MagickFalse;
2868 if (image->storage_class == DirectClass)
2870 for (pass=0; pass < num_passes; pass++)
2873 Convert image to DirectClass pixel packets.
2875 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2879 depth=(ssize_t) ping_bit_depth;
2881 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2882 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2883 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2884 MagickTrue : MagickFalse;
2886 for (y=0; y < (ssize_t) image->rows; y++)
2889 row_offset=ping_rowbytes*y;
2894 png_read_row(ping,ping_pixels+row_offset,NULL);
2895 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2897 if (q == (Quantum *) NULL)
2900 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2902 GrayQuantum,ping_pixels+row_offset,exception);
2904 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2906 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2908 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2909 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2910 RGBAQuantum,ping_pixels+row_offset,exception);
2912 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2913 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2914 IndexQuantum,ping_pixels+row_offset,exception);
2916 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2917 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2918 RGBQuantum,ping_pixels+row_offset,exception);
2920 if (found_transparent_pixel == MagickFalse)
2922 /* Is there a transparent pixel in the row? */
2923 if (y== 0 && logging != MagickFalse)
2924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2925 " Looking for cheap transparent pixel");
2927 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2929 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2930 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2931 (GetPixelAlpha(image,q) != OpaqueAlpha))
2933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2937 found_transparent_pixel = MagickTrue;
2940 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2941 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2942 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2943 transparent_color.red &&
2944 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2945 transparent_color.green &&
2946 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2947 transparent_color.blue))
2949 if (logging != MagickFalse)
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2952 found_transparent_pixel = MagickTrue;
2955 q+=GetPixelChannels(image);
2959 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2961 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2964 if (status == MagickFalse)
2967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2971 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2973 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2974 if (status == MagickFalse)
2980 else /* image->storage_class != DirectClass */
2982 for (pass=0; pass < num_passes; pass++)
2991 Convert grayscale image to PseudoClass pixel packets.
2993 if (logging != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " Converting grayscale pixels to pixel packets");
2997 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2998 MagickTrue : MagickFalse;
3000 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3001 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
3003 if (quantum_scanline == (Quantum *) NULL)
3004 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3006 for (y=0; y < (ssize_t) image->rows; y++)
3009 row_offset=ping_rowbytes*y;
3014 png_read_row(ping,ping_pixels+row_offset,NULL);
3015 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3017 if (q == (Quantum *) NULL)
3020 p=ping_pixels+row_offset;
3023 switch (ping_bit_depth)
3030 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3032 for (bit=7; bit >= 0; bit--)
3033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3037 if ((image->columns % 8) != 0)
3039 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3040 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3048 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3050 *r++=(*p >> 6) & 0x03;
3051 *r++=(*p >> 4) & 0x03;
3052 *r++=(*p >> 2) & 0x03;
3056 if ((image->columns % 4) != 0)
3058 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3059 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3067 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3069 *r++=(*p >> 4) & 0x0f;
3073 if ((image->columns % 2) != 0)
3074 *r++=(*p++ >> 4) & 0x0f;
3081 if (ping_color_type == 4)
3082 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3085 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3086 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3087 found_transparent_pixel = MagickTrue;
3088 q+=GetPixelChannels(image);
3092 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3100 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3102 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3106 if (image->colors > 256)
3107 quantum=((*p++) << 8);
3113 *r=ScaleShortToQuantum(quantum);
3116 if (ping_color_type == 4)
3118 if (image->colors > 256)
3119 quantum=((*p++) << 8);
3124 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3125 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3126 found_transparent_pixel = MagickTrue;
3127 q+=GetPixelChannels(image);
3130 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3132 p++; /* strip low byte */
3134 if (ping_color_type == 4)
3136 SetPixelAlpha(image,*p++,q);
3137 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3138 found_transparent_pixel = MagickTrue;
3140 q+=GetPixelChannels(image);
3153 Transfer image scanline.
3157 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3159 if (q == (Quantum *) NULL)
3161 for (x=0; x < (ssize_t) image->columns; x++)
3163 SetPixelIndex(image,*r++,q);
3164 q+=GetPixelChannels(image);
3167 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3170 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3172 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3175 if (status == MagickFalse)
3180 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3182 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3184 if (status == MagickFalse)
3188 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3191 image->matte=found_transparent_pixel;
3193 if (logging != MagickFalse)
3195 if (found_transparent_pixel != MagickFalse)
3196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3197 " Found transparent pixel");
3200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3201 " No transparent pixel was found");
3203 ping_color_type&=0x03;
3208 if (quantum_info != (QuantumInfo *) NULL)
3209 quantum_info=DestroyQuantumInfo(quantum_info);
3211 if (image->storage_class == PseudoClass)
3217 image->matte=MagickFalse;
3218 (void) SyncImage(image);
3222 png_read_end(ping,end_info);
3224 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3225 (ssize_t) image_info->first_scene && image->delay != 0)
3227 png_destroy_read_struct(&ping,&ping_info,&end_info);
3228 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3230 (void) SetImageBackgroundColor(image);
3231 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3232 UnlockSemaphoreInfo(ping_semaphore);
3234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage() early.");
3240 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3246 Image has a transparent background.
3248 storage_class=image->storage_class;
3249 image->matte=MagickTrue;
3251 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3253 if (storage_class == PseudoClass)
3255 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3257 for (x=0; x < ping_num_trans; x++)
3259 image->colormap[x].alpha =
3260 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3264 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3266 for (x=0; x < (int) image->colors; x++)
3268 if (ScaleQuantumToShort(image->colormap[x].red) ==
3269 transparent_color.alpha)
3271 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3275 (void) SyncImage(image);
3278 #if 1 /* Should have already been done above, but glennrp problem P10
3283 for (y=0; y < (ssize_t) image->rows; y++)
3285 image->storage_class=storage_class;
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3288 if (q == (Quantum *) NULL)
3292 /* Caution: on a Q8 build, this does not distinguish between
3293 * 16-bit colors that differ only in the low byte
3295 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3297 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3298 transparent_color.red &&
3299 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3300 transparent_color.green &&
3301 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3302 transparent_color.blue)
3304 SetPixelAlpha(image,TransparentAlpha,q);
3307 #if 0 /* I have not found a case where this is needed. */
3310 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3314 q+=GetPixelChannels(image);
3317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3323 image->storage_class=DirectClass;
3326 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3327 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3328 image->colorspace=GRAYColorspace;
3330 for (j = 0; j < 2; j++)
3333 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3334 MagickTrue : MagickFalse;
3336 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
3339 if (status != MagickFalse)
3340 for (i=0; i < (ssize_t) num_text; i++)
3342 /* Check for a profile */
3344 if (logging != MagickFalse)
3345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3346 " Reading PNG text chunk");
3348 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3350 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3359 length=text[i].text_length;
3360 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3362 if (value == (char *) NULL)
3364 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3365 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3370 (void) ConcatenateMagickString(value,text[i].text,length+2);
3372 /* Don't save "density" or "units" property if we have a pHYs
3375 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3376 (LocaleCompare(text[i].key,"density") != 0 &&
3377 LocaleCompare(text[i].key,"units") != 0))
3378 (void) SetImageProperty(image,text[i].key,value);
3380 if (logging != MagickFalse)
3382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 " length: %lu",(unsigned long) length);
3384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3385 " Keyword: %s",text[i].key);
3388 value=DestroyString(value);
3391 num_text_total += num_text;
3394 #ifdef MNG_OBJECT_BUFFERS
3396 Store the object if necessary.
3398 if (object_id && !mng_info->frozen[object_id])
3400 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3403 create a new object buffer.
3405 mng_info->ob[object_id]=(MngBuffer *)
3406 AcquireMagickMemory(sizeof(MngBuffer));
3408 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3410 mng_info->ob[object_id]->image=(Image *) NULL;
3411 mng_info->ob[object_id]->reference_count=1;
3415 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3416 mng_info->ob[object_id]->frozen)
3418 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3419 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3420 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3423 if (mng_info->ob[object_id]->frozen)
3424 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3425 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3426 "`%s'",image->filename);
3432 if (mng_info->ob[object_id]->image != (Image *) NULL)
3433 mng_info->ob[object_id]->image=DestroyImage
3434 (mng_info->ob[object_id]->image);
3436 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3439 if (mng_info->ob[object_id]->image != (Image *) NULL)
3440 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3443 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3444 ResourceLimitError,"Cloning image for object buffer failed",
3445 "`%s'",image->filename);
3447 if (ping_width > 250000L || ping_height > 250000L)
3448 png_error(ping,"PNG Image dimensions are too large.");
3450 mng_info->ob[object_id]->width=ping_width;
3451 mng_info->ob[object_id]->height=ping_height;
3452 mng_info->ob[object_id]->color_type=ping_color_type;
3453 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3454 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3455 mng_info->ob[object_id]->compression_method=
3456 ping_compression_method;
3457 mng_info->ob[object_id]->filter_method=ping_filter_method;
3459 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3468 Copy the PLTE to the object buffer.
3470 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3471 mng_info->ob[object_id]->plte_length=number_colors;
3473 for (i=0; i < number_colors; i++)
3475 mng_info->ob[object_id]->plte[i]=plte[i];
3480 mng_info->ob[object_id]->plte_length=0;
3485 /* Set image->matte to MagickTrue if the input colortype supports
3486 * alpha or if a valid tRNS chunk is present, no matter whether there
3487 * is actual transparency present.
3489 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3490 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3491 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3492 MagickTrue : MagickFalse;
3494 /* Set more properties for identify to retrieve */
3499 if (num_text_total != 0)
3501 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3502 (void) FormatLocaleString(msg,MaxTextExtent,
3503 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3504 (void) SetImageProperty(image,"PNG:text ",msg);
3507 if (num_raw_profiles != 0)
3509 (void) FormatLocaleString(msg,MaxTextExtent,
3510 "%d were found", num_raw_profiles);
3511 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3514 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3516 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3517 "chunk was found (see Chromaticity, above)");
3518 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3521 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3523 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3524 "chunk was found (see Background color, above)");
3525 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3528 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3531 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3532 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3534 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3535 (void) SetImageProperty(image,"PNG:tRNS ",msg);
3537 #if defined(PNG_sRGB_SUPPORTED)
3538 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3540 (void) FormatLocaleString(msg,MaxTextExtent,
3541 "intent=%d (See Rendering intent)",
3543 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3547 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3549 (void) FormatLocaleString(msg,MaxTextExtent,
3550 "gamma=%.8g (See Gamma, above)",
3552 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3555 #if defined(PNG_pHYs_SUPPORTED)
3556 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3558 (void) FormatLocaleString(msg,MaxTextExtent,
3559 "x_res=%.10g, y_res=%.10g, units=%d",
3560 (double) x_resolution,(double) y_resolution, unit_type);
3561 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3565 #if defined(PNG_oFFs_SUPPORTED)
3566 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3568 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3569 (double) image->page.x,(double) image->page.y);
3570 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3574 if ((image->page.width != 0 && image->page.width != image->columns) ||
3575 (image->page.height != 0 && image->page.height != image->rows))
3577 (void) FormatLocaleString(msg,MaxTextExtent,
3578 "width=%.20g, height=%.20g",
3579 (double) image->page.width,(double) image->page.height);
3580 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3585 Relinquish resources.
3587 png_destroy_read_struct(&ping,&ping_info,&end_info);
3589 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3590 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3591 UnlockSemaphoreInfo(ping_semaphore);
3594 if (logging != MagickFalse)
3595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3596 " exit ReadOnePNGImage()");
3600 /* end of reading one PNG image */
3603 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3618 magic_number[MaxTextExtent];
3626 assert(image_info != (const ImageInfo *) NULL);
3627 assert(image_info->signature == MagickSignature);
3629 if (image_info->debug != MagickFalse)
3630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3631 image_info->filename);
3633 assert(exception != (ExceptionInfo *) NULL);
3634 assert(exception->signature == MagickSignature);
3635 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3636 image=AcquireImage(image_info,exception);
3637 mng_info=(MngInfo *) NULL;
3638 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3640 if (status == MagickFalse)
3641 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3644 Verify PNG signature.
3646 count=ReadBlob(image,8,(unsigned char *) magic_number);
3648 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3649 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3652 Allocate a MngInfo structure.
3654 have_mng_structure=MagickFalse;
3655 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3657 if (mng_info == (MngInfo *) NULL)
3658 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3661 Initialize members of the MngInfo structure.
3663 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3664 mng_info->image=image;
3665 have_mng_structure=MagickTrue;
3668 image=ReadOnePNGImage(mng_info,image_info,exception);
3669 MngInfoFreeStruct(mng_info,&have_mng_structure);
3671 if (image == (Image *) NULL)
3673 if (previous != (Image *) NULL)
3675 if (previous->signature != MagickSignature)
3676 ThrowReaderException(CorruptImageError,"CorruptImage");
3678 (void) CloseBlob(previous);
3679 (void) DestroyImageList(previous);
3682 if (logging != MagickFalse)
3683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3684 "exit ReadPNGImage() with error");
3686 return((Image *) NULL);
3689 (void) CloseBlob(image);
3691 if ((image->columns == 0) || (image->rows == 0))
3693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadPNGImage() with error.");
3697 ThrowReaderException(CorruptImageError,"CorruptImage");
3700 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3702 (void) SetImageType(image,TrueColorType,exception);
3703 image->matte=MagickFalse;
3706 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3707 (void) SetImageType(image,TrueColorMatteType,exception);
3709 if (logging != MagickFalse)
3710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3711 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3712 (double) image->page.width,(double) image->page.height,
3713 (double) image->page.x,(double) image->page.y);
3715 if (logging != MagickFalse)
3716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3723 #if defined(JNG_SUPPORTED)
3725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3729 % R e a d O n e J N G I m a g e %
3733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3735 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3736 % (minus the 8-byte signature) and returns it. It allocates the memory
3737 % necessary for the new Image structure and returns a pointer to the new
3740 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3742 % The format of the ReadOneJNGImage method is:
3744 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3745 % ExceptionInfo *exception)
3747 % A description of each parameter follows:
3749 % o mng_info: Specifies a pointer to a MngInfo structure.
3751 % o image_info: the image info.
3753 % o exception: return any errors or warnings in this structure.
3756 static Image *ReadOneJNGImage(MngInfo *mng_info,
3757 const ImageInfo *image_info, ExceptionInfo *exception)
3784 jng_image_sample_depth,
3785 jng_image_compression_method,
3786 jng_image_interlace_method,
3787 jng_alpha_sample_depth,
3788 jng_alpha_compression_method,
3789 jng_alpha_filter_method,
3790 jng_alpha_interlace_method;
3792 register const Quantum
3802 register unsigned char
3813 jng_alpha_compression_method=0;
3814 jng_alpha_sample_depth=8;
3818 alpha_image=(Image *) NULL;
3819 color_image=(Image *) NULL;
3820 alpha_image_info=(ImageInfo *) NULL;
3821 color_image_info=(ImageInfo *) NULL;
3823 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3824 " Enter ReadOneJNGImage()");
3826 image=mng_info->image;
3828 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3831 Allocate next image structure.
3833 if (logging != MagickFalse)
3834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3835 " AcquireNextImage()");
3837 AcquireNextImage(image_info,image,exception);
3839 if (GetNextImageInList(image) == (Image *) NULL)
3840 return((Image *) NULL);
3842 image=SyncNextImageInList(image);
3844 mng_info->image=image;
3847 Signature bytes have already been read.
3850 read_JSEP=MagickFalse;
3851 reading_idat=MagickFalse;
3852 skip_to_iend=MagickFalse;
3856 type[MaxTextExtent];
3865 Read a new JNG chunk.
3867 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3868 2*GetBlobSize(image));
3870 if (status == MagickFalse)
3874 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3875 length=ReadBlobMSBLong(image);
3876 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3880 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3881 type[0],type[1],type[2],type[3],(double) length);
3883 if (length > PNG_UINT_31_MAX || count == 0)
3884 ThrowReaderException(CorruptImageError,"CorruptImage");
3887 chunk=(unsigned char *) NULL;
3891 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3893 if (chunk == (unsigned char *) NULL)
3894 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3896 for (i=0; i < (ssize_t) length; i++)
3897 chunk[i]=(unsigned char) ReadBlobByte(image);
3902 (void) ReadBlobMSBLong(image); /* read crc word */
3907 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3912 if (memcmp(type,mng_JHDR,4) == 0)
3916 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3917 (p[2] << 8) | p[3]);
3918 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3919 (p[6] << 8) | p[7]);
3920 jng_color_type=p[8];
3921 jng_image_sample_depth=p[9];
3922 jng_image_compression_method=p[10];
3923 jng_image_interlace_method=p[11];
3925 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3928 jng_alpha_sample_depth=p[12];
3929 jng_alpha_compression_method=p[13];
3930 jng_alpha_filter_method=p[14];
3931 jng_alpha_interlace_method=p[15];
3933 if (logging != MagickFalse)
3935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3936 " jng_width: %16lu",(unsigned long) jng_width);
3938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3939 " jng_width: %16lu",(unsigned long) jng_height);
3941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3942 " jng_color_type: %16d",jng_color_type);
3944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3945 " jng_image_sample_depth: %3d",
3946 jng_image_sample_depth);
3948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 " jng_image_compression_method:%3d",
3950 jng_image_compression_method);
3952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_image_interlace_method: %3d",
3954 jng_image_interlace_method);
3956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_alpha_sample_depth: %3d",
3958 jng_alpha_sample_depth);
3960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_alpha_compression_method:%3d",
3962 jng_alpha_compression_method);
3964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_alpha_filter_method: %3d",
3966 jng_alpha_filter_method);
3968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_interlace_method: %3d",
3970 jng_alpha_interlace_method);
3975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3981 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3982 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3983 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3986 o create color_image
3987 o open color_blob, attached to color_image
3988 o if (color type has alpha)
3989 open alpha_blob, attached to alpha_image
3992 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3994 if (color_image_info == (ImageInfo *) NULL)
3995 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3997 GetImageInfo(color_image_info);
3998 color_image=AcquireImage(color_image_info,exception);
4000 if (color_image == (Image *) NULL)
4001 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4003 if (logging != MagickFalse)
4004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " Creating color_blob.");
4007 (void) AcquireUniqueFilename(color_image->filename);
4008 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4011 if (status == MagickFalse)
4012 return((Image *) NULL);
4014 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4016 alpha_image_info=(ImageInfo *)
4017 AcquireMagickMemory(sizeof(ImageInfo));
4019 if (alpha_image_info == (ImageInfo *) NULL)
4020 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4022 GetImageInfo(alpha_image_info);
4023 alpha_image=AcquireImage(alpha_image_info,exception);
4025 if (alpha_image == (Image *) NULL)
4027 alpha_image=DestroyImage(alpha_image);
4028 ThrowReaderException(ResourceLimitError,
4029 "MemoryAllocationFailed");
4032 if (logging != MagickFalse)
4033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4034 " Creating alpha_blob.");
4036 (void) AcquireUniqueFilename(alpha_image->filename);
4037 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4040 if (status == MagickFalse)
4041 return((Image *) NULL);
4043 if (jng_alpha_compression_method == 0)
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Writing IHDR chunk to alpha_blob.");
4052 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4053 "\211PNG\r\n\032\n");
4055 (void) WriteBlobMSBULong(alpha_image,13L);
4056 PNGType(data,mng_IHDR);
4057 LogPNGChunk(logging,mng_IHDR,13L);
4058 PNGLong(data+4,jng_width);
4059 PNGLong(data+8,jng_height);
4060 data[12]=jng_alpha_sample_depth;
4061 data[13]=0; /* color_type gray */
4062 data[14]=0; /* compression method 0 */
4063 data[15]=0; /* filter_method 0 */
4064 data[16]=0; /* interlace_method 0 */
4065 (void) WriteBlob(alpha_image,17,data);
4066 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4069 reading_idat=MagickTrue;
4072 if (memcmp(type,mng_JDAT,4) == 0)
4074 /* Copy chunk to color_image->blob */
4076 if (logging != MagickFalse)
4077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4078 " Copying JDAT chunk data to color_blob.");
4080 (void) WriteBlob(color_image,length,chunk);
4083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4088 if (memcmp(type,mng_IDAT,4) == 0)
4093 /* Copy IDAT header and chunk data to alpha_image->blob */
4095 if (image_info->ping == MagickFalse)
4097 if (logging != MagickFalse)
4098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4099 " Copying IDAT chunk data to alpha_blob.");
4101 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4102 PNGType(data,mng_IDAT);
4103 LogPNGChunk(logging,mng_IDAT,length);
4104 (void) WriteBlob(alpha_image,4,data);
4105 (void) WriteBlob(alpha_image,length,chunk);
4106 (void) WriteBlobMSBULong(alpha_image,
4107 crc32(crc32(0,data,4),chunk,(uInt) length));
4111 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4116 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4118 /* Copy chunk data to alpha_image->blob */
4120 if (image_info->ping == MagickFalse)
4122 if (logging != MagickFalse)
4123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4124 " Copying JDAA chunk data to alpha_blob.");
4126 (void) WriteBlob(alpha_image,length,chunk);
4130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4135 if (memcmp(type,mng_JSEP,4) == 0)
4137 read_JSEP=MagickTrue;
4140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4145 if (memcmp(type,mng_bKGD,4) == 0)
4149 image->background_color.red=ScaleCharToQuantum(p[1]);
4150 image->background_color.green=image->background_color.red;
4151 image->background_color.blue=image->background_color.red;
4156 image->background_color.red=ScaleCharToQuantum(p[1]);
4157 image->background_color.green=ScaleCharToQuantum(p[3]);
4158 image->background_color.blue=ScaleCharToQuantum(p[5]);
4161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4165 if (memcmp(type,mng_gAMA,4) == 0)
4168 image->gamma=((float) mng_get_long(p))*0.00001;
4170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4174 if (memcmp(type,mng_cHRM,4) == 0)
4178 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4179 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4180 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4181 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4182 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4183 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4184 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4185 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4188 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4192 if (memcmp(type,mng_sRGB,4) == 0)
4196 image->rendering_intent=
4197 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4198 image->gamma=0.45455f;
4199 image->chromaticity.red_primary.x=0.6400f;
4200 image->chromaticity.red_primary.y=0.3300f;
4201 image->chromaticity.green_primary.x=0.3000f;
4202 image->chromaticity.green_primary.y=0.6000f;
4203 image->chromaticity.blue_primary.x=0.1500f;
4204 image->chromaticity.blue_primary.y=0.0600f;
4205 image->chromaticity.white_point.x=0.3127f;
4206 image->chromaticity.white_point.y=0.3290f;
4209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4213 if (memcmp(type,mng_oFFs,4) == 0)
4217 image->page.x=(ssize_t) mng_get_long(p);
4218 image->page.y=(ssize_t) mng_get_long(&p[4]);
4220 if ((int) p[8] != 0)
4222 image->page.x/=10000;
4223 image->page.y/=10000;
4228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4233 if (memcmp(type,mng_pHYs,4) == 0)
4237 image->x_resolution=(double) mng_get_long(p);
4238 image->y_resolution=(double) mng_get_long(&p[4]);
4239 if ((int) p[8] == PNG_RESOLUTION_METER)
4241 image->units=PixelsPerCentimeterResolution;
4242 image->x_resolution=image->x_resolution/100.0f;
4243 image->y_resolution=image->y_resolution/100.0f;
4247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4252 if (memcmp(type,mng_iCCP,4) == 0)
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4265 if (memcmp(type,mng_IEND,4))
4275 Finish up reading image data:
4277 o read main image from color_blob.
4281 o if (color_type has alpha)
4282 if alpha_encoding is PNG
4283 read secondary image from alpha_blob via ReadPNG
4284 if alpha_encoding is JPEG
4285 read secondary image from alpha_blob via ReadJPEG
4289 o copy intensity of secondary image into
4290 alpha samples of main image.
4292 o destroy the secondary image.
4295 (void) CloseBlob(color_image);
4297 if (logging != MagickFalse)
4298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4299 " Reading jng_image from color_blob.");
4301 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4302 color_image->filename);
4304 color_image_info->ping=MagickFalse; /* To do: avoid this */
4305 jng_image=ReadImage(color_image_info,exception);
4307 if (jng_image == (Image *) NULL)
4308 return((Image *) NULL);
4310 (void) RelinquishUniqueFileResource(color_image->filename);
4311 color_image=DestroyImage(color_image);
4312 color_image_info=DestroyImageInfo(color_image_info);
4314 if (jng_image == (Image *) NULL)
4315 return((Image *) NULL);
4317 if (logging != MagickFalse)
4318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4319 " Copying jng_image pixels to main image.");
4321 image->rows=jng_height;
4322 image->columns=jng_width;
4324 for (y=0; y < (ssize_t) image->rows; y++)
4326 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4327 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4328 for (x=(ssize_t) image->columns; x != 0; x--)
4330 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4331 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4332 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4333 q+=GetPixelChannels(image);
4334 s+=GetPixelChannels(jng_image);
4337 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4341 jng_image=DestroyImage(jng_image);
4343 if (image_info->ping == MagickFalse)
4345 if (jng_color_type >= 12)
4347 if (jng_alpha_compression_method == 0)
4351 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4352 PNGType(data,mng_IEND);
4353 LogPNGChunk(logging,mng_IEND,0L);
4354 (void) WriteBlob(alpha_image,4,data);
4355 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4358 (void) CloseBlob(alpha_image);
4360 if (logging != MagickFalse)
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4362 " Reading alpha from alpha_blob.");
4364 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4365 "%s",alpha_image->filename);
4367 jng_image=ReadImage(alpha_image_info,exception);
4369 if (jng_image != (Image *) NULL)
4370 for (y=0; y < (ssize_t) image->rows; y++)
4372 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4374 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4376 if (image->matte != MagickFalse)
4377 for (x=(ssize_t) image->columns; x != 0; x--)
4379 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4380 q+=GetPixelChannels(image);
4381 s+=GetPixelChannels(jng_image);
4385 for (x=(ssize_t) image->columns; x != 0; x--)
4387 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4388 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4389 image->matte=MagickTrue;
4390 q+=GetPixelChannels(image);
4391 s+=GetPixelChannels(jng_image);
4394 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4397 (void) RelinquishUniqueFileResource(alpha_image->filename);
4398 alpha_image=DestroyImage(alpha_image);
4399 alpha_image_info=DestroyImageInfo(alpha_image_info);
4400 if (jng_image != (Image *) NULL)
4401 jng_image=DestroyImage(jng_image);
4405 /* Read the JNG image. */
4407 if (mng_info->mng_type == 0)
4409 mng_info->mng_width=jng_width;
4410 mng_info->mng_height=jng_height;
4413 if (image->page.width == 0 && image->page.height == 0)
4415 image->page.width=jng_width;
4416 image->page.height=jng_height;
4419 if (image->page.x == 0 && image->page.y == 0)
4421 image->page.x=mng_info->x_off[mng_info->object_id];
4422 image->page.y=mng_info->y_off[mng_info->object_id];
4427 image->page.y=mng_info->y_off[mng_info->object_id];
4430 mng_info->image_found++;
4431 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4432 2*GetBlobSize(image));
4434 if (logging != MagickFalse)
4435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4436 " exit ReadOneJNGImage()");
4442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4446 % R e a d J N G I m a g e %
4450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4452 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4453 % (including the 8-byte signature) and returns it. It allocates the memory
4454 % necessary for the new Image structure and returns a pointer to the new
4457 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4459 % The format of the ReadJNGImage method is:
4461 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4464 % A description of each parameter follows:
4466 % o image_info: the image info.
4468 % o exception: return any errors or warnings in this structure.
4472 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4487 magic_number[MaxTextExtent];
4495 assert(image_info != (const ImageInfo *) NULL);
4496 assert(image_info->signature == MagickSignature);
4497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4498 assert(exception != (ExceptionInfo *) NULL);
4499 assert(exception->signature == MagickSignature);
4500 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4501 image=AcquireImage(image_info,exception);
4502 mng_info=(MngInfo *) NULL;
4503 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4505 if (status == MagickFalse)
4506 return((Image *) NULL);
4508 if (LocaleCompare(image_info->magick,"JNG") != 0)
4509 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4511 /* Verify JNG signature. */
4513 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4515 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4516 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4518 /* Allocate a MngInfo structure. */
4520 have_mng_structure=MagickFalse;
4521 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4523 if (mng_info == (MngInfo *) NULL)
4524 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4526 /* Initialize members of the MngInfo structure. */
4528 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4529 have_mng_structure=MagickTrue;
4531 mng_info->image=image;
4533 image=ReadOneJNGImage(mng_info,image_info,exception);
4534 MngInfoFreeStruct(mng_info,&have_mng_structure);
4536 if (image == (Image *) NULL)
4538 if (IsImageObject(previous) != MagickFalse)
4540 (void) CloseBlob(previous);
4541 (void) DestroyImageList(previous);
4544 if (logging != MagickFalse)
4545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4546 "exit ReadJNGImage() with error");
4548 return((Image *) NULL);
4550 (void) CloseBlob(image);
4552 if (image->columns == 0 || image->rows == 0)
4554 if (logging != MagickFalse)
4555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4556 "exit ReadJNGImage() with error");
4558 ThrowReaderException(CorruptImageError,"CorruptImage");
4561 if (logging != MagickFalse)
4562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4568 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4571 page_geometry[MaxTextExtent];
4604 #if defined(MNG_INSERT_LAYERS)
4606 mng_background_color;
4609 register unsigned char
4624 #if defined(MNG_INSERT_LAYERS)
4629 volatile unsigned int
4630 #ifdef MNG_OBJECT_BUFFERS
4631 mng_background_object=0,
4633 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4636 default_frame_timeout,
4638 #if defined(MNG_INSERT_LAYERS)
4644 /* These delays are all measured in image ticks_per_second,
4645 * not in MNG ticks_per_second
4648 default_frame_delay,
4652 #if defined(MNG_INSERT_LAYERS)
4661 previous_fb.bottom=0;
4663 previous_fb.right=0;
4665 default_fb.bottom=0;
4669 /* Open image file. */
4671 assert(image_info != (const ImageInfo *) NULL);
4672 assert(image_info->signature == MagickSignature);
4673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4674 assert(exception != (ExceptionInfo *) NULL);
4675 assert(exception->signature == MagickSignature);
4676 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4677 image=AcquireImage(image_info,exception);
4678 mng_info=(MngInfo *) NULL;
4679 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4681 if (status == MagickFalse)
4682 return((Image *) NULL);
4684 first_mng_object=MagickFalse;
4686 have_mng_structure=MagickFalse;
4688 /* Allocate a MngInfo structure. */
4690 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4692 if (mng_info == (MngInfo *) NULL)
4693 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4695 /* Initialize members of the MngInfo structure. */
4697 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4698 mng_info->image=image;
4699 have_mng_structure=MagickTrue;
4701 if (LocaleCompare(image_info->magick,"MNG") == 0)
4704 magic_number[MaxTextExtent];
4706 /* Verify MNG signature. */
4707 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4708 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4709 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4711 /* Initialize some nonzero members of the MngInfo structure. */
4712 for (i=0; i < MNG_MAX_OBJECTS; i++)
4714 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4715 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4717 mng_info->exists[0]=MagickTrue;
4720 first_mng_object=MagickTrue;
4722 #if defined(MNG_INSERT_LAYERS)
4723 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4725 default_frame_delay=0;
4726 default_frame_timeout=0;
4729 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4731 skip_to_iend=MagickFalse;
4732 term_chunk_found=MagickFalse;
4733 mng_info->framing_mode=1;
4734 #if defined(MNG_INSERT_LAYERS)
4735 mandatory_back=MagickFalse;
4737 #if defined(MNG_INSERT_LAYERS)
4738 mng_background_color=image->background_color;
4740 default_fb=mng_info->frame;
4741 previous_fb=mng_info->frame;
4745 type[MaxTextExtent];
4747 if (LocaleCompare(image_info->magick,"MNG") == 0)
4756 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4757 length=ReadBlobMSBLong(image);
4758 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4760 if (logging != MagickFalse)
4761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4762 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4763 type[0],type[1],type[2],type[3],(double) length);
4765 if (length > PNG_UINT_31_MAX)
4769 ThrowReaderException(CorruptImageError,"CorruptImage");
4772 chunk=(unsigned char *) NULL;
4776 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4778 if (chunk == (unsigned char *) NULL)
4779 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4781 for (i=0; i < (ssize_t) length; i++)
4782 chunk[i]=(unsigned char) ReadBlobByte(image);
4787 (void) ReadBlobMSBLong(image); /* read crc word */
4789 #if !defined(JNG_SUPPORTED)
4790 if (memcmp(type,mng_JHDR,4) == 0)
4792 skip_to_iend=MagickTrue;
4794 if (mng_info->jhdr_warning == 0)
4795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4796 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4798 mng_info->jhdr_warning++;
4801 if (memcmp(type,mng_DHDR,4) == 0)
4803 skip_to_iend=MagickTrue;
4805 if (mng_info->dhdr_warning == 0)
4806 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4807 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4809 mng_info->dhdr_warning++;
4811 if (memcmp(type,mng_MEND,4) == 0)
4816 if (memcmp(type,mng_IEND,4) == 0)
4817 skip_to_iend=MagickFalse;
4820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4822 if (logging != MagickFalse)
4823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4829 if (memcmp(type,mng_MHDR,4) == 0)
4831 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4832 (p[2] << 8) | p[3]);
4834 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4835 (p[6] << 8) | p[7]);
4837 if (logging != MagickFalse)
4839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4840 " MNG width: %.20g",(double) mng_info->mng_width);
4841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4842 " MNG height: %.20g",(double) mng_info->mng_height);
4846 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4848 if (mng_info->ticks_per_second == 0)
4849 default_frame_delay=0;
4852 default_frame_delay=1UL*image->ticks_per_second/
4853 mng_info->ticks_per_second;
4855 frame_delay=default_frame_delay;
4861 simplicity=(size_t) mng_get_long(p);
4864 mng_type=1; /* Full MNG */
4866 if ((simplicity != 0) && ((simplicity | 11) == 11))
4867 mng_type=2; /* LC */
4869 if ((simplicity != 0) && ((simplicity | 9) == 9))
4870 mng_type=3; /* VLC */
4872 #if defined(MNG_INSERT_LAYERS)
4874 insert_layers=MagickTrue;
4876 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4878 /* Allocate next image structure. */
4879 AcquireNextImage(image_info,image,exception);
4881 if (GetNextImageInList(image) == (Image *) NULL)
4882 return((Image *) NULL);
4884 image=SyncNextImageInList(image);
4885 mng_info->image=image;
4888 if ((mng_info->mng_width > 65535L) ||
4889 (mng_info->mng_height > 65535L))
4890 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4892 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4893 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4894 mng_info->mng_height);
4896 mng_info->frame.left=0;
4897 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4898 mng_info->frame.top=0;
4899 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4900 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4902 for (i=0; i < MNG_MAX_OBJECTS; i++)
4903 mng_info->object_clip[i]=mng_info->frame;
4905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4909 if (memcmp(type,mng_TERM,4) == 0)
4920 final_delay=(png_uint_32) mng_get_long(&p[2]);
4921 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4923 if (mng_iterations == PNG_UINT_31_MAX)
4926 image->iterations=mng_iterations;
4927 term_chunk_found=MagickTrue;
4930 if (logging != MagickFalse)
4932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4933 " repeat=%d",repeat);
4935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4936 " final_delay=%.20g",(double) final_delay);
4938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4939 " image->iterations=%.20g",(double) image->iterations);
4942 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4945 if (memcmp(type,mng_DEFI,4) == 0)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4952 object_id=(p[0] << 8) | p[1];
4954 if (mng_type == 2 && object_id != 0)
4955 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4956 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4959 if (object_id > MNG_MAX_OBJECTS)
4962 Instead ofsuing a warning we should allocate a larger
4963 MngInfo structure and continue.
4965 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4966 CoderError,"object id too large","`%s'",image->filename);
4967 object_id=MNG_MAX_OBJECTS;
4970 if (mng_info->exists[object_id])
4971 if (mng_info->frozen[object_id])
4973 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4974 (void) ThrowMagickException(&image->exception,
4975 GetMagickModule(),CoderError,
4976 "DEFI cannot redefine a frozen MNG object","`%s'",
4981 mng_info->exists[object_id]=MagickTrue;
4984 mng_info->invisible[object_id]=p[2];
4987 Extract object offset info.
4991 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4992 (p[5] << 16) | (p[6] << 8) | p[7]);
4994 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4995 (p[9] << 16) | (p[10] << 8) | p[11]);
4997 if (logging != MagickFalse)
4999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5000 " x_off[%d]: %.20g",object_id,(double)
5001 mng_info->x_off[object_id]);
5003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5004 " y_off[%d]: %.20g",object_id,(double)
5005 mng_info->y_off[object_id]);
5010 Extract object clipping info.
5013 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5016 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5019 if (memcmp(type,mng_bKGD,4) == 0)
5021 mng_info->have_global_bkgd=MagickFalse;
5025 mng_info->mng_global_bkgd.red=
5026 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5028 mng_info->mng_global_bkgd.green=
5029 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5031 mng_info->mng_global_bkgd.blue=
5032 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5034 mng_info->have_global_bkgd=MagickTrue;
5037 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5040 if (memcmp(type,mng_BACK,4) == 0)
5042 #if defined(MNG_INSERT_LAYERS)
5044 mandatory_back=p[6];
5049 if (mandatory_back && length > 5)
5051 mng_background_color.red=
5052 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5054 mng_background_color.green=
5055 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5057 mng_background_color.blue=
5058 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5060 mng_background_color.alpha=OpaqueAlpha;
5063 #ifdef MNG_OBJECT_BUFFERS
5065 mng_background_object=(p[7] << 8) | p[8];
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5072 if (memcmp(type,mng_PLTE,4) == 0)
5074 /* Read global PLTE. */
5076 if (length && (length < 769))
5078 if (mng_info->global_plte == (png_colorp) NULL)
5079 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5080 sizeof(*mng_info->global_plte));
5082 for (i=0; i < (ssize_t) (length/3); i++)
5084 mng_info->global_plte[i].red=p[3*i];
5085 mng_info->global_plte[i].green=p[3*i+1];
5086 mng_info->global_plte[i].blue=p[3*i+2];
5089 mng_info->global_plte_length=(unsigned int) (length/3);
5092 for ( ; i < 256; i++)
5094 mng_info->global_plte[i].red=i;
5095 mng_info->global_plte[i].green=i;
5096 mng_info->global_plte[i].blue=i;
5100 mng_info->global_plte_length=256;
5103 mng_info->global_plte_length=0;
5105 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5109 if (memcmp(type,mng_tRNS,4) == 0)
5111 /* read global tRNS */
5114 for (i=0; i < (ssize_t) length; i++)
5115 mng_info->global_trns[i]=p[i];
5118 for ( ; i < 256; i++)
5119 mng_info->global_trns[i]=255;
5121 mng_info->global_trns_length=(unsigned int) length;
5122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5125 if (memcmp(type,mng_gAMA,4) == 0)
5132 igamma=mng_get_long(p);
5133 mng_info->global_gamma=((float) igamma)*0.00001;
5134 mng_info->have_global_gama=MagickTrue;
5138 mng_info->have_global_gama=MagickFalse;
5140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5144 if (memcmp(type,mng_cHRM,4) == 0)
5146 /* Read global cHRM */
5150 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5151 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5152 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5153 mng_info->global_chrm.red_primary.y=0.00001*
5154 mng_get_long(&p[12]);
5155 mng_info->global_chrm.green_primary.x=0.00001*
5156 mng_get_long(&p[16]);
5157 mng_info->global_chrm.green_primary.y=0.00001*
5158 mng_get_long(&p[20]);
5159 mng_info->global_chrm.blue_primary.x=0.00001*
5160 mng_get_long(&p[24]);
5161 mng_info->global_chrm.blue_primary.y=0.00001*
5162 mng_get_long(&p[28]);
5163 mng_info->have_global_chrm=MagickTrue;
5166 mng_info->have_global_chrm=MagickFalse;
5168 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5172 if (memcmp(type,mng_sRGB,4) == 0)
5179 mng_info->global_srgb_intent=
5180 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5181 mng_info->have_global_srgb=MagickTrue;
5184 mng_info->have_global_srgb=MagickFalse;
5186 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5190 if (memcmp(type,mng_iCCP,4) == 0)
5198 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5203 if (memcmp(type,mng_FRAM,4) == 0)
5206 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5207 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5210 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5211 image->delay=frame_delay;
5213 frame_delay=default_frame_delay;
5214 frame_timeout=default_frame_timeout;
5219 mng_info->framing_mode=p[0];
5221 if (logging != MagickFalse)
5222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5223 " Framing_mode=%d",mng_info->framing_mode);
5227 /* Note the delay and frame clipping boundaries. */
5229 p++; /* framing mode */
5231 while (*p && ((p-chunk) < (ssize_t) length))
5232 p++; /* frame name */
5234 p++; /* frame name terminator */
5236 if ((p-chunk) < (ssize_t) (length-4))
5243 change_delay=(*p++);
5244 change_timeout=(*p++);
5245 change_clipping=(*p++);
5246 p++; /* change_sync */
5250 frame_delay=1UL*image->ticks_per_second*
5253 if (mng_info->ticks_per_second != 0)
5254 frame_delay/=mng_info->ticks_per_second;
5257 frame_delay=PNG_UINT_31_MAX;
5259 if (change_delay == 2)
5260 default_frame_delay=frame_delay;
5264 if (logging != MagickFalse)
5265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5266 " Framing_delay=%.20g",(double) frame_delay);
5271 frame_timeout=1UL*image->ticks_per_second*
5274 if (mng_info->ticks_per_second != 0)
5275 frame_timeout/=mng_info->ticks_per_second;
5278 frame_timeout=PNG_UINT_31_MAX;
5280 if (change_delay == 2)
5281 default_frame_timeout=frame_timeout;
5285 if (logging != MagickFalse)
5286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5287 " Framing_timeout=%.20g",(double) frame_timeout);
5290 if (change_clipping)
5292 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5296 if (logging != MagickFalse)
5297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5298 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5299 (double) fb.left,(double) fb.right,(double) fb.top,
5300 (double) fb.bottom);
5302 if (change_clipping == 2)
5308 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5310 subframe_width=(size_t) (mng_info->clip.right
5311 -mng_info->clip.left);
5313 subframe_height=(size_t) (mng_info->clip.bottom
5314 -mng_info->clip.top);
5316 Insert a background layer behind the frame if framing_mode is 4.
5318 #if defined(MNG_INSERT_LAYERS)
5319 if (logging != MagickFalse)
5320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5321 " subframe_width=%.20g, subframe_height=%.20g",(double)
5322 subframe_width,(double) subframe_height);
5324 if (insert_layers && (mng_info->framing_mode == 4) &&
5325 (subframe_width) && (subframe_height))
5327 /* Allocate next image structure. */
5328 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5330 AcquireNextImage(image_info,image,exception);
5332 if (GetNextImageInList(image) == (Image *) NULL)
5334 image=DestroyImageList(image);
5335 MngInfoFreeStruct(mng_info,&have_mng_structure);
5336 return((Image *) NULL);
5339 image=SyncNextImageInList(image);
5342 mng_info->image=image;
5344 if (term_chunk_found)
5346 image->start_loop=MagickTrue;
5347 image->iterations=mng_iterations;
5348 term_chunk_found=MagickFalse;
5352 image->start_loop=MagickFalse;
5354 image->columns=subframe_width;
5355 image->rows=subframe_height;
5356 image->page.width=subframe_width;
5357 image->page.height=subframe_height;
5358 image->page.x=mng_info->clip.left;
5359 image->page.y=mng_info->clip.top;
5360 image->background_color=mng_background_color;
5361 image->matte=MagickFalse;
5363 (void) SetImageBackgroundColor(image);
5365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5367 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5368 (double) mng_info->clip.left,(double) mng_info->clip.right,
5369 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5372 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5375 if (memcmp(type,mng_CLIP,4) == 0)
5384 first_object=(p[0] << 8) | p[1];
5385 last_object=(p[2] << 8) | p[3];
5387 for (i=(int) first_object; i <= (int) last_object; i++)
5389 if (mng_info->exists[i] && !mng_info->frozen[i])
5394 box=mng_info->object_clip[i];
5395 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5402 if (memcmp(type,mng_SAVE,4) == 0)
5404 for (i=1; i < MNG_MAX_OBJECTS; i++)
5405 if (mng_info->exists[i])
5407 mng_info->frozen[i]=MagickTrue;
5408 #ifdef MNG_OBJECT_BUFFERS
5409 if (mng_info->ob[i] != (MngBuffer *) NULL)
5410 mng_info->ob[i]->frozen=MagickTrue;
5415 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5420 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5422 /* Read DISC or SEEK. */
5424 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5426 for (i=1; i < MNG_MAX_OBJECTS; i++)
5427 MngInfoDiscardObject(mng_info,i);
5435 for (j=0; j < (ssize_t) length; j+=2)
5437 i=p[j] << 8 | p[j+1];
5438 MngInfoDiscardObject(mng_info,i);
5443 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5448 if (memcmp(type,mng_MOVE,4) == 0)
5456 first_object=(p[0] << 8) | p[1];
5457 last_object=(p[2] << 8) | p[3];
5458 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5460 if (mng_info->exists[i] && !mng_info->frozen[i])
5468 old_pair.a=mng_info->x_off[i];
5469 old_pair.b=mng_info->y_off[i];
5470 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5471 mng_info->x_off[i]=new_pair.a;
5472 mng_info->y_off[i]=new_pair.b;
5476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5480 if (memcmp(type,mng_LOOP,4) == 0)
5482 ssize_t loop_iters=1;
5483 loop_level=chunk[0];
5484 mng_info->loop_active[loop_level]=1; /* mark loop active */
5486 /* Record starting point. */
5487 loop_iters=mng_get_long(&chunk[1]);
5489 if (logging != MagickFalse)
5490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5491 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5492 (double) loop_iters);
5494 if (loop_iters == 0)
5495 skipping_loop=loop_level;
5499 mng_info->loop_jump[loop_level]=TellBlob(image);
5500 mng_info->loop_count[loop_level]=loop_iters;
5503 mng_info->loop_iteration[loop_level]=0;
5504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5508 if (memcmp(type,mng_ENDL,4) == 0)
5510 loop_level=chunk[0];
5512 if (skipping_loop > 0)
5514 if (skipping_loop == loop_level)
5517 Found end of zero-iteration loop.
5520 mng_info->loop_active[loop_level]=0;
5526 if (mng_info->loop_active[loop_level] == 1)
5528 mng_info->loop_count[loop_level]--;
5529 mng_info->loop_iteration[loop_level]++;
5531 if (logging != MagickFalse)
5532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5533 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5534 (double) loop_level,(double)
5535 mng_info->loop_count[loop_level]);
5537 if (mng_info->loop_count[loop_level] != 0)
5539 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5543 ThrowReaderException(CorruptImageError,
5544 "ImproperImageHeader");
5555 mng_info->loop_active[loop_level]=0;
5557 for (i=0; i < loop_level; i++)
5558 if (mng_info->loop_active[i] == 1)
5559 last_level=(short) i;
5560 loop_level=last_level;
5565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5569 if (memcmp(type,mng_CLON,4) == 0)
5571 if (mng_info->clon_warning == 0)
5572 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5573 CoderError,"CLON is not implemented yet","`%s'",
5576 mng_info->clon_warning++;
5579 if (memcmp(type,mng_MAGN,4) == 0)
5594 magn_first=(p[0] << 8) | p[1];
5600 magn_last=(p[2] << 8) | p[3];
5603 magn_last=magn_first;
5604 #ifndef MNG_OBJECT_BUFFERS
5605 if (magn_first || magn_last)
5606 if (mng_info->magn_warning == 0)
5608 (void) ThrowMagickException(&image->exception,
5609 GetMagickModule(),CoderError,
5610 "MAGN is not implemented yet for nonzero objects",
5611 "`%s'",image->filename);
5613 mng_info->magn_warning++;
5623 magn_mx=(p[5] << 8) | p[6];
5632 magn_my=(p[7] << 8) | p[8];
5641 magn_ml=(p[9] << 8) | p[10];
5650 magn_mr=(p[11] << 8) | p[12];
5659 magn_mt=(p[13] << 8) | p[14];
5668 magn_mb=(p[15] << 8) | p[16];
5680 magn_methy=magn_methx;
5683 if (magn_methx > 5 || magn_methy > 5)
5684 if (mng_info->magn_warning == 0)
5686 (void) ThrowMagickException(&image->exception,
5687 GetMagickModule(),CoderError,
5688 "Unknown MAGN method in MNG datastream","`%s'",
5691 mng_info->magn_warning++;
5693 #ifdef MNG_OBJECT_BUFFERS
5694 /* Magnify existing objects in the range magn_first to magn_last */
5696 if (magn_first == 0 || magn_last == 0)
5698 /* Save the magnification factors for object 0 */
5699 mng_info->magn_mb=magn_mb;
5700 mng_info->magn_ml=magn_ml;
5701 mng_info->magn_mr=magn_mr;
5702 mng_info->magn_mt=magn_mt;
5703 mng_info->magn_mx=magn_mx;
5704 mng_info->magn_my=magn_my;
5705 mng_info->magn_methx=magn_methx;
5706 mng_info->magn_methy=magn_methy;
5710 if (memcmp(type,mng_PAST,4) == 0)
5712 if (mng_info->past_warning == 0)
5713 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5714 CoderError,"PAST is not implemented yet","`%s'",
5717 mng_info->past_warning++;
5720 if (memcmp(type,mng_SHOW,4) == 0)
5722 if (mng_info->show_warning == 0)
5723 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5724 CoderError,"SHOW is not implemented yet","`%s'",
5727 mng_info->show_warning++;
5730 if (memcmp(type,mng_sBIT,4) == 0)
5733 mng_info->have_global_sbit=MagickFalse;
5737 mng_info->global_sbit.gray=p[0];
5738 mng_info->global_sbit.red=p[0];
5739 mng_info->global_sbit.green=p[1];
5740 mng_info->global_sbit.blue=p[2];
5741 mng_info->global_sbit.alpha=p[3];
5742 mng_info->have_global_sbit=MagickTrue;
5745 if (memcmp(type,mng_pHYs,4) == 0)
5749 mng_info->global_x_pixels_per_unit=
5750 (size_t) mng_get_long(p);
5751 mng_info->global_y_pixels_per_unit=
5752 (size_t) mng_get_long(&p[4]);
5753 mng_info->global_phys_unit_type=p[8];
5754 mng_info->have_global_phys=MagickTrue;
5758 mng_info->have_global_phys=MagickFalse;
5760 if (memcmp(type,mng_pHYg,4) == 0)
5762 if (mng_info->phyg_warning == 0)
5763 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5764 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5766 mng_info->phyg_warning++;
5768 if (memcmp(type,mng_BASI,4) == 0)
5770 skip_to_iend=MagickTrue;
5772 if (mng_info->basi_warning == 0)
5773 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5774 CoderError,"BASI is not implemented yet","`%s'",
5777 mng_info->basi_warning++;
5778 #ifdef MNG_BASI_SUPPORTED
5779 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5780 (p[2] << 8) | p[3]);
5781 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5782 (p[6] << 8) | p[7]);
5783 basi_color_type=p[8];
5784 basi_compression_method=p[9];
5785 basi_filter_type=p[10];
5786 basi_interlace_method=p[11];
5788 basi_red=(p[12] << 8) & p[13];
5794 basi_green=(p[14] << 8) & p[15];
5800 basi_blue=(p[16] << 8) & p[17];
5806 basi_alpha=(p[18] << 8) & p[19];
5810 if (basi_sample_depth == 16)
5817 basi_viewable=p[20];
5823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5827 if (memcmp(type,mng_IHDR,4)
5828 #if defined(JNG_SUPPORTED)
5829 && memcmp(type,mng_JHDR,4)
5833 /* Not an IHDR or JHDR chunk */
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5840 if (logging != MagickFalse)
5841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5842 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5844 mng_info->exists[object_id]=MagickTrue;
5845 mng_info->viewable[object_id]=MagickTrue;
5847 if (mng_info->invisible[object_id])
5849 if (logging != MagickFalse)
5850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5851 " Skipping invisible object");
5853 skip_to_iend=MagickTrue;
5854 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5857 #if defined(MNG_INSERT_LAYERS)
5859 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5861 image_width=(size_t) mng_get_long(p);
5862 image_height=(size_t) mng_get_long(&p[4]);
5864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5867 Insert a transparent background layer behind the entire animation
5868 if it is not full screen.
5870 #if defined(MNG_INSERT_LAYERS)
5871 if (insert_layers && mng_type && first_mng_object)
5873 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5874 (image_width < mng_info->mng_width) ||
5875 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5876 (image_height < mng_info->mng_height) ||
5877 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5879 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5882 Allocate next image structure.
5884 AcquireNextImage(image_info,image,exception);
5886 if (GetNextImageInList(image) == (Image *) NULL)
5888 image=DestroyImageList(image);
5889 MngInfoFreeStruct(mng_info,&have_mng_structure);
5890 return((Image *) NULL);
5893 image=SyncNextImageInList(image);
5895 mng_info->image=image;
5897 if (term_chunk_found)
5899 image->start_loop=MagickTrue;
5900 image->iterations=mng_iterations;
5901 term_chunk_found=MagickFalse;
5905 image->start_loop=MagickFalse;
5907 /* Make a background rectangle. */
5910 image->columns=mng_info->mng_width;
5911 image->rows=mng_info->mng_height;
5912 image->page.width=mng_info->mng_width;
5913 image->page.height=mng_info->mng_height;
5916 image->background_color=mng_background_color;
5917 (void) SetImageBackgroundColor(image);
5918 if (logging != MagickFalse)
5919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5920 " Inserted transparent background layer, W=%.20g, H=%.20g",
5921 (double) mng_info->mng_width,(double) mng_info->mng_height);
5925 Insert a background layer behind the upcoming image if
5926 framing_mode is 3, and we haven't already inserted one.
5928 if (insert_layers && (mng_info->framing_mode == 3) &&
5929 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5930 (simplicity & 0x08)))
5932 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5935 Allocate next image structure.
5937 AcquireNextImage(image_info,image,exception);
5939 if (GetNextImageInList(image) == (Image *) NULL)
5941 image=DestroyImageList(image);
5942 MngInfoFreeStruct(mng_info,&have_mng_structure);
5943 return((Image *) NULL);
5946 image=SyncNextImageInList(image);
5949 mng_info->image=image;
5951 if (term_chunk_found)
5953 image->start_loop=MagickTrue;
5954 image->iterations=mng_iterations;
5955 term_chunk_found=MagickFalse;
5959 image->start_loop=MagickFalse;
5962 image->columns=subframe_width;
5963 image->rows=subframe_height;
5964 image->page.width=subframe_width;
5965 image->page.height=subframe_height;
5966 image->page.x=mng_info->clip.left;
5967 image->page.y=mng_info->clip.top;
5968 image->background_color=mng_background_color;
5969 image->matte=MagickFalse;
5970 (void) SetImageBackgroundColor(image);
5972 if (logging != MagickFalse)
5973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5974 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5975 (double) mng_info->clip.left,(double) mng_info->clip.right,
5976 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5978 #endif /* MNG_INSERT_LAYERS */
5979 first_mng_object=MagickFalse;
5981 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5984 Allocate next image structure.
5986 AcquireNextImage(image_info,image,exception);
5988 if (GetNextImageInList(image) == (Image *) NULL)
5990 image=DestroyImageList(image);
5991 MngInfoFreeStruct(mng_info,&have_mng_structure);
5992 return((Image *) NULL);
5995 image=SyncNextImageInList(image);
5997 mng_info->image=image;
5998 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5999 GetBlobSize(image));
6001 if (status == MagickFalse)
6004 if (term_chunk_found)
6006 image->start_loop=MagickTrue;
6007 term_chunk_found=MagickFalse;
6011 image->start_loop=MagickFalse;
6013 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6015 image->delay=frame_delay;
6016 frame_delay=default_frame_delay;
6022 image->page.width=mng_info->mng_width;
6023 image->page.height=mng_info->mng_height;
6024 image->page.x=mng_info->x_off[object_id];
6025 image->page.y=mng_info->y_off[object_id];
6026 image->iterations=mng_iterations;
6029 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6032 if (logging != MagickFalse)
6033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6034 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6037 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6040 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6044 mng_info->image=image;
6045 mng_info->mng_type=mng_type;
6046 mng_info->object_id=object_id;
6048 if (memcmp(type,mng_IHDR,4) == 0)
6049 image=ReadOnePNGImage(mng_info,image_info,exception);
6051 #if defined(JNG_SUPPORTED)
6053 image=ReadOneJNGImage(mng_info,image_info,exception);
6056 if (image == (Image *) NULL)
6058 if (IsImageObject(previous) != MagickFalse)
6060 (void) DestroyImageList(previous);
6061 (void) CloseBlob(previous);
6064 MngInfoFreeStruct(mng_info,&have_mng_structure);
6065 return((Image *) NULL);
6068 if (image->columns == 0 || image->rows == 0)
6070 (void) CloseBlob(image);
6071 image=DestroyImageList(image);
6072 MngInfoFreeStruct(mng_info,&have_mng_structure);
6073 return((Image *) NULL);
6076 mng_info->image=image;
6083 if (mng_info->magn_methx || mng_info->magn_methy)
6089 if (logging != MagickFalse)
6090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091 " Processing MNG MAGN chunk");
6093 if (mng_info->magn_methx == 1)
6095 magnified_width=mng_info->magn_ml;
6097 if (image->columns > 1)
6098 magnified_width += mng_info->magn_mr;
6100 if (image->columns > 2)
6101 magnified_width += (png_uint_32)
6102 ((image->columns-2)*(mng_info->magn_mx));
6107 magnified_width=(png_uint_32) image->columns;
6109 if (image->columns > 1)
6110 magnified_width += mng_info->magn_ml-1;
6112 if (image->columns > 2)
6113 magnified_width += mng_info->magn_mr-1;
6115 if (image->columns > 3)
6116 magnified_width += (png_uint_32)
6117 ((image->columns-3)*(mng_info->magn_mx-1));
6120 if (mng_info->magn_methy == 1)
6122 magnified_height=mng_info->magn_mt;
6124 if (image->rows > 1)
6125 magnified_height += mng_info->magn_mb;
6127 if (image->rows > 2)
6128 magnified_height += (png_uint_32)
6129 ((image->rows-2)*(mng_info->magn_my));
6134 magnified_height=(png_uint_32) image->rows;
6136 if (image->rows > 1)
6137 magnified_height += mng_info->magn_mt-1;
6139 if (image->rows > 2)
6140 magnified_height += mng_info->magn_mb-1;
6142 if (image->rows > 3)
6143 magnified_height += (png_uint_32)
6144 ((image->rows-3)*(mng_info->magn_my-1));
6147 if (magnified_height > image->rows ||
6148 magnified_width > image->columns)
6175 /* Allocate next image structure. */
6177 if (logging != MagickFalse)
6178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6179 " Allocate magnified image");
6181 AcquireNextImage(image_info,image,exception);
6183 if (GetNextImageInList(image) == (Image *) NULL)
6185 image=DestroyImageList(image);
6186 MngInfoFreeStruct(mng_info,&have_mng_structure);
6187 return((Image *) NULL);
6190 large_image=SyncNextImageInList(image);
6192 large_image->columns=magnified_width;
6193 large_image->rows=magnified_height;
6195 magn_methx=mng_info->magn_methx;
6196 magn_methy=mng_info->magn_methy;
6198 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6199 #define QM unsigned short
6200 if (magn_methx != 1 || magn_methy != 1)
6203 Scale pixels to unsigned shorts to prevent
6204 overflow of intermediate values of interpolations
6206 for (y=0; y < (ssize_t) image->rows; y++)
6208 q=GetAuthenticPixels(image,0,y,image->columns,1,
6211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6213 SetPixelRed(image,ScaleQuantumToShort(
6214 GetPixelRed(image,q)),q);
6215 SetPixelGreen(image,ScaleQuantumToShort(
6216 GetPixelGreen(image,q)),q);
6217 SetPixelBlue(image,ScaleQuantumToShort(
6218 GetPixelBlue(image,q)),q);
6219 SetPixelAlpha(image,ScaleQuantumToShort(
6220 GetPixelAlpha(image,q)),q);
6221 q+=GetPixelChannels(image);
6224 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6232 if (image->matte != MagickFalse)
6233 (void) SetImageBackgroundColor(large_image);
6237 large_image->background_color.alpha=OpaqueAlpha;
6238 (void) SetImageBackgroundColor(large_image);
6240 if (magn_methx == 4)
6243 if (magn_methx == 5)
6246 if (magn_methy == 4)
6249 if (magn_methy == 5)
6253 /* magnify the rows into the right side of the large image */
6255 if (logging != MagickFalse)
6256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6257 " Magnify the rows to %.20g",(double) large_image->rows);
6258 m=(ssize_t) mng_info->magn_mt;
6260 length=(size_t) image->columns;
6261 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6262 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6264 if ((prev == (Quantum *) NULL) ||
6265 (next == (Quantum *) NULL))
6267 image=DestroyImageList(image);
6268 MngInfoFreeStruct(mng_info,&have_mng_structure);
6269 ThrowReaderException(ResourceLimitError,
6270 "MemoryAllocationFailed");
6273 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6274 (void) CopyMagickMemory(next,n,length);
6276 for (y=0; y < (ssize_t) image->rows; y++)
6279 m=(ssize_t) mng_info->magn_mt;
6281 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6282 m=(ssize_t) mng_info->magn_mb;
6284 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6285 m=(ssize_t) mng_info->magn_mb;
6287 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6291 m=(ssize_t) mng_info->magn_my;
6297 if (y < (ssize_t) image->rows-1)
6299 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6301 (void) CopyMagickMemory(next,n,length);
6304 for (i=0; i < m; i++, yy++)
6309 assert(yy < (ssize_t) large_image->rows);
6312 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6314 q+=(large_image->columns-image->columns);
6316 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6318 /* To do: get color as function of indexes[x] */
6320 if (image->storage_class == PseudoClass)
6325 if (magn_methy <= 1)
6327 /* replicate previous */
6328 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6329 SetPixelGreen(large_image,GetPixelGreen(image,
6331 SetPixelBlue(large_image,GetPixelBlue(image,
6333 SetPixelAlpha(large_image,GetPixelAlpha(image,
6337 else if (magn_methy == 2 || magn_methy == 4)
6341 SetPixelRed(large_image,GetPixelRed(image,
6343 SetPixelGreen(large_image,GetPixelGreen(image,
6345 SetPixelBlue(large_image,GetPixelBlue(image,
6347 SetPixelAlpha(large_image,GetPixelAlpha(image,
6354 SetPixelRed(large_image,((QM) (((ssize_t)
6355 (2*i*(GetPixelRed(image,n)
6356 -GetPixelRed(image,pixels)+m))/
6358 +GetPixelRed(image,pixels)))),q);
6359 SetPixelGreen(large_image,((QM) (((ssize_t)
6360 (2*i*(GetPixelGreen(image,n)
6361 -GetPixelGreen(image,pixels)+m))/
6363 +GetPixelGreen(image,pixels)))),q);
6364 SetPixelBlue(large_image,((QM) (((ssize_t)
6365 (2*i*(GetPixelBlue(image,n)
6366 -GetPixelBlue(image,pixels)+m))/
6368 +GetPixelBlue(image,pixels)))),q);
6370 if (image->matte != MagickFalse)
6371 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6372 (2*i*(GetPixelAlpha(image,n)
6373 -GetPixelAlpha(image,pixels)+m))
6375 GetPixelAlpha(image,pixels)))),q);
6378 if (magn_methy == 4)
6380 /* Replicate nearest */
6381 if (i <= ((m+1) << 1))
6382 SetPixelAlpha(large_image,GetPixelAlpha(image,
6385 SetPixelAlpha(large_image,GetPixelAlpha(image,
6390 else /* if (magn_methy == 3 || magn_methy == 5) */
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
6395 SetPixelRed(large_image,GetPixelRed(image,
6397 SetPixelGreen(large_image,GetPixelGreen(image,
6399 SetPixelBlue(large_image,GetPixelBlue(image,
6401 SetPixelAlpha(large_image,GetPixelAlpha(image,
6407 SetPixelRed(large_image,GetPixelRed(image,n),q);
6408 SetPixelGreen(large_image,GetPixelGreen(image,n),
6410 SetPixelBlue(large_image,GetPixelBlue(image,n),
6412 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6416 if (magn_methy == 5)
6418 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6419 (GetPixelAlpha(image,n)
6420 -GetPixelAlpha(image,pixels))
6421 +m))/((ssize_t) (m*2))
6422 +GetPixelAlpha(image,pixels)),q);
6425 n+=GetPixelChannels(image);
6426 q+=GetPixelChannels(large_image);
6427 pixels+=GetPixelChannels(image);
6430 if (SyncAuthenticPixels(large_image,exception) == 0)
6436 prev=(Quantum *) RelinquishMagickMemory(prev);
6437 next=(Quantum *) RelinquishMagickMemory(next);
6439 length=image->columns;
6441 if (logging != MagickFalse)
6442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6443 " Delete original image");
6445 DeleteImageFromList(&image);
6449 mng_info->image=image;
6451 /* magnify the columns */
6452 if (logging != MagickFalse)
6453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6454 " Magnify the columns to %.20g",(double) image->columns);
6456 for (y=0; y < (ssize_t) image->rows; y++)
6461 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6462 pixels=q+(image->columns-length)*GetPixelChannels(image);
6463 n=pixels+GetPixelChannels(image);
6465 for (x=(ssize_t) (image->columns-length);
6466 x < (ssize_t) image->columns; x++)
6468 /* To do: Rewrite using Get/Set***PixelChannel() */
6470 if (x == (ssize_t) (image->columns-length))
6471 m=(ssize_t) mng_info->magn_ml;
6473 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6474 m=(ssize_t) mng_info->magn_mr;
6476 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6477 m=(ssize_t) mng_info->magn_mr;
6479 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6483 m=(ssize_t) mng_info->magn_mx;
6485 for (i=0; i < m; i++)
6487 if (magn_methx <= 1)
6489 /* replicate previous */
6490 SetPixelRed(image,GetPixelRed(image,pixels),q);
6491 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6492 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6493 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6496 else if (magn_methx == 2 || magn_methx == 4)
6500 SetPixelRed(image,GetPixelRed(image,pixels),q);
6501 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6502 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6503 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6506 /* To do: Rewrite using Get/Set***PixelChannel() */
6510 SetPixelRed(image,(QM) ((2*i*(
6511 GetPixelRed(image,n)
6512 -GetPixelRed(image,pixels))+m)
6514 GetPixelRed(image,pixels)),q);
6516 SetPixelGreen(image,(QM) ((2*i*(
6517 GetPixelGreen(image,n)
6518 -GetPixelGreen(image,pixels))+m)
6520 GetPixelGreen(image,pixels)),q);
6522 SetPixelBlue(image,(QM) ((2*i*(
6523 GetPixelBlue(image,n)
6524 -GetPixelBlue(image,pixels))+m)
6526 GetPixelBlue(image,pixels)),q);
6527 if (image->matte != MagickFalse)
6528 SetPixelAlpha(image,(QM) ((2*i*(
6529 GetPixelAlpha(image,n)
6530 -GetPixelAlpha(image,pixels))+m)
6532 GetPixelAlpha(image,pixels)),q);
6535 if (magn_methx == 4)
6537 /* Replicate nearest */
6538 if (i <= ((m+1) << 1))
6540 SetPixelAlpha(image,
6541 GetPixelAlpha(image,pixels)+0,q);
6545 SetPixelAlpha(image,
6546 GetPixelAlpha(image,n)+0,q);
6551 else /* if (magn_methx == 3 || magn_methx == 5) */
6553 /* Replicate nearest */
6554 if (i <= ((m+1) << 1))
6556 SetPixelRed(image,GetPixelRed(image,pixels),q);
6557 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6558 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6559 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6564 SetPixelRed(image,GetPixelRed(image,n),q);
6565 SetPixelGreen(image,GetPixelGreen(image,n),q);
6566 SetPixelBlue(image,GetPixelBlue(image,n),q);
6567 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6570 if (magn_methx == 5)
6573 SetPixelAlpha(image,
6574 (QM) ((2*i*( GetPixelAlpha(image,n)
6575 -GetPixelAlpha(image,pixels))+m)/
6577 +GetPixelAlpha(image,pixels)),q);
6580 q+=GetPixelChannels(image);
6582 n+=GetPixelChannels(image);
6583 p+=GetPixelChannels(image);
6586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6589 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6590 if (magn_methx != 1 || magn_methy != 1)
6593 Rescale pixels to Quantum
6595 for (y=0; y < (ssize_t) image->rows; y++)
6597 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6599 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6601 SetPixelRed(image,ScaleShortToQuantum(
6602 GetPixelRed(image,q)),q);
6603 SetPixelGreen(image,ScaleShortToQuantum(
6604 GetPixelGreen(image,q)),q);
6605 SetPixelBlue(image,ScaleShortToQuantum(
6606 GetPixelBlue(image,q)),q);
6607 SetPixelAlpha(image,ScaleShortToQuantum(
6608 GetPixelAlpha(image,q)),q);
6609 q+=GetPixelChannels(image);
6612 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Finished MAGN processing");
6624 Crop_box is with respect to the upper left corner of the MNG.
6626 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6627 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6628 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6629 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6630 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6631 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6632 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6633 if ((crop_box.left != (mng_info->image_box.left
6634 +mng_info->x_off[object_id])) ||
6635 (crop_box.right != (mng_info->image_box.right
6636 +mng_info->x_off[object_id])) ||
6637 (crop_box.top != (mng_info->image_box.top
6638 +mng_info->y_off[object_id])) ||
6639 (crop_box.bottom != (mng_info->image_box.bottom
6640 +mng_info->y_off[object_id])))
6642 if (logging != MagickFalse)
6643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6644 " Crop the PNG image");
6646 if ((crop_box.left < crop_box.right) &&
6647 (crop_box.top < crop_box.bottom))
6656 Crop_info is with respect to the upper left corner of
6659 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6660 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6661 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6662 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6663 image->page.width=image->columns;
6664 image->page.height=image->rows;
6667 im=CropImage(image,&crop_info,exception);
6669 if (im != (Image *) NULL)
6671 image->columns=im->columns;
6672 image->rows=im->rows;
6673 im=DestroyImage(im);
6674 image->page.width=image->columns;
6675 image->page.height=image->rows;
6676 image->page.x=crop_box.left;
6677 image->page.y=crop_box.top;
6684 No pixels in crop area. The MNG spec still requires
6685 a layer, though, so make a single transparent pixel in
6686 the top left corner.
6691 (void) SetImageBackgroundColor(image);
6692 image->page.width=1;
6693 image->page.height=1;
6698 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6699 image=mng_info->image;
6703 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6704 /* PNG does not handle depths greater than 16 so reduce it even
6707 if (image->depth > 16)
6711 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6712 if (LosslessReduceDepthOK(image) != MagickFalse)
6716 GetImageException(image,exception);
6718 if (image_info->number_scenes != 0)
6720 if (mng_info->scenes_found >
6721 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6725 if (logging != MagickFalse)
6726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6727 " Finished reading image datastream.");
6729 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6731 (void) CloseBlob(image);
6733 if (logging != MagickFalse)
6734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6735 " Finished reading all image datastreams.");
6737 #if defined(MNG_INSERT_LAYERS)
6738 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6739 (mng_info->mng_height))
6742 Insert a background layer if nothing else was found.
6744 if (logging != MagickFalse)
6745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6746 " No images found. Inserting a background layer.");
6748 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6751 Allocate next image structure.
6753 AcquireNextImage(image_info,image,exception);
6754 if (GetNextImageInList(image) == (Image *) NULL)
6756 image=DestroyImageList(image);
6757 MngInfoFreeStruct(mng_info,&have_mng_structure);
6759 if (logging != MagickFalse)
6760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6761 " Allocation failed, returning NULL.");
6763 return((Image *) NULL);
6765 image=SyncNextImageInList(image);
6767 image->columns=mng_info->mng_width;
6768 image->rows=mng_info->mng_height;
6769 image->page.width=mng_info->mng_width;
6770 image->page.height=mng_info->mng_height;
6773 image->background_color=mng_background_color;
6774 image->matte=MagickFalse;
6776 if (image_info->ping == MagickFalse)
6777 (void) SetImageBackgroundColor(image);
6779 mng_info->image_found++;
6782 image->iterations=mng_iterations;
6784 if (mng_iterations == 1)
6785 image->start_loop=MagickTrue;
6787 while (GetPreviousImageInList(image) != (Image *) NULL)
6790 if (image_count > 10*mng_info->image_found)
6792 if (logging != MagickFalse)
6793 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6796 CoderError,"Linked list is corrupted, beginning of list not found",
6797 "`%s'",image_info->filename);
6799 return((Image *) NULL);
6802 image=GetPreviousImageInList(image);
6804 if (GetNextImageInList(image) == (Image *) NULL)
6806 if (logging != MagickFalse)
6807 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6809 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6810 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6811 image_info->filename);
6815 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6816 GetNextImageInList(image) ==
6819 if (logging != MagickFalse)
6820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821 " First image null");
6823 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6824 CoderError,"image->next for first image is NULL but shouldn't be.",
6825 "`%s'",image_info->filename);
6828 if (mng_info->image_found == 0)
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6832 " No visible images found.");
6834 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6835 CoderError,"No visible images in file","`%s'",image_info->filename);
6837 if (image != (Image *) NULL)
6838 image=DestroyImageList(image);
6840 MngInfoFreeStruct(mng_info,&have_mng_structure);
6841 return((Image *) NULL);
6844 if (mng_info->ticks_per_second)
6845 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6846 final_delay/mng_info->ticks_per_second;
6849 image->start_loop=MagickTrue;
6851 /* Find final nonzero image delay */
6852 final_image_delay=0;
6854 while (GetNextImageInList(image) != (Image *) NULL)
6857 final_image_delay=image->delay;
6859 image=GetNextImageInList(image);
6862 if (final_delay < final_image_delay)
6863 final_delay=final_image_delay;
6865 image->delay=final_delay;
6867 if (logging != MagickFalse)
6868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6869 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6870 (double) final_delay);
6872 if (logging != MagickFalse)
6878 image=GetFirstImageInList(image);
6880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " Before coalesce:");
6883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6884 " scene 0 delay=%.20g",(double) image->delay);
6886 while (GetNextImageInList(image) != (Image *) NULL)
6888 image=GetNextImageInList(image);
6889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6890 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6894 image=GetFirstImageInList(image);
6895 #ifdef MNG_COALESCE_LAYERS
6905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6909 next_image=CoalesceImages(image,&image->exception);
6911 if (next_image == (Image *) NULL)
6912 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6914 image=DestroyImageList(image);
6917 for (next=image; next != (Image *) NULL; next=next_image)
6919 next->page.width=mng_info->mng_width;
6920 next->page.height=mng_info->mng_height;
6923 next->scene=scene++;
6924 next_image=GetNextImageInList(next);
6926 if (next_image == (Image *) NULL)
6929 if (next->delay == 0)
6932 next_image->previous=GetPreviousImageInList(next);
6933 if (GetPreviousImageInList(next) == (Image *) NULL)
6936 next->previous->next=next_image;
6937 next=DestroyImage(next);
6943 while (GetNextImageInList(image) != (Image *) NULL)
6944 image=GetNextImageInList(image);
6946 image->dispose=BackgroundDispose;
6948 if (logging != MagickFalse)
6954 image=GetFirstImageInList(image);
6956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6957 " After coalesce:");
6959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6960 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6961 (double) image->dispose);
6963 while (GetNextImageInList(image) != (Image *) NULL)
6965 image=GetNextImageInList(image);
6967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6968 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6969 (double) image->delay,(double) image->dispose);
6973 image=GetFirstImageInList(image);
6974 MngInfoFreeStruct(mng_info,&have_mng_structure);
6975 have_mng_structure=MagickFalse;
6977 if (logging != MagickFalse)
6978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6980 return(GetFirstImageInList(image));
6982 #else /* PNG_LIBPNG_VER > 10011 */
6983 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6985 printf("Your PNG library is too old: You have libpng-%s\n",
6986 PNG_LIBPNG_VER_STRING);
6988 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6989 "PNG library is too old","`%s'",image_info->filename);
6991 return(Image *) NULL;
6994 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6996 return(ReadPNGImage(image_info,exception));
6998 #endif /* PNG_LIBPNG_VER > 10011 */
7002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7006 % R e g i s t e r P N G I m a g e %
7010 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7012 % RegisterPNGImage() adds properties for the PNG image format to
7013 % the list of supported formats. The properties include the image format
7014 % tag, a method to read and/or write the format, whether the format
7015 % supports the saving of more than one frame to the same file or blob,
7016 % whether the format supports native in-memory I/O, and a brief
7017 % description of the format.
7019 % The format of the RegisterPNGImage method is:
7021 % size_t RegisterPNGImage(void)
7024 ModuleExport size_t RegisterPNGImage(void)
7027 version[MaxTextExtent];
7035 "See http://www.libpng.org/ for details about the PNG format."
7040 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7046 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7052 #if defined(PNG_LIBPNG_VER_STRING)
7053 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7054 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7056 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7058 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7059 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7064 entry=SetMagickInfo("MNG");
7065 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7067 #if defined(MAGICKCORE_PNG_DELEGATE)
7068 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7069 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7072 entry->magick=(IsImageFormatHandler *) IsMNG;
7073 entry->description=ConstantString("Multiple-image Network Graphics");
7075 if (*version != '\0')
7076 entry->version=ConstantString(version);
7078 entry->module=ConstantString("PNG");
7079 entry->note=ConstantString(MNGNote);
7080 (void) RegisterMagickInfo(entry);
7082 entry=SetMagickInfo("PNG");
7084 #if defined(MAGICKCORE_PNG_DELEGATE)
7085 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7086 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7089 entry->magick=(IsImageFormatHandler *) IsPNG;
7090 entry->adjoin=MagickFalse;
7091 entry->description=ConstantString("Portable Network Graphics");
7092 entry->module=ConstantString("PNG");
7094 if (*version != '\0')
7095 entry->version=ConstantString(version);
7097 entry->note=ConstantString(PNGNote);
7098 (void) RegisterMagickInfo(entry);
7100 entry=SetMagickInfo("PNG8");
7102 #if defined(MAGICKCORE_PNG_DELEGATE)
7103 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7104 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7107 entry->magick=(IsImageFormatHandler *) IsPNG;
7108 entry->adjoin=MagickFalse;
7109 entry->description=ConstantString(
7110 "8-bit indexed with optional binary transparency");
7111 entry->module=ConstantString("PNG");
7112 (void) RegisterMagickInfo(entry);
7114 entry=SetMagickInfo("PNG24");
7117 #if defined(ZLIB_VERSION)
7118 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7119 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7121 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7123 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7124 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7128 if (*version != '\0')
7129 entry->version=ConstantString(version);
7131 #if defined(MAGICKCORE_PNG_DELEGATE)
7132 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7133 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7136 entry->magick=(IsImageFormatHandler *) IsPNG;
7137 entry->adjoin=MagickFalse;
7138 entry->description=ConstantString("opaque 24-bit RGB");
7139 entry->module=ConstantString("PNG");
7140 (void) RegisterMagickInfo(entry);
7142 entry=SetMagickInfo("PNG32");
7144 #if defined(MAGICKCORE_PNG_DELEGATE)
7145 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7146 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7149 entry->magick=(IsImageFormatHandler *) IsPNG;
7150 entry->adjoin=MagickFalse;
7151 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7152 entry->module=ConstantString("PNG");
7153 (void) RegisterMagickInfo(entry);
7155 entry=SetMagickInfo("JNG");
7157 #if defined(JNG_SUPPORTED)
7158 #if defined(MAGICKCORE_PNG_DELEGATE)
7159 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7160 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7164 entry->magick=(IsImageFormatHandler *) IsJNG;
7165 entry->adjoin=MagickFalse;
7166 entry->description=ConstantString("JPEG Network Graphics");
7167 entry->module=ConstantString("PNG");
7168 entry->note=ConstantString(JNGNote);
7169 (void) RegisterMagickInfo(entry);
7171 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7172 ping_semaphore=AllocateSemaphoreInfo();
7175 return(MagickImageCoderSignature);
7179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7183 % U n r e g i s t e r P N G I m a g e %
7187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7189 % UnregisterPNGImage() removes format registrations made by the
7190 % PNG module from the list of supported formats.
7192 % The format of the UnregisterPNGImage method is:
7194 % UnregisterPNGImage(void)
7197 ModuleExport void UnregisterPNGImage(void)
7199 (void) UnregisterMagickInfo("MNG");
7200 (void) UnregisterMagickInfo("PNG");
7201 (void) UnregisterMagickInfo("PNG8");
7202 (void) UnregisterMagickInfo("PNG24");
7203 (void) UnregisterMagickInfo("PNG32");
7204 (void) UnregisterMagickInfo("JNG");
7206 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7207 if (ping_semaphore != (SemaphoreInfo *) NULL)
7208 DestroySemaphoreInfo(&ping_semaphore);
7212 #if defined(MAGICKCORE_PNG_DELEGATE)
7213 #if PNG_LIBPNG_VER > 10011
7215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7219 % W r i t e M N G I m a g e %
7223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7225 % WriteMNGImage() writes an image in the Portable Network Graphics
7226 % Group's "Multiple-image Network Graphics" encoded image format.
7228 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7230 % The format of the WriteMNGImage method is:
7232 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7233 % Image *image,ExceptionInfo *exception)
7235 % A description of each parameter follows.
7237 % o image_info: the image info.
7239 % o image: The image.
7241 % o exception: return any errors or warnings in this structure.
7243 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7244 % "To do" under ReadPNGImage):
7246 % Preserve all unknown and not-yet-handled known chunks found in input
7247 % PNG file and copy them into output PNG files according to the PNG
7250 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7252 % Improve selection of color type (use indexed-colour or indexed-colour
7253 % with tRNS when 256 or fewer unique RGBA values are present).
7255 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7256 % This will be complicated if we limit ourselves to generating MNG-LC
7257 % files. For now we ignore disposal method 3 and simply overlay the next
7260 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7261 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7262 % [mostly done 15 June 1999 but still need to take care of tRNS]
7264 % Check for identical sRGB and replace with a global sRGB (and remove
7265 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7266 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7267 % local gAMA/cHRM with local sRGB if appropriate).
7269 % Check for identical sBIT chunks and write global ones.
7271 % Provide option to skip writing the signature tEXt chunks.
7273 % Use signatures to detect identical objects and reuse the first
7274 % instance of such objects instead of writing duplicate objects.
7276 % Use a smaller-than-32k value of compression window size when
7279 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7280 % ancillary text chunks and save profiles.
7282 % Provide an option to force LC files (to ensure exact framing rate)
7285 % Provide an option to force VLC files instead of LC, even when offsets
7286 % are present. This will involve expanding the embedded images with a
7287 % transparent region at the top and/or left.
7291 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7292 png_info *ping_info, unsigned char *profile_type, unsigned char
7293 *profile_description, unsigned char *profile_data, png_uint_32 length)
7312 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7314 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7317 if (image_info->verbose)
7319 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7320 (char *) profile_type, (double) length);
7323 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7324 description_length=(png_uint_32) strlen((const char *) profile_description);
7325 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7326 + description_length);
7327 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7328 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7329 text[0].key[0]='\0';
7330 (void) ConcatenateMagickString(text[0].key,
7331 "Raw profile type ",MaxTextExtent);
7332 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7336 (void) CopyMagickString(dp,(const char *) profile_description,
7338 dp+=description_length;
7340 (void) FormatLocaleString(dp,allocated_length-
7341 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7344 for (i=0; i < (ssize_t) length; i++)
7348 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7349 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7354 text[0].text_length=(png_size_t) (dp-text[0].text);
7355 text[0].compression=image_info->compression == NoCompression ||
7356 (image_info->compression == UndefinedCompression &&
7357 text[0].text_length < 128) ? -1 : 0;
7359 if (text[0].text_length <= allocated_length)
7360 png_set_text(ping,ping_info,text,1);
7362 png_free(ping,text[0].text);
7363 png_free(ping,text[0].key);
7364 png_free(ping,text);
7367 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7368 const char *string, MagickBooleanType logging)
7381 ResetImageProfileIterator(image);
7383 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7385 profile=GetImageProfile(image,name);
7387 if (profile != (const StringInfo *) NULL)
7392 if (LocaleNCompare(name,string,11) == 0)
7394 if (logging != MagickFalse)
7395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7396 " Found %s profile",name);
7398 ping_profile=CloneStringInfo(profile);
7399 data=GetStringInfoDatum(ping_profile),
7400 length=(png_uint_32) GetStringInfoLength(ping_profile);
7405 (void) WriteBlobMSBULong(image,length-5); /* data length */
7406 (void) WriteBlob(image,length-1,data+1);
7407 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7408 ping_profile=DestroyStringInfo(ping_profile);
7412 name=GetNextImageProfile(image);
7419 /* Write one PNG image */
7420 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7421 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7445 ping_trans_alpha[256];
7473 ping_have_cheap_transparency,
7484 /* ping_exclude_EXIF, */
7487 /* ping_exclude_iTXt, */
7492 /* ping_exclude_tRNS, */
7494 ping_exclude_zCCP, /* hex-encoded iCCP */
7497 ping_preserve_colormap,
7498 ping_need_colortype_warning,
7519 ping_interlace_method,
7520 ping_compression_method,
7537 number_semitransparent,
7539 ping_pHYs_unit_type;
7542 ping_pHYs_x_resolution,
7543 ping_pHYs_y_resolution;
7545 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7546 " Enter WriteOnePNGImage()");
7548 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7549 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7550 if (image_info == (ImageInfo *) NULL)
7551 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7553 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7554 LockSemaphoreInfo(ping_semaphore);
7557 /* Initialize some stuff */
7560 ping_interlace_method=0,
7561 ping_compression_method=0,
7562 ping_filter_method=0,
7565 ping_background.red = 0;
7566 ping_background.green = 0;
7567 ping_background.blue = 0;
7568 ping_background.gray = 0;
7569 ping_background.index = 0;
7571 ping_trans_color.red=0;
7572 ping_trans_color.green=0;
7573 ping_trans_color.blue=0;
7574 ping_trans_color.gray=0;
7576 ping_pHYs_unit_type = 0;
7577 ping_pHYs_x_resolution = 0;
7578 ping_pHYs_y_resolution = 0;
7580 ping_have_blob=MagickFalse;
7581 ping_have_color=MagickTrue;
7582 ping_have_non_bw=MagickTrue;
7583 ping_have_PLTE=MagickFalse;
7584 ping_have_bKGD=MagickFalse;
7585 ping_have_pHYs=MagickFalse;
7586 ping_have_tRNS=MagickFalse;
7588 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7589 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7590 ping_exclude_date=mng_info->ping_exclude_date;
7591 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7592 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7593 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7594 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7595 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7596 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7597 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7598 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7599 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7600 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7601 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7602 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7604 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7605 ping_need_colortype_warning = MagickFalse;
7607 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7608 * i.e., eliminate the ICC profile and set image->rendering_intent.
7609 * Note that this will not involve any changes to the actual pixels
7610 * but merely passes information to applications that read the resulting
7613 if (ping_exclude_sRGB == MagickFalse)
7621 ResetImageProfileIterator(image);
7622 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7624 profile=GetImageProfile(image,name);
7626 if (profile != (StringInfo *) NULL)
7628 if ((LocaleCompare(name,"ICC") == 0) ||
7629 (LocaleCompare(name,"ICM") == 0))
7634 /* 0: not a known sRGB profile
7635 * 1: HP-Microsoft sRGB v2
7636 * 2: ICC sRGB v4 perceptual
7637 * 3: ICC sRGB v2 perceptual no black-compensation
7640 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7641 check_len[4] = {0, 3144, 60960, 3052};
7650 length=(png_uint_32) GetStringInfoLength(profile);
7652 for (icheck=3; icheck > 0; icheck--)
7654 if (length == check_len[icheck])
7656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " Got a %lu-byte ICC profile (potentially sRGB)",
7658 (unsigned long) length);
7660 data=GetStringInfoDatum(profile);
7661 profile_crc=crc32(0,data,length);
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " with crc=%8x",profile_crc);
7666 if (profile_crc == check_crc[icheck])
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670 if (image->rendering_intent==UndefinedIntent)
7671 image->rendering_intent=PerceptualIntent;
7677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7678 " Got a %lu-byte ICC profile",
7679 (unsigned long) length);
7682 name=GetNextImageProfile(image);
7687 number_semitransparent = 0;
7688 number_transparent = 0;
7690 if (logging != MagickFalse)
7692 if (image->storage_class == UndefinedClass)
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " storage_class=UndefinedClass");
7695 if (image->storage_class == DirectClass)
7696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7697 " storage_class=DirectClass");
7698 if (image->storage_class == PseudoClass)
7699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7700 " storage_class=PseudoClass");
7703 if (image->storage_class == PseudoClass &&
7704 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7705 (mng_info->write_png_colortype != 0 &&
7706 mng_info->write_png_colortype != 4)))
7708 (void) SyncImage(image);
7709 image->storage_class = DirectClass;
7712 if (ping_preserve_colormap == MagickFalse)
7714 if (image->storage_class != PseudoClass && image->colormap != NULL)
7716 /* Free the bogus colormap; it can cause trouble later */
7717 if (logging != MagickFalse)
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " Freeing bogus colormap");
7720 (void) RelinquishMagickMemory(image->colormap);
7721 image->colormap=NULL;
7725 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7726 (void) TransformImageColorspace(image,RGBColorspace);
7729 Sometimes we get PseudoClass images whose RGB values don't match
7730 the colors in the colormap. This code syncs the RGB values.
7732 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7733 (void) SyncImage(image);
7735 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7736 if (image->depth > 8)
7738 if (logging != MagickFalse)
7739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7740 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7746 /* Respect the -depth option */
7747 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7755 exception=(&image->exception);
7757 if (image->depth > 8)
7759 #if MAGICKCORE_QUANTUM_DEPTH > 16
7760 /* Scale to 16-bit */
7761 LBR16PacketRGBO(image->background_color);
7763 for (y=0; y < (ssize_t) image->rows; y++)
7765 r=GetAuthenticPixels(image,0,y,image->columns,1,
7768 if (r == (Quantum *) NULL)
7771 for (x=0; x < (ssize_t) image->columns; x++)
7777 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7781 if (image->storage_class == PseudoClass && image->colormap != NULL)
7783 for (i=0; i < (ssize_t) image->colors; i++)
7785 LBR16PacketRGBO(image->colormap[i]);
7788 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7791 else if (image->depth > 4)
7793 #if MAGICKCORE_QUANTUM_DEPTH > 8
7794 /* Scale to 8-bit */
7795 LBR08PacketRGBO(image->background_color);
7797 for (y=0; y < (ssize_t) image->rows; y++)
7799 r=GetAuthenticPixels(image,0,y,image->columns,1,
7802 if (r == (Quantum *) NULL)
7805 for (x=0; x < (ssize_t) image->columns; x++)
7811 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7815 if (image->storage_class == PseudoClass && image->colormap != NULL)
7817 for (i=0; i < (ssize_t) image->colors; i++)
7819 LBR08PacketRGBO(image->colormap[i]);
7822 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7825 if (image->depth > 2)
7827 /* Scale to 4-bit */
7828 LBR04PacketRGBO(image->background_color);
7830 for (y=0; y < (ssize_t) image->rows; y++)
7832 r=GetAuthenticPixels(image,0,y,image->columns,1,
7835 if (r == (Quantum *) NULL)
7838 for (x=0; x < (ssize_t) image->columns; x++)
7844 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7848 if (image->storage_class == PseudoClass && image->colormap != NULL)
7850 for (i=0; i < (ssize_t) image->colors; i++)
7852 LBR04PacketRGBO(image->colormap[i]);
7857 else if (image->depth > 1)
7859 /* Scale to 2-bit */
7860 LBR02PacketRGBO(image->background_color);
7862 for (y=0; y < (ssize_t) image->rows; y++)
7864 r=GetAuthenticPixels(image,0,y,image->columns,1,
7867 if (r == (Quantum *) NULL)
7870 for (x=0; x < (ssize_t) image->columns; x++)
7876 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7880 if (image->storage_class == PseudoClass && image->colormap != NULL)
7882 for (i=0; i < (ssize_t) image->colors; i++)
7884 LBR02PacketRGBO(image->colormap[i]);
7890 /* Scale to 1-bit */
7891 LBR01PacketRGBO(image->background_color);
7893 for (y=0; y < (ssize_t) image->rows; y++)
7895 r=GetAuthenticPixels(image,0,y,image->columns,1,
7898 if (r == (Quantum *) NULL)
7901 for (x=0; x < (ssize_t) image->columns; x++)
7907 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7911 if (image->storage_class == PseudoClass && image->colormap != NULL)
7913 for (i=0; i < (ssize_t) image->colors; i++)
7915 LBR01PacketRGBO(image->colormap[i]);
7921 /* To do: set to next higher multiple of 8 */
7922 if (image->depth < 8)
7925 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7926 /* PNG does not handle depths greater than 16 so reduce it even
7929 if (image->depth > 8)
7933 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7934 if (image->depth == 16 && mng_info->write_png_depth != 16)
7935 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7939 /* Normally we run this just once, but in the case of writing PNG8
7940 * we reduce the transparency to binary and run again, then if there
7941 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7942 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7943 * palette. Then (To do) we take care of a final reduction that is only
7944 * needed if there are still 256 colors present and one of them has both
7945 * transparent and opaque instances.
7948 tried_332 = MagickFalse;
7949 tried_333 = MagickFalse;
7950 tried_444 = MagickFalse;
7956 * Sometimes we get DirectClass images that have 256 colors or fewer.
7957 * This code will build a colormap.
7959 * Also, sometimes we get PseudoClass images with an out-of-date
7960 * colormap. This code will replace the colormap with a new one.
7961 * Sometimes we get PseudoClass images that have more than 256 colors.
7962 * This code will delete the colormap and change the image to
7965 * If image->matte is MagickFalse, we ignore the alpha channel
7966 * even though it sometimes contains left-over non-opaque values.
7968 * Also we gather some information (number of opaque, transparent,
7969 * and semitransparent pixels, and whether the image has any non-gray
7970 * pixels or only black-and-white pixels) that we might need later.
7972 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7973 * we need to check for bogus non-opaque values, at least.
7984 semitransparent[260],
7987 register const Quantum
7994 if (logging != MagickFalse)
7995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7996 " Enter BUILD_PALETTE:");
7998 if (logging != MagickFalse)
8000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8001 " image->columns=%.20g",(double) image->columns);
8002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8003 " image->rows=%.20g",(double) image->rows);
8004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8005 " image->matte=%.20g",(double) image->matte);
8006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8007 " image->depth=%.20g",(double) image->depth);
8009 if (image->storage_class == PseudoClass && image->colormap != NULL)
8011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8012 " Original colormap:");
8013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8014 " i (red,green,blue,alpha)");
8016 for (i=0; i < 256; i++)
8018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8019 " %d (%d,%d,%d,%d)",
8021 (int) image->colormap[i].red,
8022 (int) image->colormap[i].green,
8023 (int) image->colormap[i].blue,
8024 (int) image->colormap[i].alpha);
8027 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8032 " %d (%d,%d,%d,%d)",
8034 (int) image->colormap[i].red,
8035 (int) image->colormap[i].green,
8036 (int) image->colormap[i].blue,
8037 (int) image->colormap[i].alpha);
8042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8043 " image->colors=%d",(int) image->colors);
8045 if (image->colors == 0)
8046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " (zero means unknown)");
8049 if (ping_preserve_colormap == MagickFalse)
8050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051 " Regenerate the colormap");
8054 exception=(&image->exception);
8058 number_semitransparent = 0;
8059 number_transparent = 0;
8061 for (y=0; y < (ssize_t) image->rows; y++)
8063 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8065 if (q == (Quantum *) NULL)
8068 for (x=0; x < (ssize_t) image->columns; x++)
8070 if (image->matte == MagickFalse ||
8071 GetPixelAlpha(image,q) == OpaqueAlpha)
8073 if (number_opaque < 259)
8075 if (number_opaque == 0)
8077 GetPixelPacketPixel(image, q, opaque);
8078 opaque[0].alpha=OpaqueAlpha;
8082 for (i=0; i< (ssize_t) number_opaque; i++)
8084 if (IsPixelEquivalent(image,q, opaque+i))
8088 if (i == (ssize_t) number_opaque &&
8089 number_opaque < 259)
8092 GetPixelPacketPixel(image, q, opaque+i);
8093 opaque[i].alpha=OpaqueAlpha;
8097 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8099 if (number_transparent < 259)
8101 if (number_transparent == 0)
8103 GetPixelPacketPixel(image, q, transparent);
8104 ping_trans_color.red=(unsigned short)
8105 GetPixelRed(image,q);
8106 ping_trans_color.green=(unsigned short)
8107 GetPixelGreen(image,q);
8108 ping_trans_color.blue=(unsigned short)
8109 GetPixelBlue(image,q);
8110 ping_trans_color.gray=(unsigned short)
8111 GetPixelRed(image,q);
8112 number_transparent = 1;
8115 for (i=0; i< (ssize_t) number_transparent; i++)
8117 if (IsPixelEquivalent(image,q, transparent+i))
8121 if (i == (ssize_t) number_transparent &&
8122 number_transparent < 259)
8124 number_transparent++;
8125 GetPixelPacketPixel(image,q,transparent+i);
8131 if (number_semitransparent < 259)
8133 if (number_semitransparent == 0)
8135 GetPixelPacketPixel(image,q,semitransparent);
8136 number_semitransparent = 1;
8139 for (i=0; i< (ssize_t) number_semitransparent; i++)
8141 if (IsPixelEquivalent(image,q, semitransparent+i)
8142 && GetPixelAlpha(image,q) ==
8143 semitransparent[i].alpha)
8147 if (i == (ssize_t) number_semitransparent &&
8148 number_semitransparent < 259)
8150 number_semitransparent++;
8151 GetPixelPacketPixel(image, q, semitransparent+i);
8155 q+=GetPixelChannels(image);
8159 if (mng_info->write_png8 == MagickFalse &&
8160 ping_exclude_bKGD == MagickFalse)
8162 /* Add the background color to the palette, if it
8163 * isn't already there.
8165 if (logging != MagickFalse)
8167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8168 " Check colormap for background (%d,%d,%d)",
8169 (int) image->background_color.red,
8170 (int) image->background_color.green,
8171 (int) image->background_color.blue);
8173 for (i=0; i<number_opaque; i++)
8175 if (opaque[i].red == image->background_color.red &&
8176 opaque[i].green == image->background_color.green &&
8177 opaque[i].blue == image->background_color.blue)
8180 if (number_opaque < 259 && i == number_opaque)
8182 opaque[i] = image->background_color;
8183 ping_background.index = i;
8184 if (logging != MagickFalse)
8186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8187 " background_color index is %d",(int) i);
8191 else if (logging != MagickFalse)
8192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8193 " No room in the colormap to add background color");
8196 image_colors=number_opaque+number_transparent+number_semitransparent;
8198 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8200 /* No room for the background color; remove it. */
8205 if (logging != MagickFalse)
8207 if (image_colors > 256)
8208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8209 " image has more than 256 colors");
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " image has %d colors",image_colors);
8216 if (ping_preserve_colormap != MagickFalse)
8219 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8221 ping_have_color=MagickFalse;
8222 ping_have_non_bw=MagickFalse;
8224 if(image_colors > 256)
8226 for (y=0; y < (ssize_t) image->rows; y++)
8228 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8230 if (q == (Quantum *) NULL)
8234 for (x=0; x < (ssize_t) image->columns; x++)
8236 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8237 GetPixelRed(image,s) != GetPixelBlue(image,s))
8239 ping_have_color=MagickTrue;
8240 ping_have_non_bw=MagickTrue;
8243 s+=GetPixelChannels(image);
8246 if (ping_have_color != MagickFalse)
8249 /* Worst case is black-and-white; we are looking at every
8253 if (ping_have_non_bw == MagickFalse)
8256 for (x=0; x < (ssize_t) image->columns; x++)
8258 if (GetPixelRed(image,s) != 0 &&
8259 GetPixelRed(image,s) != QuantumRange)
8261 ping_have_non_bw=MagickTrue;
8264 s+=GetPixelChannels(image);
8271 if (image_colors < 257)
8277 * Initialize image colormap.
8280 if (logging != MagickFalse)
8281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8282 " Sort the new colormap");
8284 /* Sort palette, transparent first */;
8288 for (i=0; i<number_transparent; i++)
8289 colormap[n++] = transparent[i];
8291 for (i=0; i<number_semitransparent; i++)
8292 colormap[n++] = semitransparent[i];
8294 for (i=0; i<number_opaque; i++)
8295 colormap[n++] = opaque[i];
8297 ping_background.index +=
8298 (number_transparent + number_semitransparent);
8300 /* image_colors < 257; search the colormap instead of the pixels
8301 * to get ping_have_color and ping_have_non_bw
8305 if (ping_have_color == MagickFalse)
8307 if (colormap[i].red != colormap[i].green ||
8308 colormap[i].red != colormap[i].blue)
8310 ping_have_color=MagickTrue;
8311 ping_have_non_bw=MagickTrue;
8316 if (ping_have_non_bw == MagickFalse)
8318 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8319 ping_have_non_bw=MagickTrue;
8323 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8324 (number_transparent == 0 && number_semitransparent == 0)) &&
8325 (((mng_info->write_png_colortype-1) ==
8326 PNG_COLOR_TYPE_PALETTE) ||
8327 (mng_info->write_png_colortype == 0)))
8329 if (logging != MagickFalse)
8331 if (n != (ssize_t) image_colors)
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " image_colors (%d) and n (%d) don't match",
8336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " AcquireImageColormap");
8340 image->colors = image_colors;
8342 if (AcquireImageColormap(image,image_colors,exception) ==
8344 ThrowWriterException(ResourceLimitError,
8345 "MemoryAllocationFailed");
8347 for (i=0; i< (ssize_t) image_colors; i++)
8348 image->colormap[i] = colormap[i];
8350 if (logging != MagickFalse)
8352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8353 " image->colors=%d (%d)",
8354 (int) image->colors, image_colors);
8356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8357 " Update the pixel indexes");
8360 /* Sync the pixel indices with the new colormap */
8362 for (y=0; y < (ssize_t) image->rows; y++)
8364 q=GetAuthenticPixels(image,0,y,image->columns,1,
8367 if (q == (Quantum *) NULL)
8371 for (x=0; x < (ssize_t) image->columns; x++)
8373 for (i=0; i< (ssize_t) image_colors; i++)
8375 if ((image->matte == MagickFalse ||
8376 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8377 image->colormap[i].red == GetPixelRed(image,q) &&
8378 image->colormap[i].green == GetPixelGreen(image,q) &&
8379 image->colormap[i].blue == GetPixelBlue(image,q))
8381 SetPixelIndex(image,i,q);
8385 q+=GetPixelChannels(image);
8388 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8394 if (logging != MagickFalse)
8396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " image->colors=%d", (int) image->colors);
8399 if (image->colormap != NULL)
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " i (red,green,blue,alpha)");
8404 for (i=0; i < (ssize_t) image->colors; i++)
8406 if (i < 300 || i >= (ssize_t) image->colors - 10)
8408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " %d (%d,%d,%d,%d)",
8411 (int) image->colormap[i].red,
8412 (int) image->colormap[i].green,
8413 (int) image->colormap[i].blue,
8414 (int) image->colormap[i].alpha);
8419 if (number_transparent < 257)
8420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8421 " number_transparent = %d",
8422 number_transparent);
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " number_transparent > 256");
8428 if (number_opaque < 257)
8429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8430 " number_opaque = %d",
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " number_opaque > 256");
8437 if (number_semitransparent < 257)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " number_semitransparent = %d",
8440 number_semitransparent);
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " number_semitransparent > 256");
8446 if (ping_have_non_bw == MagickFalse)
8447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8448 " All pixels and the background are black or white");
8450 else if (ping_have_color == MagickFalse)
8451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " All pixels and the background are gray");
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " At least one pixel or the background is non-gray");
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " Exit BUILD_PALETTE:");
8462 if (mng_info->write_png8 == MagickFalse)
8465 /* Make any reductions necessary for the PNG8 format */
8466 if (image_colors <= 256 &&
8467 image_colors != 0 && image->colormap != NULL &&
8468 number_semitransparent == 0 &&
8469 number_transparent <= 1)
8472 /* PNG8 can't have semitransparent colors so we threshold the
8473 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8474 * transparent color so if more than one is transparent we merge
8475 * them into image->background_color.
8477 if (number_semitransparent != 0 || number_transparent > 1)
8479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 " Thresholding the alpha channel to binary");
8482 for (y=0; y < (ssize_t) image->rows; y++)
8484 r=GetAuthenticPixels(image,0,y,image->columns,1,
8487 if (r == (Quantum *) NULL)
8490 for (x=0; x < (ssize_t) image->columns; x++)
8492 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8494 SetPixelPacket(image,&image->background_color,r);
8495 SetPixelAlpha(image,TransparentAlpha,r);
8498 SetPixelAlpha(image,OpaqueAlpha,r);
8499 r+=GetPixelChannels(image);
8502 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8505 if (image_colors != 0 && image_colors <= 256 &&
8506 image->colormap != NULL)
8507 for (i=0; i<image_colors; i++)
8508 image->colormap[i].alpha =
8509 (image->colormap[i].alpha > TransparentAlpha/2 ?
8510 TransparentAlpha : OpaqueAlpha);
8515 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8516 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8517 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8520 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8522 if (logging != MagickFalse)
8523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8524 " Quantizing the background color to 4-4-4");
8526 tried_444 = MagickTrue;
8528 LBR04PacketRGB(image->background_color);
8530 if (logging != MagickFalse)
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532 " Quantizing the pixel colors to 4-4-4");
8534 if (image->colormap == NULL)
8536 for (y=0; y < (ssize_t) image->rows; y++)
8538 r=GetAuthenticPixels(image,0,y,image->columns,1,
8541 if (r == (Quantum *) NULL)
8544 for (x=0; x < (ssize_t) image->columns; x++)
8546 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8551 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8556 else /* Should not reach this; colormap already exists and
8559 if (logging != MagickFalse)
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8561 " Quantizing the colormap to 4-4-4");
8563 for (i=0; i<image_colors; i++)
8565 LBR04PacketRGB(image->colormap[i]);
8571 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8573 if (logging != MagickFalse)
8574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8575 " Quantizing the background color to 3-3-3");
8577 tried_333 = MagickTrue;
8579 LBR03PacketRGB(image->background_color);
8581 if (logging != MagickFalse)
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " Quantizing the pixel colors to 3-3-3-1");
8585 if (image->colormap == NULL)
8587 for (y=0; y < (ssize_t) image->rows; y++)
8589 r=GetAuthenticPixels(image,0,y,image->columns,1,
8592 if (r == (Quantum *) NULL)
8595 for (x=0; x < (ssize_t) image->columns; x++)
8597 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8602 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8607 else /* Should not reach this; colormap already exists and
8610 if (logging != MagickFalse)
8611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8612 " Quantizing the colormap to 3-3-3-1");
8613 for (i=0; i<image_colors; i++)
8615 LBR03PacketRGB(image->colormap[i]);
8621 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8623 if (logging != MagickFalse)
8624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625 " Quantizing the background color to 3-3-2");
8627 tried_332 = MagickTrue;
8629 /* Red and green were already done so we only quantize the blue
8633 LBR02PacketBlue(image->background_color);
8635 if (logging != MagickFalse)
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637 " Quantizing the pixel colors to 3-3-2-1");
8639 if (image->colormap == NULL)
8641 for (y=0; y < (ssize_t) image->rows; y++)
8643 r=GetAuthenticPixels(image,0,y,image->columns,1,
8646 if (r == (Quantum *) NULL)
8649 for (x=0; x < (ssize_t) image->columns; x++)
8651 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8656 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8661 else /* Should not reach this; colormap already exists and
8664 if (logging != MagickFalse)
8665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8666 " Quantizing the colormap to 3-3-2-1");
8667 for (i=0; i<image_colors; i++)
8669 LBR02PacketBlue(image->colormap[i]);
8676 if (image_colors == 0 || image_colors > 256)
8678 /* Take care of special case with 256 colors + 1 transparent
8679 * color. We don't need to quantize to 2-3-2-1; we only need to
8680 * eliminate one color, so we'll merge the two darkest red
8681 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8683 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8684 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8685 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8687 image->background_color.red=ScaleCharToQuantum(0x24);
8690 if (image->colormap == NULL)
8692 for (y=0; y < (ssize_t) image->rows; y++)
8694 r=GetAuthenticPixels(image,0,y,image->columns,1,
8697 if (r == (Quantum *) NULL)
8700 for (x=0; x < (ssize_t) image->columns; x++)
8702 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8703 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8704 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8705 GetPixelAlpha(image,r) == OpaqueAlpha)
8707 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8709 r+=GetPixelChannels(image);
8712 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8720 for (i=0; i<image_colors; i++)
8722 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8723 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8724 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8726 image->colormap[i].red=ScaleCharToQuantum(0x24);
8732 /* END OF BUILD_PALETTE */
8734 /* If we are excluding the tRNS chunk and there is transparency,
8735 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8738 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8739 (number_transparent != 0 || number_semitransparent != 0))
8741 unsigned int colortype=mng_info->write_png_colortype;
8743 if (ping_have_color == MagickFalse)
8744 mng_info->write_png_colortype = 5;
8747 mng_info->write_png_colortype = 7;
8749 if (colortype != 0 &&
8750 mng_info->write_png_colortype != colortype)
8751 ping_need_colortype_warning=MagickTrue;
8755 /* See if cheap transparency is possible. It is only possible
8756 * when there is a single transparent color, no semitransparent
8757 * color, and no opaque color that has the same RGB components
8758 * as the transparent color. We only need this information if
8759 * we are writing a PNG with colortype 0 or 2, and we have not
8760 * excluded the tRNS chunk.
8762 if (number_transparent == 1 &&
8763 mng_info->write_png_colortype < 4)
8765 ping_have_cheap_transparency = MagickTrue;
8767 if (number_semitransparent != 0)
8768 ping_have_cheap_transparency = MagickFalse;
8770 else if (image_colors == 0 || image_colors > 256 ||
8771 image->colormap == NULL)
8776 register const Quantum
8779 exception=(&image->exception);
8781 for (y=0; y < (ssize_t) image->rows; y++)
8783 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8785 if (q == (Quantum *) NULL)
8788 for (x=0; x < (ssize_t) image->columns; x++)
8790 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8791 (unsigned short) GetPixelRed(image,q) ==
8792 ping_trans_color.red &&
8793 (unsigned short) GetPixelGreen(image,q) ==
8794 ping_trans_color.green &&
8795 (unsigned short) GetPixelBlue(image,q) ==
8796 ping_trans_color.blue)
8798 ping_have_cheap_transparency = MagickFalse;
8802 q+=GetPixelChannels(image);
8805 if (ping_have_cheap_transparency == MagickFalse)
8811 /* Assuming that image->colormap[0] is the one transparent color
8812 * and that all others are opaque.
8814 if (image_colors > 1)
8815 for (i=1; i<image_colors; i++)
8816 if (image->colormap[i].red == image->colormap[0].red &&
8817 image->colormap[i].green == image->colormap[0].green &&
8818 image->colormap[i].blue == image->colormap[0].blue)
8820 ping_have_cheap_transparency = MagickFalse;
8825 if (logging != MagickFalse)
8827 if (ping_have_cheap_transparency == MagickFalse)
8828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8829 " Cheap transparency is not possible.");
8832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8833 " Cheap transparency is possible.");
8837 ping_have_cheap_transparency = MagickFalse;
8839 image_depth=image->depth;
8841 quantum_info = (QuantumInfo *) NULL;
8843 image_colors=(int) image->colors;
8844 image_matte=image->matte;
8846 mng_info->IsPalette=image->storage_class == PseudoClass &&
8847 image_colors <= 256 && image->colormap != NULL;
8849 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8850 (image->colors == 0 || image->colormap == NULL))
8852 image_info=DestroyImageInfo(image_info);
8853 image=DestroyImage(image);
8854 (void) ThrowMagickException(&IMimage->exception,
8855 GetMagickModule(),CoderError,
8856 "Cannot write PNG8 or color-type 3; colormap is NULL",
8857 "`%s'",IMimage->filename);
8858 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8859 UnlockSemaphoreInfo(ping_semaphore);
8861 return(MagickFalse);
8865 Allocate the PNG structures
8867 #ifdef PNG_USER_MEM_SUPPORTED
8868 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8869 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8870 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8873 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8874 MagickPNGErrorHandler,MagickPNGWarningHandler);
8877 if (ping == (png_struct *) NULL)
8878 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8880 ping_info=png_create_info_struct(ping);
8882 if (ping_info == (png_info *) NULL)
8884 png_destroy_write_struct(&ping,(png_info **) NULL);
8885 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8888 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8889 ping_pixels=(unsigned char *) NULL;
8891 if (setjmp(png_jmpbuf(ping)))
8897 if (image_info->verbose)
8898 (void) printf("PNG write has failed.\n");
8900 png_destroy_write_struct(&ping,&ping_info);
8901 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8902 UnlockSemaphoreInfo(ping_semaphore);
8904 if (ping_have_blob != MagickFalse)
8905 (void) CloseBlob(image);
8906 image_info=DestroyImageInfo(image_info);
8907 image=DestroyImage(image);
8908 return(MagickFalse);
8911 Prepare PNG for writing.
8913 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8914 if (mng_info->write_mng)
8915 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8918 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8919 if (mng_info->write_mng)
8920 png_permit_empty_plte(ping,MagickTrue);
8927 ping_width=(png_uint_32) image->columns;
8928 ping_height=(png_uint_32) image->rows;
8930 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8933 if (mng_info->write_png_depth != 0)
8934 image_depth=mng_info->write_png_depth;
8936 /* Adjust requested depth to next higher valid depth if necessary */
8937 if (image_depth > 8)
8940 if ((image_depth > 4) && (image_depth < 8))
8943 if (image_depth == 3)
8946 if (logging != MagickFalse)
8948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949 " width=%.20g",(double) ping_width);
8950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951 " height=%.20g",(double) ping_height);
8952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8953 " image_matte=%.20g",(double) image->matte);
8954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8955 " image->depth=%.20g",(double) image->depth);
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8957 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8960 save_image_depth=image_depth;
8961 ping_bit_depth=(png_byte) save_image_depth;
8964 #if defined(PNG_pHYs_SUPPORTED)
8965 if (ping_exclude_pHYs == MagickFalse)
8967 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8968 (!mng_info->write_mng || !mng_info->equal_physs))
8970 if (logging != MagickFalse)
8971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8972 " Setting up pHYs chunk");
8974 if (image->units == PixelsPerInchResolution)
8976 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8977 ping_pHYs_x_resolution=
8978 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8979 ping_pHYs_y_resolution=
8980 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8983 else if (image->units == PixelsPerCentimeterResolution)
8985 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8986 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8987 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8992 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8993 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8994 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8997 if (logging != MagickFalse)
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9000 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9001 (int) ping_pHYs_unit_type);
9002 ping_have_pHYs = MagickTrue;
9007 if (ping_exclude_bKGD == MagickFalse)
9009 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9015 if (ping_bit_depth == 8)
9018 if (ping_bit_depth == 4)
9021 if (ping_bit_depth == 2)
9024 if (ping_bit_depth == 1)
9027 ping_background.red=(png_uint_16)
9028 (ScaleQuantumToShort(image->background_color.red) & mask);
9030 ping_background.green=(png_uint_16)
9031 (ScaleQuantumToShort(image->background_color.green) & mask);
9033 ping_background.blue=(png_uint_16)
9034 (ScaleQuantumToShort(image->background_color.blue) & mask);
9036 ping_background.gray=(png_uint_16) ping_background.green;
9039 if (logging != MagickFalse)
9041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9042 " Setting up bKGD chunk (1)");
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " background_color index is %d",
9045 (int) ping_background.index);
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048 " ping_bit_depth=%d",ping_bit_depth);
9051 ping_have_bKGD = MagickTrue;
9055 Select the color type.
9060 if (mng_info->IsPalette && mng_info->write_png8)
9063 /* To do: make this a function cause it's used twice, except
9064 for reducing the sample depth from 8. */
9066 number_colors=image_colors;
9068 ping_have_tRNS=MagickFalse;
9073 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9075 if (logging != MagickFalse)
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " Setting up PLTE chunk with %d colors (%d)",
9078 number_colors, image_colors);
9080 for (i=0; i < (ssize_t) number_colors; i++)
9082 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9083 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9084 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9085 if (logging != MagickFalse)
9086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9087 #if MAGICKCORE_QUANTUM_DEPTH == 8
9088 " %3ld (%3d,%3d,%3d)",
9090 " %5ld (%5d,%5d,%5d)",
9092 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9096 ping_have_PLTE=MagickTrue;
9097 image_depth=ping_bit_depth;
9100 if (matte != MagickFalse)
9103 Identify which colormap entry is transparent.
9105 assert(number_colors <= 256);
9106 assert(image->colormap != NULL);
9108 for (i=0; i < (ssize_t) number_transparent; i++)
9109 ping_trans_alpha[i]=0;
9112 ping_num_trans=(unsigned short) (number_transparent +
9113 number_semitransparent);
9115 if (ping_num_trans == 0)
9116 ping_have_tRNS=MagickFalse;
9119 ping_have_tRNS=MagickTrue;
9122 if (ping_exclude_bKGD == MagickFalse)
9125 * Identify which colormap entry is the background color.
9128 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9129 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9132 ping_background.index=(png_byte) i;
9134 if (logging != MagickFalse)
9136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137 " background_color index is %d",
9138 (int) ping_background.index);
9141 } /* end of write_png8 */
9143 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9145 image_matte=MagickFalse;
9146 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9149 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9151 image_matte=MagickTrue;
9152 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9155 else /* mng_info->write_pngNN not specified */
9157 image_depth=ping_bit_depth;
9159 if (mng_info->write_png_colortype != 0)
9161 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9163 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9164 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9165 image_matte=MagickTrue;
9168 image_matte=MagickFalse;
9170 if (logging != MagickFalse)
9171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9172 " PNG colortype %d was specified:",(int) ping_color_type);
9175 else /* write_png_colortype not specified */
9177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Selecting PNG colortype:");
9181 ping_color_type=(png_byte) ((matte != MagickFalse)?
9182 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9184 if (image_info->type == TrueColorType)
9186 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9187 image_matte=MagickFalse;
9190 if (image_info->type == TrueColorMatteType)
9192 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9193 image_matte=MagickTrue;
9196 if (image_info->type == PaletteType ||
9197 image_info->type == PaletteMatteType)
9198 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9200 if (mng_info->write_png_colortype == 0 &&
9201 (image_info->type == UndefinedType ||
9202 image_info->type == OptimizeType))
9204 if (ping_have_color == MagickFalse)
9206 if (image_matte == MagickFalse)
9208 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9209 image_matte=MagickFalse;
9214 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9215 image_matte=MagickTrue;
9220 if (image_matte == MagickFalse)
9222 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9223 image_matte=MagickFalse;
9228 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9229 image_matte=MagickTrue;
9236 if (logging != MagickFalse)
9237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238 " Selected PNG colortype=%d",ping_color_type);
9240 if (ping_bit_depth < 8)
9242 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9243 ping_color_type == PNG_COLOR_TYPE_RGB ||
9244 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9248 old_bit_depth=ping_bit_depth;
9250 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9252 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9256 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9261 if (image->colors == 0)
9264 (void) ThrowMagickException(&image->exception,
9265 GetMagickModule(),CoderError,
9266 "image has 0 colors", "`%s'","");
9269 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9270 ping_bit_depth <<= 1;
9273 if (logging != MagickFalse)
9275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9276 " Number of colors: %.20g",(double) image_colors);
9278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9279 " Tentative PNG bit depth: %d",ping_bit_depth);
9282 if (ping_bit_depth < (int) mng_info->write_png_depth)
9283 ping_bit_depth = mng_info->write_png_depth;
9286 image_depth=ping_bit_depth;
9288 if (logging != MagickFalse)
9290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9291 " Tentative PNG color type: %.20g",(double) ping_color_type);
9293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9294 " image_info->type: %.20g",(double) image_info->type);
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " image_depth: %.20g",(double) image_depth);
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9301 " image->depth: %.20g",(double) image->depth);
9303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9304 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9307 if (matte != MagickFalse)
9309 if (mng_info->IsPalette)
9311 if (mng_info->write_png_colortype == 0)
9313 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9315 if (ping_have_color != MagickFalse)
9316 ping_color_type=PNG_COLOR_TYPE_RGBA;
9320 * Determine if there is any transparent color.
9322 if (number_transparent + number_semitransparent == 0)
9325 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9328 image_matte=MagickFalse;
9330 if (mng_info->write_png_colortype == 0)
9331 ping_color_type&=0x03;
9341 if (ping_bit_depth == 8)
9344 if (ping_bit_depth == 4)
9347 if (ping_bit_depth == 2)
9350 if (ping_bit_depth == 1)
9353 ping_trans_color.red=(png_uint_16)
9354 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9356 ping_trans_color.green=(png_uint_16)
9357 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9359 ping_trans_color.blue=(png_uint_16)
9360 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9362 ping_trans_color.gray=(png_uint_16)
9363 (ScaleQuantumToShort(GetPixelPacketIntensity(
9364 image->colormap)) & mask);
9366 ping_trans_color.index=(png_byte) 0;
9368 ping_have_tRNS=MagickTrue;
9371 if (ping_have_tRNS != MagickFalse)
9374 * Determine if there is one and only one transparent color
9375 * and if so if it is fully transparent.
9377 if (ping_have_cheap_transparency == MagickFalse)
9378 ping_have_tRNS=MagickFalse;
9381 if (ping_have_tRNS != MagickFalse)
9383 if (mng_info->write_png_colortype == 0)
9384 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9386 if (image_depth == 8)
9388 ping_trans_color.red&=0xff;
9389 ping_trans_color.green&=0xff;
9390 ping_trans_color.blue&=0xff;
9391 ping_trans_color.gray&=0xff;
9397 if (image_depth == 8)
9399 ping_trans_color.red&=0xff;
9400 ping_trans_color.green&=0xff;
9401 ping_trans_color.blue&=0xff;
9402 ping_trans_color.gray&=0xff;
9409 if (ping_have_tRNS != MagickFalse)
9410 image_matte=MagickFalse;
9412 if ((mng_info->IsPalette) &&
9413 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9414 ping_have_color == MagickFalse &&
9415 (image_matte == MagickFalse || image_depth >= 8))
9419 if (image_matte != MagickFalse)
9420 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9422 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9424 ping_color_type=PNG_COLOR_TYPE_GRAY;
9426 if (save_image_depth == 16 && image_depth == 8)
9428 if (logging != MagickFalse)
9430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9431 " Scaling ping_trans_color (0)");
9433 ping_trans_color.gray*=0x0101;
9437 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9438 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9440 if ((image_colors == 0) ||
9441 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9442 image_colors=(int) (one << image_depth);
9444 if (image_depth > 8)
9450 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9452 if(!mng_info->write_png_depth)
9456 while ((int) (one << ping_bit_depth)
9457 < (ssize_t) image_colors)
9458 ping_bit_depth <<= 1;
9462 else if (ping_color_type ==
9463 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9464 mng_info->IsPalette)
9466 /* Check if grayscale is reducible */
9469 depth_4_ok=MagickTrue,
9470 depth_2_ok=MagickTrue,
9471 depth_1_ok=MagickTrue;
9473 for (i=0; i < (ssize_t) image_colors; i++)
9478 intensity=ScaleQuantumToChar(image->colormap[i].red);
9480 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9481 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9482 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9483 depth_2_ok=depth_1_ok=MagickFalse;
9484 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9485 depth_1_ok=MagickFalse;
9488 if (depth_1_ok && mng_info->write_png_depth <= 1)
9491 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9494 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9499 image_depth=ping_bit_depth;
9504 if (mng_info->IsPalette)
9506 number_colors=image_colors;
9508 if (image_depth <= 8)
9513 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9515 if (mng_info->have_write_global_plte && matte == MagickFalse)
9517 png_set_PLTE(ping,ping_info,NULL,0);
9519 if (logging != MagickFalse)
9520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9521 " Setting up empty PLTE chunk");
9526 for (i=0; i < (ssize_t) number_colors; i++)
9528 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9529 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9530 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9533 if (logging != MagickFalse)
9534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9535 " Setting up PLTE chunk with %d colors",
9538 ping_have_PLTE=MagickTrue;
9541 /* color_type is PNG_COLOR_TYPE_PALETTE */
9542 if (mng_info->write_png_depth == 0)
9550 while ((one << ping_bit_depth) < (size_t) number_colors)
9551 ping_bit_depth <<= 1;
9556 if (matte != MagickFalse)
9559 * Set up trans_colors array.
9561 assert(number_colors <= 256);
9563 ping_num_trans=(unsigned short) (number_transparent +
9564 number_semitransparent);
9566 if (ping_num_trans == 0)
9567 ping_have_tRNS=MagickFalse;
9571 if (logging != MagickFalse)
9573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9574 " Scaling ping_trans_color (1)");
9576 ping_have_tRNS=MagickTrue;
9578 for (i=0; i < ping_num_trans; i++)
9580 ping_trans_alpha[i]= (png_byte)
9581 ScaleQuantumToChar(image->colormap[i].alpha);
9591 if (image_depth < 8)
9594 if ((save_image_depth == 16) && (image_depth == 8))
9596 if (logging != MagickFalse)
9598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9599 " Scaling ping_trans_color from (%d,%d,%d)",
9600 (int) ping_trans_color.red,
9601 (int) ping_trans_color.green,
9602 (int) ping_trans_color.blue);
9605 ping_trans_color.red*=0x0101;
9606 ping_trans_color.green*=0x0101;
9607 ping_trans_color.blue*=0x0101;
9608 ping_trans_color.gray*=0x0101;
9610 if (logging != MagickFalse)
9612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9614 (int) ping_trans_color.red,
9615 (int) ping_trans_color.green,
9616 (int) ping_trans_color.blue);
9621 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9622 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9625 Adjust background and transparency samples in sub-8-bit grayscale files.
9627 if (ping_bit_depth < 8 && ping_color_type ==
9628 PNG_COLOR_TYPE_GRAY)
9636 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9638 if (ping_exclude_bKGD == MagickFalse)
9641 ping_background.gray=(png_uint_16)
9642 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9645 if (logging != MagickFalse)
9646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9647 " Setting up bKGD chunk (2)");
9648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9649 " background_color index is %d",
9650 (int) ping_background.index);
9652 ping_have_bKGD = MagickTrue;
9655 if (logging != MagickFalse)
9656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9657 " Scaling ping_trans_color.gray from %d",
9658 (int)ping_trans_color.gray);
9660 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9661 ping_trans_color.gray)+.5);
9663 if (logging != MagickFalse)
9664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9665 " to %d", (int)ping_trans_color.gray);
9668 if (ping_exclude_bKGD == MagickFalse)
9670 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9673 Identify which colormap entry is the background color.
9676 number_colors=image_colors;
9678 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9679 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9682 ping_background.index=(png_byte) i;
9684 if (logging != MagickFalse)
9686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9687 " Setting up bKGD chunk with index=%d",(int) i);
9690 if (i < (ssize_t) number_colors)
9692 ping_have_bKGD = MagickTrue;
9694 if (logging != MagickFalse)
9696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9697 " background =(%d,%d,%d)",
9698 (int) ping_background.red,
9699 (int) ping_background.green,
9700 (int) ping_background.blue);
9704 else /* Can't happen */
9706 if (logging != MagickFalse)
9707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9708 " No room in PLTE to add bKGD color");
9709 ping_have_bKGD = MagickFalse;
9714 if (logging != MagickFalse)
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 " PNG color type: %d",ping_color_type);
9718 Initialize compression level and filtering.
9720 if (logging != MagickFalse)
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " Setting up deflate compression");
9725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9726 " Compression buffer size: 32768");
9729 png_set_compression_buffer_size(ping,32768L);
9731 if (logging != MagickFalse)
9732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9733 " Compression mem level: 9");
9735 png_set_compression_mem_level(ping, 9);
9737 /* Untangle the "-quality" setting:
9739 Undefined is 0; the default is used.
9744 0: Use Z_HUFFMAN_ONLY strategy with the
9745 zlib default compression level
9747 1-9: the zlib compression level
9751 0-4: the PNG filter method
9753 5: libpng adaptive filtering if compression level > 5
9754 libpng filter type "none" if compression level <= 5
9755 or if image is grayscale or palette
9757 6: libpng adaptive filtering
9759 7: "LOCO" filtering (intrapixel differing) if writing
9760 a MNG, othewise "none". Did not work in IM-6.7.0-9
9761 and earlier because of a missing "else".
9763 8: Z_RLE strategy, all filters
9764 Unused prior to IM-6.7.0-10, was same as 6
9766 9: Z_RLE strategy, no PNG filters
9767 Unused prior to IM-6.7.0-10, was same as 6
9769 Note that using the -quality option, not all combinations of
9770 PNG filter type, zlib compression level, and zlib compression
9771 strategy are possible. This will be addressed soon in a
9772 release that accomodates "-define PNG:compression-strategy", etc.
9776 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9781 if (mng_info->write_png_compression_strategy == 0)
9782 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9785 else if (mng_info->write_png_compression_level == 0)
9790 level=(int) MagickMin((ssize_t) quality/10,9);
9792 mng_info->write_png_compression_level = level+1;
9795 if (mng_info->write_png_compression_strategy == 0)
9797 if ((quality %10) == 8 || (quality %10) == 9)
9798 mng_info->write_png_compression_strategy=Z_RLE;
9801 if (mng_info->write_png_compression_filter == 0)
9802 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9804 if (logging != MagickFalse)
9806 if (mng_info->write_png_compression_level)
9807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9808 " Compression level: %d",
9809 (int) mng_info->write_png_compression_level-1);
9811 if (mng_info->write_png_compression_strategy)
9812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813 " Compression strategy: %d",
9814 (int) mng_info->write_png_compression_strategy-1);
9816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9817 " Setting up filtering");
9819 if (mng_info->write_png_compression_filter == 6)
9820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9821 " Base filter method: ADAPTIVE");
9822 else if (mng_info->write_png_compression_filter == 0 ||
9823 mng_info->write_png_compression_filter == 1)
9824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825 " Base filter method: NONE");
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Base filter method: %d",
9829 (int) mng_info->write_png_compression_filter-1);
9832 if (mng_info->write_png_compression_level != 0)
9833 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9835 if (mng_info->write_png_compression_filter == 6)
9837 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9838 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9840 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9842 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9844 else if (mng_info->write_png_compression_filter == 7 ||
9845 mng_info->write_png_compression_filter == 10)
9846 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9848 else if (mng_info->write_png_compression_filter == 8)
9850 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9851 if (mng_info->write_mng)
9853 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9854 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9855 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9858 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9861 else if (mng_info->write_png_compression_filter == 9)
9862 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9864 else if (mng_info->write_png_compression_filter != 0)
9865 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9866 mng_info->write_png_compression_filter-1);
9868 if (mng_info->write_png_compression_strategy != 0)
9869 png_set_compression_strategy(ping,
9870 mng_info->write_png_compression_strategy-1);
9872 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9873 if (ping_exclude_sRGB != MagickFalse ||
9874 (image->rendering_intent == UndefinedIntent))
9876 if ((ping_exclude_tEXt == MagickFalse ||
9877 ping_exclude_zTXt == MagickFalse) &&
9878 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9880 ResetImageProfileIterator(image);
9881 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9883 profile=GetImageProfile(image,name);
9885 if (profile != (StringInfo *) NULL)
9887 #ifdef PNG_WRITE_iCCP_SUPPORTED
9888 if ((LocaleCompare(name,"ICC") == 0) ||
9889 (LocaleCompare(name,"ICM") == 0))
9892 if (ping_exclude_iCCP == MagickFalse)
9894 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9895 #if (PNG_LIBPNG_VER < 10500)
9896 (png_charp) GetStringInfoDatum(profile),
9898 (png_const_bytep) GetStringInfoDatum(profile),
9900 (png_uint_32) GetStringInfoLength(profile));
9906 if (ping_exclude_zCCP == MagickFalse)
9908 Magick_png_write_raw_profile(image_info,ping,ping_info,
9909 (unsigned char *) name,(unsigned char *) name,
9910 GetStringInfoDatum(profile),
9911 (png_uint_32) GetStringInfoLength(profile));
9915 if (logging != MagickFalse)
9916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9917 " Setting up text chunk with %s profile",name);
9919 name=GetNextImageProfile(image);
9924 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9925 if ((mng_info->have_write_global_srgb == 0) &&
9926 ((image->rendering_intent != UndefinedIntent) ||
9927 (image->colorspace == sRGBColorspace)))
9929 if (ping_exclude_sRGB == MagickFalse)
9932 Note image rendering intent.
9934 if (logging != MagickFalse)
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " Setting up sRGB chunk");
9938 (void) png_set_sRGB(ping,ping_info,(
9939 Magick_RenderingIntent_to_PNG_RenderingIntent(
9940 image->rendering_intent)));
9944 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9947 if (ping_exclude_gAMA == MagickFalse &&
9948 (ping_exclude_sRGB == MagickFalse ||
9949 (image->gamma < .45 || image->gamma > .46)))
9951 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9955 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9957 if (logging != MagickFalse)
9958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9959 " Setting up gAMA chunk");
9961 png_set_gAMA(ping,ping_info,image->gamma);
9965 if (ping_exclude_cHRM == MagickFalse)
9967 if ((mng_info->have_write_global_chrm == 0) &&
9968 (image->chromaticity.red_primary.x != 0.0))
9971 Note image chromaticity.
9972 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9980 wp=image->chromaticity.white_point;
9981 rp=image->chromaticity.red_primary;
9982 gp=image->chromaticity.green_primary;
9983 bp=image->chromaticity.blue_primary;
9985 if (logging != MagickFalse)
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up cHRM chunk");
9989 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9995 ping_interlace_method=image_info->interlace != NoInterlace;
9997 if (mng_info->write_mng)
9998 png_set_sig_bytes(ping,8);
10000 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10002 if (mng_info->write_png_colortype != 0)
10004 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10005 if (ping_have_color != MagickFalse)
10007 ping_color_type = PNG_COLOR_TYPE_RGB;
10009 if (ping_bit_depth < 8)
10013 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10014 if (ping_have_color != MagickFalse)
10015 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10018 if (ping_need_colortype_warning != MagickFalse ||
10019 ((mng_info->write_png_depth &&
10020 (int) mng_info->write_png_depth != ping_bit_depth) ||
10021 (mng_info->write_png_colortype &&
10022 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10023 mng_info->write_png_colortype != 7 &&
10024 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10026 if (logging != MagickFalse)
10028 if (ping_need_colortype_warning != MagickFalse)
10030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10031 " Image has transparency but tRNS chunk was excluded");
10034 if (mng_info->write_png_depth)
10036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10037 " Defined PNG:bit-depth=%u, Computed depth=%u",
10038 mng_info->write_png_depth,
10042 if (mng_info->write_png_colortype)
10044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10045 " Defined PNG:color-type=%u, Computed color type=%u",
10046 mng_info->write_png_colortype-1,
10052 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10055 if (image_matte != MagickFalse && image->matte == MagickFalse)
10057 /* Add an opaque matte channel */
10058 image->matte = MagickTrue;
10059 (void) SetImageAlpha(image,OpaqueAlpha);
10061 if (logging != MagickFalse)
10062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10063 " Added an opaque matte channel");
10066 if (number_transparent != 0 || number_semitransparent != 0)
10068 if (ping_color_type < 4)
10070 ping_have_tRNS=MagickTrue;
10071 if (logging != MagickFalse)
10072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10073 " Setting ping_have_tRNS=MagickTrue.");
10077 if (logging != MagickFalse)
10078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10079 " Writing PNG header chunks");
10081 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10082 ping_bit_depth,ping_color_type,
10083 ping_interlace_method,ping_compression_method,
10084 ping_filter_method);
10086 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10088 png_set_PLTE(ping,ping_info,palette,number_colors);
10090 if (logging != MagickFalse)
10092 for (i=0; i< (ssize_t) number_colors; i++)
10094 if (i < ping_num_trans)
10095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10096 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10098 (int) palette[i].red,
10099 (int) palette[i].green,
10100 (int) palette[i].blue,
10102 (int) ping_trans_alpha[i]);
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " PLTE[%d] = (%d,%d,%d)",
10107 (int) palette[i].red,
10108 (int) palette[i].green,
10109 (int) palette[i].blue);
10114 if (ping_exclude_bKGD == MagickFalse)
10116 if (ping_have_bKGD != MagickFalse)
10118 png_set_bKGD(ping,ping_info,&ping_background);
10121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10122 " Setting up bKGD chunk");
10123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10124 " background color = (%d,%d,%d)",
10125 (int) ping_background.red,
10126 (int) ping_background.green,
10127 (int) ping_background.blue);
10128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10129 " index = %d, gray=%d",
10130 (int) ping_background.index,
10131 (int) ping_background.gray);
10136 if (ping_exclude_pHYs == MagickFalse)
10138 if (ping_have_pHYs != MagickFalse)
10140 png_set_pHYs(ping,ping_info,
10141 ping_pHYs_x_resolution,
10142 ping_pHYs_y_resolution,
10143 ping_pHYs_unit_type);
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " Setting up pHYs chunk");
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " x_resolution=%lu",
10151 (unsigned long) ping_pHYs_x_resolution);
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153 " y_resolution=%lu",
10154 (unsigned long) ping_pHYs_y_resolution);
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 (unsigned long) ping_pHYs_unit_type);
10162 #if defined(PNG_oFFs_SUPPORTED)
10163 if (ping_exclude_oFFs == MagickFalse)
10165 if (image->page.x || image->page.y)
10167 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10168 (png_int_32) image->page.y, 0);
10170 if (logging != MagickFalse)
10171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10172 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10173 (int) image->page.x, (int) image->page.y);
10178 if (mng_info->need_blob != MagickFalse)
10180 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10182 png_error(ping,"WriteBlob Failed");
10184 ping_have_blob=MagickTrue;
10187 png_write_info_before_PLTE(ping, ping_info);
10189 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10191 if (logging != MagickFalse)
10193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10194 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10197 if (ping_color_type == 3)
10198 (void) png_set_tRNS(ping, ping_info,
10205 (void) png_set_tRNS(ping, ping_info,
10208 &ping_trans_color);
10210 if (logging != MagickFalse)
10212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10213 " tRNS color =(%d,%d,%d)",
10214 (int) ping_trans_color.red,
10215 (int) ping_trans_color.green,
10216 (int) ping_trans_color.blue);
10221 /* write any png-chunk-b profiles */
10222 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10224 png_write_info(ping,ping_info);
10226 /* write any PNG-chunk-m profiles */
10227 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10229 if (ping_exclude_vpAg == MagickFalse)
10231 if ((image->page.width != 0 && image->page.width != image->columns) ||
10232 (image->page.height != 0 && image->page.height != image->rows))
10237 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10238 PNGType(chunk,mng_vpAg);
10239 LogPNGChunk(logging,mng_vpAg,9L);
10240 PNGLong(chunk+4,(png_uint_32) image->page.width);
10241 PNGLong(chunk+8,(png_uint_32) image->page.height);
10242 chunk[12]=0; /* unit = pixels */
10243 (void) WriteBlob(image,13,chunk);
10244 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10248 #if (PNG_LIBPNG_VER == 10206)
10249 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10250 #define PNG_HAVE_IDAT 0x04
10251 ping->mode |= PNG_HAVE_IDAT;
10252 #undef PNG_HAVE_IDAT
10255 png_set_packing(ping);
10259 rowbytes=image->columns;
10260 if (image_depth > 8)
10262 switch (ping_color_type)
10264 case PNG_COLOR_TYPE_RGB:
10268 case PNG_COLOR_TYPE_GRAY_ALPHA:
10272 case PNG_COLOR_TYPE_RGBA:
10280 if (logging != MagickFalse)
10282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283 " Writing PNG image data");
10285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10286 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10288 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10289 sizeof(*ping_pixels));
10291 if (ping_pixels == (unsigned char *) NULL)
10292 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10295 Initialize image scanlines.
10297 if (setjmp(png_jmpbuf(ping)))
10303 if (image_info->verbose)
10304 (void) printf("PNG write has failed.\n");
10306 png_destroy_write_struct(&ping,&ping_info);
10307 if (quantum_info != (QuantumInfo *) NULL)
10308 quantum_info=DestroyQuantumInfo(quantum_info);
10309 if (ping_pixels != (unsigned char *) NULL)
10310 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10311 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10312 UnlockSemaphoreInfo(ping_semaphore);
10314 if (ping_have_blob != MagickFalse)
10315 (void) CloseBlob(image);
10316 image_info=DestroyImageInfo(image_info);
10317 image=DestroyImage(image);
10318 return(MagickFalse);
10320 quantum_info=AcquireQuantumInfo(image_info,image);
10321 if (quantum_info == (QuantumInfo *) NULL)
10322 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10323 quantum_info->format=UndefinedQuantumFormat;
10324 quantum_info->depth=image_depth;
10325 num_passes=png_set_interlace_handling(ping);
10327 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10328 !mng_info->write_png32) &&
10329 (mng_info->IsPalette ||
10330 (image_info->type == BilevelType)) &&
10331 image_matte == MagickFalse &&
10332 ping_have_non_bw == MagickFalse)
10334 /* Palette, Bilevel, or Opaque Monochrome */
10335 register const Quantum
10338 quantum_info->depth=8;
10339 for (pass=0; pass < num_passes; pass++)
10342 Convert PseudoClass image to a PNG monochrome image.
10344 for (y=0; y < (ssize_t) image->rows; y++)
10346 if (logging != MagickFalse && y == 0)
10347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " Writing row of pixels (0)");
10350 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10352 if (p == (const Quantum *) NULL)
10355 if (mng_info->IsPalette)
10357 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10358 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10359 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10360 mng_info->write_png_depth &&
10361 mng_info->write_png_depth != old_bit_depth)
10363 /* Undo pixel scaling */
10364 for (i=0; i < (ssize_t) image->columns; i++)
10365 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10366 >> (8-old_bit_depth));
10372 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10373 quantum_info,RedQuantum,ping_pixels,&image->exception);
10376 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10377 for (i=0; i < (ssize_t) image->columns; i++)
10378 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10381 if (logging != MagickFalse && y == 0)
10382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10383 " Writing row of pixels (1)");
10385 png_write_row(ping,ping_pixels);
10387 if (image->previous == (Image *) NULL)
10389 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10390 if (status == MagickFalse)
10396 else /* Not Palette, Bilevel, or Opaque Monochrome */
10398 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10399 !mng_info->write_png32) &&
10400 (image_matte != MagickFalse ||
10401 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10402 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10404 register const Quantum
10407 for (pass=0; pass < num_passes; pass++)
10410 for (y=0; y < (ssize_t) image->rows; y++)
10412 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10414 if (p == (const Quantum *) NULL)
10417 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10419 if (mng_info->IsPalette)
10420 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10421 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10424 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10425 quantum_info,RedQuantum,ping_pixels,&image->exception);
10427 if (logging != MagickFalse && y == 0)
10428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10429 " Writing GRAY PNG pixels (2)");
10432 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10434 if (logging != MagickFalse && y == 0)
10435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 " Writing GRAY_ALPHA PNG pixels (2)");
10438 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10439 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
10442 if (logging != MagickFalse && y == 0)
10443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444 " Writing row of pixels (2)");
10446 png_write_row(ping,ping_pixels);
10449 if (image->previous == (Image *) NULL)
10451 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10452 if (status == MagickFalse)
10460 register const Quantum
10463 for (pass=0; pass < num_passes; pass++)
10465 if ((image_depth > 8) || (mng_info->write_png24 ||
10466 mng_info->write_png32 ||
10467 (!mng_info->write_png8 && !mng_info->IsPalette)))
10469 for (y=0; y < (ssize_t) image->rows; y++)
10471 p=GetVirtualPixels(image,0,y,image->columns,1,
10472 &image->exception);
10474 if (p == (const Quantum *) NULL)
10477 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10479 if (image->storage_class == DirectClass)
10480 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10481 quantum_info,RedQuantum,ping_pixels,&image->exception);
10484 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10485 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10488 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10490 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10491 quantum_info,GrayAlphaQuantum,ping_pixels,
10492 &image->exception);
10494 if (logging != MagickFalse && y == 0)
10495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10496 " Writing GRAY_ALPHA PNG pixels (3)");
10499 else if (image_matte != MagickFalse)
10500 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10501 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
10504 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10505 quantum_info,RGBQuantum,ping_pixels,&image->exception);
10507 if (logging != MagickFalse && y == 0)
10508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10509 " Writing row of pixels (3)");
10511 png_write_row(ping,ping_pixels);
10516 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10517 mng_info->write_png32 ||
10518 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10520 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10521 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10523 if (logging != MagickFalse)
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10527 quantum_info->depth=8;
10531 for (y=0; y < (ssize_t) image->rows; y++)
10533 if (logging != MagickFalse && y == 0)
10534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10535 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10537 p=GetVirtualPixels(image,0,y,image->columns,1,
10538 &image->exception);
10540 if (p == (const Quantum *) NULL)
10543 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10545 quantum_info->depth=image->depth;
10547 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10548 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10551 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10553 if (logging != MagickFalse && y == 0)
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " Writing GRAY_ALPHA PNG pixels (4)");
10557 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10558 quantum_info,GrayAlphaQuantum,ping_pixels,
10559 &image->exception);
10564 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10565 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10567 if (logging != MagickFalse && y <= 2)
10569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10570 " Writing row of non-gray pixels (4)");
10572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10573 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10574 (int)ping_pixels[0],(int)ping_pixels[1]);
10577 png_write_row(ping,ping_pixels);
10581 if (image->previous == (Image *) NULL)
10583 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10584 if (status == MagickFalse)
10591 if (quantum_info != (QuantumInfo *) NULL)
10592 quantum_info=DestroyQuantumInfo(quantum_info);
10594 if (logging != MagickFalse)
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10597 " Wrote PNG image data");
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600 " Width: %.20g",(double) ping_width);
10602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10603 " Height: %.20g",(double) ping_height);
10605 if (mng_info->write_png_depth)
10607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10608 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10612 " PNG bit-depth written: %d",ping_bit_depth);
10614 if (mng_info->write_png_colortype)
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10621 " PNG color-type written: %d",ping_color_type);
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " PNG Interlace method: %d",ping_interlace_method);
10627 Generate text chunks after IDAT.
10629 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10631 ResetImagePropertyIterator(image);
10632 property=GetNextImageProperty(image);
10633 while (property != (const char *) NULL)
10638 value=GetImageProperty(image,property);
10640 /* Don't write any "png:" properties; those are just for "identify" */
10641 if (LocaleNCompare(property,"png:",4) != 0 &&
10643 /* Suppress density and units if we wrote a pHYs chunk */
10644 (ping_exclude_pHYs != MagickFalse ||
10645 LocaleCompare(property,"density") != 0 ||
10646 LocaleCompare(property,"units") != 0) &&
10648 /* Suppress the IM-generated Date:create and Date:modify */
10649 (ping_exclude_date == MagickFalse ||
10650 LocaleNCompare(property, "Date:",5) != 0))
10652 if (value != (const char *) NULL)
10654 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10655 text[0].key=(char *) property;
10656 text[0].text=(char *) value;
10657 text[0].text_length=strlen(value);
10659 if (ping_exclude_tEXt != MagickFalse)
10660 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10662 else if (ping_exclude_zTXt != MagickFalse)
10663 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10667 text[0].compression=image_info->compression == NoCompression ||
10668 (image_info->compression == UndefinedCompression &&
10669 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10670 PNG_TEXT_COMPRESSION_zTXt ;
10673 if (logging != MagickFalse)
10675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10676 " Setting up text chunk");
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " keyword: %s",text[0].key);
10682 png_set_text(ping,ping_info,text,1);
10683 png_free(ping,text);
10686 property=GetNextImageProperty(image);
10690 /* write any PNG-chunk-e profiles */
10691 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10693 if (logging != MagickFalse)
10694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10695 " Writing PNG end info");
10697 png_write_end(ping,ping_info);
10699 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10701 if (mng_info->page.x || mng_info->page.y ||
10702 (ping_width != mng_info->page.width) ||
10703 (ping_height != mng_info->page.height))
10709 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10711 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10712 PNGType(chunk,mng_FRAM);
10713 LogPNGChunk(logging,mng_FRAM,27L);
10715 chunk[5]=0; /* frame name separator (no name) */
10716 chunk[6]=1; /* flag for changing delay, for next frame only */
10717 chunk[7]=0; /* flag for changing frame timeout */
10718 chunk[8]=1; /* flag for changing frame clipping for next frame */
10719 chunk[9]=0; /* flag for changing frame sync_id */
10720 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10721 chunk[14]=0; /* clipping boundaries delta type */
10722 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10724 (png_uint_32) (mng_info->page.x + ping_width));
10725 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10727 (png_uint_32) (mng_info->page.y + ping_height));
10728 (void) WriteBlob(image,31,chunk);
10729 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10730 mng_info->old_framing_mode=4;
10731 mng_info->framing_mode=1;
10735 mng_info->framing_mode=3;
10737 if (mng_info->write_mng && !mng_info->need_fram &&
10738 ((int) image->dispose == 3))
10739 (void) ThrowMagickException(&image->exception,GetMagickModule(),
10740 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10741 "`%s'",image->filename);
10744 Free PNG resources.
10747 png_destroy_write_struct(&ping,&ping_info);
10749 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10751 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10752 UnlockSemaphoreInfo(ping_semaphore);
10755 if (ping_have_blob != MagickFalse)
10756 (void) CloseBlob(image);
10758 image_info=DestroyImageInfo(image_info);
10759 image=DestroyImage(image);
10761 /* Store bit depth actually written */
10762 s[0]=(char) ping_bit_depth;
10765 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10767 if (logging != MagickFalse)
10768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10769 " exit WriteOnePNGImage()");
10771 return(MagickTrue);
10772 /* End write one PNG image */
10776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10780 % W r i t e P N G I m a g e %
10784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10786 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10787 % Multiple-image Network Graphics (MNG) image file.
10789 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10791 % The format of the WritePNGImage method is:
10793 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10794 % Image *image,ExceptionInfo *exception)
10796 % A description of each parameter follows:
10798 % o image_info: the image info.
10800 % o image: The image.
10802 % o exception: return any errors or warnings in this structure.
10804 % Returns MagickTrue on success, MagickFalse on failure.
10806 % Communicating with the PNG encoder:
10808 % While the datastream written is always in PNG format and normally would
10809 % be given the "png" file extension, this method also writes the following
10810 % pseudo-formats which are subsets of PNG:
10812 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10813 % a depth greater than 8, the depth is reduced. If transparency
10814 % is present, the tRNS chunk must only have values 0 and 255
10815 % (i.e., transparency is binary: fully opaque or fully
10816 % transparent). If other values are present they will be
10817 % 50%-thresholded to binary transparency. If more than 256
10818 % colors are present, they will be quantized to the 4-4-4-1,
10819 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10820 % of any resulting fully-transparent pixels is changed to
10821 % the image's background color.
10823 % If you want better quantization or dithering of the colors
10824 % or alpha than that, you need to do it before calling the
10825 % PNG encoder. The pixels contain 8-bit indices even if
10826 % they could be represented with 1, 2, or 4 bits. Grayscale
10827 % images will be written as indexed PNG files even though the
10828 % PNG grayscale type might be slightly more efficient. Please
10829 % note that writing to the PNG8 format may result in loss
10830 % of color and alpha data.
10832 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10833 % chunk can be present to convey binary transparency by naming
10834 % one of the colors as transparent. The only loss incurred
10835 % is reduction of sample depth to 8. If the image has more
10836 % than one transparent color, has semitransparent pixels, or
10837 % has an opaque pixel with the same RGB components as the
10838 % transparent color, an image is not written.
10840 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10841 % transparency is permitted, i.e., the alpha sample for
10842 % each pixel can have any value from 0 to 255. The alpha
10843 % channel is present even if the image is fully opaque.
10844 % The only loss in data is the reduction of the sample depth
10847 % o -define: For more precise control of the PNG output, you can use the
10848 % Image options "png:bit-depth" and "png:color-type". These
10849 % can be set from the commandline with "-define" and also
10850 % from the application programming interfaces. The options
10851 % are case-independent and are converted to lowercase before
10852 % being passed to this encoder.
10854 % png:color-type can be 0, 2, 3, 4, or 6.
10856 % When png:color-type is 0 (Grayscale), png:bit-depth can
10857 % be 1, 2, 4, 8, or 16.
10859 % When png:color-type is 2 (RGB), png:bit-depth can
10862 % When png:color-type is 3 (Indexed), png:bit-depth can
10863 % be 1, 2, 4, or 8. This refers to the number of bits
10864 % used to store the index. The color samples always have
10865 % bit-depth 8 in indexed PNG files.
10867 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10868 % png:bit-depth can be 8 or 16.
10870 % If the image cannot be written without loss with the requested bit-depth
10871 % and color-type, a PNG file will not be written, and the encoder will
10872 % return MagickFalse.
10874 % Since image encoders should not be responsible for the "heavy lifting",
10875 % the user should make sure that ImageMagick has already reduced the
10876 % image depth and number of colors and limit transparency to binary
10877 % transparency prior to attempting to write the image with depth, color,
10878 % or transparency limitations.
10880 % Note that another definition, "png:bit-depth-written" exists, but it
10881 % is not intended for external use. It is only used internally by the
10882 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10884 % It is possible to request that the PNG encoder write previously-formatted
10885 % ancillary chunks in the output PNG file, using the "-profile" commandline
10886 % option as shown below or by setting the profile via a programming
10889 % -profile PNG-chunk-x:<file>
10891 % where x is a location flag and <file> is a file containing the chunk
10892 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10893 % This encoder will compute the chunk length and CRC, so those must not
10894 % be included in the file.
10896 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10897 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10898 % of the same type, then add a short unique string after the "x" to prevent
10899 % subsequent profiles from overwriting the preceding ones, e.g.,
10901 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10903 % As of version 6.6.6 the following optimizations are always done:
10905 % o 32-bit depth is reduced to 16.
10906 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10907 % high byte and low byte are identical.
10908 % o Palette is sorted to remove unused entries and to put a
10909 % transparent color first, if BUILD_PNG_PALETTE is defined.
10910 % o Opaque matte channel is removed when writing an indexed PNG.
10911 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10912 % this can be done without loss and a larger bit depth N was not
10913 % requested via the "-define PNG:bit-depth=N" option.
10914 % o If matte channel is present but only one transparent color is
10915 % present, RGB+tRNS is written instead of RGBA
10916 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10917 % was requested when converting an opaque image).
10919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10921 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10922 Image *image,ExceptionInfo *exception)
10927 have_mng_structure,
10943 assert(image_info != (const ImageInfo *) NULL);
10944 assert(image_info->signature == MagickSignature);
10945 assert(image != (Image *) NULL);
10946 assert(image->signature == MagickSignature);
10947 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10948 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10950 Allocate a MngInfo structure.
10952 have_mng_structure=MagickFalse;
10953 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10955 if (mng_info == (MngInfo *) NULL)
10956 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10959 Initialize members of the MngInfo structure.
10961 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10962 mng_info->image=image;
10963 mng_info->equal_backgrounds=MagickTrue;
10964 have_mng_structure=MagickTrue;
10966 /* See if user has requested a specific PNG subformat */
10968 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10969 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10970 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10972 if (mng_info->write_png8)
10974 mng_info->write_png_colortype = /* 3 */ 4;
10975 mng_info->write_png_depth = 8;
10979 if (mng_info->write_png24)
10981 mng_info->write_png_colortype = /* 2 */ 3;
10982 mng_info->write_png_depth = 8;
10985 if (image->matte == MagickTrue)
10986 (void) SetImageType(image,TrueColorMatteType,exception);
10989 (void) SetImageType(image,TrueColorType,exception);
10991 (void) SyncImage(image);
10994 if (mng_info->write_png32)
10996 mng_info->write_png_colortype = /* 6 */ 7;
10997 mng_info->write_png_depth = 8;
11000 if (image->matte == MagickTrue)
11001 (void) SetImageType(image,TrueColorMatteType,exception);
11004 (void) SetImageType(image,TrueColorType,exception);
11006 (void) SyncImage(image);
11009 value=GetImageOption(image_info,"png:bit-depth");
11011 if (value != (char *) NULL)
11013 if (LocaleCompare(value,"1") == 0)
11014 mng_info->write_png_depth = 1;
11016 else if (LocaleCompare(value,"2") == 0)
11017 mng_info->write_png_depth = 2;
11019 else if (LocaleCompare(value,"4") == 0)
11020 mng_info->write_png_depth = 4;
11022 else if (LocaleCompare(value,"8") == 0)
11023 mng_info->write_png_depth = 8;
11025 else if (LocaleCompare(value,"16") == 0)
11026 mng_info->write_png_depth = 16;
11029 (void) ThrowMagickException(&image->exception,
11030 GetMagickModule(),CoderWarning,
11031 "ignoring invalid defined png:bit-depth",
11034 if (logging != MagickFalse)
11035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11036 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11039 value=GetImageOption(image_info,"png:color-type");
11041 if (value != (char *) NULL)
11043 /* We must store colortype+1 because 0 is a valid colortype */
11044 if (LocaleCompare(value,"0") == 0)
11045 mng_info->write_png_colortype = 1;
11047 else if (LocaleCompare(value,"2") == 0)
11048 mng_info->write_png_colortype = 3;
11050 else if (LocaleCompare(value,"3") == 0)
11051 mng_info->write_png_colortype = 4;
11053 else if (LocaleCompare(value,"4") == 0)
11054 mng_info->write_png_colortype = 5;
11056 else if (LocaleCompare(value,"6") == 0)
11057 mng_info->write_png_colortype = 7;
11060 (void) ThrowMagickException(&image->exception,
11061 GetMagickModule(),CoderWarning,
11062 "ignoring invalid defined png:color-type",
11065 if (logging != MagickFalse)
11066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11067 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11070 /* Check for chunks to be excluded:
11072 * The default is to not exclude any known chunks except for any
11073 * listed in the "unused_chunks" array, above.
11075 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11076 * define (in the image properties or in the image artifacts)
11077 * or via a mng_info member. For convenience, in addition
11078 * to or instead of a comma-separated list of chunks, the
11079 * "exclude-chunk" string can be simply "all" or "none".
11081 * The exclude-chunk define takes priority over the mng_info.
11083 * A "PNG:include-chunk" define takes priority over both the
11084 * mng_info and the "PNG:exclude-chunk" define. Like the
11085 * "exclude-chunk" string, it can define "all" or "none" as
11086 * well as a comma-separated list. Chunks that are unknown to
11087 * ImageMagick are always excluded, regardless of their "copy-safe"
11088 * status according to the PNG specification, and even if they
11089 * appear in the "include-chunk" list.
11091 * Finally, all chunks listed in the "unused_chunks" array are
11092 * automatically excluded, regardless of the other instructions
11095 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11096 * will not be written and the gAMA chunk will only be written if it
11097 * is not between .45 and .46, or approximately (1.0/2.2).
11099 * If you exclude tRNS and the image has transparency, the colortype
11100 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11102 * The -strip option causes StripImage() to set the png:include-chunk
11103 * artifact to "none,trns,gama".
11106 mng_info->ping_exclude_bKGD=MagickFalse;
11107 mng_info->ping_exclude_cHRM=MagickFalse;
11108 mng_info->ping_exclude_date=MagickFalse;
11109 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11110 mng_info->ping_exclude_gAMA=MagickFalse;
11111 mng_info->ping_exclude_iCCP=MagickFalse;
11112 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11113 mng_info->ping_exclude_oFFs=MagickFalse;
11114 mng_info->ping_exclude_pHYs=MagickFalse;
11115 mng_info->ping_exclude_sRGB=MagickFalse;
11116 mng_info->ping_exclude_tEXt=MagickFalse;
11117 mng_info->ping_exclude_tRNS=MagickFalse;
11118 mng_info->ping_exclude_vpAg=MagickFalse;
11119 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11120 mng_info->ping_exclude_zTXt=MagickFalse;
11122 mng_info->ping_preserve_colormap=MagickFalse;
11124 value=GetImageArtifact(image,"png:preserve-colormap");
11126 value=GetImageOption(image_info,"png:preserve-colormap");
11128 mng_info->ping_preserve_colormap=MagickTrue;
11130 /* Thes compression-level, compression-strategy, and compression-filter
11131 * defines take precedence over values from the -quality option.
11133 value=GetImageArtifact(image,"png:compression-level");
11135 value=GetImageOption(image_info,"png:compression-level");
11138 /* We have to add 1 to everything because 0 is a valid input,
11139 * and we want to use 0 (the default) to mean undefined.
11141 if (LocaleCompare(value,"0") == 0)
11142 mng_info->write_png_compression_level = 1;
11144 if (LocaleCompare(value,"1") == 0)
11145 mng_info->write_png_compression_level = 2;
11147 else if (LocaleCompare(value,"2") == 0)
11148 mng_info->write_png_compression_level = 3;
11150 else if (LocaleCompare(value,"3") == 0)
11151 mng_info->write_png_compression_level = 4;
11153 else if (LocaleCompare(value,"4") == 0)
11154 mng_info->write_png_compression_level = 5;
11156 else if (LocaleCompare(value,"5") == 0)
11157 mng_info->write_png_compression_level = 6;
11159 else if (LocaleCompare(value,"6") == 0)
11160 mng_info->write_png_compression_level = 7;
11162 else if (LocaleCompare(value,"7") == 0)
11163 mng_info->write_png_compression_level = 8;
11165 else if (LocaleCompare(value,"8") == 0)
11166 mng_info->write_png_compression_level = 9;
11168 else if (LocaleCompare(value,"9") == 0)
11169 mng_info->write_png_compression_level = 10;
11172 (void) ThrowMagickException(&image->exception,
11173 GetMagickModule(),CoderWarning,
11174 "ignoring invalid defined png:compression-level",
11178 value=GetImageArtifact(image,"png:compression-strategy");
11180 value=GetImageOption(image_info,"png:compression-strategy");
11184 if (LocaleCompare(value,"0") == 0)
11185 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11187 else if (LocaleCompare(value,"1") == 0)
11188 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11190 else if (LocaleCompare(value,"2") == 0)
11191 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11193 else if (LocaleCompare(value,"3") == 0)
11194 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11195 mng_info->write_png_compression_strategy = Z_RLE+1;
11197 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11200 else if (LocaleCompare(value,"4") == 0)
11201 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11202 mng_info->write_png_compression_strategy = Z_FIXED+1;
11204 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11208 (void) ThrowMagickException(&image->exception,
11209 GetMagickModule(),CoderWarning,
11210 "ignoring invalid defined png:compression-strategy",
11214 value=GetImageArtifact(image,"png:compression-filter");
11216 value=GetImageOption(image_info,"png:compression-filter");
11220 /* To do: combinations of filters allowed by libpng
11221 * masks 0x08 through 0xf8
11223 * Implement this as a comma-separated list of 0,1,2,3,4,5
11224 * where 5 is a special case meaning PNG_ALL_FILTERS.
11227 if (LocaleCompare(value,"0") == 0)
11228 mng_info->write_png_compression_filter = 1;
11230 if (LocaleCompare(value,"1") == 0)
11231 mng_info->write_png_compression_filter = 2;
11233 else if (LocaleCompare(value,"2") == 0)
11234 mng_info->write_png_compression_filter = 3;
11236 else if (LocaleCompare(value,"3") == 0)
11237 mng_info->write_png_compression_filter = 4;
11239 else if (LocaleCompare(value,"4") == 0)
11240 mng_info->write_png_compression_filter = 5;
11242 else if (LocaleCompare(value,"5") == 0)
11243 mng_info->write_png_compression_filter = 6;
11246 (void) ThrowMagickException(&image->exception,
11247 GetMagickModule(),CoderWarning,
11248 "ignoring invalid defined png:compression-filter",
11252 excluding=MagickFalse;
11254 for (source=0; source<1; source++)
11258 value=GetImageArtifact(image,"png:exclude-chunk");
11261 value=GetImageArtifact(image,"png:exclude-chunks");
11265 value=GetImageOption(image_info,"png:exclude-chunk");
11268 value=GetImageOption(image_info,"png:exclude-chunks");
11277 excluding=MagickTrue;
11279 if (logging != MagickFalse)
11282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11283 " png:exclude-chunk=%s found in image artifacts.\n", value);
11285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11286 " png:exclude-chunk=%s found in image properties.\n", value);
11289 last=strlen(value);
11291 for (i=0; i<(int) last; i+=5)
11294 if (LocaleNCompare(value+i,"all",3) == 0)
11296 mng_info->ping_exclude_bKGD=MagickTrue;
11297 mng_info->ping_exclude_cHRM=MagickTrue;
11298 mng_info->ping_exclude_date=MagickTrue;
11299 mng_info->ping_exclude_EXIF=MagickTrue;
11300 mng_info->ping_exclude_gAMA=MagickTrue;
11301 mng_info->ping_exclude_iCCP=MagickTrue;
11302 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11303 mng_info->ping_exclude_oFFs=MagickTrue;
11304 mng_info->ping_exclude_pHYs=MagickTrue;
11305 mng_info->ping_exclude_sRGB=MagickTrue;
11306 mng_info->ping_exclude_tEXt=MagickTrue;
11307 mng_info->ping_exclude_tRNS=MagickTrue;
11308 mng_info->ping_exclude_vpAg=MagickTrue;
11309 mng_info->ping_exclude_zCCP=MagickTrue;
11310 mng_info->ping_exclude_zTXt=MagickTrue;
11314 if (LocaleNCompare(value+i,"none",4) == 0)
11316 mng_info->ping_exclude_bKGD=MagickFalse;
11317 mng_info->ping_exclude_cHRM=MagickFalse;
11318 mng_info->ping_exclude_date=MagickFalse;
11319 mng_info->ping_exclude_EXIF=MagickFalse;
11320 mng_info->ping_exclude_gAMA=MagickFalse;
11321 mng_info->ping_exclude_iCCP=MagickFalse;
11322 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11323 mng_info->ping_exclude_oFFs=MagickFalse;
11324 mng_info->ping_exclude_pHYs=MagickFalse;
11325 mng_info->ping_exclude_sRGB=MagickFalse;
11326 mng_info->ping_exclude_tEXt=MagickFalse;
11327 mng_info->ping_exclude_tRNS=MagickFalse;
11328 mng_info->ping_exclude_vpAg=MagickFalse;
11329 mng_info->ping_exclude_zCCP=MagickFalse;
11330 mng_info->ping_exclude_zTXt=MagickFalse;
11333 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11334 mng_info->ping_exclude_bKGD=MagickTrue;
11336 if (LocaleNCompare(value+i,"chrm",4) == 0)
11337 mng_info->ping_exclude_cHRM=MagickTrue;
11339 if (LocaleNCompare(value+i,"date",4) == 0)
11340 mng_info->ping_exclude_date=MagickTrue;
11342 if (LocaleNCompare(value+i,"exif",4) == 0)
11343 mng_info->ping_exclude_EXIF=MagickTrue;
11345 if (LocaleNCompare(value+i,"gama",4) == 0)
11346 mng_info->ping_exclude_gAMA=MagickTrue;
11348 if (LocaleNCompare(value+i,"iccp",4) == 0)
11349 mng_info->ping_exclude_iCCP=MagickTrue;
11352 if (LocaleNCompare(value+i,"itxt",4) == 0)
11353 mng_info->ping_exclude_iTXt=MagickTrue;
11356 if (LocaleNCompare(value+i,"gama",4) == 0)
11357 mng_info->ping_exclude_gAMA=MagickTrue;
11359 if (LocaleNCompare(value+i,"offs",4) == 0)
11360 mng_info->ping_exclude_oFFs=MagickTrue;
11362 if (LocaleNCompare(value+i,"phys",4) == 0)
11363 mng_info->ping_exclude_pHYs=MagickTrue;
11365 if (LocaleNCompare(value+i,"srgb",4) == 0)
11366 mng_info->ping_exclude_sRGB=MagickTrue;
11368 if (LocaleNCompare(value+i,"text",4) == 0)
11369 mng_info->ping_exclude_tEXt=MagickTrue;
11371 if (LocaleNCompare(value+i,"trns",4) == 0)
11372 mng_info->ping_exclude_tRNS=MagickTrue;
11374 if (LocaleNCompare(value+i,"vpag",4) == 0)
11375 mng_info->ping_exclude_vpAg=MagickTrue;
11377 if (LocaleNCompare(value+i,"zccp",4) == 0)
11378 mng_info->ping_exclude_zCCP=MagickTrue;
11380 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11381 mng_info->ping_exclude_zTXt=MagickTrue;
11387 for (source=0; source<1; source++)
11391 value=GetImageArtifact(image,"png:include-chunk");
11394 value=GetImageArtifact(image,"png:include-chunks");
11398 value=GetImageOption(image_info,"png:include-chunk");
11401 value=GetImageOption(image_info,"png:include-chunks");
11409 excluding=MagickTrue;
11411 if (logging != MagickFalse)
11414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11415 " png:include-chunk=%s found in image artifacts.\n", value);
11417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11418 " png:include-chunk=%s found in image properties.\n", value);
11421 last=strlen(value);
11423 for (i=0; i<(int) last; i+=5)
11425 if (LocaleNCompare(value+i,"all",3) == 0)
11427 mng_info->ping_exclude_bKGD=MagickFalse;
11428 mng_info->ping_exclude_cHRM=MagickFalse;
11429 mng_info->ping_exclude_date=MagickFalse;
11430 mng_info->ping_exclude_EXIF=MagickFalse;
11431 mng_info->ping_exclude_gAMA=MagickFalse;
11432 mng_info->ping_exclude_iCCP=MagickFalse;
11433 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11434 mng_info->ping_exclude_oFFs=MagickFalse;
11435 mng_info->ping_exclude_pHYs=MagickFalse;
11436 mng_info->ping_exclude_sRGB=MagickFalse;
11437 mng_info->ping_exclude_tEXt=MagickFalse;
11438 mng_info->ping_exclude_tRNS=MagickFalse;
11439 mng_info->ping_exclude_vpAg=MagickFalse;
11440 mng_info->ping_exclude_zCCP=MagickFalse;
11441 mng_info->ping_exclude_zTXt=MagickFalse;
11445 if (LocaleNCompare(value+i,"none",4) == 0)
11447 mng_info->ping_exclude_bKGD=MagickTrue;
11448 mng_info->ping_exclude_cHRM=MagickTrue;
11449 mng_info->ping_exclude_date=MagickTrue;
11450 mng_info->ping_exclude_EXIF=MagickTrue;
11451 mng_info->ping_exclude_gAMA=MagickTrue;
11452 mng_info->ping_exclude_iCCP=MagickTrue;
11453 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11454 mng_info->ping_exclude_oFFs=MagickTrue;
11455 mng_info->ping_exclude_pHYs=MagickTrue;
11456 mng_info->ping_exclude_sRGB=MagickTrue;
11457 mng_info->ping_exclude_tEXt=MagickTrue;
11458 mng_info->ping_exclude_tRNS=MagickTrue;
11459 mng_info->ping_exclude_vpAg=MagickTrue;
11460 mng_info->ping_exclude_zCCP=MagickTrue;
11461 mng_info->ping_exclude_zTXt=MagickTrue;
11464 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11465 mng_info->ping_exclude_bKGD=MagickFalse;
11467 if (LocaleNCompare(value+i,"chrm",4) == 0)
11468 mng_info->ping_exclude_cHRM=MagickFalse;
11470 if (LocaleNCompare(value+i,"date",4) == 0)
11471 mng_info->ping_exclude_date=MagickFalse;
11473 if (LocaleNCompare(value+i,"exif",4) == 0)
11474 mng_info->ping_exclude_EXIF=MagickFalse;
11476 if (LocaleNCompare(value+i,"gama",4) == 0)
11477 mng_info->ping_exclude_gAMA=MagickFalse;
11479 if (LocaleNCompare(value+i,"iccp",4) == 0)
11480 mng_info->ping_exclude_iCCP=MagickFalse;
11483 if (LocaleNCompare(value+i,"itxt",4) == 0)
11484 mng_info->ping_exclude_iTXt=MagickFalse;
11487 if (LocaleNCompare(value+i,"gama",4) == 0)
11488 mng_info->ping_exclude_gAMA=MagickFalse;
11490 if (LocaleNCompare(value+i,"offs",4) == 0)
11491 mng_info->ping_exclude_oFFs=MagickFalse;
11493 if (LocaleNCompare(value+i,"phys",4) == 0)
11494 mng_info->ping_exclude_pHYs=MagickFalse;
11496 if (LocaleNCompare(value+i,"srgb",4) == 0)
11497 mng_info->ping_exclude_sRGB=MagickFalse;
11499 if (LocaleNCompare(value+i,"text",4) == 0)
11500 mng_info->ping_exclude_tEXt=MagickFalse;
11502 if (LocaleNCompare(value+i,"trns",4) == 0)
11503 mng_info->ping_exclude_tRNS=MagickFalse;
11505 if (LocaleNCompare(value+i,"vpag",4) == 0)
11506 mng_info->ping_exclude_vpAg=MagickFalse;
11508 if (LocaleNCompare(value+i,"zccp",4) == 0)
11509 mng_info->ping_exclude_zCCP=MagickFalse;
11511 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11512 mng_info->ping_exclude_zTXt=MagickFalse;
11518 if (excluding != MagickFalse && logging != MagickFalse)
11520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11521 " Chunks to be excluded from the output PNG:");
11522 if (mng_info->ping_exclude_bKGD != MagickFalse)
11523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 if (mng_info->ping_exclude_cHRM != MagickFalse)
11526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528 if (mng_info->ping_exclude_date != MagickFalse)
11529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11531 if (mng_info->ping_exclude_EXIF != MagickFalse)
11532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11534 if (mng_info->ping_exclude_gAMA != MagickFalse)
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11537 if (mng_info->ping_exclude_iCCP != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11541 if (mng_info->ping_exclude_iTXt != MagickFalse)
11542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11545 if (mng_info->ping_exclude_oFFs != MagickFalse)
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 if (mng_info->ping_exclude_pHYs != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 if (mng_info->ping_exclude_sRGB != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 if (mng_info->ping_exclude_tEXt != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 if (mng_info->ping_exclude_tRNS != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11560 if (mng_info->ping_exclude_vpAg != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11563 if (mng_info->ping_exclude_zCCP != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11566 if (mng_info->ping_exclude_zTXt != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 mng_info->need_blob = MagickTrue;
11573 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11575 MngInfoFreeStruct(mng_info,&have_mng_structure);
11577 if (logging != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11583 #if defined(JNG_SUPPORTED)
11585 /* Write one JNG image */
11586 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11587 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11608 jng_alpha_compression_method,
11609 jng_alpha_sample_depth,
11616 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11617 " Enter WriteOneJNGImage()");
11619 blob=(unsigned char *) NULL;
11620 jpeg_image=(Image *) NULL;
11621 jpeg_image_info=(ImageInfo *) NULL;
11624 transparent=image_info->type==GrayscaleMatteType ||
11625 image_info->type==TrueColorMatteType;
11627 jng_alpha_sample_depth=0;
11628 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11629 jng_alpha_compression_method=0;
11631 if (image->matte != MagickFalse)
11633 /* if any pixels are transparent */
11634 transparent=MagickTrue;
11635 if (image_info->compression==JPEGCompression)
11636 jng_alpha_compression_method=8;
11646 /* Create JPEG blob, image, and image_info */
11647 if (logging != MagickFalse)
11648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11649 " Creating jpeg_image_info for alpha.");
11651 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11653 if (jpeg_image_info == (ImageInfo *) NULL)
11654 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11656 if (logging != MagickFalse)
11657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11658 " Creating jpeg_image.");
11660 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11662 if (jpeg_image == (Image *) NULL)
11663 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11665 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11666 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
11667 status=SeparateImage(jpeg_image);
11668 (void) SetPixelChannelMap(jpeg_image,channel_mask);
11669 jpeg_image->matte=MagickFalse;
11671 if (jng_quality >= 1000)
11672 jpeg_image_info->quality=jng_quality/1000;
11675 jpeg_image_info->quality=jng_quality;
11677 jpeg_image_info->type=GrayscaleType;
11678 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11679 (void) AcquireUniqueFilename(jpeg_image->filename);
11680 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11681 "%s",jpeg_image->filename);
11684 /* To do: check bit depth of PNG alpha channel */
11686 /* Check if image is grayscale. */
11687 if (image_info->type != TrueColorMatteType && image_info->type !=
11688 TrueColorType && ImageIsGray(image))
11693 if (jng_alpha_compression_method==0)
11698 /* Encode alpha as a grayscale PNG blob */
11699 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11700 &image->exception);
11701 if (logging != MagickFalse)
11702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703 " Creating PNG blob.");
11706 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11707 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11708 jpeg_image_info->interlace=NoInterlace;
11710 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11711 &image->exception);
11713 /* Retrieve sample depth used */
11714 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11715 if (value != (char *) NULL)
11716 jng_alpha_sample_depth= (unsigned int) value[0];
11720 /* Encode alpha as a grayscale JPEG blob */
11722 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11723 &image->exception);
11725 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11726 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11727 jpeg_image_info->interlace=NoInterlace;
11728 if (logging != MagickFalse)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " Creating blob.");
11731 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11732 &image->exception);
11733 jng_alpha_sample_depth=8;
11735 if (logging != MagickFalse)
11736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11737 " Successfully read jpeg_image into a blob, length=%.20g.",
11741 /* Destroy JPEG image and image_info */
11742 jpeg_image=DestroyImage(jpeg_image);
11743 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11744 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11747 /* Write JHDR chunk */
11748 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11749 PNGType(chunk,mng_JHDR);
11750 LogPNGChunk(logging,mng_JHDR,16L);
11751 PNGLong(chunk+4,(png_uint_32) image->columns);
11752 PNGLong(chunk+8,(png_uint_32) image->rows);
11753 chunk[12]=jng_color_type;
11754 chunk[13]=8; /* sample depth */
11755 chunk[14]=8; /*jng_image_compression_method */
11756 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11757 chunk[16]=jng_alpha_sample_depth;
11758 chunk[17]=jng_alpha_compression_method;
11759 chunk[18]=0; /*jng_alpha_filter_method */
11760 chunk[19]=0; /*jng_alpha_interlace_method */
11761 (void) WriteBlob(image,20,chunk);
11762 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11763 if (logging != MagickFalse)
11765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11766 " JNG width:%15lu",(unsigned long) image->columns);
11768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11769 " JNG height:%14lu",(unsigned long) image->rows);
11771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11772 " JNG color type:%10d",jng_color_type);
11774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11775 " JNG sample depth:%8d",8);
11777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11778 " JNG compression:%9d",8);
11780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11781 " JNG interlace:%11d",0);
11783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11784 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11787 " JNG alpha compression:%3d",jng_alpha_compression_method);
11789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11790 " JNG alpha filter:%8d",0);
11792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11793 " JNG alpha interlace:%5d",0);
11796 /* Write any JNG-chunk-b profiles */
11797 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11800 Write leading ancillary chunks
11806 Write JNG bKGD chunk
11817 if (jng_color_type == 8 || jng_color_type == 12)
11821 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11822 PNGType(chunk,mng_bKGD);
11823 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11824 red=ScaleQuantumToChar(image->background_color.red);
11825 green=ScaleQuantumToChar(image->background_color.green);
11826 blue=ScaleQuantumToChar(image->background_color.blue);
11833 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11834 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11837 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11840 Write JNG sRGB chunk
11842 (void) WriteBlobMSBULong(image,1L);
11843 PNGType(chunk,mng_sRGB);
11844 LogPNGChunk(logging,mng_sRGB,1L);
11846 if (image->rendering_intent != UndefinedIntent)
11847 chunk[4]=(unsigned char)
11848 Magick_RenderingIntent_to_PNG_RenderingIntent(
11849 (image->rendering_intent));
11852 chunk[4]=(unsigned char)
11853 Magick_RenderingIntent_to_PNG_RenderingIntent(
11854 (PerceptualIntent));
11856 (void) WriteBlob(image,5,chunk);
11857 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11861 if (image->gamma != 0.0)
11864 Write JNG gAMA chunk
11866 (void) WriteBlobMSBULong(image,4L);
11867 PNGType(chunk,mng_gAMA);
11868 LogPNGChunk(logging,mng_gAMA,4L);
11869 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11870 (void) WriteBlob(image,8,chunk);
11871 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11874 if ((mng_info->equal_chrms == MagickFalse) &&
11875 (image->chromaticity.red_primary.x != 0.0))
11881 Write JNG cHRM chunk
11883 (void) WriteBlobMSBULong(image,32L);
11884 PNGType(chunk,mng_cHRM);
11885 LogPNGChunk(logging,mng_cHRM,32L);
11886 primary=image->chromaticity.white_point;
11887 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11888 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11889 primary=image->chromaticity.red_primary;
11890 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11891 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11892 primary=image->chromaticity.green_primary;
11893 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11894 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11895 primary=image->chromaticity.blue_primary;
11896 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11897 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11898 (void) WriteBlob(image,36,chunk);
11899 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11903 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11906 Write JNG pHYs chunk
11908 (void) WriteBlobMSBULong(image,9L);
11909 PNGType(chunk,mng_pHYs);
11910 LogPNGChunk(logging,mng_pHYs,9L);
11911 if (image->units == PixelsPerInchResolution)
11913 PNGLong(chunk+4,(png_uint_32)
11914 (image->x_resolution*100.0/2.54+0.5));
11916 PNGLong(chunk+8,(png_uint_32)
11917 (image->y_resolution*100.0/2.54+0.5));
11924 if (image->units == PixelsPerCentimeterResolution)
11926 PNGLong(chunk+4,(png_uint_32)
11927 (image->x_resolution*100.0+0.5));
11929 PNGLong(chunk+8,(png_uint_32)
11930 (image->y_resolution*100.0+0.5));
11937 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11938 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11942 (void) WriteBlob(image,13,chunk);
11943 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11946 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11949 Write JNG oFFs chunk
11951 (void) WriteBlobMSBULong(image,9L);
11952 PNGType(chunk,mng_oFFs);
11953 LogPNGChunk(logging,mng_oFFs,9L);
11954 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11955 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11957 (void) WriteBlob(image,13,chunk);
11958 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11960 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11962 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11963 PNGType(chunk,mng_vpAg);
11964 LogPNGChunk(logging,mng_vpAg,9L);
11965 PNGLong(chunk+4,(png_uint_32) image->page.width);
11966 PNGLong(chunk+8,(png_uint_32) image->page.height);
11967 chunk[12]=0; /* unit = pixels */
11968 (void) WriteBlob(image,13,chunk);
11969 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11975 if (jng_alpha_compression_method==0)
11983 /* Write IDAT chunk header */
11984 if (logging != MagickFalse)
11985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11986 " Write IDAT chunks from blob, length=%.20g.",(double)
11989 /* Copy IDAT chunks */
11992 for (i=8; i<(ssize_t) length; i+=len+12)
11994 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11997 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11999 /* Found an IDAT chunk. */
12000 (void) WriteBlobMSBULong(image,(size_t) len);
12001 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12002 (void) WriteBlob(image,(size_t) len+4,p);
12003 (void) WriteBlobMSBULong(image,
12004 crc32(0,p,(uInt) len+4));
12009 if (logging != MagickFalse)
12010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12011 " Skipping %c%c%c%c chunk, length=%.20g.",
12012 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12019 /* Write JDAA chunk header */
12020 if (logging != MagickFalse)
12021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12022 " Write JDAA chunk, length=%.20g.",(double) length);
12023 (void) WriteBlobMSBULong(image,(size_t) length);
12024 PNGType(chunk,mng_JDAA);
12025 LogPNGChunk(logging,mng_JDAA,length);
12026 /* Write JDAT chunk(s) data */
12027 (void) WriteBlob(image,4,chunk);
12028 (void) WriteBlob(image,length,blob);
12029 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12032 blob=(unsigned char *) RelinquishMagickMemory(blob);
12035 /* Encode image as a JPEG blob */
12036 if (logging != MagickFalse)
12037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12038 " Creating jpeg_image_info.");
12039 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12040 if (jpeg_image_info == (ImageInfo *) NULL)
12041 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12043 if (logging != MagickFalse)
12044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12045 " Creating jpeg_image.");
12047 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12048 if (jpeg_image == (Image *) NULL)
12049 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12050 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12052 (void) AcquireUniqueFilename(jpeg_image->filename);
12053 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12054 jpeg_image->filename);
12056 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12057 &image->exception);
12059 if (logging != MagickFalse)
12060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12061 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12062 (double) jpeg_image->rows);
12064 if (jng_color_type == 8 || jng_color_type == 12)
12065 jpeg_image_info->type=GrayscaleType;
12067 jpeg_image_info->quality=jng_quality % 1000;
12068 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12069 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12071 if (logging != MagickFalse)
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12073 " Creating blob.");
12075 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
12077 if (logging != MagickFalse)
12079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12080 " Successfully read jpeg_image into a blob, length=%.20g.",
12083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12084 " Write JDAT chunk, length=%.20g.",(double) length);
12087 /* Write JDAT chunk(s) */
12088 (void) WriteBlobMSBULong(image,(size_t) length);
12089 PNGType(chunk,mng_JDAT);
12090 LogPNGChunk(logging,mng_JDAT,length);
12091 (void) WriteBlob(image,4,chunk);
12092 (void) WriteBlob(image,length,blob);
12093 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12095 jpeg_image=DestroyImage(jpeg_image);
12096 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12097 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12098 blob=(unsigned char *) RelinquishMagickMemory(blob);
12100 /* Write any JNG-chunk-e profiles */
12101 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12103 /* Write IEND chunk */
12104 (void) WriteBlobMSBULong(image,0L);
12105 PNGType(chunk,mng_IEND);
12106 LogPNGChunk(logging,mng_IEND,0);
12107 (void) WriteBlob(image,4,chunk);
12108 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12110 if (logging != MagickFalse)
12111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12112 " exit WriteOneJNGImage()");
12119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12123 % W r i t e J N G I m a g e %
12127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12129 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12131 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12133 % The format of the WriteJNGImage method is:
12135 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12136 % Image *image,ExceptionInfo *exception)
12138 % A description of each parameter follows:
12140 % o image_info: the image info.
12142 % o image: The image.
12144 % o exception: return any errors or warnings in this structure.
12146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12148 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12149 ExceptionInfo *exception)
12152 have_mng_structure,
12162 assert(image_info != (const ImageInfo *) NULL);
12163 assert(image_info->signature == MagickSignature);
12164 assert(image != (Image *) NULL);
12165 assert(image->signature == MagickSignature);
12166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12167 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12168 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12169 if (status == MagickFalse)
12173 Allocate a MngInfo structure.
12175 have_mng_structure=MagickFalse;
12176 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12177 if (mng_info == (MngInfo *) NULL)
12178 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12180 Initialize members of the MngInfo structure.
12182 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12183 mng_info->image=image;
12184 have_mng_structure=MagickTrue;
12186 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12188 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12189 (void) CloseBlob(image);
12191 (void) CatchImageException(image);
12192 MngInfoFreeStruct(mng_info,&have_mng_structure);
12193 if (logging != MagickFalse)
12194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12199 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12200 ExceptionInfo *exception)
12209 have_mng_structure,
12212 volatile MagickBooleanType
12224 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12225 defined(PNG_MNG_FEATURES_SUPPORTED)
12228 all_images_are_gray,
12238 volatile unsigned int
12249 #if (PNG_LIBPNG_VER < 10200)
12250 if (image_info->verbose)
12251 printf("Your PNG library (libpng-%s) is rather old.\n",
12252 PNG_LIBPNG_VER_STRING);
12258 assert(image_info != (const ImageInfo *) NULL);
12259 assert(image_info->signature == MagickSignature);
12260 assert(image != (Image *) NULL);
12261 assert(image->signature == MagickSignature);
12262 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12263 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12264 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12265 if (status == MagickFalse)
12269 Allocate a MngInfo structure.
12271 have_mng_structure=MagickFalse;
12272 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12273 if (mng_info == (MngInfo *) NULL)
12274 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12276 Initialize members of the MngInfo structure.
12278 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12279 mng_info->image=image;
12280 have_mng_structure=MagickTrue;
12281 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12284 * See if user has requested a specific PNG subformat to be used
12285 * for all of the PNGs in the MNG being written, e.g.,
12287 * convert *.png png8:animation.mng
12289 * To do: check -define png:bit_depth and png:color_type as well,
12290 * or perhaps use mng:bit_depth and mng:color_type instead for
12294 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12295 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12296 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12298 write_jng=MagickFalse;
12299 if (image_info->compression == JPEGCompression)
12300 write_jng=MagickTrue;
12302 mng_info->adjoin=image_info->adjoin &&
12303 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12305 if (logging != MagickFalse)
12307 /* Log some info about the input */
12311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12312 " Checking input image(s)");
12314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12315 " Image_info depth: %.20g",(double) image_info->depth);
12317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12318 " Type: %d",image_info->type);
12321 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 " Scene: %.20g",(double) scene++);
12326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12327 " Image depth: %.20g",(double) p->depth);
12330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12337 if (p->storage_class == PseudoClass)
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " Storage class: PseudoClass");
12342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12343 " Storage class: DirectClass");
12346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12347 " Number of colors: %.20g",(double) p->colors);
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " Number of colors: unspecified");
12353 if (mng_info->adjoin == MagickFalse)
12358 use_global_plte=MagickFalse;
12359 all_images_are_gray=MagickFalse;
12360 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12361 need_local_plte=MagickTrue;
12363 need_defi=MagickFalse;
12364 need_matte=MagickFalse;
12365 mng_info->framing_mode=1;
12366 mng_info->old_framing_mode=1;
12369 if (image_info->page != (char *) NULL)
12372 Determine image bounding box.
12374 SetGeometry(image,&mng_info->page);
12375 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12376 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12388 mng_info->page=image->page;
12389 need_geom=MagickTrue;
12390 if (mng_info->page.width || mng_info->page.height)
12391 need_geom=MagickFalse;
12393 Check all the scenes.
12395 initial_delay=image->delay;
12396 need_iterations=MagickFalse;
12397 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12398 mng_info->equal_physs=MagickTrue,
12399 mng_info->equal_gammas=MagickTrue;
12400 mng_info->equal_srgbs=MagickTrue;
12401 mng_info->equal_backgrounds=MagickTrue;
12403 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12404 defined(PNG_MNG_FEATURES_SUPPORTED)
12405 all_images_are_gray=MagickTrue;
12406 mng_info->equal_palettes=MagickFalse;
12407 need_local_plte=MagickFalse;
12409 for (next_image=image; next_image != (Image *) NULL; )
12413 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12414 mng_info->page.width=next_image->columns+next_image->page.x;
12416 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12417 mng_info->page.height=next_image->rows+next_image->page.y;
12420 if (next_image->page.x || next_image->page.y)
12421 need_defi=MagickTrue;
12423 if (next_image->matte)
12424 need_matte=MagickTrue;
12426 if ((int) next_image->dispose >= BackgroundDispose)
12427 if (next_image->matte || next_image->page.x || next_image->page.y ||
12428 ((next_image->columns < mng_info->page.width) &&
12429 (next_image->rows < mng_info->page.height)))
12430 mng_info->need_fram=MagickTrue;
12432 if (next_image->iterations)
12433 need_iterations=MagickTrue;
12435 final_delay=next_image->delay;
12437 if (final_delay != initial_delay || final_delay > 1UL*
12438 next_image->ticks_per_second)
12439 mng_info->need_fram=1;
12441 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12442 defined(PNG_MNG_FEATURES_SUPPORTED)
12444 check for global palette possibility.
12446 if (image->matte != MagickFalse)
12447 need_local_plte=MagickTrue;
12449 if (need_local_plte == 0)
12451 if (ImageIsGray(image) == MagickFalse)
12452 all_images_are_gray=MagickFalse;
12453 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12454 if (use_global_plte == 0)
12455 use_global_plte=mng_info->equal_palettes;
12456 need_local_plte=!mng_info->equal_palettes;
12459 if (GetNextImageInList(next_image) != (Image *) NULL)
12461 if (next_image->background_color.red !=
12462 next_image->next->background_color.red ||
12463 next_image->background_color.green !=
12464 next_image->next->background_color.green ||
12465 next_image->background_color.blue !=
12466 next_image->next->background_color.blue)
12467 mng_info->equal_backgrounds=MagickFalse;
12469 if (next_image->gamma != next_image->next->gamma)
12470 mng_info->equal_gammas=MagickFalse;
12472 if (next_image->rendering_intent !=
12473 next_image->next->rendering_intent)
12474 mng_info->equal_srgbs=MagickFalse;
12476 if ((next_image->units != next_image->next->units) ||
12477 (next_image->x_resolution != next_image->next->x_resolution) ||
12478 (next_image->y_resolution != next_image->next->y_resolution))
12479 mng_info->equal_physs=MagickFalse;
12481 if (mng_info->equal_chrms)
12483 if (next_image->chromaticity.red_primary.x !=
12484 next_image->next->chromaticity.red_primary.x ||
12485 next_image->chromaticity.red_primary.y !=
12486 next_image->next->chromaticity.red_primary.y ||
12487 next_image->chromaticity.green_primary.x !=
12488 next_image->next->chromaticity.green_primary.x ||
12489 next_image->chromaticity.green_primary.y !=
12490 next_image->next->chromaticity.green_primary.y ||
12491 next_image->chromaticity.blue_primary.x !=
12492 next_image->next->chromaticity.blue_primary.x ||
12493 next_image->chromaticity.blue_primary.y !=
12494 next_image->next->chromaticity.blue_primary.y ||
12495 next_image->chromaticity.white_point.x !=
12496 next_image->next->chromaticity.white_point.x ||
12497 next_image->chromaticity.white_point.y !=
12498 next_image->next->chromaticity.white_point.y)
12499 mng_info->equal_chrms=MagickFalse;
12503 next_image=GetNextImageInList(next_image);
12505 if (image_count < 2)
12507 mng_info->equal_backgrounds=MagickFalse;
12508 mng_info->equal_chrms=MagickFalse;
12509 mng_info->equal_gammas=MagickFalse;
12510 mng_info->equal_srgbs=MagickFalse;
12511 mng_info->equal_physs=MagickFalse;
12512 use_global_plte=MagickFalse;
12513 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12514 need_local_plte=MagickTrue;
12516 need_iterations=MagickFalse;
12519 if (mng_info->need_fram == MagickFalse)
12522 Only certain framing rates 100/n are exactly representable without
12523 the FRAM chunk but we'll allow some slop in VLC files
12525 if (final_delay == 0)
12527 if (need_iterations != MagickFalse)
12530 It's probably a GIF with loop; don't run it *too* fast.
12532 if (mng_info->adjoin)
12535 (void) ThrowMagickException(&image->exception,
12536 GetMagickModule(),CoderWarning,
12537 "input has zero delay between all frames; assuming",
12542 mng_info->ticks_per_second=0;
12544 if (final_delay != 0)
12545 mng_info->ticks_per_second=(png_uint_32)
12546 (image->ticks_per_second/final_delay);
12547 if (final_delay > 50)
12548 mng_info->ticks_per_second=2;
12550 if (final_delay > 75)
12551 mng_info->ticks_per_second=1;
12553 if (final_delay > 125)
12554 mng_info->need_fram=MagickTrue;
12556 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12557 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12558 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12559 1UL*image->ticks_per_second))
12560 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12563 if (mng_info->need_fram != MagickFalse)
12564 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12566 If pseudocolor, we should also check to see if all the
12567 palettes are identical and write a global PLTE if they are.
12571 Write the MNG version 1.0 signature and MHDR chunk.
12573 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12574 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12575 PNGType(chunk,mng_MHDR);
12576 LogPNGChunk(logging,mng_MHDR,28L);
12577 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12578 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12579 PNGLong(chunk+12,mng_info->ticks_per_second);
12580 PNGLong(chunk+16,0L); /* layer count=unknown */
12581 PNGLong(chunk+20,0L); /* frame count=unknown */
12582 PNGLong(chunk+24,0L); /* play time=unknown */
12587 if (need_defi || mng_info->need_fram || use_global_plte)
12588 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12591 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12596 if (need_defi || mng_info->need_fram || use_global_plte)
12597 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12600 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12608 if (need_defi || mng_info->need_fram || use_global_plte)
12609 PNGLong(chunk+28,11L); /* simplicity=LC */
12612 PNGLong(chunk+28,9L); /* simplicity=VLC */
12617 if (need_defi || mng_info->need_fram || use_global_plte)
12618 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12621 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12624 (void) WriteBlob(image,32,chunk);
12625 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12626 option=GetImageOption(image_info,"mng:need-cacheoff");
12627 if (option != (const char *) NULL)
12633 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12635 PNGType(chunk,mng_nEED);
12636 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12637 (void) WriteBlobMSBULong(image,(size_t) length);
12638 LogPNGChunk(logging,mng_nEED,(size_t) length);
12640 (void) WriteBlob(image,length,chunk);
12641 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12643 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12644 (GetNextImageInList(image) != (Image *) NULL) &&
12645 (image->iterations != 1))
12648 Write MNG TERM chunk
12650 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12651 PNGType(chunk,mng_TERM);
12652 LogPNGChunk(logging,mng_TERM,10L);
12653 chunk[4]=3; /* repeat animation */
12654 chunk[5]=0; /* show last frame when done */
12655 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12656 final_delay/MagickMax(image->ticks_per_second,1)));
12658 if (image->iterations == 0)
12659 PNGLong(chunk+10,PNG_UINT_31_MAX);
12662 PNGLong(chunk+10,(png_uint_32) image->iterations);
12664 if (logging != MagickFalse)
12666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12667 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12668 final_delay/MagickMax(image->ticks_per_second,1)));
12670 if (image->iterations == 0)
12671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12672 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12676 " Image iterations: %.20g",(double) image->iterations);
12678 (void) WriteBlob(image,14,chunk);
12679 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12682 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12684 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12685 mng_info->equal_srgbs)
12688 Write MNG sRGB chunk
12690 (void) WriteBlobMSBULong(image,1L);
12691 PNGType(chunk,mng_sRGB);
12692 LogPNGChunk(logging,mng_sRGB,1L);
12694 if (image->rendering_intent != UndefinedIntent)
12695 chunk[4]=(unsigned char)
12696 Magick_RenderingIntent_to_PNG_RenderingIntent(
12697 (image->rendering_intent));
12700 chunk[4]=(unsigned char)
12701 Magick_RenderingIntent_to_PNG_RenderingIntent(
12702 (PerceptualIntent));
12704 (void) WriteBlob(image,5,chunk);
12705 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12706 mng_info->have_write_global_srgb=MagickTrue;
12711 if (image->gamma && mng_info->equal_gammas)
12714 Write MNG gAMA chunk
12716 (void) WriteBlobMSBULong(image,4L);
12717 PNGType(chunk,mng_gAMA);
12718 LogPNGChunk(logging,mng_gAMA,4L);
12719 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12720 (void) WriteBlob(image,8,chunk);
12721 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12722 mng_info->have_write_global_gama=MagickTrue;
12724 if (mng_info->equal_chrms)
12730 Write MNG cHRM chunk
12732 (void) WriteBlobMSBULong(image,32L);
12733 PNGType(chunk,mng_cHRM);
12734 LogPNGChunk(logging,mng_cHRM,32L);
12735 primary=image->chromaticity.white_point;
12736 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12737 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12738 primary=image->chromaticity.red_primary;
12739 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12740 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12741 primary=image->chromaticity.green_primary;
12742 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12743 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12744 primary=image->chromaticity.blue_primary;
12745 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12746 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12747 (void) WriteBlob(image,36,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12749 mng_info->have_write_global_chrm=MagickTrue;
12752 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12755 Write MNG pHYs chunk
12757 (void) WriteBlobMSBULong(image,9L);
12758 PNGType(chunk,mng_pHYs);
12759 LogPNGChunk(logging,mng_pHYs,9L);
12761 if (image->units == PixelsPerInchResolution)
12763 PNGLong(chunk+4,(png_uint_32)
12764 (image->x_resolution*100.0/2.54+0.5));
12766 PNGLong(chunk+8,(png_uint_32)
12767 (image->y_resolution*100.0/2.54+0.5));
12774 if (image->units == PixelsPerCentimeterResolution)
12776 PNGLong(chunk+4,(png_uint_32)
12777 (image->x_resolution*100.0+0.5));
12779 PNGLong(chunk+8,(png_uint_32)
12780 (image->y_resolution*100.0+0.5));
12787 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12788 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
12792 (void) WriteBlob(image,13,chunk);
12793 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12796 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12797 or does not cover the entire frame.
12799 if (write_mng && (image->matte || image->page.x > 0 ||
12800 image->page.y > 0 || (image->page.width &&
12801 (image->page.width+image->page.x < mng_info->page.width))
12802 || (image->page.height && (image->page.height+image->page.y
12803 < mng_info->page.height))))
12805 (void) WriteBlobMSBULong(image,6L);
12806 PNGType(chunk,mng_BACK);
12807 LogPNGChunk(logging,mng_BACK,6L);
12808 red=ScaleQuantumToShort(image->background_color.red);
12809 green=ScaleQuantumToShort(image->background_color.green);
12810 blue=ScaleQuantumToShort(image->background_color.blue);
12811 PNGShort(chunk+4,red);
12812 PNGShort(chunk+6,green);
12813 PNGShort(chunk+8,blue);
12814 (void) WriteBlob(image,10,chunk);
12815 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12816 if (mng_info->equal_backgrounds)
12818 (void) WriteBlobMSBULong(image,6L);
12819 PNGType(chunk,mng_bKGD);
12820 LogPNGChunk(logging,mng_bKGD,6L);
12821 (void) WriteBlob(image,10,chunk);
12822 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12826 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12827 if ((need_local_plte == MagickFalse) &&
12828 (image->storage_class == PseudoClass) &&
12829 (all_images_are_gray == MagickFalse))
12835 Write MNG PLTE chunk
12837 data_length=3*image->colors;
12838 (void) WriteBlobMSBULong(image,data_length);
12839 PNGType(chunk,mng_PLTE);
12840 LogPNGChunk(logging,mng_PLTE,data_length);
12842 for (i=0; i < (ssize_t) image->colors; i++)
12844 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12845 image->colormap[i].red) & 0xff);
12846 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12847 image->colormap[i].green) & 0xff);
12848 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12849 image->colormap[i].blue) & 0xff);
12852 (void) WriteBlob(image,data_length+4,chunk);
12853 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12854 mng_info->have_write_global_plte=MagickTrue;
12860 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12861 defined(PNG_MNG_FEATURES_SUPPORTED)
12862 mng_info->equal_palettes=MagickFalse;
12866 if (mng_info->adjoin)
12868 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12869 defined(PNG_MNG_FEATURES_SUPPORTED)
12871 If we aren't using a global palette for the entire MNG, check to
12872 see if we can use one for two or more consecutive images.
12874 if (need_local_plte && use_global_plte && !all_images_are_gray)
12876 if (mng_info->IsPalette)
12879 When equal_palettes is true, this image has the same palette
12880 as the previous PseudoClass image
12882 mng_info->have_write_global_plte=mng_info->equal_palettes;
12883 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12884 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12887 Write MNG PLTE chunk
12892 data_length=3*image->colors;
12893 (void) WriteBlobMSBULong(image,data_length);
12894 PNGType(chunk,mng_PLTE);
12895 LogPNGChunk(logging,mng_PLTE,data_length);
12897 for (i=0; i < (ssize_t) image->colors; i++)
12899 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12900 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12901 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12904 (void) WriteBlob(image,data_length+4,chunk);
12905 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12906 (uInt) (data_length+4)));
12907 mng_info->have_write_global_plte=MagickTrue;
12911 mng_info->have_write_global_plte=MagickFalse;
12922 previous_x=mng_info->page.x;
12923 previous_y=mng_info->page.y;
12930 mng_info->page=image->page;
12931 if ((mng_info->page.x != previous_x) ||
12932 (mng_info->page.y != previous_y))
12934 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12935 PNGType(chunk,mng_DEFI);
12936 LogPNGChunk(logging,mng_DEFI,12L);
12937 chunk[4]=0; /* object 0 MSB */
12938 chunk[5]=0; /* object 0 LSB */
12939 chunk[6]=0; /* visible */
12940 chunk[7]=0; /* abstract */
12941 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12942 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12943 (void) WriteBlob(image,16,chunk);
12944 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12949 mng_info->write_mng=write_mng;
12951 if ((int) image->dispose >= 3)
12952 mng_info->framing_mode=3;
12954 if (mng_info->need_fram && mng_info->adjoin &&
12955 ((image->delay != mng_info->delay) ||
12956 (mng_info->framing_mode != mng_info->old_framing_mode)))
12958 if (image->delay == mng_info->delay)
12961 Write a MNG FRAM chunk with the new framing mode.
12963 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12964 PNGType(chunk,mng_FRAM);
12965 LogPNGChunk(logging,mng_FRAM,1L);
12966 chunk[4]=(unsigned char) mng_info->framing_mode;
12967 (void) WriteBlob(image,5,chunk);
12968 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12973 Write a MNG FRAM chunk with the delay.
12975 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12976 PNGType(chunk,mng_FRAM);
12977 LogPNGChunk(logging,mng_FRAM,10L);
12978 chunk[4]=(unsigned char) mng_info->framing_mode;
12979 chunk[5]=0; /* frame name separator (no name) */
12980 chunk[6]=2; /* flag for changing default delay */
12981 chunk[7]=0; /* flag for changing frame timeout */
12982 chunk[8]=0; /* flag for changing frame clipping */
12983 chunk[9]=0; /* flag for changing frame sync_id */
12984 PNGLong(chunk+10,(png_uint_32)
12985 ((mng_info->ticks_per_second*
12986 image->delay)/MagickMax(image->ticks_per_second,1)));
12987 (void) WriteBlob(image,14,chunk);
12988 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12989 mng_info->delay=(png_uint_32) image->delay;
12991 mng_info->old_framing_mode=mng_info->framing_mode;
12994 #if defined(JNG_SUPPORTED)
12995 if (image_info->compression == JPEGCompression)
13000 if (logging != MagickFalse)
13001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13002 " Writing JNG object.");
13003 /* To do: specify the desired alpha compression method. */
13004 write_info=CloneImageInfo(image_info);
13005 write_info->compression=UndefinedCompression;
13006 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13007 write_info=DestroyImageInfo(write_info);
13012 if (logging != MagickFalse)
13013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13014 " Writing PNG object.");
13016 mng_info->need_blob = MagickFalse;
13017 mng_info->ping_preserve_colormap = MagickFalse;
13019 /* We don't want any ancillary chunks written */
13020 mng_info->ping_exclude_bKGD=MagickTrue;
13021 mng_info->ping_exclude_cHRM=MagickTrue;
13022 mng_info->ping_exclude_date=MagickTrue;
13023 mng_info->ping_exclude_EXIF=MagickTrue;
13024 mng_info->ping_exclude_gAMA=MagickTrue;
13025 mng_info->ping_exclude_iCCP=MagickTrue;
13026 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13027 mng_info->ping_exclude_oFFs=MagickTrue;
13028 mng_info->ping_exclude_pHYs=MagickTrue;
13029 mng_info->ping_exclude_sRGB=MagickTrue;
13030 mng_info->ping_exclude_tEXt=MagickTrue;
13031 mng_info->ping_exclude_tRNS=MagickTrue;
13032 mng_info->ping_exclude_vpAg=MagickTrue;
13033 mng_info->ping_exclude_zCCP=MagickTrue;
13034 mng_info->ping_exclude_zTXt=MagickTrue;
13036 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13039 if (status == MagickFalse)
13041 MngInfoFreeStruct(mng_info,&have_mng_structure);
13042 (void) CloseBlob(image);
13043 return(MagickFalse);
13045 (void) CatchImageException(image);
13046 if (GetNextImageInList(image) == (Image *) NULL)
13048 image=SyncNextImageInList(image);
13049 status=SetImageProgress(image,SaveImagesTag,scene++,
13050 GetImageListLength(image));
13052 if (status == MagickFalse)
13055 } while (mng_info->adjoin);
13059 while (GetPreviousImageInList(image) != (Image *) NULL)
13060 image=GetPreviousImageInList(image);
13062 Write the MEND chunk.
13064 (void) WriteBlobMSBULong(image,0x00000000L);
13065 PNGType(chunk,mng_MEND);
13066 LogPNGChunk(logging,mng_MEND,0L);
13067 (void) WriteBlob(image,4,chunk);
13068 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13071 Relinquish resources.
13073 (void) CloseBlob(image);
13074 MngInfoFreeStruct(mng_info,&have_mng_structure);
13076 if (logging != MagickFalse)
13077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13079 return(MagickTrue);
13081 #else /* PNG_LIBPNG_VER > 10011 */
13083 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13086 printf("Your PNG library is too old: You have libpng-%s\n",
13087 PNG_LIBPNG_VER_STRING);
13089 ThrowBinaryException(CoderError,"PNG library is too old",
13090 image_info->filename);
13093 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13095 return(WritePNGImage(image_info,image));
13097 #endif /* PNG_LIBPNG_VER > 10011 */