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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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))
7637 length=(png_uint_32) GetStringInfoLength(profile);
7641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7642 " got a 3144-byte ICC profile (potentially sRGB)");
7644 data=GetStringInfoDatum(profile);
7646 if (data[52]=='s' && data[53]=='R' &&
7647 data[54]=='G' && data[55]=='B')
7649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7650 " It is the HP-Microsoft sRGB)");
7651 if (image->rendering_intent==UndefinedIntent);
7652 image->rendering_intent=PerceptualIntent;
7655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " It is not sRGB (%c%c%c%c)",data[52],
7657 data[53],data[54],data[55]);
7660 else if (length == 3052)
7662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7663 " got a 3052-byte ICC profile (potentially sRGB)");
7665 data=GetStringInfoDatum(profile);
7667 if (data[336]=='s' && data[337]=='R' &&
7668 data[338]=='G' && data[339]=='B')
7670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7671 " It is the ICC no-black sRGB)");
7672 if (image->rendering_intent==UndefinedIntent);
7673 image->rendering_intent=PerceptualIntent;
7676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7677 " It is not sRGB (%c%c%c%c)",data[52],
7678 data[53],data[54],data[55]);
7682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7683 " got a %lu-byte ICC profile",
7684 (unsigned long) length);
7687 name=GetNextImageProfile(image);
7692 number_semitransparent = 0;
7693 number_transparent = 0;
7695 if (logging != MagickFalse)
7697 if (image->storage_class == UndefinedClass)
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " storage_class=UndefinedClass");
7700 if (image->storage_class == DirectClass)
7701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7702 " storage_class=DirectClass");
7703 if (image->storage_class == PseudoClass)
7704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7705 " storage_class=PseudoClass");
7708 if (image->storage_class == PseudoClass &&
7709 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7710 (mng_info->write_png_colortype != 0 &&
7711 mng_info->write_png_colortype != 4)))
7713 (void) SyncImage(image);
7714 image->storage_class = DirectClass;
7717 if (ping_preserve_colormap == MagickFalse)
7719 if (image->storage_class != PseudoClass && image->colormap != NULL)
7721 /* Free the bogus colormap; it can cause trouble later */
7722 if (logging != MagickFalse)
7723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7724 " Freeing bogus colormap");
7725 (void *) RelinquishMagickMemory(image->colormap);
7726 image->colormap=NULL;
7730 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7731 (void) TransformImageColorspace(image,RGBColorspace);
7734 Sometimes we get PseudoClass images whose RGB values don't match
7735 the colors in the colormap. This code syncs the RGB values.
7737 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7738 (void) SyncImage(image);
7740 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7741 if (image->depth > 8)
7743 if (logging != MagickFalse)
7744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7751 /* Respect the -depth option */
7752 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7760 exception=(&image->exception);
7762 if (image->depth > 8)
7764 #if MAGICKCORE_QUANTUM_DEPTH > 16
7765 /* Scale to 16-bit */
7766 LBR16PacketRGBO(image->background_color);
7768 for (y=0; y < (ssize_t) image->rows; y++)
7770 r=GetAuthenticPixels(image,0,y,image->columns,1,
7773 if (r == (Quantum *) NULL)
7776 for (x=0; x < (ssize_t) image->columns; x++)
7782 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7786 if (image->storage_class == PseudoClass && image->colormap != NULL)
7788 for (i=0; i < (ssize_t) image->colors; i++)
7790 LBR16PacketRGBO(image->colormap[i]);
7793 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7796 else if (image->depth > 4)
7798 #if MAGICKCORE_QUANTUM_DEPTH > 8
7799 /* Scale to 8-bit */
7800 LBR08PacketRGBO(image->background_color);
7802 for (y=0; y < (ssize_t) image->rows; y++)
7804 r=GetAuthenticPixels(image,0,y,image->columns,1,
7807 if (r == (Quantum *) NULL)
7810 for (x=0; x < (ssize_t) image->columns; x++)
7816 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7820 if (image->storage_class == PseudoClass && image->colormap != NULL)
7822 for (i=0; i < (ssize_t) image->colors; i++)
7824 LBR08PacketRGBO(image->colormap[i]);
7827 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7830 if (image->depth > 2)
7832 /* Scale to 4-bit */
7833 LBR04PacketRGBO(image->background_color);
7835 for (y=0; y < (ssize_t) image->rows; y++)
7837 r=GetAuthenticPixels(image,0,y,image->columns,1,
7840 if (r == (Quantum *) NULL)
7843 for (x=0; x < (ssize_t) image->columns; x++)
7849 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7853 if (image->storage_class == PseudoClass && image->colormap != NULL)
7855 for (i=0; i < (ssize_t) image->colors; i++)
7857 LBR04PacketRGBO(image->colormap[i]);
7862 else if (image->depth > 1)
7864 /* Scale to 2-bit */
7865 LBR02PacketRGBO(image->background_color);
7867 for (y=0; y < (ssize_t) image->rows; y++)
7869 r=GetAuthenticPixels(image,0,y,image->columns,1,
7872 if (r == (Quantum *) NULL)
7875 for (x=0; x < (ssize_t) image->columns; x++)
7881 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7885 if (image->storage_class == PseudoClass && image->colormap != NULL)
7887 for (i=0; i < (ssize_t) image->colors; i++)
7889 LBR02PacketRGBO(image->colormap[i]);
7895 /* Scale to 1-bit */
7896 LBR01PacketRGBO(image->background_color);
7898 for (y=0; y < (ssize_t) image->rows; y++)
7900 r=GetAuthenticPixels(image,0,y,image->columns,1,
7903 if (r == (Quantum *) NULL)
7906 for (x=0; x < (ssize_t) image->columns; x++)
7912 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7916 if (image->storage_class == PseudoClass && image->colormap != NULL)
7918 for (i=0; i < (ssize_t) image->colors; i++)
7920 LBR01PacketRGBO(image->colormap[i]);
7926 /* To do: set to next higher multiple of 8 */
7927 if (image->depth < 8)
7930 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7931 /* PNG does not handle depths greater than 16 so reduce it even
7934 if (image->depth > 8)
7938 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7939 if (image->depth == 16 && mng_info->write_png_depth != 16)
7940 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7944 /* Normally we run this just once, but in the case of writing PNG8
7945 * we reduce the transparency to binary and run again, then if there
7946 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7947 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7948 * palette. Then (To do) we take care of a final reduction that is only
7949 * needed if there are still 256 colors present and one of them has both
7950 * transparent and opaque instances.
7953 tried_332 = MagickFalse;
7954 tried_333 = MagickFalse;
7955 tried_444 = MagickFalse;
7961 * Sometimes we get DirectClass images that have 256 colors or fewer.
7962 * This code will build a colormap.
7964 * Also, sometimes we get PseudoClass images with an out-of-date
7965 * colormap. This code will replace the colormap with a new one.
7966 * Sometimes we get PseudoClass images that have more than 256 colors.
7967 * This code will delete the colormap and change the image to
7970 * If image->matte is MagickFalse, we ignore the alpha channel
7971 * even though it sometimes contains left-over non-opaque values.
7973 * Also we gather some information (number of opaque, transparent,
7974 * and semitransparent pixels, and whether the image has any non-gray
7975 * pixels or only black-and-white pixels) that we might need later.
7977 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7978 * we need to check for bogus non-opaque values, at least.
7989 semitransparent[260],
7992 register const Quantum
7999 if (logging != MagickFalse)
8000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8001 " Enter BUILD_PALETTE:");
8003 if (logging != MagickFalse)
8005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8006 " image->columns=%.20g",(double) image->columns);
8007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8008 " image->rows=%.20g",(double) image->rows);
8009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8010 " image->matte=%.20g",(double) image->matte);
8011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8012 " image->depth=%.20g",(double) image->depth);
8014 if (image->storage_class == PseudoClass && image->colormap != NULL)
8016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8017 " Original colormap:");
8018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8019 " i (red,green,blue,alpha)");
8021 for (i=0; i < 256; i++)
8023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8024 " %d (%d,%d,%d,%d)",
8026 (int) image->colormap[i].red,
8027 (int) image->colormap[i].green,
8028 (int) image->colormap[i].blue,
8029 (int) image->colormap[i].alpha);
8032 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8037 " %d (%d,%d,%d,%d)",
8039 (int) image->colormap[i].red,
8040 (int) image->colormap[i].green,
8041 (int) image->colormap[i].blue,
8042 (int) image->colormap[i].alpha);
8047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " image->colors=%d",(int) image->colors);
8050 if (image->colors == 0)
8051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8052 " (zero means unknown)");
8054 if (ping_preserve_colormap == MagickFalse)
8055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056 " Regenerate the colormap");
8059 exception=(&image->exception);
8063 number_semitransparent = 0;
8064 number_transparent = 0;
8066 for (y=0; y < (ssize_t) image->rows; y++)
8068 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8070 if (q == (Quantum *) NULL)
8073 for (x=0; x < (ssize_t) image->columns; x++)
8075 if (image->matte == MagickFalse ||
8076 GetPixelAlpha(image,q) == OpaqueAlpha)
8078 if (number_opaque < 259)
8080 if (number_opaque == 0)
8082 GetPixelPacket(image, q, opaque);
8083 opaque[0].alpha=OpaqueAlpha;
8087 for (i=0; i< (ssize_t) number_opaque; i++)
8089 if (IsPixelEquivalent(image,q, opaque+i))
8093 if (i == (ssize_t) number_opaque &&
8094 number_opaque < 259)
8097 GetPixelPacket(image, q, opaque+i);
8098 opaque[i].alpha=OpaqueAlpha;
8102 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8104 if (number_transparent < 259)
8106 if (number_transparent == 0)
8108 GetPixelPacket(image, q, transparent);
8109 ping_trans_color.red=(unsigned short)
8110 GetPixelRed(image,q);
8111 ping_trans_color.green=(unsigned short)
8112 GetPixelGreen(image,q);
8113 ping_trans_color.blue=(unsigned short)
8114 GetPixelBlue(image,q);
8115 ping_trans_color.gray=(unsigned short)
8116 GetPixelRed(image,q);
8117 number_transparent = 1;
8120 for (i=0; i< (ssize_t) number_transparent; i++)
8122 if (IsPixelEquivalent(image,q, transparent+i))
8126 if (i == (ssize_t) number_transparent &&
8127 number_transparent < 259)
8129 number_transparent++;
8130 GetPixelPacket(image,q,transparent+i);
8136 if (number_semitransparent < 259)
8138 if (number_semitransparent == 0)
8140 GetPixelPacket(image,q,semitransparent);
8141 number_semitransparent = 1;
8144 for (i=0; i< (ssize_t) number_semitransparent; i++)
8146 if (IsPixelEquivalent(image,q, semitransparent+i)
8147 && GetPixelAlpha(image,q) ==
8148 semitransparent[i].alpha)
8152 if (i == (ssize_t) number_semitransparent &&
8153 number_semitransparent < 259)
8155 number_semitransparent++;
8156 GetPixelPacket(image, q, semitransparent+i);
8160 q+=GetPixelChannels(image);
8164 if (mng_info->write_png8 == MagickFalse &&
8165 ping_exclude_bKGD == MagickFalse)
8167 /* Add the background color to the palette, if it
8168 * isn't already there.
8170 if (logging != MagickFalse)
8172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8173 " Check colormap for background (%d,%d,%d)",
8174 (int) image->background_color.red,
8175 (int) image->background_color.green,
8176 (int) image->background_color.blue);
8178 for (i=0; i<number_opaque; i++)
8180 if (opaque[i].red == image->background_color.red &&
8181 opaque[i].green == image->background_color.green &&
8182 opaque[i].blue == image->background_color.blue)
8185 if (number_opaque < 259 && i == number_opaque)
8187 opaque[i] = image->background_color;
8188 ping_background.index = i;
8189 if (logging != MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " background_color index is %d",(int) i);
8196 else if (logging != MagickFalse)
8197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " No room in the colormap to add background color");
8201 image_colors=number_opaque+number_transparent+number_semitransparent;
8203 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8205 /* No room for the background color; remove it. */
8210 if (logging != MagickFalse)
8212 if (image_colors > 256)
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " image has more than 256 colors");
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " image has %d colors",image_colors);
8221 if (ping_preserve_colormap != MagickFalse)
8224 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8226 ping_have_color=MagickFalse;
8227 ping_have_non_bw=MagickFalse;
8229 if(image_colors > 256)
8231 for (y=0; y < (ssize_t) image->rows; y++)
8233 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8235 if (q == (Quantum *) NULL)
8239 for (x=0; x < (ssize_t) image->columns; x++)
8241 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8242 GetPixelRed(image,s) != GetPixelBlue(image,s))
8244 ping_have_color=MagickTrue;
8245 ping_have_non_bw=MagickTrue;
8248 s+=GetPixelChannels(image);
8251 if (ping_have_color != MagickFalse)
8254 /* Worst case is black-and-white; we are looking at every
8258 if (ping_have_non_bw == MagickFalse)
8261 for (x=0; x < (ssize_t) image->columns; x++)
8263 if (GetPixelRed(image,s) != 0 &&
8264 GetPixelRed(image,s) != QuantumRange)
8266 ping_have_non_bw=MagickTrue;
8269 s+=GetPixelChannels(image);
8276 if (image_colors < 257)
8282 * Initialize image colormap.
8285 if (logging != MagickFalse)
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " Sort the new colormap");
8289 /* Sort palette, transparent first */;
8293 for (i=0; i<number_transparent; i++)
8294 colormap[n++] = transparent[i];
8296 for (i=0; i<number_semitransparent; i++)
8297 colormap[n++] = semitransparent[i];
8299 for (i=0; i<number_opaque; i++)
8300 colormap[n++] = opaque[i];
8302 ping_background.index +=
8303 (number_transparent + number_semitransparent);
8305 /* image_colors < 257; search the colormap instead of the pixels
8306 * to get ping_have_color and ping_have_non_bw
8310 if (ping_have_color == MagickFalse)
8312 if (colormap[i].red != colormap[i].green ||
8313 colormap[i].red != colormap[i].blue)
8315 ping_have_color=MagickTrue;
8316 ping_have_non_bw=MagickTrue;
8321 if (ping_have_non_bw == MagickFalse)
8323 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8324 ping_have_non_bw=MagickTrue;
8328 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8329 (number_transparent == 0 && number_semitransparent == 0)) &&
8330 (((mng_info->write_png_colortype-1) ==
8331 PNG_COLOR_TYPE_PALETTE) ||
8332 (mng_info->write_png_colortype == 0)))
8334 if (logging != MagickFalse)
8336 if (n != (ssize_t) image_colors)
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " image_colors (%d) and n (%d) don't match",
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " AcquireImageColormap");
8345 image->colors = image_colors;
8347 if (AcquireImageColormap(image,image_colors,exception) ==
8349 ThrowWriterException(ResourceLimitError,
8350 "MemoryAllocationFailed");
8352 for (i=0; i< (ssize_t) image_colors; i++)
8353 image->colormap[i] = colormap[i];
8355 if (logging != MagickFalse)
8357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " image->colors=%d (%d)",
8359 (int) image->colors, image_colors);
8361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " Update the pixel indexes");
8365 /* Sync the pixel indices with the new colormap */
8367 for (y=0; y < (ssize_t) image->rows; y++)
8369 q=GetAuthenticPixels(image,0,y,image->columns,1,
8372 if (q == (Quantum *) NULL)
8376 for (x=0; x < (ssize_t) image->columns; x++)
8378 for (i=0; i< (ssize_t) image_colors; i++)
8380 if ((image->matte == MagickFalse ||
8381 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8382 image->colormap[i].red == GetPixelRed(image,q) &&
8383 image->colormap[i].green == GetPixelGreen(image,q) &&
8384 image->colormap[i].blue == GetPixelBlue(image,q))
8386 SetPixelIndex(image,i,q);
8390 q+=GetPixelChannels(image);
8393 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8399 if (logging != MagickFalse)
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " image->colors=%d", (int) image->colors);
8404 if (image->colormap != NULL)
8406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8407 " i (red,green,blue,alpha)");
8409 for (i=0; i < (ssize_t) image->colors; i++)
8411 if (i < 300 || i >= (ssize_t) image->colors - 10)
8413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8414 " %d (%d,%d,%d,%d)",
8416 (int) image->colormap[i].red,
8417 (int) image->colormap[i].green,
8418 (int) image->colormap[i].blue,
8419 (int) image->colormap[i].alpha);
8424 if (number_transparent < 257)
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " number_transparent = %d",
8427 number_transparent);
8430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8431 " number_transparent > 256");
8433 if (number_opaque < 257)
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " number_opaque = %d",
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8440 " number_opaque > 256");
8442 if (number_semitransparent < 257)
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " number_semitransparent = %d",
8445 number_semitransparent);
8448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8449 " number_semitransparent > 256");
8451 if (ping_have_non_bw == MagickFalse)
8452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8453 " All pixels and the background are black or white");
8455 else if (ping_have_color == MagickFalse)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " All pixels and the background are gray");
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " At least one pixel or the background is non-gray");
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " Exit BUILD_PALETTE:");
8467 if (mng_info->write_png8 == MagickFalse)
8470 /* Make any reductions necessary for the PNG8 format */
8471 if (image_colors <= 256 &&
8472 image_colors != 0 && image->colormap != NULL &&
8473 number_semitransparent == 0 &&
8474 number_transparent <= 1)
8477 /* PNG8 can't have semitransparent colors so we threshold the
8478 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8479 * transparent color so if more than one is transparent we merge
8480 * them into image->background_color.
8482 if (number_semitransparent != 0 || number_transparent > 1)
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " Thresholding the alpha channel to binary");
8487 for (y=0; y < (ssize_t) image->rows; y++)
8489 r=GetAuthenticPixels(image,0,y,image->columns,1,
8492 if (r == (Quantum *) NULL)
8495 for (x=0; x < (ssize_t) image->columns; x++)
8497 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8499 SetPixelPacket(image,&image->background_color,r);
8500 SetPixelAlpha(image,TransparentAlpha,r);
8503 SetPixelAlpha(image,OpaqueAlpha,r);
8504 r+=GetPixelChannels(image);
8507 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8510 if (image_colors != 0 && image_colors <= 256 &&
8511 image->colormap != NULL)
8512 for (i=0; i<image_colors; i++)
8513 image->colormap[i].alpha =
8514 (image->colormap[i].alpha > TransparentAlpha/2 ?
8515 TransparentAlpha : OpaqueAlpha);
8520 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8521 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8522 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8525 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8527 if (logging != MagickFalse)
8528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8529 " Quantizing the background color to 4-4-4");
8531 tried_444 = MagickTrue;
8533 LBR04PacketRGB(image->background_color);
8535 if (logging != MagickFalse)
8536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8537 " Quantizing the pixel colors to 4-4-4");
8539 if (image->colormap == NULL)
8541 for (y=0; y < (ssize_t) image->rows; y++)
8543 r=GetAuthenticPixels(image,0,y,image->columns,1,
8546 if (r == (Quantum *) NULL)
8549 for (x=0; x < (ssize_t) image->columns; x++)
8551 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8556 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8561 else /* Should not reach this; colormap already exists and
8564 if (logging != MagickFalse)
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Quantizing the colormap to 4-4-4");
8568 for (i=0; i<image_colors; i++)
8570 LBR04PacketRGB(image->colormap[i]);
8576 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8578 if (logging != MagickFalse)
8579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8580 " Quantizing the background color to 3-3-3");
8582 tried_333 = MagickTrue;
8584 LBR03PacketRGB(image->background_color);
8586 if (logging != MagickFalse)
8587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8588 " Quantizing the pixel colors to 3-3-3-1");
8590 if (image->colormap == NULL)
8592 for (y=0; y < (ssize_t) image->rows; y++)
8594 r=GetAuthenticPixels(image,0,y,image->columns,1,
8597 if (r == (Quantum *) NULL)
8600 for (x=0; x < (ssize_t) image->columns; x++)
8602 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8607 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8612 else /* Should not reach this; colormap already exists and
8615 if (logging != MagickFalse)
8616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8617 " Quantizing the colormap to 3-3-3-1");
8618 for (i=0; i<image_colors; i++)
8620 LBR03PacketRGB(image->colormap[i]);
8626 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8628 if (logging != MagickFalse)
8629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8630 " Quantizing the background color to 3-3-2");
8632 tried_332 = MagickTrue;
8634 /* Red and green were already done so we only quantize the blue
8638 LBR02PacketBlue(image->background_color);
8640 if (logging != MagickFalse)
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642 " Quantizing the pixel colors to 3-3-2-1");
8644 if (image->colormap == NULL)
8646 for (y=0; y < (ssize_t) image->rows; y++)
8648 r=GetAuthenticPixels(image,0,y,image->columns,1,
8651 if (r == (Quantum *) NULL)
8654 for (x=0; x < (ssize_t) image->columns; x++)
8656 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8661 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8666 else /* Should not reach this; colormap already exists and
8669 if (logging != MagickFalse)
8670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671 " Quantizing the colormap to 3-3-2-1");
8672 for (i=0; i<image_colors; i++)
8674 LBR02PacketBlue(image->colormap[i]);
8681 if (image_colors == 0 || image_colors > 256)
8683 /* Take care of special case with 256 colors + 1 transparent
8684 * color. We don't need to quantize to 2-3-2-1; we only need to
8685 * eliminate one color, so we'll merge the two darkest red
8686 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8688 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8689 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8690 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8692 image->background_color.red=ScaleCharToQuantum(0x24);
8695 if (image->colormap == NULL)
8697 for (y=0; y < (ssize_t) image->rows; y++)
8699 r=GetAuthenticPixels(image,0,y,image->columns,1,
8702 if (r == (Quantum *) NULL)
8705 for (x=0; x < (ssize_t) image->columns; x++)
8707 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8708 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8709 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8710 GetPixelAlpha(image,r) == OpaqueAlpha)
8712 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8714 r+=GetPixelChannels(image);
8717 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8725 for (i=0; i<image_colors; i++)
8727 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8728 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8729 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8731 image->colormap[i].red=ScaleCharToQuantum(0x24);
8737 /* END OF BUILD_PALETTE */
8739 /* If we are excluding the tRNS chunk and there is transparency,
8740 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8743 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8744 (number_transparent != 0 || number_semitransparent != 0))
8746 unsigned int colortype=mng_info->write_png_colortype;
8748 if (ping_have_color == MagickFalse)
8749 mng_info->write_png_colortype = 5;
8752 mng_info->write_png_colortype = 7;
8754 if (colortype != 0 &&
8755 mng_info->write_png_colortype != colortype)
8756 ping_need_colortype_warning=MagickTrue;
8760 /* See if cheap transparency is possible. It is only possible
8761 * when there is a single transparent color, no semitransparent
8762 * color, and no opaque color that has the same RGB components
8763 * as the transparent color. We only need this information if
8764 * we are writing a PNG with colortype 0 or 2, and we have not
8765 * excluded the tRNS chunk.
8767 if (number_transparent == 1 &&
8768 mng_info->write_png_colortype < 4)
8770 ping_have_cheap_transparency = MagickTrue;
8772 if (number_semitransparent != 0)
8773 ping_have_cheap_transparency = MagickFalse;
8775 else if (image_colors == 0 || image_colors > 256 ||
8776 image->colormap == NULL)
8781 register const Quantum
8784 exception=(&image->exception);
8786 for (y=0; y < (ssize_t) image->rows; y++)
8788 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8790 if (q == (Quantum *) NULL)
8793 for (x=0; x < (ssize_t) image->columns; x++)
8795 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8796 (unsigned short) GetPixelRed(image,q) ==
8797 ping_trans_color.red &&
8798 (unsigned short) GetPixelGreen(image,q) ==
8799 ping_trans_color.green &&
8800 (unsigned short) GetPixelBlue(image,q) ==
8801 ping_trans_color.blue)
8803 ping_have_cheap_transparency = MagickFalse;
8807 q+=GetPixelChannels(image);
8810 if (ping_have_cheap_transparency == MagickFalse)
8816 /* Assuming that image->colormap[0] is the one transparent color
8817 * and that all others are opaque.
8819 if (image_colors > 1)
8820 for (i=1; i<image_colors; i++)
8821 if (image->colormap[i].red == image->colormap[0].red &&
8822 image->colormap[i].green == image->colormap[0].green &&
8823 image->colormap[i].blue == image->colormap[0].blue)
8825 ping_have_cheap_transparency = MagickFalse;
8830 if (logging != MagickFalse)
8832 if (ping_have_cheap_transparency == MagickFalse)
8833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8834 " Cheap transparency is not possible.");
8837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8838 " Cheap transparency is possible.");
8842 ping_have_cheap_transparency = MagickFalse;
8844 image_depth=image->depth;
8846 quantum_info = (QuantumInfo *) NULL;
8848 image_colors=(int) image->colors;
8849 image_matte=image->matte;
8851 mng_info->IsPalette=image->storage_class == PseudoClass &&
8852 image_colors <= 256 && image->colormap != NULL;
8854 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8855 (image->colors == 0 || image->colormap == NULL))
8857 image_info=DestroyImageInfo(image_info);
8858 image=DestroyImage(image);
8859 (void) ThrowMagickException(&IMimage->exception,
8860 GetMagickModule(),CoderError,
8861 "Cannot write PNG8 or color-type 3; colormap is NULL",
8862 "`%s'",IMimage->filename);
8863 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8864 UnlockSemaphoreInfo(ping_semaphore);
8866 return(MagickFalse);
8870 Allocate the PNG structures
8872 #ifdef PNG_USER_MEM_SUPPORTED
8873 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8874 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8875 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8878 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8879 MagickPNGErrorHandler,MagickPNGWarningHandler);
8882 if (ping == (png_struct *) NULL)
8883 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8885 ping_info=png_create_info_struct(ping);
8887 if (ping_info == (png_info *) NULL)
8889 png_destroy_write_struct(&ping,(png_info **) NULL);
8890 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8893 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8894 ping_pixels=(unsigned char *) NULL;
8896 if (setjmp(png_jmpbuf(ping)))
8902 if (image_info->verbose)
8903 (void) printf("PNG write has failed.\n");
8905 png_destroy_write_struct(&ping,&ping_info);
8906 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8907 UnlockSemaphoreInfo(ping_semaphore);
8909 if (ping_have_blob != MagickFalse)
8910 (void) CloseBlob(image);
8911 image_info=DestroyImageInfo(image_info);
8912 image=DestroyImage(image);
8913 return(MagickFalse);
8916 Prepare PNG for writing.
8918 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8919 if (mng_info->write_mng)
8920 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8923 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8924 if (mng_info->write_mng)
8925 png_permit_empty_plte(ping,MagickTrue);
8932 ping_width=(png_uint_32) image->columns;
8933 ping_height=(png_uint_32) image->rows;
8935 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8938 if (mng_info->write_png_depth != 0)
8939 image_depth=mng_info->write_png_depth;
8941 /* Adjust requested depth to next higher valid depth if necessary */
8942 if (image_depth > 8)
8945 if ((image_depth > 4) && (image_depth < 8))
8948 if (image_depth == 3)
8951 if (logging != MagickFalse)
8953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8954 " width=%.20g",(double) ping_width);
8955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8956 " height=%.20g",(double) ping_height);
8957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8958 " image_matte=%.20g",(double) image->matte);
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960 " image->depth=%.20g",(double) image->depth);
8961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8962 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8965 save_image_depth=image_depth;
8966 ping_bit_depth=(png_byte) save_image_depth;
8969 #if defined(PNG_pHYs_SUPPORTED)
8970 if (ping_exclude_pHYs == MagickFalse)
8972 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8973 (!mng_info->write_mng || !mng_info->equal_physs))
8975 if (logging != MagickFalse)
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " Setting up pHYs chunk");
8979 if (image->units == PixelsPerInchResolution)
8981 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8982 ping_pHYs_x_resolution=
8983 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8984 ping_pHYs_y_resolution=
8985 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8988 else if (image->units == PixelsPerCentimeterResolution)
8990 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8991 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8992 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8997 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8998 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8999 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
9002 if (logging != MagickFalse)
9003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9005 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9006 (int) ping_pHYs_unit_type);
9007 ping_have_pHYs = MagickTrue;
9012 if (ping_exclude_bKGD == MagickFalse)
9014 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9020 if (ping_bit_depth == 8)
9023 if (ping_bit_depth == 4)
9026 if (ping_bit_depth == 2)
9029 if (ping_bit_depth == 1)
9032 ping_background.red=(png_uint_16)
9033 (ScaleQuantumToShort(image->background_color.red) & mask);
9035 ping_background.green=(png_uint_16)
9036 (ScaleQuantumToShort(image->background_color.green) & mask);
9038 ping_background.blue=(png_uint_16)
9039 (ScaleQuantumToShort(image->background_color.blue) & mask);
9041 ping_background.gray=(png_uint_16) ping_background.green;
9044 if (logging != MagickFalse)
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " Setting up bKGD chunk (1)");
9048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " background_color index is %d",
9050 (int) ping_background.index);
9052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053 " ping_bit_depth=%d",ping_bit_depth);
9056 ping_have_bKGD = MagickTrue;
9060 Select the color type.
9065 if (mng_info->IsPalette && mng_info->write_png8)
9068 /* To do: make this a function cause it's used twice, except
9069 for reducing the sample depth from 8. */
9071 number_colors=image_colors;
9073 ping_have_tRNS=MagickFalse;
9078 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9080 if (logging != MagickFalse)
9081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9082 " Setting up PLTE chunk with %d colors (%d)",
9083 number_colors, image_colors);
9085 for (i=0; i < (ssize_t) number_colors; i++)
9087 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9088 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9089 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9090 if (logging != MagickFalse)
9091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9092 #if MAGICKCORE_QUANTUM_DEPTH == 8
9093 " %3ld (%3d,%3d,%3d)",
9095 " %5ld (%5d,%5d,%5d)",
9097 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9101 ping_have_PLTE=MagickTrue;
9102 image_depth=ping_bit_depth;
9105 if (matte != MagickFalse)
9108 Identify which colormap entry is transparent.
9110 assert(number_colors <= 256);
9111 assert(image->colormap != NULL);
9113 for (i=0; i < (ssize_t) number_transparent; i++)
9114 ping_trans_alpha[i]=0;
9117 ping_num_trans=(unsigned short) (number_transparent +
9118 number_semitransparent);
9120 if (ping_num_trans == 0)
9121 ping_have_tRNS=MagickFalse;
9124 ping_have_tRNS=MagickTrue;
9127 if (ping_exclude_bKGD == MagickFalse)
9130 * Identify which colormap entry is the background color.
9133 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9134 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9137 ping_background.index=(png_byte) i;
9139 if (logging != MagickFalse)
9141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9142 " background_color index is %d",
9143 (int) ping_background.index);
9146 } /* end of write_png8 */
9148 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9150 image_matte=MagickFalse;
9151 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9154 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9156 image_matte=MagickTrue;
9157 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9160 else /* mng_info->write_pngNN not specified */
9162 image_depth=ping_bit_depth;
9164 if (mng_info->write_png_colortype != 0)
9166 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9168 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9169 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9170 image_matte=MagickTrue;
9173 image_matte=MagickFalse;
9175 if (logging != MagickFalse)
9176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 " PNG colortype %d was specified:",(int) ping_color_type);
9180 else /* write_png_colortype not specified */
9182 if (logging != MagickFalse)
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184 " Selecting PNG colortype:");
9186 ping_color_type=(png_byte) ((matte != MagickFalse)?
9187 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9189 if (image_info->type == TrueColorType)
9191 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9192 image_matte=MagickFalse;
9195 if (image_info->type == TrueColorMatteType)
9197 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9198 image_matte=MagickTrue;
9201 if (image_info->type == PaletteType ||
9202 image_info->type == PaletteMatteType)
9203 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9205 if (mng_info->write_png_colortype == 0 &&
9206 (image_info->type == UndefinedType ||
9207 image_info->type == OptimizeType))
9209 if (ping_have_color == MagickFalse)
9211 if (image_matte == MagickFalse)
9213 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9214 image_matte=MagickFalse;
9219 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9220 image_matte=MagickTrue;
9225 if (image_matte == MagickFalse)
9227 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9228 image_matte=MagickFalse;
9233 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9234 image_matte=MagickTrue;
9241 if (logging != MagickFalse)
9242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9243 " Selected PNG colortype=%d",ping_color_type);
9245 if (ping_bit_depth < 8)
9247 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9248 ping_color_type == PNG_COLOR_TYPE_RGB ||
9249 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9253 old_bit_depth=ping_bit_depth;
9255 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9257 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9261 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9266 if (image->colors == 0)
9269 (void) ThrowMagickException(&image->exception,
9270 GetMagickModule(),CoderError,
9271 "image has 0 colors", "`%s'","");
9274 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9275 ping_bit_depth <<= 1;
9278 if (logging != MagickFalse)
9280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9281 " Number of colors: %.20g",(double) image_colors);
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " Tentative PNG bit depth: %d",ping_bit_depth);
9287 if (ping_bit_depth < (int) mng_info->write_png_depth)
9288 ping_bit_depth = mng_info->write_png_depth;
9291 image_depth=ping_bit_depth;
9293 if (logging != MagickFalse)
9295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9296 " Tentative PNG color type: %.20g",(double) ping_color_type);
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " image_info->type: %.20g",(double) image_info->type);
9301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302 " image_depth: %.20g",(double) image_depth);
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306 " image->depth: %.20g",(double) image->depth);
9308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9309 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9312 if (matte != MagickFalse)
9314 if (mng_info->IsPalette)
9316 if (mng_info->write_png_colortype == 0)
9318 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9320 if (ping_have_color != MagickFalse)
9321 ping_color_type=PNG_COLOR_TYPE_RGBA;
9325 * Determine if there is any transparent color.
9327 if (number_transparent + number_semitransparent == 0)
9330 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9333 image_matte=MagickFalse;
9335 if (mng_info->write_png_colortype == 0)
9336 ping_color_type&=0x03;
9346 if (ping_bit_depth == 8)
9349 if (ping_bit_depth == 4)
9352 if (ping_bit_depth == 2)
9355 if (ping_bit_depth == 1)
9358 ping_trans_color.red=(png_uint_16)
9359 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9361 ping_trans_color.green=(png_uint_16)
9362 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9364 ping_trans_color.blue=(png_uint_16)
9365 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9367 ping_trans_color.gray=(png_uint_16)
9368 (ScaleQuantumToShort(GetPixelPacketIntensity(
9369 image->colormap)) & mask);
9371 ping_trans_color.index=(png_byte) 0;
9373 ping_have_tRNS=MagickTrue;
9376 if (ping_have_tRNS != MagickFalse)
9379 * Determine if there is one and only one transparent color
9380 * and if so if it is fully transparent.
9382 if (ping_have_cheap_transparency == MagickFalse)
9383 ping_have_tRNS=MagickFalse;
9386 if (ping_have_tRNS != MagickFalse)
9388 if (mng_info->write_png_colortype == 0)
9389 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9391 if (image_depth == 8)
9393 ping_trans_color.red&=0xff;
9394 ping_trans_color.green&=0xff;
9395 ping_trans_color.blue&=0xff;
9396 ping_trans_color.gray&=0xff;
9402 if (image_depth == 8)
9404 ping_trans_color.red&=0xff;
9405 ping_trans_color.green&=0xff;
9406 ping_trans_color.blue&=0xff;
9407 ping_trans_color.gray&=0xff;
9414 if (ping_have_tRNS != MagickFalse)
9415 image_matte=MagickFalse;
9417 if ((mng_info->IsPalette) &&
9418 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9419 ping_have_color == MagickFalse &&
9420 (image_matte == MagickFalse || image_depth >= 8))
9424 if (image_matte != MagickFalse)
9425 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9427 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9429 ping_color_type=PNG_COLOR_TYPE_GRAY;
9431 if (save_image_depth == 16 && image_depth == 8)
9433 if (logging != MagickFalse)
9435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436 " Scaling ping_trans_color (0)");
9438 ping_trans_color.gray*=0x0101;
9442 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9443 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9445 if ((image_colors == 0) ||
9446 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9447 image_colors=(int) (one << image_depth);
9449 if (image_depth > 8)
9455 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9457 if(!mng_info->write_png_depth)
9461 while ((int) (one << ping_bit_depth)
9462 < (ssize_t) image_colors)
9463 ping_bit_depth <<= 1;
9467 else if (ping_color_type ==
9468 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9469 mng_info->IsPalette)
9471 /* Check if grayscale is reducible */
9474 depth_4_ok=MagickTrue,
9475 depth_2_ok=MagickTrue,
9476 depth_1_ok=MagickTrue;
9478 for (i=0; i < (ssize_t) image_colors; i++)
9483 intensity=ScaleQuantumToChar(image->colormap[i].red);
9485 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9486 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9487 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9488 depth_2_ok=depth_1_ok=MagickFalse;
9489 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9490 depth_1_ok=MagickFalse;
9493 if (depth_1_ok && mng_info->write_png_depth <= 1)
9496 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9499 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9504 image_depth=ping_bit_depth;
9509 if (mng_info->IsPalette)
9511 number_colors=image_colors;
9513 if (image_depth <= 8)
9518 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9520 if (mng_info->have_write_global_plte && matte == MagickFalse)
9522 png_set_PLTE(ping,ping_info,NULL,0);
9524 if (logging != MagickFalse)
9525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9526 " Setting up empty PLTE chunk");
9531 for (i=0; i < (ssize_t) number_colors; i++)
9533 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9534 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9535 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9538 if (logging != MagickFalse)
9539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9540 " Setting up PLTE chunk with %d colors",
9543 ping_have_PLTE=MagickTrue;
9546 /* color_type is PNG_COLOR_TYPE_PALETTE */
9547 if (mng_info->write_png_depth == 0)
9555 while ((one << ping_bit_depth) < (size_t) number_colors)
9556 ping_bit_depth <<= 1;
9561 if (matte != MagickFalse)
9564 * Set up trans_colors array.
9566 assert(number_colors <= 256);
9568 ping_num_trans=(unsigned short) (number_transparent +
9569 number_semitransparent);
9571 if (ping_num_trans == 0)
9572 ping_have_tRNS=MagickFalse;
9576 if (logging != MagickFalse)
9578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579 " Scaling ping_trans_color (1)");
9581 ping_have_tRNS=MagickTrue;
9583 for (i=0; i < ping_num_trans; i++)
9585 ping_trans_alpha[i]= (png_byte)
9586 ScaleQuantumToChar(image->colormap[i].alpha);
9596 if (image_depth < 8)
9599 if ((save_image_depth == 16) && (image_depth == 8))
9601 if (logging != MagickFalse)
9603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " Scaling ping_trans_color from (%d,%d,%d)",
9605 (int) ping_trans_color.red,
9606 (int) ping_trans_color.green,
9607 (int) ping_trans_color.blue);
9610 ping_trans_color.red*=0x0101;
9611 ping_trans_color.green*=0x0101;
9612 ping_trans_color.blue*=0x0101;
9613 ping_trans_color.gray*=0x0101;
9615 if (logging != MagickFalse)
9617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 (int) ping_trans_color.red,
9620 (int) ping_trans_color.green,
9621 (int) ping_trans_color.blue);
9626 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9627 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9630 Adjust background and transparency samples in sub-8-bit grayscale files.
9632 if (ping_bit_depth < 8 && ping_color_type ==
9633 PNG_COLOR_TYPE_GRAY)
9641 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9643 if (ping_exclude_bKGD == MagickFalse)
9646 ping_background.gray=(png_uint_16)
9647 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9650 if (logging != MagickFalse)
9651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9652 " Setting up bKGD chunk (2)");
9653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9654 " background_color index is %d",
9655 (int) ping_background.index);
9657 ping_have_bKGD = MagickTrue;
9660 if (logging != MagickFalse)
9661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9662 " Scaling ping_trans_color.gray from %d",
9663 (int)ping_trans_color.gray);
9665 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9666 ping_trans_color.gray)+.5);
9668 if (logging != MagickFalse)
9669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9670 " to %d", (int)ping_trans_color.gray);
9673 if (ping_exclude_bKGD == MagickFalse)
9675 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9678 Identify which colormap entry is the background color.
9681 number_colors=image_colors;
9683 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9684 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9687 ping_background.index=(png_byte) i;
9689 if (logging != MagickFalse)
9691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9692 " Setting up bKGD chunk with index=%d",(int) i);
9695 if (i < (ssize_t) number_colors)
9697 ping_have_bKGD = MagickTrue;
9699 if (logging != MagickFalse)
9701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9702 " background =(%d,%d,%d)",
9703 (int) ping_background.red,
9704 (int) ping_background.green,
9705 (int) ping_background.blue);
9709 else /* Can't happen */
9711 if (logging != MagickFalse)
9712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9713 " No room in PLTE to add bKGD color");
9714 ping_have_bKGD = MagickFalse;
9719 if (logging != MagickFalse)
9720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721 " PNG color type: %d",ping_color_type);
9723 Initialize compression level and filtering.
9725 if (logging != MagickFalse)
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9728 " Setting up deflate compression");
9730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9731 " Compression buffer size: 32768");
9734 png_set_compression_buffer_size(ping,32768L);
9736 if (logging != MagickFalse)
9737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " Compression mem level: 9");
9740 png_set_compression_mem_level(ping, 9);
9742 /* Untangle the "-quality" setting:
9744 Undefined is 0; the default is used.
9749 0: Use Z_HUFFMAN_ONLY strategy with the
9750 zlib default compression level
9752 1-9: the zlib compression level
9756 0-4: the PNG filter method
9758 5: libpng adaptive filtering if compression level > 5
9759 libpng filter type "none" if compression level <= 5
9760 or if image is grayscale or palette
9762 6: libpng adaptive filtering
9764 7: "LOCO" filtering (intrapixel differing) if writing
9765 a MNG, othewise "none". Did not work in IM-6.7.0-9
9766 and earlier because of a missing "else".
9768 8: Z_RLE strategy, all filters
9769 Unused prior to IM-6.7.0-10, was same as 6
9771 9: Z_RLE strategy, no PNG filters
9772 Unused prior to IM-6.7.0-10, was same as 6
9774 Note that using the -quality option, not all combinations of
9775 PNG filter type, zlib compression level, and zlib compression
9776 strategy are possible. This will be addressed soon in a
9777 release that accomodates "-define PNG:compression-strategy", etc.
9781 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9786 if (mng_info->write_png_compression_strategy == 0)
9787 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9790 else if (mng_info->write_png_compression_level == 0)
9795 level=(int) MagickMin((ssize_t) quality/10,9);
9797 mng_info->write_png_compression_level = level+1;
9800 if (mng_info->write_png_compression_strategy == 0)
9802 if ((quality %10) == 8 || (quality %10) == 9)
9803 mng_info->write_png_compression_strategy=Z_RLE;
9806 if (mng_info->write_png_compression_filter == 0)
9807 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9809 if (logging != MagickFalse)
9811 if (mng_info->write_png_compression_level)
9812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813 " Compression level: %d",
9814 (int) mng_info->write_png_compression_level-1);
9816 if (mng_info->write_png_compression_strategy)
9817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9818 " Compression strategy: %d",
9819 (int) mng_info->write_png_compression_strategy-1);
9821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9822 " Setting up filtering");
9824 if (mng_info->write_png_compression_filter == 6)
9825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 " Base filter method: ADAPTIVE");
9827 else if (mng_info->write_png_compression_filter == 0 ||
9828 mng_info->write_png_compression_filter == 1)
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Base filter method: NONE");
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Base filter method: %d",
9834 (int) mng_info->write_png_compression_filter-1);
9837 if (mng_info->write_png_compression_level != 0)
9838 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9840 if (mng_info->write_png_compression_filter == 6)
9842 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9843 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9845 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9847 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9849 else if (mng_info->write_png_compression_filter == 7 ||
9850 mng_info->write_png_compression_filter == 10)
9851 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9853 else if (mng_info->write_png_compression_filter == 8)
9855 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9856 if (mng_info->write_mng)
9858 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9859 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9860 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9863 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9866 else if (mng_info->write_png_compression_filter == 9)
9867 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9869 else if (mng_info->write_png_compression_filter != 0)
9870 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9871 mng_info->write_png_compression_filter-1);
9873 if (mng_info->write_png_compression_strategy != 0)
9874 png_set_compression_strategy(ping,
9875 mng_info->write_png_compression_strategy-1);
9877 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9878 if (ping_exclude_sRGB != MagickFalse ||
9879 (image->rendering_intent == UndefinedIntent))
9881 if ((ping_exclude_tEXt == MagickFalse ||
9882 ping_exclude_zTXt == MagickFalse) &&
9883 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9885 ResetImageProfileIterator(image);
9886 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9888 profile=GetImageProfile(image,name);
9890 if (profile != (StringInfo *) NULL)
9892 #ifdef PNG_WRITE_iCCP_SUPPORTED
9893 if ((LocaleCompare(name,"ICC") == 0) ||
9894 (LocaleCompare(name,"ICM") == 0))
9897 if (ping_exclude_iCCP == MagickFalse)
9899 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9900 #if (PNG_LIBPNG_VER < 10500)
9901 (png_charp) GetStringInfoDatum(profile),
9903 (png_const_bytep) GetStringInfoDatum(profile),
9905 (png_uint_32) GetStringInfoLength(profile));
9911 if (ping_exclude_zCCP == MagickFalse)
9913 Magick_png_write_raw_profile(image_info,ping,ping_info,
9914 (unsigned char *) name,(unsigned char *) name,
9915 GetStringInfoDatum(profile),
9916 (png_uint_32) GetStringInfoLength(profile));
9920 if (logging != MagickFalse)
9921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9922 " Setting up text chunk with %s profile",name);
9924 name=GetNextImageProfile(image);
9929 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9930 if ((mng_info->have_write_global_srgb == 0) &&
9931 ((image->rendering_intent != UndefinedIntent) ||
9932 (image->colorspace == sRGBColorspace)))
9934 if (ping_exclude_sRGB == MagickFalse)
9937 Note image rendering intent.
9939 if (logging != MagickFalse)
9940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9941 " Setting up sRGB chunk");
9943 (void) png_set_sRGB(ping,ping_info,(
9944 Magick_RenderingIntent_to_PNG_RenderingIntent(
9945 image->rendering_intent)));
9949 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9952 if (ping_exclude_gAMA == MagickFalse &&
9953 (ping_exclude_sRGB == MagickFalse ||
9954 (image->gamma < .45 || image->gamma > .46)))
9956 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9960 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9962 if (logging != MagickFalse)
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " Setting up gAMA chunk");
9966 png_set_gAMA(ping,ping_info,image->gamma);
9970 if (ping_exclude_cHRM == MagickFalse)
9972 if ((mng_info->have_write_global_chrm == 0) &&
9973 (image->chromaticity.red_primary.x != 0.0))
9976 Note image chromaticity.
9977 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9985 wp=image->chromaticity.white_point;
9986 rp=image->chromaticity.red_primary;
9987 gp=image->chromaticity.green_primary;
9988 bp=image->chromaticity.blue_primary;
9990 if (logging != MagickFalse)
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " Setting up cHRM chunk");
9994 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10000 ping_interlace_method=image_info->interlace != NoInterlace;
10002 if (mng_info->write_mng)
10003 png_set_sig_bytes(ping,8);
10005 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10007 if (mng_info->write_png_colortype != 0)
10009 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10010 if (ping_have_color != MagickFalse)
10012 ping_color_type = PNG_COLOR_TYPE_RGB;
10014 if (ping_bit_depth < 8)
10018 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10019 if (ping_have_color != MagickFalse)
10020 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10023 if (ping_need_colortype_warning != MagickFalse ||
10024 ((mng_info->write_png_depth &&
10025 (int) mng_info->write_png_depth != ping_bit_depth) ||
10026 (mng_info->write_png_colortype &&
10027 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10028 mng_info->write_png_colortype != 7 &&
10029 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10031 if (logging != MagickFalse)
10033 if (ping_need_colortype_warning != MagickFalse)
10035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10036 " Image has transparency but tRNS chunk was excluded");
10039 if (mng_info->write_png_depth)
10041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10042 " Defined PNG:bit-depth=%u, Computed depth=%u",
10043 mng_info->write_png_depth,
10047 if (mng_info->write_png_colortype)
10049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050 " Defined PNG:color-type=%u, Computed color type=%u",
10051 mng_info->write_png_colortype-1,
10057 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10060 if (image_matte != MagickFalse && image->matte == MagickFalse)
10062 /* Add an opaque matte channel */
10063 image->matte = MagickTrue;
10064 (void) SetImageOpacity(image,0);
10066 if (logging != MagickFalse)
10067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10068 " Added an opaque matte channel");
10071 if (number_transparent != 0 || number_semitransparent != 0)
10073 if (ping_color_type < 4)
10075 ping_have_tRNS=MagickTrue;
10076 if (logging != MagickFalse)
10077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10078 " Setting ping_have_tRNS=MagickTrue.");
10082 if (logging != MagickFalse)
10083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10084 " Writing PNG header chunks");
10086 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10087 ping_bit_depth,ping_color_type,
10088 ping_interlace_method,ping_compression_method,
10089 ping_filter_method);
10091 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10093 png_set_PLTE(ping,ping_info,palette,number_colors);
10095 if (logging != MagickFalse)
10097 for (i=0; i< (ssize_t) number_colors; i++)
10099 if (i < ping_num_trans)
10100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10101 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10103 (int) palette[i].red,
10104 (int) palette[i].green,
10105 (int) palette[i].blue,
10107 (int) ping_trans_alpha[i]);
10109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10110 " PLTE[%d] = (%d,%d,%d)",
10112 (int) palette[i].red,
10113 (int) palette[i].green,
10114 (int) palette[i].blue);
10119 if (ping_exclude_bKGD == MagickFalse)
10121 if (ping_have_bKGD != MagickFalse)
10123 png_set_bKGD(ping,ping_info,&ping_background);
10126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10127 " Setting up bKGD chunk");
10128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10129 " background color = (%d,%d,%d)",
10130 (int) ping_background.red,
10131 (int) ping_background.green,
10132 (int) ping_background.blue);
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10134 " index = %d, gray=%d",
10135 (int) ping_background.index,
10136 (int) ping_background.gray);
10141 if (ping_exclude_pHYs == MagickFalse)
10143 if (ping_have_pHYs != MagickFalse)
10145 png_set_pHYs(ping,ping_info,
10146 ping_pHYs_x_resolution,
10147 ping_pHYs_y_resolution,
10148 ping_pHYs_unit_type);
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153 " Setting up pHYs chunk");
10154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 " x_resolution=%lu",
10156 (unsigned long) ping_pHYs_x_resolution);
10157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10158 " y_resolution=%lu",
10159 (unsigned long) ping_pHYs_y_resolution);
10160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 (unsigned long) ping_pHYs_unit_type);
10167 #if defined(PNG_oFFs_SUPPORTED)
10168 if (ping_exclude_oFFs == MagickFalse)
10170 if (image->page.x || image->page.y)
10172 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10173 (png_int_32) image->page.y, 0);
10175 if (logging != MagickFalse)
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10177 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10178 (int) image->page.x, (int) image->page.y);
10183 if (mng_info->need_blob != MagickFalse)
10185 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10187 png_error(ping,"WriteBlob Failed");
10189 ping_have_blob=MagickTrue;
10192 png_write_info_before_PLTE(ping, ping_info);
10194 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10196 if (logging != MagickFalse)
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10202 if (ping_color_type == 3)
10203 (void) png_set_tRNS(ping, ping_info,
10210 (void) png_set_tRNS(ping, ping_info,
10213 &ping_trans_color);
10215 if (logging != MagickFalse)
10217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10218 " tRNS color =(%d,%d,%d)",
10219 (int) ping_trans_color.red,
10220 (int) ping_trans_color.green,
10221 (int) ping_trans_color.blue);
10226 /* write any png-chunk-b profiles */
10227 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10229 png_write_info(ping,ping_info);
10231 /* write any PNG-chunk-m profiles */
10232 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10234 if (ping_exclude_vpAg == MagickFalse)
10236 if ((image->page.width != 0 && image->page.width != image->columns) ||
10237 (image->page.height != 0 && image->page.height != image->rows))
10242 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10243 PNGType(chunk,mng_vpAg);
10244 LogPNGChunk(logging,mng_vpAg,9L);
10245 PNGLong(chunk+4,(png_uint_32) image->page.width);
10246 PNGLong(chunk+8,(png_uint_32) image->page.height);
10247 chunk[12]=0; /* unit = pixels */
10248 (void) WriteBlob(image,13,chunk);
10249 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10253 #if (PNG_LIBPNG_VER == 10206)
10254 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10255 #define PNG_HAVE_IDAT 0x04
10256 ping->mode |= PNG_HAVE_IDAT;
10257 #undef PNG_HAVE_IDAT
10260 png_set_packing(ping);
10264 rowbytes=image->columns;
10265 if (image_depth > 8)
10267 switch (ping_color_type)
10269 case PNG_COLOR_TYPE_RGB:
10273 case PNG_COLOR_TYPE_GRAY_ALPHA:
10277 case PNG_COLOR_TYPE_RGBA:
10285 if (logging != MagickFalse)
10287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10288 " Writing PNG image data");
10290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10293 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10294 sizeof(*ping_pixels));
10296 if (ping_pixels == (unsigned char *) NULL)
10297 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10300 Initialize image scanlines.
10302 if (setjmp(png_jmpbuf(ping)))
10308 if (image_info->verbose)
10309 (void) printf("PNG write has failed.\n");
10311 png_destroy_write_struct(&ping,&ping_info);
10312 if (quantum_info != (QuantumInfo *) NULL)
10313 quantum_info=DestroyQuantumInfo(quantum_info);
10314 if (ping_pixels != (unsigned char *) NULL)
10315 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10316 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10317 UnlockSemaphoreInfo(ping_semaphore);
10319 if (ping_have_blob != MagickFalse)
10320 (void) CloseBlob(image);
10321 image_info=DestroyImageInfo(image_info);
10322 image=DestroyImage(image);
10323 return(MagickFalse);
10325 quantum_info=AcquireQuantumInfo(image_info,image);
10326 if (quantum_info == (QuantumInfo *) NULL)
10327 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10328 quantum_info->format=UndefinedQuantumFormat;
10329 quantum_info->depth=image_depth;
10330 num_passes=png_set_interlace_handling(ping);
10332 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10333 !mng_info->write_png32) &&
10334 (mng_info->IsPalette ||
10335 (image_info->type == BilevelType)) &&
10336 image_matte == MagickFalse &&
10337 ping_have_non_bw == MagickFalse)
10339 /* Palette, Bilevel, or Opaque Monochrome */
10340 register const Quantum
10343 quantum_info->depth=8;
10344 for (pass=0; pass < num_passes; pass++)
10347 Convert PseudoClass image to a PNG monochrome image.
10349 for (y=0; y < (ssize_t) image->rows; y++)
10351 if (logging != MagickFalse && y == 0)
10352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10353 " Writing row of pixels (0)");
10355 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10357 if (p == (const Quantum *) NULL)
10360 if (mng_info->IsPalette)
10362 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10363 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10364 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10365 mng_info->write_png_depth &&
10366 mng_info->write_png_depth != old_bit_depth)
10368 /* Undo pixel scaling */
10369 for (i=0; i < (ssize_t) image->columns; i++)
10370 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10371 >> (8-old_bit_depth));
10377 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10378 quantum_info,RedQuantum,ping_pixels,&image->exception);
10381 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10382 for (i=0; i < (ssize_t) image->columns; i++)
10383 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10386 if (logging != MagickFalse && y == 0)
10387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " Writing row of pixels (1)");
10390 png_write_row(ping,ping_pixels);
10392 if (image->previous == (Image *) NULL)
10394 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10395 if (status == MagickFalse)
10401 else /* Not Palette, Bilevel, or Opaque Monochrome */
10403 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10404 !mng_info->write_png32) &&
10405 (image_matte != MagickFalse ||
10406 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10407 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10409 register const Quantum
10412 for (pass=0; pass < num_passes; pass++)
10415 for (y=0; y < (ssize_t) image->rows; y++)
10417 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10419 if (p == (const Quantum *) NULL)
10422 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10424 if (mng_info->IsPalette)
10425 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10426 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10429 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10430 quantum_info,RedQuantum,ping_pixels,&image->exception);
10432 if (logging != MagickFalse && y == 0)
10433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10434 " Writing GRAY PNG pixels (2)");
10437 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10439 if (logging != MagickFalse && y == 0)
10440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10441 " Writing GRAY_ALPHA PNG pixels (2)");
10443 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10444 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
10447 if (logging != MagickFalse && y == 0)
10448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10449 " Writing row of pixels (2)");
10451 png_write_row(ping,ping_pixels);
10454 if (image->previous == (Image *) NULL)
10456 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10457 if (status == MagickFalse)
10465 register const Quantum
10468 for (pass=0; pass < num_passes; pass++)
10470 if ((image_depth > 8) || (mng_info->write_png24 ||
10471 mng_info->write_png32 ||
10472 (!mng_info->write_png8 && !mng_info->IsPalette)))
10474 for (y=0; y < (ssize_t) image->rows; y++)
10476 p=GetVirtualPixels(image,0,y,image->columns,1,
10477 &image->exception);
10479 if (p == (const Quantum *) NULL)
10482 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10484 if (image->storage_class == DirectClass)
10485 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10486 quantum_info,RedQuantum,ping_pixels,&image->exception);
10489 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10490 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10493 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10495 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10496 quantum_info,GrayAlphaQuantum,ping_pixels,
10497 &image->exception);
10499 if (logging != MagickFalse && y == 0)
10500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10501 " Writing GRAY_ALPHA PNG pixels (3)");
10504 else if (image_matte != MagickFalse)
10505 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10506 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
10509 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10510 quantum_info,RGBQuantum,ping_pixels,&image->exception);
10512 if (logging != MagickFalse && y == 0)
10513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514 " Writing row of pixels (3)");
10516 png_write_row(ping,ping_pixels);
10521 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10522 mng_info->write_png32 ||
10523 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10525 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10526 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10528 if (logging != MagickFalse)
10529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10530 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10532 quantum_info->depth=8;
10536 for (y=0; y < (ssize_t) image->rows; y++)
10538 if (logging != MagickFalse && y == 0)
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10542 p=GetVirtualPixels(image,0,y,image->columns,1,
10543 &image->exception);
10545 if (p == (const Quantum *) NULL)
10548 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10550 quantum_info->depth=image->depth;
10552 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10553 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10556 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10558 if (logging != MagickFalse && y == 0)
10559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560 " Writing GRAY_ALPHA PNG pixels (4)");
10562 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10563 quantum_info,GrayAlphaQuantum,ping_pixels,
10564 &image->exception);
10569 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10570 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10572 if (logging != MagickFalse && y <= 2)
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " Writing row of non-gray pixels (4)");
10577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10579 (int)ping_pixels[0],(int)ping_pixels[1]);
10582 png_write_row(ping,ping_pixels);
10586 if (image->previous == (Image *) NULL)
10588 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10589 if (status == MagickFalse)
10596 if (quantum_info != (QuantumInfo *) NULL)
10597 quantum_info=DestroyQuantumInfo(quantum_info);
10599 if (logging != MagickFalse)
10601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602 " Wrote PNG image data");
10604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10605 " Width: %.20g",(double) ping_width);
10607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10608 " Height: %.20g",(double) ping_height);
10610 if (mng_info->write_png_depth)
10612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10613 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " PNG bit-depth written: %d",ping_bit_depth);
10619 if (mng_info->write_png_colortype)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626 " PNG color-type written: %d",ping_color_type);
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " PNG Interlace method: %d",ping_interlace_method);
10632 Generate text chunks after IDAT.
10634 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10636 ResetImagePropertyIterator(image);
10637 property=GetNextImageProperty(image);
10638 while (property != (const char *) NULL)
10643 value=GetImageProperty(image,property);
10645 /* Don't write any "png:" properties; those are just for "identify" */
10646 if (LocaleNCompare(property,"png:",4) != 0 &&
10648 /* Suppress density and units if we wrote a pHYs chunk */
10649 (ping_exclude_pHYs != MagickFalse ||
10650 LocaleCompare(property,"density") != 0 ||
10651 LocaleCompare(property,"units") != 0) &&
10653 /* Suppress the IM-generated Date:create and Date:modify */
10654 (ping_exclude_date == MagickFalse ||
10655 LocaleNCompare(property, "Date:",5) != 0))
10657 if (value != (const char *) NULL)
10659 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10660 text[0].key=(char *) property;
10661 text[0].text=(char *) value;
10662 text[0].text_length=strlen(value);
10664 if (ping_exclude_tEXt != MagickFalse)
10665 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10667 else if (ping_exclude_zTXt != MagickFalse)
10668 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10672 text[0].compression=image_info->compression == NoCompression ||
10673 (image_info->compression == UndefinedCompression &&
10674 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10675 PNG_TEXT_COMPRESSION_zTXt ;
10678 if (logging != MagickFalse)
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " Setting up text chunk");
10683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684 " keyword: %s",text[0].key);
10687 png_set_text(ping,ping_info,text,1);
10688 png_free(ping,text);
10691 property=GetNextImageProperty(image);
10695 /* write any PNG-chunk-e profiles */
10696 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10698 if (logging != MagickFalse)
10699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700 " Writing PNG end info");
10702 png_write_end(ping,ping_info);
10704 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10706 if (mng_info->page.x || mng_info->page.y ||
10707 (ping_width != mng_info->page.width) ||
10708 (ping_height != mng_info->page.height))
10714 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10716 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10717 PNGType(chunk,mng_FRAM);
10718 LogPNGChunk(logging,mng_FRAM,27L);
10720 chunk[5]=0; /* frame name separator (no name) */
10721 chunk[6]=1; /* flag for changing delay, for next frame only */
10722 chunk[7]=0; /* flag for changing frame timeout */
10723 chunk[8]=1; /* flag for changing frame clipping for next frame */
10724 chunk[9]=0; /* flag for changing frame sync_id */
10725 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10726 chunk[14]=0; /* clipping boundaries delta type */
10727 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10729 (png_uint_32) (mng_info->page.x + ping_width));
10730 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10732 (png_uint_32) (mng_info->page.y + ping_height));
10733 (void) WriteBlob(image,31,chunk);
10734 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10735 mng_info->old_framing_mode=4;
10736 mng_info->framing_mode=1;
10740 mng_info->framing_mode=3;
10742 if (mng_info->write_mng && !mng_info->need_fram &&
10743 ((int) image->dispose == 3))
10744 (void) ThrowMagickException(&image->exception,GetMagickModule(),
10745 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10746 "`%s'",image->filename);
10749 Free PNG resources.
10752 png_destroy_write_struct(&ping,&ping_info);
10754 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10756 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10757 UnlockSemaphoreInfo(ping_semaphore);
10760 if (ping_have_blob != MagickFalse)
10761 (void) CloseBlob(image);
10763 image_info=DestroyImageInfo(image_info);
10764 image=DestroyImage(image);
10766 /* Store bit depth actually written */
10767 s[0]=(char) ping_bit_depth;
10770 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10772 if (logging != MagickFalse)
10773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10774 " exit WriteOnePNGImage()");
10776 return(MagickTrue);
10777 /* End write one PNG image */
10781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10785 % W r i t e P N G I m a g e %
10789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10791 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10792 % Multiple-image Network Graphics (MNG) image file.
10794 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10796 % The format of the WritePNGImage method is:
10798 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10799 % Image *image,ExceptionInfo *exception)
10801 % A description of each parameter follows:
10803 % o image_info: the image info.
10805 % o image: The image.
10807 % o exception: return any errors or warnings in this structure.
10809 % Returns MagickTrue on success, MagickFalse on failure.
10811 % Communicating with the PNG encoder:
10813 % While the datastream written is always in PNG format and normally would
10814 % be given the "png" file extension, this method also writes the following
10815 % pseudo-formats which are subsets of PNG:
10817 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10818 % a depth greater than 8, the depth is reduced. If transparency
10819 % is present, the tRNS chunk must only have values 0 and 255
10820 % (i.e., transparency is binary: fully opaque or fully
10821 % transparent). If other values are present they will be
10822 % 50%-thresholded to binary transparency. If more than 256
10823 % colors are present, they will be quantized to the 4-4-4-1,
10824 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10825 % of any resulting fully-transparent pixels is changed to
10826 % the image's background color.
10828 % If you want better quantization or dithering of the colors
10829 % or alpha than that, you need to do it before calling the
10830 % PNG encoder. The pixels contain 8-bit indices even if
10831 % they could be represented with 1, 2, or 4 bits. Grayscale
10832 % images will be written as indexed PNG files even though the
10833 % PNG grayscale type might be slightly more efficient. Please
10834 % note that writing to the PNG8 format may result in loss
10835 % of color and alpha data.
10837 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10838 % chunk can be present to convey binary transparency by naming
10839 % one of the colors as transparent. The only loss incurred
10840 % is reduction of sample depth to 8. If the image has more
10841 % than one transparent color, has semitransparent pixels, or
10842 % has an opaque pixel with the same RGB components as the
10843 % transparent color, an image is not written.
10845 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10846 % transparency is permitted, i.e., the alpha sample for
10847 % each pixel can have any value from 0 to 255. The alpha
10848 % channel is present even if the image is fully opaque.
10849 % The only loss in data is the reduction of the sample depth
10852 % o -define: For more precise control of the PNG output, you can use the
10853 % Image options "png:bit-depth" and "png:color-type". These
10854 % can be set from the commandline with "-define" and also
10855 % from the application programming interfaces. The options
10856 % are case-independent and are converted to lowercase before
10857 % being passed to this encoder.
10859 % png:color-type can be 0, 2, 3, 4, or 6.
10861 % When png:color-type is 0 (Grayscale), png:bit-depth can
10862 % be 1, 2, 4, 8, or 16.
10864 % When png:color-type is 2 (RGB), png:bit-depth can
10867 % When png:color-type is 3 (Indexed), png:bit-depth can
10868 % be 1, 2, 4, or 8. This refers to the number of bits
10869 % used to store the index. The color samples always have
10870 % bit-depth 8 in indexed PNG files.
10872 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10873 % png:bit-depth can be 8 or 16.
10875 % If the image cannot be written without loss with the requested bit-depth
10876 % and color-type, a PNG file will not be written, and the encoder will
10877 % return MagickFalse.
10879 % Since image encoders should not be responsible for the "heavy lifting",
10880 % the user should make sure that ImageMagick has already reduced the
10881 % image depth and number of colors and limit transparency to binary
10882 % transparency prior to attempting to write the image with depth, color,
10883 % or transparency limitations.
10885 % Note that another definition, "png:bit-depth-written" exists, but it
10886 % is not intended for external use. It is only used internally by the
10887 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10889 % It is possible to request that the PNG encoder write previously-formatted
10890 % ancillary chunks in the output PNG file, using the "-profile" commandline
10891 % option as shown below or by setting the profile via a programming
10894 % -profile PNG-chunk-x:<file>
10896 % where x is a location flag and <file> is a file containing the chunk
10897 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10898 % This encoder will compute the chunk length and CRC, so those must not
10899 % be included in the file.
10901 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10902 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10903 % of the same type, then add a short unique string after the "x" to prevent
10904 % subsequent profiles from overwriting the preceding ones, e.g.,
10906 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10908 % As of version 6.6.6 the following optimizations are always done:
10910 % o 32-bit depth is reduced to 16.
10911 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10912 % high byte and low byte are identical.
10913 % o Palette is sorted to remove unused entries and to put a
10914 % transparent color first, if BUILD_PNG_PALETTE is defined.
10915 % o Opaque matte channel is removed when writing an indexed PNG.
10916 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10917 % this can be done without loss and a larger bit depth N was not
10918 % requested via the "-define PNG:bit-depth=N" option.
10919 % o If matte channel is present but only one transparent color is
10920 % present, RGB+tRNS is written instead of RGBA
10921 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10922 % was requested when converting an opaque image).
10924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10926 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10927 Image *image,ExceptionInfo *exception)
10932 have_mng_structure,
10948 assert(image_info != (const ImageInfo *) NULL);
10949 assert(image_info->signature == MagickSignature);
10950 assert(image != (Image *) NULL);
10951 assert(image->signature == MagickSignature);
10952 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10953 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10955 Allocate a MngInfo structure.
10957 have_mng_structure=MagickFalse;
10958 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10960 if (mng_info == (MngInfo *) NULL)
10961 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10964 Initialize members of the MngInfo structure.
10966 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10967 mng_info->image=image;
10968 mng_info->equal_backgrounds=MagickTrue;
10969 have_mng_structure=MagickTrue;
10971 /* See if user has requested a specific PNG subformat */
10973 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10974 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10975 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10977 if (mng_info->write_png8)
10979 mng_info->write_png_colortype = /* 3 */ 4;
10980 mng_info->write_png_depth = 8;
10984 if (mng_info->write_png24)
10986 mng_info->write_png_colortype = /* 2 */ 3;
10987 mng_info->write_png_depth = 8;
10990 if (image->matte == MagickTrue)
10991 (void) SetImageType(image,TrueColorMatteType,exception);
10994 (void) SetImageType(image,TrueColorType,exception);
10996 (void) SyncImage(image);
10999 if (mng_info->write_png32)
11001 mng_info->write_png_colortype = /* 6 */ 7;
11002 mng_info->write_png_depth = 8;
11005 if (image->matte == MagickTrue)
11006 (void) SetImageType(image,TrueColorMatteType,exception);
11009 (void) SetImageType(image,TrueColorType,exception);
11011 (void) SyncImage(image);
11014 value=GetImageOption(image_info,"png:bit-depth");
11016 if (value != (char *) NULL)
11018 if (LocaleCompare(value,"1") == 0)
11019 mng_info->write_png_depth = 1;
11021 else if (LocaleCompare(value,"2") == 0)
11022 mng_info->write_png_depth = 2;
11024 else if (LocaleCompare(value,"4") == 0)
11025 mng_info->write_png_depth = 4;
11027 else if (LocaleCompare(value,"8") == 0)
11028 mng_info->write_png_depth = 8;
11030 else if (LocaleCompare(value,"16") == 0)
11031 mng_info->write_png_depth = 16;
11034 (void) ThrowMagickException(&image->exception,
11035 GetMagickModule(),CoderWarning,
11036 "ignoring invalid defined png:bit-depth",
11039 if (logging != MagickFalse)
11040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11041 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11044 value=GetImageOption(image_info,"png:color-type");
11046 if (value != (char *) NULL)
11048 /* We must store colortype+1 because 0 is a valid colortype */
11049 if (LocaleCompare(value,"0") == 0)
11050 mng_info->write_png_colortype = 1;
11052 else if (LocaleCompare(value,"2") == 0)
11053 mng_info->write_png_colortype = 3;
11055 else if (LocaleCompare(value,"3") == 0)
11056 mng_info->write_png_colortype = 4;
11058 else if (LocaleCompare(value,"4") == 0)
11059 mng_info->write_png_colortype = 5;
11061 else if (LocaleCompare(value,"6") == 0)
11062 mng_info->write_png_colortype = 7;
11065 (void) ThrowMagickException(&image->exception,
11066 GetMagickModule(),CoderWarning,
11067 "ignoring invalid defined png:color-type",
11070 if (logging != MagickFalse)
11071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11072 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11075 /* Check for chunks to be excluded:
11077 * The default is to not exclude any known chunks except for any
11078 * listed in the "unused_chunks" array, above.
11080 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11081 * define (in the image properties or in the image artifacts)
11082 * or via a mng_info member. For convenience, in addition
11083 * to or instead of a comma-separated list of chunks, the
11084 * "exclude-chunk" string can be simply "all" or "none".
11086 * The exclude-chunk define takes priority over the mng_info.
11088 * A "PNG:include-chunk" define takes priority over both the
11089 * mng_info and the "PNG:exclude-chunk" define. Like the
11090 * "exclude-chunk" string, it can define "all" or "none" as
11091 * well as a comma-separated list. Chunks that are unknown to
11092 * ImageMagick are always excluded, regardless of their "copy-safe"
11093 * status according to the PNG specification, and even if they
11094 * appear in the "include-chunk" list.
11096 * Finally, all chunks listed in the "unused_chunks" array are
11097 * automatically excluded, regardless of the other instructions
11100 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11101 * will not be written and the gAMA chunk will only be written if it
11102 * is not between .45 and .46, or approximately (1.0/2.2).
11104 * If you exclude tRNS and the image has transparency, the colortype
11105 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11107 * The -strip option causes StripImage() to set the png:include-chunk
11108 * artifact to "none,trns,gama".
11111 mng_info->ping_exclude_bKGD=MagickFalse;
11112 mng_info->ping_exclude_cHRM=MagickFalse;
11113 mng_info->ping_exclude_date=MagickFalse;
11114 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11115 mng_info->ping_exclude_gAMA=MagickFalse;
11116 mng_info->ping_exclude_iCCP=MagickFalse;
11117 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11118 mng_info->ping_exclude_oFFs=MagickFalse;
11119 mng_info->ping_exclude_pHYs=MagickFalse;
11120 mng_info->ping_exclude_sRGB=MagickFalse;
11121 mng_info->ping_exclude_tEXt=MagickFalse;
11122 mng_info->ping_exclude_tRNS=MagickFalse;
11123 mng_info->ping_exclude_vpAg=MagickFalse;
11124 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11125 mng_info->ping_exclude_zTXt=MagickFalse;
11127 mng_info->ping_preserve_colormap=MagickFalse;
11129 value=GetImageArtifact(image,"png:preserve-colormap");
11131 value=GetImageOption(image_info,"png:preserve-colormap");
11133 mng_info->ping_preserve_colormap=MagickTrue;
11135 /* Thes compression-level, compression-strategy, and compression-filter
11136 * defines take precedence over values from the -quality option.
11138 value=GetImageArtifact(image,"png:compression-level");
11140 value=GetImageOption(image_info,"png:compression-level");
11143 /* We have to add 1 to everything because 0 is a valid input,
11144 * and we want to use 0 (the default) to mean undefined.
11146 if (LocaleCompare(value,"0") == 0)
11147 mng_info->write_png_compression_level = 1;
11149 if (LocaleCompare(value,"1") == 0)
11150 mng_info->write_png_compression_level = 2;
11152 else if (LocaleCompare(value,"2") == 0)
11153 mng_info->write_png_compression_level = 3;
11155 else if (LocaleCompare(value,"3") == 0)
11156 mng_info->write_png_compression_level = 4;
11158 else if (LocaleCompare(value,"4") == 0)
11159 mng_info->write_png_compression_level = 5;
11161 else if (LocaleCompare(value,"5") == 0)
11162 mng_info->write_png_compression_level = 6;
11164 else if (LocaleCompare(value,"6") == 0)
11165 mng_info->write_png_compression_level = 7;
11167 else if (LocaleCompare(value,"7") == 0)
11168 mng_info->write_png_compression_level = 8;
11170 else if (LocaleCompare(value,"8") == 0)
11171 mng_info->write_png_compression_level = 9;
11173 else if (LocaleCompare(value,"9") == 0)
11174 mng_info->write_png_compression_level = 10;
11177 (void) ThrowMagickException(&image->exception,
11178 GetMagickModule(),CoderWarning,
11179 "ignoring invalid defined png:compression-level",
11183 value=GetImageArtifact(image,"png:compression-strategy");
11185 value=GetImageOption(image_info,"png:compression-strategy");
11189 if (LocaleCompare(value,"0") == 0)
11190 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11192 else if (LocaleCompare(value,"1") == 0)
11193 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11195 else if (LocaleCompare(value,"2") == 0)
11196 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11198 else if (LocaleCompare(value,"3") == 0)
11199 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11200 mng_info->write_png_compression_strategy = Z_RLE+1;
11202 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11205 else if (LocaleCompare(value,"4") == 0)
11206 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11207 mng_info->write_png_compression_strategy = Z_FIXED+1;
11209 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11213 (void) ThrowMagickException(&image->exception,
11214 GetMagickModule(),CoderWarning,
11215 "ignoring invalid defined png:compression-strategy",
11219 value=GetImageArtifact(image,"png:compression-filter");
11221 value=GetImageOption(image_info,"png:compression-filter");
11225 /* To do: combinations of filters allowed by libpng
11226 * masks 0x08 through 0xf8
11228 * Implement this as a comma-separated list of 0,1,2,3,4,5
11229 * where 5 is a special case meaning PNG_ALL_FILTERS.
11232 if (LocaleCompare(value,"0") == 0)
11233 mng_info->write_png_compression_filter = 1;
11235 if (LocaleCompare(value,"1") == 0)
11236 mng_info->write_png_compression_filter = 2;
11238 else if (LocaleCompare(value,"2") == 0)
11239 mng_info->write_png_compression_filter = 3;
11241 else if (LocaleCompare(value,"3") == 0)
11242 mng_info->write_png_compression_filter = 4;
11244 else if (LocaleCompare(value,"4") == 0)
11245 mng_info->write_png_compression_filter = 5;
11247 else if (LocaleCompare(value,"5") == 0)
11248 mng_info->write_png_compression_filter = 6;
11251 (void) ThrowMagickException(&image->exception,
11252 GetMagickModule(),CoderWarning,
11253 "ignoring invalid defined png:compression-filter",
11257 excluding=MagickFalse;
11259 for (source=0; source<1; source++)
11263 value=GetImageArtifact(image,"png:exclude-chunk");
11266 value=GetImageArtifact(image,"png:exclude-chunks");
11270 value=GetImageOption(image_info,"png:exclude-chunk");
11273 value=GetImageOption(image_info,"png:exclude-chunks");
11282 excluding=MagickTrue;
11284 if (logging != MagickFalse)
11287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11288 " png:exclude-chunk=%s found in image artifacts.\n", value);
11290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11291 " png:exclude-chunk=%s found in image properties.\n", value);
11294 last=strlen(value);
11296 for (i=0; i<(int) last; i+=5)
11299 if (LocaleNCompare(value+i,"all",3) == 0)
11301 mng_info->ping_exclude_bKGD=MagickTrue;
11302 mng_info->ping_exclude_cHRM=MagickTrue;
11303 mng_info->ping_exclude_date=MagickTrue;
11304 mng_info->ping_exclude_EXIF=MagickTrue;
11305 mng_info->ping_exclude_gAMA=MagickTrue;
11306 mng_info->ping_exclude_iCCP=MagickTrue;
11307 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11308 mng_info->ping_exclude_oFFs=MagickTrue;
11309 mng_info->ping_exclude_pHYs=MagickTrue;
11310 mng_info->ping_exclude_sRGB=MagickTrue;
11311 mng_info->ping_exclude_tEXt=MagickTrue;
11312 mng_info->ping_exclude_tRNS=MagickTrue;
11313 mng_info->ping_exclude_vpAg=MagickTrue;
11314 mng_info->ping_exclude_zCCP=MagickTrue;
11315 mng_info->ping_exclude_zTXt=MagickTrue;
11319 if (LocaleNCompare(value+i,"none",4) == 0)
11321 mng_info->ping_exclude_bKGD=MagickFalse;
11322 mng_info->ping_exclude_cHRM=MagickFalse;
11323 mng_info->ping_exclude_date=MagickFalse;
11324 mng_info->ping_exclude_EXIF=MagickFalse;
11325 mng_info->ping_exclude_gAMA=MagickFalse;
11326 mng_info->ping_exclude_iCCP=MagickFalse;
11327 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11328 mng_info->ping_exclude_oFFs=MagickFalse;
11329 mng_info->ping_exclude_pHYs=MagickFalse;
11330 mng_info->ping_exclude_sRGB=MagickFalse;
11331 mng_info->ping_exclude_tEXt=MagickFalse;
11332 mng_info->ping_exclude_tRNS=MagickFalse;
11333 mng_info->ping_exclude_vpAg=MagickFalse;
11334 mng_info->ping_exclude_zCCP=MagickFalse;
11335 mng_info->ping_exclude_zTXt=MagickFalse;
11338 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11339 mng_info->ping_exclude_bKGD=MagickTrue;
11341 if (LocaleNCompare(value+i,"chrm",4) == 0)
11342 mng_info->ping_exclude_cHRM=MagickTrue;
11344 if (LocaleNCompare(value+i,"date",4) == 0)
11345 mng_info->ping_exclude_date=MagickTrue;
11347 if (LocaleNCompare(value+i,"exif",4) == 0)
11348 mng_info->ping_exclude_EXIF=MagickTrue;
11350 if (LocaleNCompare(value+i,"gama",4) == 0)
11351 mng_info->ping_exclude_gAMA=MagickTrue;
11353 if (LocaleNCompare(value+i,"iccp",4) == 0)
11354 mng_info->ping_exclude_iCCP=MagickTrue;
11357 if (LocaleNCompare(value+i,"itxt",4) == 0)
11358 mng_info->ping_exclude_iTXt=MagickTrue;
11361 if (LocaleNCompare(value+i,"gama",4) == 0)
11362 mng_info->ping_exclude_gAMA=MagickTrue;
11364 if (LocaleNCompare(value+i,"offs",4) == 0)
11365 mng_info->ping_exclude_oFFs=MagickTrue;
11367 if (LocaleNCompare(value+i,"phys",4) == 0)
11368 mng_info->ping_exclude_pHYs=MagickTrue;
11370 if (LocaleNCompare(value+i,"srgb",4) == 0)
11371 mng_info->ping_exclude_sRGB=MagickTrue;
11373 if (LocaleNCompare(value+i,"text",4) == 0)
11374 mng_info->ping_exclude_tEXt=MagickTrue;
11376 if (LocaleNCompare(value+i,"trns",4) == 0)
11377 mng_info->ping_exclude_tRNS=MagickTrue;
11379 if (LocaleNCompare(value+i,"vpag",4) == 0)
11380 mng_info->ping_exclude_vpAg=MagickTrue;
11382 if (LocaleNCompare(value+i,"zccp",4) == 0)
11383 mng_info->ping_exclude_zCCP=MagickTrue;
11385 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11386 mng_info->ping_exclude_zTXt=MagickTrue;
11392 for (source=0; source<1; source++)
11396 value=GetImageArtifact(image,"png:include-chunk");
11399 value=GetImageArtifact(image,"png:include-chunks");
11403 value=GetImageOption(image_info,"png:include-chunk");
11406 value=GetImageOption(image_info,"png:include-chunks");
11414 excluding=MagickTrue;
11416 if (logging != MagickFalse)
11419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11420 " png:include-chunk=%s found in image artifacts.\n", value);
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11423 " png:include-chunk=%s found in image properties.\n", value);
11426 last=strlen(value);
11428 for (i=0; i<(int) last; i+=5)
11430 if (LocaleNCompare(value+i,"all",3) == 0)
11432 mng_info->ping_exclude_bKGD=MagickFalse;
11433 mng_info->ping_exclude_cHRM=MagickFalse;
11434 mng_info->ping_exclude_date=MagickFalse;
11435 mng_info->ping_exclude_EXIF=MagickFalse;
11436 mng_info->ping_exclude_gAMA=MagickFalse;
11437 mng_info->ping_exclude_iCCP=MagickFalse;
11438 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11439 mng_info->ping_exclude_oFFs=MagickFalse;
11440 mng_info->ping_exclude_pHYs=MagickFalse;
11441 mng_info->ping_exclude_sRGB=MagickFalse;
11442 mng_info->ping_exclude_tEXt=MagickFalse;
11443 mng_info->ping_exclude_tRNS=MagickFalse;
11444 mng_info->ping_exclude_vpAg=MagickFalse;
11445 mng_info->ping_exclude_zCCP=MagickFalse;
11446 mng_info->ping_exclude_zTXt=MagickFalse;
11450 if (LocaleNCompare(value+i,"none",4) == 0)
11452 mng_info->ping_exclude_bKGD=MagickTrue;
11453 mng_info->ping_exclude_cHRM=MagickTrue;
11454 mng_info->ping_exclude_date=MagickTrue;
11455 mng_info->ping_exclude_EXIF=MagickTrue;
11456 mng_info->ping_exclude_gAMA=MagickTrue;
11457 mng_info->ping_exclude_iCCP=MagickTrue;
11458 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11459 mng_info->ping_exclude_oFFs=MagickTrue;
11460 mng_info->ping_exclude_pHYs=MagickTrue;
11461 mng_info->ping_exclude_sRGB=MagickTrue;
11462 mng_info->ping_exclude_tEXt=MagickTrue;
11463 mng_info->ping_exclude_tRNS=MagickTrue;
11464 mng_info->ping_exclude_vpAg=MagickTrue;
11465 mng_info->ping_exclude_zCCP=MagickTrue;
11466 mng_info->ping_exclude_zTXt=MagickTrue;
11469 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11470 mng_info->ping_exclude_bKGD=MagickFalse;
11472 if (LocaleNCompare(value+i,"chrm",4) == 0)
11473 mng_info->ping_exclude_cHRM=MagickFalse;
11475 if (LocaleNCompare(value+i,"date",4) == 0)
11476 mng_info->ping_exclude_date=MagickFalse;
11478 if (LocaleNCompare(value+i,"exif",4) == 0)
11479 mng_info->ping_exclude_EXIF=MagickFalse;
11481 if (LocaleNCompare(value+i,"gama",4) == 0)
11482 mng_info->ping_exclude_gAMA=MagickFalse;
11484 if (LocaleNCompare(value+i,"iccp",4) == 0)
11485 mng_info->ping_exclude_iCCP=MagickFalse;
11488 if (LocaleNCompare(value+i,"itxt",4) == 0)
11489 mng_info->ping_exclude_iTXt=MagickFalse;
11492 if (LocaleNCompare(value+i,"gama",4) == 0)
11493 mng_info->ping_exclude_gAMA=MagickFalse;
11495 if (LocaleNCompare(value+i,"offs",4) == 0)
11496 mng_info->ping_exclude_oFFs=MagickFalse;
11498 if (LocaleNCompare(value+i,"phys",4) == 0)
11499 mng_info->ping_exclude_pHYs=MagickFalse;
11501 if (LocaleNCompare(value+i,"srgb",4) == 0)
11502 mng_info->ping_exclude_sRGB=MagickFalse;
11504 if (LocaleNCompare(value+i,"text",4) == 0)
11505 mng_info->ping_exclude_tEXt=MagickFalse;
11507 if (LocaleNCompare(value+i,"trns",4) == 0)
11508 mng_info->ping_exclude_tRNS=MagickFalse;
11510 if (LocaleNCompare(value+i,"vpag",4) == 0)
11511 mng_info->ping_exclude_vpAg=MagickFalse;
11513 if (LocaleNCompare(value+i,"zccp",4) == 0)
11514 mng_info->ping_exclude_zCCP=MagickFalse;
11516 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11517 mng_info->ping_exclude_zTXt=MagickFalse;
11523 if (excluding != MagickFalse && logging != MagickFalse)
11525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11526 " Chunks to be excluded from the output PNG:");
11527 if (mng_info->ping_exclude_bKGD != MagickFalse)
11528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11530 if (mng_info->ping_exclude_cHRM != MagickFalse)
11531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11533 if (mng_info->ping_exclude_date != MagickFalse)
11534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11536 if (mng_info->ping_exclude_EXIF != MagickFalse)
11537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11539 if (mng_info->ping_exclude_gAMA != MagickFalse)
11540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11542 if (mng_info->ping_exclude_iCCP != MagickFalse)
11543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11546 if (mng_info->ping_exclude_iTXt != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 if (mng_info->ping_exclude_oFFs != MagickFalse)
11551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 if (mng_info->ping_exclude_pHYs != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 if (mng_info->ping_exclude_sRGB != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 if (mng_info->ping_exclude_tEXt != MagickFalse)
11560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 if (mng_info->ping_exclude_tRNS != MagickFalse)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 if (mng_info->ping_exclude_vpAg != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11568 if (mng_info->ping_exclude_zCCP != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 if (mng_info->ping_exclude_zTXt != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 mng_info->need_blob = MagickTrue;
11578 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11580 MngInfoFreeStruct(mng_info,&have_mng_structure);
11582 if (logging != MagickFalse)
11583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11588 #if defined(JNG_SUPPORTED)
11590 /* Write one JNG image */
11591 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11592 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11613 jng_alpha_compression_method,
11614 jng_alpha_sample_depth,
11621 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11622 " Enter WriteOneJNGImage()");
11624 blob=(unsigned char *) NULL;
11625 jpeg_image=(Image *) NULL;
11626 jpeg_image_info=(ImageInfo *) NULL;
11629 transparent=image_info->type==GrayscaleMatteType ||
11630 image_info->type==TrueColorMatteType;
11632 jng_alpha_sample_depth=0;
11633 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11634 jng_alpha_compression_method=0;
11636 if (image->matte != MagickFalse)
11638 /* if any pixels are transparent */
11639 transparent=MagickTrue;
11640 if (image_info->compression==JPEGCompression)
11641 jng_alpha_compression_method=8;
11651 /* Create JPEG blob, image, and image_info */
11652 if (logging != MagickFalse)
11653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11654 " Creating jpeg_image_info for alpha.");
11656 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11658 if (jpeg_image_info == (ImageInfo *) NULL)
11659 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11661 if (logging != MagickFalse)
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11663 " Creating jpeg_image.");
11665 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11667 if (jpeg_image == (Image *) NULL)
11668 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11670 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11671 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
11672 status=SeparateImage(jpeg_image);
11673 (void) SetPixelChannelMap(jpeg_image,channel_mask);
11674 jpeg_image->matte=MagickFalse;
11676 if (jng_quality >= 1000)
11677 jpeg_image_info->quality=jng_quality/1000;
11680 jpeg_image_info->quality=jng_quality;
11682 jpeg_image_info->type=GrayscaleType;
11683 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11684 (void) AcquireUniqueFilename(jpeg_image->filename);
11685 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11686 "%s",jpeg_image->filename);
11689 /* To do: check bit depth of PNG alpha channel */
11691 /* Check if image is grayscale. */
11692 if (image_info->type != TrueColorMatteType && image_info->type !=
11693 TrueColorType && ImageIsGray(image))
11698 if (jng_alpha_compression_method==0)
11703 /* Encode alpha as a grayscale PNG blob */
11704 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11705 &image->exception);
11706 if (logging != MagickFalse)
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 " Creating PNG blob.");
11711 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11712 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11713 jpeg_image_info->interlace=NoInterlace;
11715 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11716 &image->exception);
11718 /* Retrieve sample depth used */
11719 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11720 if (value != (char *) NULL)
11721 jng_alpha_sample_depth= (unsigned int) value[0];
11725 /* Encode alpha as a grayscale JPEG blob */
11727 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11728 &image->exception);
11730 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11731 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11732 jpeg_image_info->interlace=NoInterlace;
11733 if (logging != MagickFalse)
11734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11735 " Creating blob.");
11736 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11737 &image->exception);
11738 jng_alpha_sample_depth=8;
11740 if (logging != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " Successfully read jpeg_image into a blob, length=%.20g.",
11746 /* Destroy JPEG image and image_info */
11747 jpeg_image=DestroyImage(jpeg_image);
11748 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11749 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11752 /* Write JHDR chunk */
11753 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11754 PNGType(chunk,mng_JHDR);
11755 LogPNGChunk(logging,mng_JHDR,16L);
11756 PNGLong(chunk+4,(png_uint_32) image->columns);
11757 PNGLong(chunk+8,(png_uint_32) image->rows);
11758 chunk[12]=jng_color_type;
11759 chunk[13]=8; /* sample depth */
11760 chunk[14]=8; /*jng_image_compression_method */
11761 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11762 chunk[16]=jng_alpha_sample_depth;
11763 chunk[17]=jng_alpha_compression_method;
11764 chunk[18]=0; /*jng_alpha_filter_method */
11765 chunk[19]=0; /*jng_alpha_interlace_method */
11766 (void) WriteBlob(image,20,chunk);
11767 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11768 if (logging != MagickFalse)
11770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11771 " JNG width:%15lu",(unsigned long) image->columns);
11773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11774 " JNG height:%14lu",(unsigned long) image->rows);
11776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11777 " JNG color type:%10d",jng_color_type);
11779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780 " JNG sample depth:%8d",8);
11782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11783 " JNG compression:%9d",8);
11785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11786 " JNG interlace:%11d",0);
11788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11789 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792 " JNG alpha compression:%3d",jng_alpha_compression_method);
11794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG alpha filter:%8d",0);
11797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG alpha interlace:%5d",0);
11801 /* Write any JNG-chunk-b profiles */
11802 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11805 Write leading ancillary chunks
11811 Write JNG bKGD chunk
11822 if (jng_color_type == 8 || jng_color_type == 12)
11826 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11827 PNGType(chunk,mng_bKGD);
11828 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11829 red=ScaleQuantumToChar(image->background_color.red);
11830 green=ScaleQuantumToChar(image->background_color.green);
11831 blue=ScaleQuantumToChar(image->background_color.blue);
11838 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11839 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11842 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11845 Write JNG sRGB chunk
11847 (void) WriteBlobMSBULong(image,1L);
11848 PNGType(chunk,mng_sRGB);
11849 LogPNGChunk(logging,mng_sRGB,1L);
11851 if (image->rendering_intent != UndefinedIntent)
11852 chunk[4]=(unsigned char)
11853 Magick_RenderingIntent_to_PNG_RenderingIntent(
11854 (image->rendering_intent));
11857 chunk[4]=(unsigned char)
11858 Magick_RenderingIntent_to_PNG_RenderingIntent(
11859 (PerceptualIntent));
11861 (void) WriteBlob(image,5,chunk);
11862 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11866 if (image->gamma != 0.0)
11869 Write JNG gAMA chunk
11871 (void) WriteBlobMSBULong(image,4L);
11872 PNGType(chunk,mng_gAMA);
11873 LogPNGChunk(logging,mng_gAMA,4L);
11874 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11875 (void) WriteBlob(image,8,chunk);
11876 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11879 if ((mng_info->equal_chrms == MagickFalse) &&
11880 (image->chromaticity.red_primary.x != 0.0))
11886 Write JNG cHRM chunk
11888 (void) WriteBlobMSBULong(image,32L);
11889 PNGType(chunk,mng_cHRM);
11890 LogPNGChunk(logging,mng_cHRM,32L);
11891 primary=image->chromaticity.white_point;
11892 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11893 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11894 primary=image->chromaticity.red_primary;
11895 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11896 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11897 primary=image->chromaticity.green_primary;
11898 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11899 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11900 primary=image->chromaticity.blue_primary;
11901 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11902 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11903 (void) WriteBlob(image,36,chunk);
11904 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11908 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11911 Write JNG pHYs chunk
11913 (void) WriteBlobMSBULong(image,9L);
11914 PNGType(chunk,mng_pHYs);
11915 LogPNGChunk(logging,mng_pHYs,9L);
11916 if (image->units == PixelsPerInchResolution)
11918 PNGLong(chunk+4,(png_uint_32)
11919 (image->x_resolution*100.0/2.54+0.5));
11921 PNGLong(chunk+8,(png_uint_32)
11922 (image->y_resolution*100.0/2.54+0.5));
11929 if (image->units == PixelsPerCentimeterResolution)
11931 PNGLong(chunk+4,(png_uint_32)
11932 (image->x_resolution*100.0+0.5));
11934 PNGLong(chunk+8,(png_uint_32)
11935 (image->y_resolution*100.0+0.5));
11942 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11943 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11947 (void) WriteBlob(image,13,chunk);
11948 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11951 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11954 Write JNG oFFs chunk
11956 (void) WriteBlobMSBULong(image,9L);
11957 PNGType(chunk,mng_oFFs);
11958 LogPNGChunk(logging,mng_oFFs,9L);
11959 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11960 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11962 (void) WriteBlob(image,13,chunk);
11963 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11965 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11967 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11968 PNGType(chunk,mng_vpAg);
11969 LogPNGChunk(logging,mng_vpAg,9L);
11970 PNGLong(chunk+4,(png_uint_32) image->page.width);
11971 PNGLong(chunk+8,(png_uint_32) image->page.height);
11972 chunk[12]=0; /* unit = pixels */
11973 (void) WriteBlob(image,13,chunk);
11974 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11980 if (jng_alpha_compression_method==0)
11988 /* Write IDAT chunk header */
11989 if (logging != MagickFalse)
11990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11991 " Write IDAT chunks from blob, length=%.20g.",(double)
11994 /* Copy IDAT chunks */
11997 for (i=8; i<(ssize_t) length; i+=len+12)
11999 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12002 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12004 /* Found an IDAT chunk. */
12005 (void) WriteBlobMSBULong(image,(size_t) len);
12006 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12007 (void) WriteBlob(image,(size_t) len+4,p);
12008 (void) WriteBlobMSBULong(image,
12009 crc32(0,p,(uInt) len+4));
12014 if (logging != MagickFalse)
12015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12016 " Skipping %c%c%c%c chunk, length=%.20g.",
12017 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12024 /* Write JDAA chunk header */
12025 if (logging != MagickFalse)
12026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12027 " Write JDAA chunk, length=%.20g.",(double) length);
12028 (void) WriteBlobMSBULong(image,(size_t) length);
12029 PNGType(chunk,mng_JDAA);
12030 LogPNGChunk(logging,mng_JDAA,length);
12031 /* Write JDAT chunk(s) data */
12032 (void) WriteBlob(image,4,chunk);
12033 (void) WriteBlob(image,length,blob);
12034 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12037 blob=(unsigned char *) RelinquishMagickMemory(blob);
12040 /* Encode image as a JPEG blob */
12041 if (logging != MagickFalse)
12042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12043 " Creating jpeg_image_info.");
12044 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12045 if (jpeg_image_info == (ImageInfo *) NULL)
12046 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12048 if (logging != MagickFalse)
12049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12050 " Creating jpeg_image.");
12052 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12053 if (jpeg_image == (Image *) NULL)
12054 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12055 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12057 (void) AcquireUniqueFilename(jpeg_image->filename);
12058 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12059 jpeg_image->filename);
12061 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12062 &image->exception);
12064 if (logging != MagickFalse)
12065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12066 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12067 (double) jpeg_image->rows);
12069 if (jng_color_type == 8 || jng_color_type == 12)
12070 jpeg_image_info->type=GrayscaleType;
12072 jpeg_image_info->quality=jng_quality % 1000;
12073 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12074 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12076 if (logging != MagickFalse)
12077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12078 " Creating blob.");
12080 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
12082 if (logging != MagickFalse)
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 " Successfully read jpeg_image into a blob, length=%.20g.",
12088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12089 " Write JDAT chunk, length=%.20g.",(double) length);
12092 /* Write JDAT chunk(s) */
12093 (void) WriteBlobMSBULong(image,(size_t) length);
12094 PNGType(chunk,mng_JDAT);
12095 LogPNGChunk(logging,mng_JDAT,length);
12096 (void) WriteBlob(image,4,chunk);
12097 (void) WriteBlob(image,length,blob);
12098 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12100 jpeg_image=DestroyImage(jpeg_image);
12101 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12102 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12103 blob=(unsigned char *) RelinquishMagickMemory(blob);
12105 /* Write any JNG-chunk-e profiles */
12106 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12108 /* Write IEND chunk */
12109 (void) WriteBlobMSBULong(image,0L);
12110 PNGType(chunk,mng_IEND);
12111 LogPNGChunk(logging,mng_IEND,0);
12112 (void) WriteBlob(image,4,chunk);
12113 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12115 if (logging != MagickFalse)
12116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12117 " exit WriteOneJNGImage()");
12124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12128 % W r i t e J N G I m a g e %
12132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12134 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12136 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12138 % The format of the WriteJNGImage method is:
12140 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12141 % Image *image,ExceptionInfo *exception)
12143 % A description of each parameter follows:
12145 % o image_info: the image info.
12147 % o image: The image.
12149 % o exception: return any errors or warnings in this structure.
12151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12153 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12154 ExceptionInfo *exception)
12157 have_mng_structure,
12167 assert(image_info != (const ImageInfo *) NULL);
12168 assert(image_info->signature == MagickSignature);
12169 assert(image != (Image *) NULL);
12170 assert(image->signature == MagickSignature);
12171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12172 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12173 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12174 if (status == MagickFalse)
12178 Allocate a MngInfo structure.
12180 have_mng_structure=MagickFalse;
12181 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12182 if (mng_info == (MngInfo *) NULL)
12183 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12185 Initialize members of the MngInfo structure.
12187 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12188 mng_info->image=image;
12189 have_mng_structure=MagickTrue;
12191 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12193 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12194 (void) CloseBlob(image);
12196 (void) CatchImageException(image);
12197 MngInfoFreeStruct(mng_info,&have_mng_structure);
12198 if (logging != MagickFalse)
12199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12204 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12205 ExceptionInfo *exception)
12214 have_mng_structure,
12217 volatile MagickBooleanType
12229 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12230 defined(PNG_MNG_FEATURES_SUPPORTED)
12233 all_images_are_gray,
12243 volatile unsigned int
12254 #if (PNG_LIBPNG_VER < 10200)
12255 if (image_info->verbose)
12256 printf("Your PNG library (libpng-%s) is rather old.\n",
12257 PNG_LIBPNG_VER_STRING);
12263 assert(image_info != (const ImageInfo *) NULL);
12264 assert(image_info->signature == MagickSignature);
12265 assert(image != (Image *) NULL);
12266 assert(image->signature == MagickSignature);
12267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12268 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12269 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12270 if (status == MagickFalse)
12274 Allocate a MngInfo structure.
12276 have_mng_structure=MagickFalse;
12277 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12278 if (mng_info == (MngInfo *) NULL)
12279 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12281 Initialize members of the MngInfo structure.
12283 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12284 mng_info->image=image;
12285 have_mng_structure=MagickTrue;
12286 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12289 * See if user has requested a specific PNG subformat to be used
12290 * for all of the PNGs in the MNG being written, e.g.,
12292 * convert *.png png8:animation.mng
12294 * To do: check -define png:bit_depth and png:color_type as well,
12295 * or perhaps use mng:bit_depth and mng:color_type instead for
12299 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12300 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12301 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12303 write_jng=MagickFalse;
12304 if (image_info->compression == JPEGCompression)
12305 write_jng=MagickTrue;
12307 mng_info->adjoin=image_info->adjoin &&
12308 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12310 if (logging != MagickFalse)
12312 /* Log some info about the input */
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12317 " Checking input image(s)");
12319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320 " Image_info depth: %.20g",(double) image_info->depth);
12322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12323 " Type: %d",image_info->type);
12326 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12329 " Scene: %.20g",(double) scene++);
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 " Image depth: %.20g",(double) p->depth);
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12342 if (p->storage_class == PseudoClass)
12343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " Storage class: PseudoClass");
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348 " Storage class: DirectClass");
12351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12352 " Number of colors: %.20g",(double) p->colors);
12355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12356 " Number of colors: unspecified");
12358 if (mng_info->adjoin == MagickFalse)
12363 use_global_plte=MagickFalse;
12364 all_images_are_gray=MagickFalse;
12365 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12366 need_local_plte=MagickTrue;
12368 need_defi=MagickFalse;
12369 need_matte=MagickFalse;
12370 mng_info->framing_mode=1;
12371 mng_info->old_framing_mode=1;
12374 if (image_info->page != (char *) NULL)
12377 Determine image bounding box.
12379 SetGeometry(image,&mng_info->page);
12380 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12381 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12393 mng_info->page=image->page;
12394 need_geom=MagickTrue;
12395 if (mng_info->page.width || mng_info->page.height)
12396 need_geom=MagickFalse;
12398 Check all the scenes.
12400 initial_delay=image->delay;
12401 need_iterations=MagickFalse;
12402 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12403 mng_info->equal_physs=MagickTrue,
12404 mng_info->equal_gammas=MagickTrue;
12405 mng_info->equal_srgbs=MagickTrue;
12406 mng_info->equal_backgrounds=MagickTrue;
12408 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12409 defined(PNG_MNG_FEATURES_SUPPORTED)
12410 all_images_are_gray=MagickTrue;
12411 mng_info->equal_palettes=MagickFalse;
12412 need_local_plte=MagickFalse;
12414 for (next_image=image; next_image != (Image *) NULL; )
12418 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12419 mng_info->page.width=next_image->columns+next_image->page.x;
12421 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12422 mng_info->page.height=next_image->rows+next_image->page.y;
12425 if (next_image->page.x || next_image->page.y)
12426 need_defi=MagickTrue;
12428 if (next_image->matte)
12429 need_matte=MagickTrue;
12431 if ((int) next_image->dispose >= BackgroundDispose)
12432 if (next_image->matte || next_image->page.x || next_image->page.y ||
12433 ((next_image->columns < mng_info->page.width) &&
12434 (next_image->rows < mng_info->page.height)))
12435 mng_info->need_fram=MagickTrue;
12437 if (next_image->iterations)
12438 need_iterations=MagickTrue;
12440 final_delay=next_image->delay;
12442 if (final_delay != initial_delay || final_delay > 1UL*
12443 next_image->ticks_per_second)
12444 mng_info->need_fram=1;
12446 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12447 defined(PNG_MNG_FEATURES_SUPPORTED)
12449 check for global palette possibility.
12451 if (image->matte != MagickFalse)
12452 need_local_plte=MagickTrue;
12454 if (need_local_plte == 0)
12456 if (ImageIsGray(image) == MagickFalse)
12457 all_images_are_gray=MagickFalse;
12458 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12459 if (use_global_plte == 0)
12460 use_global_plte=mng_info->equal_palettes;
12461 need_local_plte=!mng_info->equal_palettes;
12464 if (GetNextImageInList(next_image) != (Image *) NULL)
12466 if (next_image->background_color.red !=
12467 next_image->next->background_color.red ||
12468 next_image->background_color.green !=
12469 next_image->next->background_color.green ||
12470 next_image->background_color.blue !=
12471 next_image->next->background_color.blue)
12472 mng_info->equal_backgrounds=MagickFalse;
12474 if (next_image->gamma != next_image->next->gamma)
12475 mng_info->equal_gammas=MagickFalse;
12477 if (next_image->rendering_intent !=
12478 next_image->next->rendering_intent)
12479 mng_info->equal_srgbs=MagickFalse;
12481 if ((next_image->units != next_image->next->units) ||
12482 (next_image->x_resolution != next_image->next->x_resolution) ||
12483 (next_image->y_resolution != next_image->next->y_resolution))
12484 mng_info->equal_physs=MagickFalse;
12486 if (mng_info->equal_chrms)
12488 if (next_image->chromaticity.red_primary.x !=
12489 next_image->next->chromaticity.red_primary.x ||
12490 next_image->chromaticity.red_primary.y !=
12491 next_image->next->chromaticity.red_primary.y ||
12492 next_image->chromaticity.green_primary.x !=
12493 next_image->next->chromaticity.green_primary.x ||
12494 next_image->chromaticity.green_primary.y !=
12495 next_image->next->chromaticity.green_primary.y ||
12496 next_image->chromaticity.blue_primary.x !=
12497 next_image->next->chromaticity.blue_primary.x ||
12498 next_image->chromaticity.blue_primary.y !=
12499 next_image->next->chromaticity.blue_primary.y ||
12500 next_image->chromaticity.white_point.x !=
12501 next_image->next->chromaticity.white_point.x ||
12502 next_image->chromaticity.white_point.y !=
12503 next_image->next->chromaticity.white_point.y)
12504 mng_info->equal_chrms=MagickFalse;
12508 next_image=GetNextImageInList(next_image);
12510 if (image_count < 2)
12512 mng_info->equal_backgrounds=MagickFalse;
12513 mng_info->equal_chrms=MagickFalse;
12514 mng_info->equal_gammas=MagickFalse;
12515 mng_info->equal_srgbs=MagickFalse;
12516 mng_info->equal_physs=MagickFalse;
12517 use_global_plte=MagickFalse;
12518 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12519 need_local_plte=MagickTrue;
12521 need_iterations=MagickFalse;
12524 if (mng_info->need_fram == MagickFalse)
12527 Only certain framing rates 100/n are exactly representable without
12528 the FRAM chunk but we'll allow some slop in VLC files
12530 if (final_delay == 0)
12532 if (need_iterations != MagickFalse)
12535 It's probably a GIF with loop; don't run it *too* fast.
12537 if (mng_info->adjoin)
12540 (void) ThrowMagickException(&image->exception,
12541 GetMagickModule(),CoderWarning,
12542 "input has zero delay between all frames; assuming",
12547 mng_info->ticks_per_second=0;
12549 if (final_delay != 0)
12550 mng_info->ticks_per_second=(png_uint_32)
12551 (image->ticks_per_second/final_delay);
12552 if (final_delay > 50)
12553 mng_info->ticks_per_second=2;
12555 if (final_delay > 75)
12556 mng_info->ticks_per_second=1;
12558 if (final_delay > 125)
12559 mng_info->need_fram=MagickTrue;
12561 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12562 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12563 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12564 1UL*image->ticks_per_second))
12565 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12568 if (mng_info->need_fram != MagickFalse)
12569 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12571 If pseudocolor, we should also check to see if all the
12572 palettes are identical and write a global PLTE if they are.
12576 Write the MNG version 1.0 signature and MHDR chunk.
12578 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12579 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12580 PNGType(chunk,mng_MHDR);
12581 LogPNGChunk(logging,mng_MHDR,28L);
12582 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12583 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12584 PNGLong(chunk+12,mng_info->ticks_per_second);
12585 PNGLong(chunk+16,0L); /* layer count=unknown */
12586 PNGLong(chunk+20,0L); /* frame count=unknown */
12587 PNGLong(chunk+24,0L); /* play time=unknown */
12592 if (need_defi || mng_info->need_fram || use_global_plte)
12593 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12596 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12601 if (need_defi || mng_info->need_fram || use_global_plte)
12602 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12605 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12613 if (need_defi || mng_info->need_fram || use_global_plte)
12614 PNGLong(chunk+28,11L); /* simplicity=LC */
12617 PNGLong(chunk+28,9L); /* simplicity=VLC */
12622 if (need_defi || mng_info->need_fram || use_global_plte)
12623 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12626 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12629 (void) WriteBlob(image,32,chunk);
12630 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12631 option=GetImageOption(image_info,"mng:need-cacheoff");
12632 if (option != (const char *) NULL)
12638 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12640 PNGType(chunk,mng_nEED);
12641 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12642 (void) WriteBlobMSBULong(image,(size_t) length);
12643 LogPNGChunk(logging,mng_nEED,(size_t) length);
12645 (void) WriteBlob(image,length,chunk);
12646 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12648 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12649 (GetNextImageInList(image) != (Image *) NULL) &&
12650 (image->iterations != 1))
12653 Write MNG TERM chunk
12655 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12656 PNGType(chunk,mng_TERM);
12657 LogPNGChunk(logging,mng_TERM,10L);
12658 chunk[4]=3; /* repeat animation */
12659 chunk[5]=0; /* show last frame when done */
12660 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12661 final_delay/MagickMax(image->ticks_per_second,1)));
12663 if (image->iterations == 0)
12664 PNGLong(chunk+10,PNG_UINT_31_MAX);
12667 PNGLong(chunk+10,(png_uint_32) image->iterations);
12669 if (logging != MagickFalse)
12671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12672 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12673 final_delay/MagickMax(image->ticks_per_second,1)));
12675 if (image->iterations == 0)
12676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12677 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12681 " Image iterations: %.20g",(double) image->iterations);
12683 (void) WriteBlob(image,14,chunk);
12684 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12687 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12689 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12690 mng_info->equal_srgbs)
12693 Write MNG sRGB chunk
12695 (void) WriteBlobMSBULong(image,1L);
12696 PNGType(chunk,mng_sRGB);
12697 LogPNGChunk(logging,mng_sRGB,1L);
12699 if (image->rendering_intent != UndefinedIntent)
12700 chunk[4]=(unsigned char)
12701 Magick_RenderingIntent_to_PNG_RenderingIntent(
12702 (image->rendering_intent));
12705 chunk[4]=(unsigned char)
12706 Magick_RenderingIntent_to_PNG_RenderingIntent(
12707 (PerceptualIntent));
12709 (void) WriteBlob(image,5,chunk);
12710 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12711 mng_info->have_write_global_srgb=MagickTrue;
12716 if (image->gamma && mng_info->equal_gammas)
12719 Write MNG gAMA chunk
12721 (void) WriteBlobMSBULong(image,4L);
12722 PNGType(chunk,mng_gAMA);
12723 LogPNGChunk(logging,mng_gAMA,4L);
12724 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12725 (void) WriteBlob(image,8,chunk);
12726 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12727 mng_info->have_write_global_gama=MagickTrue;
12729 if (mng_info->equal_chrms)
12735 Write MNG cHRM chunk
12737 (void) WriteBlobMSBULong(image,32L);
12738 PNGType(chunk,mng_cHRM);
12739 LogPNGChunk(logging,mng_cHRM,32L);
12740 primary=image->chromaticity.white_point;
12741 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12742 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12743 primary=image->chromaticity.red_primary;
12744 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12745 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12746 primary=image->chromaticity.green_primary;
12747 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12748 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12749 primary=image->chromaticity.blue_primary;
12750 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12751 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12752 (void) WriteBlob(image,36,chunk);
12753 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12754 mng_info->have_write_global_chrm=MagickTrue;
12757 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12760 Write MNG pHYs chunk
12762 (void) WriteBlobMSBULong(image,9L);
12763 PNGType(chunk,mng_pHYs);
12764 LogPNGChunk(logging,mng_pHYs,9L);
12766 if (image->units == PixelsPerInchResolution)
12768 PNGLong(chunk+4,(png_uint_32)
12769 (image->x_resolution*100.0/2.54+0.5));
12771 PNGLong(chunk+8,(png_uint_32)
12772 (image->y_resolution*100.0/2.54+0.5));
12779 if (image->units == PixelsPerCentimeterResolution)
12781 PNGLong(chunk+4,(png_uint_32)
12782 (image->x_resolution*100.0+0.5));
12784 PNGLong(chunk+8,(png_uint_32)
12785 (image->y_resolution*100.0+0.5));
12792 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12793 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
12797 (void) WriteBlob(image,13,chunk);
12798 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12801 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12802 or does not cover the entire frame.
12804 if (write_mng && (image->matte || image->page.x > 0 ||
12805 image->page.y > 0 || (image->page.width &&
12806 (image->page.width+image->page.x < mng_info->page.width))
12807 || (image->page.height && (image->page.height+image->page.y
12808 < mng_info->page.height))))
12810 (void) WriteBlobMSBULong(image,6L);
12811 PNGType(chunk,mng_BACK);
12812 LogPNGChunk(logging,mng_BACK,6L);
12813 red=ScaleQuantumToShort(image->background_color.red);
12814 green=ScaleQuantumToShort(image->background_color.green);
12815 blue=ScaleQuantumToShort(image->background_color.blue);
12816 PNGShort(chunk+4,red);
12817 PNGShort(chunk+6,green);
12818 PNGShort(chunk+8,blue);
12819 (void) WriteBlob(image,10,chunk);
12820 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12821 if (mng_info->equal_backgrounds)
12823 (void) WriteBlobMSBULong(image,6L);
12824 PNGType(chunk,mng_bKGD);
12825 LogPNGChunk(logging,mng_bKGD,6L);
12826 (void) WriteBlob(image,10,chunk);
12827 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12831 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12832 if ((need_local_plte == MagickFalse) &&
12833 (image->storage_class == PseudoClass) &&
12834 (all_images_are_gray == MagickFalse))
12840 Write MNG PLTE chunk
12842 data_length=3*image->colors;
12843 (void) WriteBlobMSBULong(image,data_length);
12844 PNGType(chunk,mng_PLTE);
12845 LogPNGChunk(logging,mng_PLTE,data_length);
12847 for (i=0; i < (ssize_t) image->colors; i++)
12849 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12850 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12851 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12854 (void) WriteBlob(image,data_length+4,chunk);
12855 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12856 mng_info->have_write_global_plte=MagickTrue;
12862 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12863 defined(PNG_MNG_FEATURES_SUPPORTED)
12864 mng_info->equal_palettes=MagickFalse;
12868 if (mng_info->adjoin)
12870 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12871 defined(PNG_MNG_FEATURES_SUPPORTED)
12873 If we aren't using a global palette for the entire MNG, check to
12874 see if we can use one for two or more consecutive images.
12876 if (need_local_plte && use_global_plte && !all_images_are_gray)
12878 if (mng_info->IsPalette)
12881 When equal_palettes is true, this image has the same palette
12882 as the previous PseudoClass image
12884 mng_info->have_write_global_plte=mng_info->equal_palettes;
12885 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12886 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12889 Write MNG PLTE chunk
12894 data_length=3*image->colors;
12895 (void) WriteBlobMSBULong(image,data_length);
12896 PNGType(chunk,mng_PLTE);
12897 LogPNGChunk(logging,mng_PLTE,data_length);
12899 for (i=0; i < (ssize_t) image->colors; i++)
12901 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12902 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12903 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12906 (void) WriteBlob(image,data_length+4,chunk);
12907 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12908 (uInt) (data_length+4)));
12909 mng_info->have_write_global_plte=MagickTrue;
12913 mng_info->have_write_global_plte=MagickFalse;
12924 previous_x=mng_info->page.x;
12925 previous_y=mng_info->page.y;
12932 mng_info->page=image->page;
12933 if ((mng_info->page.x != previous_x) ||
12934 (mng_info->page.y != previous_y))
12936 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12937 PNGType(chunk,mng_DEFI);
12938 LogPNGChunk(logging,mng_DEFI,12L);
12939 chunk[4]=0; /* object 0 MSB */
12940 chunk[5]=0; /* object 0 LSB */
12941 chunk[6]=0; /* visible */
12942 chunk[7]=0; /* abstract */
12943 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12944 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12945 (void) WriteBlob(image,16,chunk);
12946 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12951 mng_info->write_mng=write_mng;
12953 if ((int) image->dispose >= 3)
12954 mng_info->framing_mode=3;
12956 if (mng_info->need_fram && mng_info->adjoin &&
12957 ((image->delay != mng_info->delay) ||
12958 (mng_info->framing_mode != mng_info->old_framing_mode)))
12960 if (image->delay == mng_info->delay)
12963 Write a MNG FRAM chunk with the new framing mode.
12965 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12966 PNGType(chunk,mng_FRAM);
12967 LogPNGChunk(logging,mng_FRAM,1L);
12968 chunk[4]=(unsigned char) mng_info->framing_mode;
12969 (void) WriteBlob(image,5,chunk);
12970 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12975 Write a MNG FRAM chunk with the delay.
12977 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12978 PNGType(chunk,mng_FRAM);
12979 LogPNGChunk(logging,mng_FRAM,10L);
12980 chunk[4]=(unsigned char) mng_info->framing_mode;
12981 chunk[5]=0; /* frame name separator (no name) */
12982 chunk[6]=2; /* flag for changing default delay */
12983 chunk[7]=0; /* flag for changing frame timeout */
12984 chunk[8]=0; /* flag for changing frame clipping */
12985 chunk[9]=0; /* flag for changing frame sync_id */
12986 PNGLong(chunk+10,(png_uint_32)
12987 ((mng_info->ticks_per_second*
12988 image->delay)/MagickMax(image->ticks_per_second,1)));
12989 (void) WriteBlob(image,14,chunk);
12990 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12991 mng_info->delay=(png_uint_32) image->delay;
12993 mng_info->old_framing_mode=mng_info->framing_mode;
12996 #if defined(JNG_SUPPORTED)
12997 if (image_info->compression == JPEGCompression)
13002 if (logging != MagickFalse)
13003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13004 " Writing JNG object.");
13005 /* To do: specify the desired alpha compression method. */
13006 write_info=CloneImageInfo(image_info);
13007 write_info->compression=UndefinedCompression;
13008 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13009 write_info=DestroyImageInfo(write_info);
13014 if (logging != MagickFalse)
13015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13016 " Writing PNG object.");
13018 mng_info->need_blob = MagickFalse;
13019 mng_info->ping_preserve_colormap = MagickFalse;
13021 /* We don't want any ancillary chunks written */
13022 mng_info->ping_exclude_bKGD=MagickTrue;
13023 mng_info->ping_exclude_cHRM=MagickTrue;
13024 mng_info->ping_exclude_date=MagickTrue;
13025 mng_info->ping_exclude_EXIF=MagickTrue;
13026 mng_info->ping_exclude_gAMA=MagickTrue;
13027 mng_info->ping_exclude_iCCP=MagickTrue;
13028 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13029 mng_info->ping_exclude_oFFs=MagickTrue;
13030 mng_info->ping_exclude_pHYs=MagickTrue;
13031 mng_info->ping_exclude_sRGB=MagickTrue;
13032 mng_info->ping_exclude_tEXt=MagickTrue;
13033 mng_info->ping_exclude_tRNS=MagickTrue;
13034 mng_info->ping_exclude_vpAg=MagickTrue;
13035 mng_info->ping_exclude_zCCP=MagickTrue;
13036 mng_info->ping_exclude_zTXt=MagickTrue;
13038 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13041 if (status == MagickFalse)
13043 MngInfoFreeStruct(mng_info,&have_mng_structure);
13044 (void) CloseBlob(image);
13045 return(MagickFalse);
13047 (void) CatchImageException(image);
13048 if (GetNextImageInList(image) == (Image *) NULL)
13050 image=SyncNextImageInList(image);
13051 status=SetImageProgress(image,SaveImagesTag,scene++,
13052 GetImageListLength(image));
13054 if (status == MagickFalse)
13057 } while (mng_info->adjoin);
13061 while (GetPreviousImageInList(image) != (Image *) NULL)
13062 image=GetPreviousImageInList(image);
13064 Write the MEND chunk.
13066 (void) WriteBlobMSBULong(image,0x00000000L);
13067 PNGType(chunk,mng_MEND);
13068 LogPNGChunk(logging,mng_MEND,0L);
13069 (void) WriteBlob(image,4,chunk);
13070 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13073 Relinquish resources.
13075 (void) CloseBlob(image);
13076 MngInfoFreeStruct(mng_info,&have_mng_structure);
13078 if (logging != MagickFalse)
13079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13081 return(MagickTrue);
13083 #else /* PNG_LIBPNG_VER > 10011 */
13085 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13088 printf("Your PNG library is too old: You have libpng-%s\n",
13089 PNG_LIBPNG_VER_STRING);
13091 ThrowBinaryException(CoderError,"PNG library is too old",
13092 image_info->filename);
13095 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13097 return(WritePNGImage(image_info,image));
13099 #endif /* PNG_LIBPNG_VER > 10011 */