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 PixelInfos all have the same 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 (IsPixelInfoGray(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,ExceptionInfo *exception)
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,exception);
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,exception);
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 %.20g.",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,exception);
2771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg,exception);
2774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg,exception);
2777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2778 (int) ping_interlace_method);
2779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg,exception);
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,exception);
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,exception);
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,exception);
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,
3360 length=text[i].text_length;
3361 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3363 if (value == (char *) NULL)
3365 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3366 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3371 (void) ConcatenateMagickString(value,text[i].text,length+2);
3373 /* Don't save "density" or "units" property if we have a pHYs
3376 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3377 (LocaleCompare(text[i].key,"density") != 0 &&
3378 LocaleCompare(text[i].key,"units") != 0))
3379 (void) SetImageProperty(image,text[i].key,value,exception);
3381 if (logging != MagickFalse)
3383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384 " length: %lu",(unsigned long) length);
3385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386 " Keyword: %s",text[i].key);
3389 value=DestroyString(value);
3392 num_text_total += num_text;
3395 #ifdef MNG_OBJECT_BUFFERS
3397 Store the object if necessary.
3399 if (object_id && !mng_info->frozen[object_id])
3401 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3404 create a new object buffer.
3406 mng_info->ob[object_id]=(MngBuffer *)
3407 AcquireMagickMemory(sizeof(MngBuffer));
3409 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3411 mng_info->ob[object_id]->image=(Image *) NULL;
3412 mng_info->ob[object_id]->reference_count=1;
3416 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3417 mng_info->ob[object_id]->frozen)
3419 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3421 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3424 if (mng_info->ob[object_id]->frozen)
3425 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3426 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3427 "`%s'",image->filename);
3433 if (mng_info->ob[object_id]->image != (Image *) NULL)
3434 mng_info->ob[object_id]->image=DestroyImage
3435 (mng_info->ob[object_id]->image);
3437 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3440 if (mng_info->ob[object_id]->image != (Image *) NULL)
3441 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3444 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3445 ResourceLimitError,"Cloning image for object buffer failed",
3446 "`%s'",image->filename);
3448 if (ping_width > 250000L || ping_height > 250000L)
3449 png_error(ping,"PNG Image dimensions are too large.");
3451 mng_info->ob[object_id]->width=ping_width;
3452 mng_info->ob[object_id]->height=ping_height;
3453 mng_info->ob[object_id]->color_type=ping_color_type;
3454 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3455 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3456 mng_info->ob[object_id]->compression_method=
3457 ping_compression_method;
3458 mng_info->ob[object_id]->filter_method=ping_filter_method;
3460 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3469 Copy the PLTE to the object buffer.
3471 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3472 mng_info->ob[object_id]->plte_length=number_colors;
3474 for (i=0; i < number_colors; i++)
3476 mng_info->ob[object_id]->plte[i]=plte[i];
3481 mng_info->ob[object_id]->plte_length=0;
3486 /* Set image->matte to MagickTrue if the input colortype supports
3487 * alpha or if a valid tRNS chunk is present, no matter whether there
3488 * is actual transparency present.
3490 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3491 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3492 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3493 MagickTrue : MagickFalse;
3495 /* Set more properties for identify to retrieve */
3500 if (num_text_total != 0)
3502 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3503 (void) FormatLocaleString(msg,MaxTextExtent,
3504 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3505 (void) SetImageProperty(image,"PNG:text ",msg,
3509 if (num_raw_profiles != 0)
3511 (void) FormatLocaleString(msg,MaxTextExtent,
3512 "%d were found", num_raw_profiles);
3513 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg,
3517 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3519 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3520 "chunk was found (see Chromaticity, above)");
3521 (void) SetImageProperty(image,"PNG:cHRM ",msg,
3525 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3527 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3528 "chunk was found (see Background color, above)");
3529 (void) SetImageProperty(image,"PNG:bKGD ",msg,
3533 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3536 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3537 (void) SetImageProperty(image,"PNG:iCCP ",msg,
3540 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3541 (void) SetImageProperty(image,"PNG:tRNS ",msg,
3544 #if defined(PNG_sRGB_SUPPORTED)
3545 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3547 (void) FormatLocaleString(msg,MaxTextExtent,
3548 "intent=%d (See Rendering intent)",
3550 (void) SetImageProperty(image,"PNG:sRGB ",msg,
3555 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3557 (void) FormatLocaleString(msg,MaxTextExtent,
3558 "gamma=%.8g (See Gamma, above)",
3560 (void) SetImageProperty(image,"PNG:gAMA ",msg,
3564 #if defined(PNG_pHYs_SUPPORTED)
3565 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3567 (void) FormatLocaleString(msg,MaxTextExtent,
3568 "x_res=%.10g, y_res=%.10g, units=%d",
3569 (double) x_resolution,(double) y_resolution, unit_type);
3570 (void) SetImageProperty(image,"PNG:pHYs ",msg,
3575 #if defined(PNG_oFFs_SUPPORTED)
3576 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3578 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3579 (double) image->page.x,(double) image->page.y);
3580 (void) SetImageProperty(image,"PNG:oFFs ",msg,
3585 if ((image->page.width != 0 && image->page.width != image->columns) ||
3586 (image->page.height != 0 && image->page.height != image->rows))
3588 (void) FormatLocaleString(msg,MaxTextExtent,
3589 "width=%.20g, height=%.20g",
3590 (double) image->page.width,(double) image->page.height);
3591 (void) SetImageProperty(image,"PNG:vpAg ",msg,
3597 Relinquish resources.
3599 png_destroy_read_struct(&ping,&ping_info,&end_info);
3601 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3602 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3603 UnlockSemaphoreInfo(ping_semaphore);
3606 if (logging != MagickFalse)
3607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3608 " exit ReadOnePNGImage()");
3612 /* end of reading one PNG image */
3615 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3630 magic_number[MaxTextExtent];
3638 assert(image_info != (const ImageInfo *) NULL);
3639 assert(image_info->signature == MagickSignature);
3641 if (image_info->debug != MagickFalse)
3642 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3643 image_info->filename);
3645 assert(exception != (ExceptionInfo *) NULL);
3646 assert(exception->signature == MagickSignature);
3647 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3648 image=AcquireImage(image_info,exception);
3649 mng_info=(MngInfo *) NULL;
3650 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3652 if (status == MagickFalse)
3653 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3656 Verify PNG signature.
3658 count=ReadBlob(image,8,(unsigned char *) magic_number);
3660 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3661 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3664 Allocate a MngInfo structure.
3666 have_mng_structure=MagickFalse;
3667 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3669 if (mng_info == (MngInfo *) NULL)
3670 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3673 Initialize members of the MngInfo structure.
3675 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3676 mng_info->image=image;
3677 have_mng_structure=MagickTrue;
3680 image=ReadOnePNGImage(mng_info,image_info,exception);
3681 MngInfoFreeStruct(mng_info,&have_mng_structure);
3683 if (image == (Image *) NULL)
3685 if (previous != (Image *) NULL)
3687 if (previous->signature != MagickSignature)
3688 ThrowReaderException(CorruptImageError,"CorruptImage");
3690 (void) CloseBlob(previous);
3691 (void) DestroyImageList(previous);
3694 if (logging != MagickFalse)
3695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3696 "exit ReadPNGImage() with error");
3698 return((Image *) NULL);
3701 (void) CloseBlob(image);
3703 if ((image->columns == 0) || (image->rows == 0))
3705 if (logging != MagickFalse)
3706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3707 "exit ReadPNGImage() with error.");
3709 ThrowReaderException(CorruptImageError,"CorruptImage");
3712 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3714 (void) SetImageType(image,TrueColorType,exception);
3715 image->matte=MagickFalse;
3718 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3719 (void) SetImageType(image,TrueColorMatteType,exception);
3721 if (logging != MagickFalse)
3722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3723 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3724 (double) image->page.width,(double) image->page.height,
3725 (double) image->page.x,(double) image->page.y);
3727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3735 #if defined(JNG_SUPPORTED)
3737 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3741 % R e a d O n e J N G I m a g e %
3745 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3747 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3748 % (minus the 8-byte signature) and returns it. It allocates the memory
3749 % necessary for the new Image structure and returns a pointer to the new
3752 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3754 % The format of the ReadOneJNGImage method is:
3756 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3757 % ExceptionInfo *exception)
3759 % A description of each parameter follows:
3761 % o mng_info: Specifies a pointer to a MngInfo structure.
3763 % o image_info: the image info.
3765 % o exception: return any errors or warnings in this structure.
3768 static Image *ReadOneJNGImage(MngInfo *mng_info,
3769 const ImageInfo *image_info, ExceptionInfo *exception)
3796 jng_image_sample_depth,
3797 jng_image_compression_method,
3798 jng_image_interlace_method,
3799 jng_alpha_sample_depth,
3800 jng_alpha_compression_method,
3801 jng_alpha_filter_method,
3802 jng_alpha_interlace_method;
3804 register const Quantum
3814 register unsigned char
3825 jng_alpha_compression_method=0;
3826 jng_alpha_sample_depth=8;
3830 alpha_image=(Image *) NULL;
3831 color_image=(Image *) NULL;
3832 alpha_image_info=(ImageInfo *) NULL;
3833 color_image_info=(ImageInfo *) NULL;
3835 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3836 " Enter ReadOneJNGImage()");
3838 image=mng_info->image;
3840 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3843 Allocate next image structure.
3845 if (logging != MagickFalse)
3846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3847 " AcquireNextImage()");
3849 AcquireNextImage(image_info,image,exception);
3851 if (GetNextImageInList(image) == (Image *) NULL)
3852 return((Image *) NULL);
3854 image=SyncNextImageInList(image);
3856 mng_info->image=image;
3859 Signature bytes have already been read.
3862 read_JSEP=MagickFalse;
3863 reading_idat=MagickFalse;
3864 skip_to_iend=MagickFalse;
3868 type[MaxTextExtent];
3877 Read a new JNG chunk.
3879 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3880 2*GetBlobSize(image));
3882 if (status == MagickFalse)
3886 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3887 length=ReadBlobMSBLong(image);
3888 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3890 if (logging != MagickFalse)
3891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3892 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3893 type[0],type[1],type[2],type[3],(double) length);
3895 if (length > PNG_UINT_31_MAX || count == 0)
3896 ThrowReaderException(CorruptImageError,"CorruptImage");
3899 chunk=(unsigned char *) NULL;
3903 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3905 if (chunk == (unsigned char *) NULL)
3906 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3908 for (i=0; i < (ssize_t) length; i++)
3909 chunk[i]=(unsigned char) ReadBlobByte(image);
3914 (void) ReadBlobMSBLong(image); /* read crc word */
3919 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3924 if (memcmp(type,mng_JHDR,4) == 0)
3928 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3929 (p[2] << 8) | p[3]);
3930 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3931 (p[6] << 8) | p[7]);
3932 jng_color_type=p[8];
3933 jng_image_sample_depth=p[9];
3934 jng_image_compression_method=p[10];
3935 jng_image_interlace_method=p[11];
3937 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3940 jng_alpha_sample_depth=p[12];
3941 jng_alpha_compression_method=p[13];
3942 jng_alpha_filter_method=p[14];
3943 jng_alpha_interlace_method=p[15];
3945 if (logging != MagickFalse)
3947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3948 " jng_width: %16lu",(unsigned long) jng_width);
3950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3951 " jng_width: %16lu",(unsigned long) jng_height);
3953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3954 " jng_color_type: %16d",jng_color_type);
3956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_image_sample_depth: %3d",
3958 jng_image_sample_depth);
3960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_image_compression_method:%3d",
3962 jng_image_compression_method);
3964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_image_interlace_method: %3d",
3966 jng_image_interlace_method);
3968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_sample_depth: %3d",
3970 jng_alpha_sample_depth);
3972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3973 " jng_alpha_compression_method:%3d",
3974 jng_alpha_compression_method);
3976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977 " jng_alpha_filter_method: %3d",
3978 jng_alpha_filter_method);
3980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3981 " jng_alpha_interlace_method: %3d",
3982 jng_alpha_interlace_method);
3987 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3993 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3994 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3995 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3998 o create color_image
3999 o open color_blob, attached to color_image
4000 o if (color type has alpha)
4001 open alpha_blob, attached to alpha_image
4004 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4006 if (color_image_info == (ImageInfo *) NULL)
4007 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4009 GetImageInfo(color_image_info);
4010 color_image=AcquireImage(color_image_info,exception);
4012 if (color_image == (Image *) NULL)
4013 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4015 if (logging != MagickFalse)
4016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4017 " Creating color_blob.");
4019 (void) AcquireUniqueFilename(color_image->filename);
4020 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4023 if (status == MagickFalse)
4024 return((Image *) NULL);
4026 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4028 alpha_image_info=(ImageInfo *)
4029 AcquireMagickMemory(sizeof(ImageInfo));
4031 if (alpha_image_info == (ImageInfo *) NULL)
4032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4034 GetImageInfo(alpha_image_info);
4035 alpha_image=AcquireImage(alpha_image_info,exception);
4037 if (alpha_image == (Image *) NULL)
4039 alpha_image=DestroyImage(alpha_image);
4040 ThrowReaderException(ResourceLimitError,
4041 "MemoryAllocationFailed");
4044 if (logging != MagickFalse)
4045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4046 " Creating alpha_blob.");
4048 (void) AcquireUniqueFilename(alpha_image->filename);
4049 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4052 if (status == MagickFalse)
4053 return((Image *) NULL);
4055 if (jng_alpha_compression_method == 0)
4060 if (logging != MagickFalse)
4061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4062 " Writing IHDR chunk to alpha_blob.");
4064 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4065 "\211PNG\r\n\032\n");
4067 (void) WriteBlobMSBULong(alpha_image,13L);
4068 PNGType(data,mng_IHDR);
4069 LogPNGChunk(logging,mng_IHDR,13L);
4070 PNGLong(data+4,jng_width);
4071 PNGLong(data+8,jng_height);
4072 data[12]=jng_alpha_sample_depth;
4073 data[13]=0; /* color_type gray */
4074 data[14]=0; /* compression method 0 */
4075 data[15]=0; /* filter_method 0 */
4076 data[16]=0; /* interlace_method 0 */
4077 (void) WriteBlob(alpha_image,17,data);
4078 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4081 reading_idat=MagickTrue;
4084 if (memcmp(type,mng_JDAT,4) == 0)
4086 /* Copy chunk to color_image->blob */
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " Copying JDAT chunk data to color_blob.");
4092 (void) WriteBlob(color_image,length,chunk);
4095 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4100 if (memcmp(type,mng_IDAT,4) == 0)
4105 /* Copy IDAT header and chunk data to alpha_image->blob */
4107 if (image_info->ping == MagickFalse)
4109 if (logging != MagickFalse)
4110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4111 " Copying IDAT chunk data to alpha_blob.");
4113 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4114 PNGType(data,mng_IDAT);
4115 LogPNGChunk(logging,mng_IDAT,length);
4116 (void) WriteBlob(alpha_image,4,data);
4117 (void) WriteBlob(alpha_image,length,chunk);
4118 (void) WriteBlobMSBULong(alpha_image,
4119 crc32(crc32(0,data,4),chunk,(uInt) length));
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4128 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4130 /* Copy chunk data to alpha_image->blob */
4132 if (image_info->ping == MagickFalse)
4134 if (logging != MagickFalse)
4135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4136 " Copying JDAA chunk data to alpha_blob.");
4138 (void) WriteBlob(alpha_image,length,chunk);
4142 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4147 if (memcmp(type,mng_JSEP,4) == 0)
4149 read_JSEP=MagickTrue;
4152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4157 if (memcmp(type,mng_bKGD,4) == 0)
4161 image->background_color.red=ScaleCharToQuantum(p[1]);
4162 image->background_color.green=image->background_color.red;
4163 image->background_color.blue=image->background_color.red;
4168 image->background_color.red=ScaleCharToQuantum(p[1]);
4169 image->background_color.green=ScaleCharToQuantum(p[3]);
4170 image->background_color.blue=ScaleCharToQuantum(p[5]);
4173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4177 if (memcmp(type,mng_gAMA,4) == 0)
4180 image->gamma=((float) mng_get_long(p))*0.00001;
4182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4186 if (memcmp(type,mng_cHRM,4) == 0)
4190 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4191 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4192 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4193 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4194 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4195 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4196 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4197 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4200 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4204 if (memcmp(type,mng_sRGB,4) == 0)
4208 image->rendering_intent=
4209 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4210 image->gamma=0.45455f;
4211 image->chromaticity.red_primary.x=0.6400f;
4212 image->chromaticity.red_primary.y=0.3300f;
4213 image->chromaticity.green_primary.x=0.3000f;
4214 image->chromaticity.green_primary.y=0.6000f;
4215 image->chromaticity.blue_primary.x=0.1500f;
4216 image->chromaticity.blue_primary.y=0.0600f;
4217 image->chromaticity.white_point.x=0.3127f;
4218 image->chromaticity.white_point.y=0.3290f;
4221 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4225 if (memcmp(type,mng_oFFs,4) == 0)
4229 image->page.x=(ssize_t) mng_get_long(p);
4230 image->page.y=(ssize_t) mng_get_long(&p[4]);
4232 if ((int) p[8] != 0)
4234 image->page.x/=10000;
4235 image->page.y/=10000;
4240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4245 if (memcmp(type,mng_pHYs,4) == 0)
4249 image->x_resolution=(double) mng_get_long(p);
4250 image->y_resolution=(double) mng_get_long(&p[4]);
4251 if ((int) p[8] == PNG_RESOLUTION_METER)
4253 image->units=PixelsPerCentimeterResolution;
4254 image->x_resolution=image->x_resolution/100.0f;
4255 image->y_resolution=image->y_resolution/100.0f;
4259 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4264 if (memcmp(type,mng_iCCP,4) == 0)
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4277 if (memcmp(type,mng_IEND,4))
4287 Finish up reading image data:
4289 o read main image from color_blob.
4293 o if (color_type has alpha)
4294 if alpha_encoding is PNG
4295 read secondary image from alpha_blob via ReadPNG
4296 if alpha_encoding is JPEG
4297 read secondary image from alpha_blob via ReadJPEG
4301 o copy intensity of secondary image into
4302 alpha samples of main image.
4304 o destroy the secondary image.
4307 (void) CloseBlob(color_image);
4309 if (logging != MagickFalse)
4310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4311 " Reading jng_image from color_blob.");
4313 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4314 color_image->filename);
4316 color_image_info->ping=MagickFalse; /* To do: avoid this */
4317 jng_image=ReadImage(color_image_info,exception);
4319 if (jng_image == (Image *) NULL)
4320 return((Image *) NULL);
4322 (void) RelinquishUniqueFileResource(color_image->filename);
4323 color_image=DestroyImage(color_image);
4324 color_image_info=DestroyImageInfo(color_image_info);
4326 if (jng_image == (Image *) NULL)
4327 return((Image *) NULL);
4329 if (logging != MagickFalse)
4330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331 " Copying jng_image pixels to main image.");
4333 image->rows=jng_height;
4334 image->columns=jng_width;
4336 for (y=0; y < (ssize_t) image->rows; y++)
4338 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4339 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4340 for (x=(ssize_t) image->columns; x != 0; x--)
4342 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4343 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4344 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4345 q+=GetPixelChannels(image);
4346 s+=GetPixelChannels(jng_image);
4349 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4353 jng_image=DestroyImage(jng_image);
4355 if (image_info->ping == MagickFalse)
4357 if (jng_color_type >= 12)
4359 if (jng_alpha_compression_method == 0)
4363 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4364 PNGType(data,mng_IEND);
4365 LogPNGChunk(logging,mng_IEND,0L);
4366 (void) WriteBlob(alpha_image,4,data);
4367 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4370 (void) CloseBlob(alpha_image);
4372 if (logging != MagickFalse)
4373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4374 " Reading alpha from alpha_blob.");
4376 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4377 "%s",alpha_image->filename);
4379 jng_image=ReadImage(alpha_image_info,exception);
4381 if (jng_image != (Image *) NULL)
4382 for (y=0; y < (ssize_t) image->rows; y++)
4384 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4386 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4388 if (image->matte != MagickFalse)
4389 for (x=(ssize_t) image->columns; x != 0; x--)
4391 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4392 q+=GetPixelChannels(image);
4393 s+=GetPixelChannels(jng_image);
4397 for (x=(ssize_t) image->columns; x != 0; x--)
4399 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4400 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4401 image->matte=MagickTrue;
4402 q+=GetPixelChannels(image);
4403 s+=GetPixelChannels(jng_image);
4406 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4409 (void) RelinquishUniqueFileResource(alpha_image->filename);
4410 alpha_image=DestroyImage(alpha_image);
4411 alpha_image_info=DestroyImageInfo(alpha_image_info);
4412 if (jng_image != (Image *) NULL)
4413 jng_image=DestroyImage(jng_image);
4417 /* Read the JNG image. */
4419 if (mng_info->mng_type == 0)
4421 mng_info->mng_width=jng_width;
4422 mng_info->mng_height=jng_height;
4425 if (image->page.width == 0 && image->page.height == 0)
4427 image->page.width=jng_width;
4428 image->page.height=jng_height;
4431 if (image->page.x == 0 && image->page.y == 0)
4433 image->page.x=mng_info->x_off[mng_info->object_id];
4434 image->page.y=mng_info->y_off[mng_info->object_id];
4439 image->page.y=mng_info->y_off[mng_info->object_id];
4442 mng_info->image_found++;
4443 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4444 2*GetBlobSize(image));
4446 if (logging != MagickFalse)
4447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4448 " exit ReadOneJNGImage()");
4454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4458 % R e a d J N G I m a g e %
4462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4464 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4465 % (including the 8-byte signature) and returns it. It allocates the memory
4466 % necessary for the new Image structure and returns a pointer to the new
4469 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4471 % The format of the ReadJNGImage method is:
4473 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4476 % A description of each parameter follows:
4478 % o image_info: the image info.
4480 % o exception: return any errors or warnings in this structure.
4484 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4499 magic_number[MaxTextExtent];
4507 assert(image_info != (const ImageInfo *) NULL);
4508 assert(image_info->signature == MagickSignature);
4509 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4510 assert(exception != (ExceptionInfo *) NULL);
4511 assert(exception->signature == MagickSignature);
4512 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4513 image=AcquireImage(image_info,exception);
4514 mng_info=(MngInfo *) NULL;
4515 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4517 if (status == MagickFalse)
4518 return((Image *) NULL);
4520 if (LocaleCompare(image_info->magick,"JNG") != 0)
4521 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4523 /* Verify JNG signature. */
4525 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4527 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4528 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4530 /* Allocate a MngInfo structure. */
4532 have_mng_structure=MagickFalse;
4533 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4535 if (mng_info == (MngInfo *) NULL)
4536 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4538 /* Initialize members of the MngInfo structure. */
4540 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4541 have_mng_structure=MagickTrue;
4543 mng_info->image=image;
4545 image=ReadOneJNGImage(mng_info,image_info,exception);
4546 MngInfoFreeStruct(mng_info,&have_mng_structure);
4548 if (image == (Image *) NULL)
4550 if (IsImageObject(previous) != MagickFalse)
4552 (void) CloseBlob(previous);
4553 (void) DestroyImageList(previous);
4556 if (logging != MagickFalse)
4557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4558 "exit ReadJNGImage() with error");
4560 return((Image *) NULL);
4562 (void) CloseBlob(image);
4564 if (image->columns == 0 || image->rows == 0)
4566 if (logging != MagickFalse)
4567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4568 "exit ReadJNGImage() with error");
4570 ThrowReaderException(CorruptImageError,"CorruptImage");
4573 if (logging != MagickFalse)
4574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4580 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4583 page_geometry[MaxTextExtent];
4616 #if defined(MNG_INSERT_LAYERS)
4618 mng_background_color;
4621 register unsigned char
4636 #if defined(MNG_INSERT_LAYERS)
4641 volatile unsigned int
4642 #ifdef MNG_OBJECT_BUFFERS
4643 mng_background_object=0,
4645 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4648 default_frame_timeout,
4650 #if defined(MNG_INSERT_LAYERS)
4656 /* These delays are all measured in image ticks_per_second,
4657 * not in MNG ticks_per_second
4660 default_frame_delay,
4664 #if defined(MNG_INSERT_LAYERS)
4673 previous_fb.bottom=0;
4675 previous_fb.right=0;
4677 default_fb.bottom=0;
4681 /* Open image file. */
4683 assert(image_info != (const ImageInfo *) NULL);
4684 assert(image_info->signature == MagickSignature);
4685 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4686 assert(exception != (ExceptionInfo *) NULL);
4687 assert(exception->signature == MagickSignature);
4688 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4689 image=AcquireImage(image_info,exception);
4690 mng_info=(MngInfo *) NULL;
4691 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4693 if (status == MagickFalse)
4694 return((Image *) NULL);
4696 first_mng_object=MagickFalse;
4698 have_mng_structure=MagickFalse;
4700 /* Allocate a MngInfo structure. */
4702 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4704 if (mng_info == (MngInfo *) NULL)
4705 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4707 /* Initialize members of the MngInfo structure. */
4709 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4710 mng_info->image=image;
4711 have_mng_structure=MagickTrue;
4713 if (LocaleCompare(image_info->magick,"MNG") == 0)
4716 magic_number[MaxTextExtent];
4718 /* Verify MNG signature. */
4719 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4720 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4721 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4723 /* Initialize some nonzero members of the MngInfo structure. */
4724 for (i=0; i < MNG_MAX_OBJECTS; i++)
4726 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4727 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4729 mng_info->exists[0]=MagickTrue;
4732 first_mng_object=MagickTrue;
4734 #if defined(MNG_INSERT_LAYERS)
4735 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4737 default_frame_delay=0;
4738 default_frame_timeout=0;
4741 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4743 skip_to_iend=MagickFalse;
4744 term_chunk_found=MagickFalse;
4745 mng_info->framing_mode=1;
4746 #if defined(MNG_INSERT_LAYERS)
4747 mandatory_back=MagickFalse;
4749 #if defined(MNG_INSERT_LAYERS)
4750 mng_background_color=image->background_color;
4752 default_fb=mng_info->frame;
4753 previous_fb=mng_info->frame;
4757 type[MaxTextExtent];
4759 if (LocaleCompare(image_info->magick,"MNG") == 0)
4768 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4769 length=ReadBlobMSBLong(image);
4770 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4772 if (logging != MagickFalse)
4773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4774 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4775 type[0],type[1],type[2],type[3],(double) length);
4777 if (length > PNG_UINT_31_MAX)
4781 ThrowReaderException(CorruptImageError,"CorruptImage");
4784 chunk=(unsigned char *) NULL;
4788 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4790 if (chunk == (unsigned char *) NULL)
4791 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4793 for (i=0; i < (ssize_t) length; i++)
4794 chunk[i]=(unsigned char) ReadBlobByte(image);
4799 (void) ReadBlobMSBLong(image); /* read crc word */
4801 #if !defined(JNG_SUPPORTED)
4802 if (memcmp(type,mng_JHDR,4) == 0)
4804 skip_to_iend=MagickTrue;
4806 if (mng_info->jhdr_warning == 0)
4807 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4808 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4810 mng_info->jhdr_warning++;
4813 if (memcmp(type,mng_DHDR,4) == 0)
4815 skip_to_iend=MagickTrue;
4817 if (mng_info->dhdr_warning == 0)
4818 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4819 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4821 mng_info->dhdr_warning++;
4823 if (memcmp(type,mng_MEND,4) == 0)
4828 if (memcmp(type,mng_IEND,4) == 0)
4829 skip_to_iend=MagickFalse;
4832 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4834 if (logging != MagickFalse)
4835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4841 if (memcmp(type,mng_MHDR,4) == 0)
4843 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4844 (p[2] << 8) | p[3]);
4846 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4847 (p[6] << 8) | p[7]);
4849 if (logging != MagickFalse)
4851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4852 " MNG width: %.20g",(double) mng_info->mng_width);
4853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4854 " MNG height: %.20g",(double) mng_info->mng_height);
4858 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4860 if (mng_info->ticks_per_second == 0)
4861 default_frame_delay=0;
4864 default_frame_delay=1UL*image->ticks_per_second/
4865 mng_info->ticks_per_second;
4867 frame_delay=default_frame_delay;
4873 simplicity=(size_t) mng_get_long(p);
4876 mng_type=1; /* Full MNG */
4878 if ((simplicity != 0) && ((simplicity | 11) == 11))
4879 mng_type=2; /* LC */
4881 if ((simplicity != 0) && ((simplicity | 9) == 9))
4882 mng_type=3; /* VLC */
4884 #if defined(MNG_INSERT_LAYERS)
4886 insert_layers=MagickTrue;
4888 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4890 /* Allocate next image structure. */
4891 AcquireNextImage(image_info,image,exception);
4893 if (GetNextImageInList(image) == (Image *) NULL)
4894 return((Image *) NULL);
4896 image=SyncNextImageInList(image);
4897 mng_info->image=image;
4900 if ((mng_info->mng_width > 65535L) ||
4901 (mng_info->mng_height > 65535L))
4902 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4904 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4905 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4906 mng_info->mng_height);
4908 mng_info->frame.left=0;
4909 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4910 mng_info->frame.top=0;
4911 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4912 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4914 for (i=0; i < MNG_MAX_OBJECTS; i++)
4915 mng_info->object_clip[i]=mng_info->frame;
4917 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4921 if (memcmp(type,mng_TERM,4) == 0)
4932 final_delay=(png_uint_32) mng_get_long(&p[2]);
4933 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4935 if (mng_iterations == PNG_UINT_31_MAX)
4938 image->iterations=mng_iterations;
4939 term_chunk_found=MagickTrue;
4942 if (logging != MagickFalse)
4944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4945 " repeat=%d",repeat);
4947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4948 " final_delay=%.20g",(double) final_delay);
4950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4951 " image->iterations=%.20g",(double) image->iterations);
4954 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4957 if (memcmp(type,mng_DEFI,4) == 0)
4960 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4961 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4964 object_id=(p[0] << 8) | p[1];
4966 if (mng_type == 2 && object_id != 0)
4967 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4968 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4971 if (object_id > MNG_MAX_OBJECTS)
4974 Instead ofsuing a warning we should allocate a larger
4975 MngInfo structure and continue.
4977 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4978 CoderError,"object id too large","`%s'",image->filename);
4979 object_id=MNG_MAX_OBJECTS;
4982 if (mng_info->exists[object_id])
4983 if (mng_info->frozen[object_id])
4985 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4986 (void) ThrowMagickException(&image->exception,
4987 GetMagickModule(),CoderError,
4988 "DEFI cannot redefine a frozen MNG object","`%s'",
4993 mng_info->exists[object_id]=MagickTrue;
4996 mng_info->invisible[object_id]=p[2];
4999 Extract object offset info.
5003 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5004 (p[5] << 16) | (p[6] << 8) | p[7]);
5006 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5007 (p[9] << 16) | (p[10] << 8) | p[11]);
5009 if (logging != MagickFalse)
5011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5012 " x_off[%d]: %.20g",object_id,(double)
5013 mng_info->x_off[object_id]);
5015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5016 " y_off[%d]: %.20g",object_id,(double)
5017 mng_info->y_off[object_id]);
5022 Extract object clipping info.
5025 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5028 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5031 if (memcmp(type,mng_bKGD,4) == 0)
5033 mng_info->have_global_bkgd=MagickFalse;
5037 mng_info->mng_global_bkgd.red=
5038 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5040 mng_info->mng_global_bkgd.green=
5041 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5043 mng_info->mng_global_bkgd.blue=
5044 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5046 mng_info->have_global_bkgd=MagickTrue;
5049 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5052 if (memcmp(type,mng_BACK,4) == 0)
5054 #if defined(MNG_INSERT_LAYERS)
5056 mandatory_back=p[6];
5061 if (mandatory_back && length > 5)
5063 mng_background_color.red=
5064 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5066 mng_background_color.green=
5067 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5069 mng_background_color.blue=
5070 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5072 mng_background_color.alpha=OpaqueAlpha;
5075 #ifdef MNG_OBJECT_BUFFERS
5077 mng_background_object=(p[7] << 8) | p[8];
5080 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5084 if (memcmp(type,mng_PLTE,4) == 0)
5086 /* Read global PLTE. */
5088 if (length && (length < 769))
5090 if (mng_info->global_plte == (png_colorp) NULL)
5091 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5092 sizeof(*mng_info->global_plte));
5094 for (i=0; i < (ssize_t) (length/3); i++)
5096 mng_info->global_plte[i].red=p[3*i];
5097 mng_info->global_plte[i].green=p[3*i+1];
5098 mng_info->global_plte[i].blue=p[3*i+2];
5101 mng_info->global_plte_length=(unsigned int) (length/3);
5104 for ( ; i < 256; i++)
5106 mng_info->global_plte[i].red=i;
5107 mng_info->global_plte[i].green=i;
5108 mng_info->global_plte[i].blue=i;
5112 mng_info->global_plte_length=256;
5115 mng_info->global_plte_length=0;
5117 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5121 if (memcmp(type,mng_tRNS,4) == 0)
5123 /* read global tRNS */
5126 for (i=0; i < (ssize_t) length; i++)
5127 mng_info->global_trns[i]=p[i];
5130 for ( ; i < 256; i++)
5131 mng_info->global_trns[i]=255;
5133 mng_info->global_trns_length=(unsigned int) length;
5134 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5137 if (memcmp(type,mng_gAMA,4) == 0)
5144 igamma=mng_get_long(p);
5145 mng_info->global_gamma=((float) igamma)*0.00001;
5146 mng_info->have_global_gama=MagickTrue;
5150 mng_info->have_global_gama=MagickFalse;
5152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5156 if (memcmp(type,mng_cHRM,4) == 0)
5158 /* Read global cHRM */
5162 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5163 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5164 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5165 mng_info->global_chrm.red_primary.y=0.00001*
5166 mng_get_long(&p[12]);
5167 mng_info->global_chrm.green_primary.x=0.00001*
5168 mng_get_long(&p[16]);
5169 mng_info->global_chrm.green_primary.y=0.00001*
5170 mng_get_long(&p[20]);
5171 mng_info->global_chrm.blue_primary.x=0.00001*
5172 mng_get_long(&p[24]);
5173 mng_info->global_chrm.blue_primary.y=0.00001*
5174 mng_get_long(&p[28]);
5175 mng_info->have_global_chrm=MagickTrue;
5178 mng_info->have_global_chrm=MagickFalse;
5180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5184 if (memcmp(type,mng_sRGB,4) == 0)
5191 mng_info->global_srgb_intent=
5192 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5193 mng_info->have_global_srgb=MagickTrue;
5196 mng_info->have_global_srgb=MagickFalse;
5198 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5202 if (memcmp(type,mng_iCCP,4) == 0)
5210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5215 if (memcmp(type,mng_FRAM,4) == 0)
5218 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5219 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5222 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5223 image->delay=frame_delay;
5225 frame_delay=default_frame_delay;
5226 frame_timeout=default_frame_timeout;
5231 mng_info->framing_mode=p[0];
5233 if (logging != MagickFalse)
5234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5235 " Framing_mode=%d",mng_info->framing_mode);
5239 /* Note the delay and frame clipping boundaries. */
5241 p++; /* framing mode */
5243 while (*p && ((p-chunk) < (ssize_t) length))
5244 p++; /* frame name */
5246 p++; /* frame name terminator */
5248 if ((p-chunk) < (ssize_t) (length-4))
5255 change_delay=(*p++);
5256 change_timeout=(*p++);
5257 change_clipping=(*p++);
5258 p++; /* change_sync */
5262 frame_delay=1UL*image->ticks_per_second*
5265 if (mng_info->ticks_per_second != 0)
5266 frame_delay/=mng_info->ticks_per_second;
5269 frame_delay=PNG_UINT_31_MAX;
5271 if (change_delay == 2)
5272 default_frame_delay=frame_delay;
5276 if (logging != MagickFalse)
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5278 " Framing_delay=%.20g",(double) frame_delay);
5283 frame_timeout=1UL*image->ticks_per_second*
5286 if (mng_info->ticks_per_second != 0)
5287 frame_timeout/=mng_info->ticks_per_second;
5290 frame_timeout=PNG_UINT_31_MAX;
5292 if (change_delay == 2)
5293 default_frame_timeout=frame_timeout;
5297 if (logging != MagickFalse)
5298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5299 " Framing_timeout=%.20g",(double) frame_timeout);
5302 if (change_clipping)
5304 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5308 if (logging != MagickFalse)
5309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5310 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5311 (double) fb.left,(double) fb.right,(double) fb.top,
5312 (double) fb.bottom);
5314 if (change_clipping == 2)
5320 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5322 subframe_width=(size_t) (mng_info->clip.right
5323 -mng_info->clip.left);
5325 subframe_height=(size_t) (mng_info->clip.bottom
5326 -mng_info->clip.top);
5328 Insert a background layer behind the frame if framing_mode is 4.
5330 #if defined(MNG_INSERT_LAYERS)
5331 if (logging != MagickFalse)
5332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5333 " subframe_width=%.20g, subframe_height=%.20g",(double)
5334 subframe_width,(double) subframe_height);
5336 if (insert_layers && (mng_info->framing_mode == 4) &&
5337 (subframe_width) && (subframe_height))
5339 /* Allocate next image structure. */
5340 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5342 AcquireNextImage(image_info,image,exception);
5344 if (GetNextImageInList(image) == (Image *) NULL)
5346 image=DestroyImageList(image);
5347 MngInfoFreeStruct(mng_info,&have_mng_structure);
5348 return((Image *) NULL);
5351 image=SyncNextImageInList(image);
5354 mng_info->image=image;
5356 if (term_chunk_found)
5358 image->start_loop=MagickTrue;
5359 image->iterations=mng_iterations;
5360 term_chunk_found=MagickFalse;
5364 image->start_loop=MagickFalse;
5366 image->columns=subframe_width;
5367 image->rows=subframe_height;
5368 image->page.width=subframe_width;
5369 image->page.height=subframe_height;
5370 image->page.x=mng_info->clip.left;
5371 image->page.y=mng_info->clip.top;
5372 image->background_color=mng_background_color;
5373 image->matte=MagickFalse;
5375 (void) SetImageBackgroundColor(image,exception);
5377 if (logging != MagickFalse)
5378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5379 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5380 (double) mng_info->clip.left,(double) mng_info->clip.right,
5381 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5384 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5387 if (memcmp(type,mng_CLIP,4) == 0)
5396 first_object=(p[0] << 8) | p[1];
5397 last_object=(p[2] << 8) | p[3];
5399 for (i=(int) first_object; i <= (int) last_object; i++)
5401 if (mng_info->exists[i] && !mng_info->frozen[i])
5406 box=mng_info->object_clip[i];
5407 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5414 if (memcmp(type,mng_SAVE,4) == 0)
5416 for (i=1; i < MNG_MAX_OBJECTS; i++)
5417 if (mng_info->exists[i])
5419 mng_info->frozen[i]=MagickTrue;
5420 #ifdef MNG_OBJECT_BUFFERS
5421 if (mng_info->ob[i] != (MngBuffer *) NULL)
5422 mng_info->ob[i]->frozen=MagickTrue;
5427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5432 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5434 /* Read DISC or SEEK. */
5436 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5438 for (i=1; i < MNG_MAX_OBJECTS; i++)
5439 MngInfoDiscardObject(mng_info,i);
5447 for (j=0; j < (ssize_t) length; j+=2)
5449 i=p[j] << 8 | p[j+1];
5450 MngInfoDiscardObject(mng_info,i);
5455 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5460 if (memcmp(type,mng_MOVE,4) == 0)
5468 first_object=(p[0] << 8) | p[1];
5469 last_object=(p[2] << 8) | p[3];
5470 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5472 if (mng_info->exists[i] && !mng_info->frozen[i])
5480 old_pair.a=mng_info->x_off[i];
5481 old_pair.b=mng_info->y_off[i];
5482 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5483 mng_info->x_off[i]=new_pair.a;
5484 mng_info->y_off[i]=new_pair.b;
5488 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5492 if (memcmp(type,mng_LOOP,4) == 0)
5494 ssize_t loop_iters=1;
5495 loop_level=chunk[0];
5496 mng_info->loop_active[loop_level]=1; /* mark loop active */
5498 /* Record starting point. */
5499 loop_iters=mng_get_long(&chunk[1]);
5501 if (logging != MagickFalse)
5502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5503 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5504 (double) loop_iters);
5506 if (loop_iters == 0)
5507 skipping_loop=loop_level;
5511 mng_info->loop_jump[loop_level]=TellBlob(image);
5512 mng_info->loop_count[loop_level]=loop_iters;
5515 mng_info->loop_iteration[loop_level]=0;
5516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5520 if (memcmp(type,mng_ENDL,4) == 0)
5522 loop_level=chunk[0];
5524 if (skipping_loop > 0)
5526 if (skipping_loop == loop_level)
5529 Found end of zero-iteration loop.
5532 mng_info->loop_active[loop_level]=0;
5538 if (mng_info->loop_active[loop_level] == 1)
5540 mng_info->loop_count[loop_level]--;
5541 mng_info->loop_iteration[loop_level]++;
5543 if (logging != MagickFalse)
5544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5545 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5546 (double) loop_level,(double)
5547 mng_info->loop_count[loop_level]);
5549 if (mng_info->loop_count[loop_level] != 0)
5551 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5555 ThrowReaderException(CorruptImageError,
5556 "ImproperImageHeader");
5567 mng_info->loop_active[loop_level]=0;
5569 for (i=0; i < loop_level; i++)
5570 if (mng_info->loop_active[i] == 1)
5571 last_level=(short) i;
5572 loop_level=last_level;
5577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5581 if (memcmp(type,mng_CLON,4) == 0)
5583 if (mng_info->clon_warning == 0)
5584 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5585 CoderError,"CLON is not implemented yet","`%s'",
5588 mng_info->clon_warning++;
5591 if (memcmp(type,mng_MAGN,4) == 0)
5606 magn_first=(p[0] << 8) | p[1];
5612 magn_last=(p[2] << 8) | p[3];
5615 magn_last=magn_first;
5616 #ifndef MNG_OBJECT_BUFFERS
5617 if (magn_first || magn_last)
5618 if (mng_info->magn_warning == 0)
5620 (void) ThrowMagickException(&image->exception,
5621 GetMagickModule(),CoderError,
5622 "MAGN is not implemented yet for nonzero objects",
5623 "`%s'",image->filename);
5625 mng_info->magn_warning++;
5635 magn_mx=(p[5] << 8) | p[6];
5644 magn_my=(p[7] << 8) | p[8];
5653 magn_ml=(p[9] << 8) | p[10];
5662 magn_mr=(p[11] << 8) | p[12];
5671 magn_mt=(p[13] << 8) | p[14];
5680 magn_mb=(p[15] << 8) | p[16];
5692 magn_methy=magn_methx;
5695 if (magn_methx > 5 || magn_methy > 5)
5696 if (mng_info->magn_warning == 0)
5698 (void) ThrowMagickException(&image->exception,
5699 GetMagickModule(),CoderError,
5700 "Unknown MAGN method in MNG datastream","`%s'",
5703 mng_info->magn_warning++;
5705 #ifdef MNG_OBJECT_BUFFERS
5706 /* Magnify existing objects in the range magn_first to magn_last */
5708 if (magn_first == 0 || magn_last == 0)
5710 /* Save the magnification factors for object 0 */
5711 mng_info->magn_mb=magn_mb;
5712 mng_info->magn_ml=magn_ml;
5713 mng_info->magn_mr=magn_mr;
5714 mng_info->magn_mt=magn_mt;
5715 mng_info->magn_mx=magn_mx;
5716 mng_info->magn_my=magn_my;
5717 mng_info->magn_methx=magn_methx;
5718 mng_info->magn_methy=magn_methy;
5722 if (memcmp(type,mng_PAST,4) == 0)
5724 if (mng_info->past_warning == 0)
5725 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5726 CoderError,"PAST is not implemented yet","`%s'",
5729 mng_info->past_warning++;
5732 if (memcmp(type,mng_SHOW,4) == 0)
5734 if (mng_info->show_warning == 0)
5735 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5736 CoderError,"SHOW is not implemented yet","`%s'",
5739 mng_info->show_warning++;
5742 if (memcmp(type,mng_sBIT,4) == 0)
5745 mng_info->have_global_sbit=MagickFalse;
5749 mng_info->global_sbit.gray=p[0];
5750 mng_info->global_sbit.red=p[0];
5751 mng_info->global_sbit.green=p[1];
5752 mng_info->global_sbit.blue=p[2];
5753 mng_info->global_sbit.alpha=p[3];
5754 mng_info->have_global_sbit=MagickTrue;
5757 if (memcmp(type,mng_pHYs,4) == 0)
5761 mng_info->global_x_pixels_per_unit=
5762 (size_t) mng_get_long(p);
5763 mng_info->global_y_pixels_per_unit=
5764 (size_t) mng_get_long(&p[4]);
5765 mng_info->global_phys_unit_type=p[8];
5766 mng_info->have_global_phys=MagickTrue;
5770 mng_info->have_global_phys=MagickFalse;
5772 if (memcmp(type,mng_pHYg,4) == 0)
5774 if (mng_info->phyg_warning == 0)
5775 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5776 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5778 mng_info->phyg_warning++;
5780 if (memcmp(type,mng_BASI,4) == 0)
5782 skip_to_iend=MagickTrue;
5784 if (mng_info->basi_warning == 0)
5785 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5786 CoderError,"BASI is not implemented yet","`%s'",
5789 mng_info->basi_warning++;
5790 #ifdef MNG_BASI_SUPPORTED
5791 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5792 (p[2] << 8) | p[3]);
5793 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5794 (p[6] << 8) | p[7]);
5795 basi_color_type=p[8];
5796 basi_compression_method=p[9];
5797 basi_filter_type=p[10];
5798 basi_interlace_method=p[11];
5800 basi_red=(p[12] << 8) & p[13];
5806 basi_green=(p[14] << 8) & p[15];
5812 basi_blue=(p[16] << 8) & p[17];
5818 basi_alpha=(p[18] << 8) & p[19];
5822 if (basi_sample_depth == 16)
5829 basi_viewable=p[20];
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5839 if (memcmp(type,mng_IHDR,4)
5840 #if defined(JNG_SUPPORTED)
5841 && memcmp(type,mng_JHDR,4)
5845 /* Not an IHDR or JHDR chunk */
5847 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5852 if (logging != MagickFalse)
5853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5854 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5856 mng_info->exists[object_id]=MagickTrue;
5857 mng_info->viewable[object_id]=MagickTrue;
5859 if (mng_info->invisible[object_id])
5861 if (logging != MagickFalse)
5862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5863 " Skipping invisible object");
5865 skip_to_iend=MagickTrue;
5866 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5869 #if defined(MNG_INSERT_LAYERS)
5871 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5873 image_width=(size_t) mng_get_long(p);
5874 image_height=(size_t) mng_get_long(&p[4]);
5876 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5879 Insert a transparent background layer behind the entire animation
5880 if it is not full screen.
5882 #if defined(MNG_INSERT_LAYERS)
5883 if (insert_layers && mng_type && first_mng_object)
5885 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5886 (image_width < mng_info->mng_width) ||
5887 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5888 (image_height < mng_info->mng_height) ||
5889 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5891 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5894 Allocate next image structure.
5896 AcquireNextImage(image_info,image,exception);
5898 if (GetNextImageInList(image) == (Image *) NULL)
5900 image=DestroyImageList(image);
5901 MngInfoFreeStruct(mng_info,&have_mng_structure);
5902 return((Image *) NULL);
5905 image=SyncNextImageInList(image);
5907 mng_info->image=image;
5909 if (term_chunk_found)
5911 image->start_loop=MagickTrue;
5912 image->iterations=mng_iterations;
5913 term_chunk_found=MagickFalse;
5917 image->start_loop=MagickFalse;
5919 /* Make a background rectangle. */
5922 image->columns=mng_info->mng_width;
5923 image->rows=mng_info->mng_height;
5924 image->page.width=mng_info->mng_width;
5925 image->page.height=mng_info->mng_height;
5928 image->background_color=mng_background_color;
5929 (void) SetImageBackgroundColor(image,exception);
5930 if (logging != MagickFalse)
5931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5932 " Inserted transparent background layer, W=%.20g, H=%.20g",
5933 (double) mng_info->mng_width,(double) mng_info->mng_height);
5937 Insert a background layer behind the upcoming image if
5938 framing_mode is 3, and we haven't already inserted one.
5940 if (insert_layers && (mng_info->framing_mode == 3) &&
5941 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5942 (simplicity & 0x08)))
5944 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5947 Allocate next image structure.
5949 AcquireNextImage(image_info,image,exception);
5951 if (GetNextImageInList(image) == (Image *) NULL)
5953 image=DestroyImageList(image);
5954 MngInfoFreeStruct(mng_info,&have_mng_structure);
5955 return((Image *) NULL);
5958 image=SyncNextImageInList(image);
5961 mng_info->image=image;
5963 if (term_chunk_found)
5965 image->start_loop=MagickTrue;
5966 image->iterations=mng_iterations;
5967 term_chunk_found=MagickFalse;
5971 image->start_loop=MagickFalse;
5974 image->columns=subframe_width;
5975 image->rows=subframe_height;
5976 image->page.width=subframe_width;
5977 image->page.height=subframe_height;
5978 image->page.x=mng_info->clip.left;
5979 image->page.y=mng_info->clip.top;
5980 image->background_color=mng_background_color;
5981 image->matte=MagickFalse;
5982 (void) SetImageBackgroundColor(image,exception);
5984 if (logging != MagickFalse)
5985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5986 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5987 (double) mng_info->clip.left,(double) mng_info->clip.right,
5988 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5990 #endif /* MNG_INSERT_LAYERS */
5991 first_mng_object=MagickFalse;
5993 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5996 Allocate next image structure.
5998 AcquireNextImage(image_info,image,exception);
6000 if (GetNextImageInList(image) == (Image *) NULL)
6002 image=DestroyImageList(image);
6003 MngInfoFreeStruct(mng_info,&have_mng_structure);
6004 return((Image *) NULL);
6007 image=SyncNextImageInList(image);
6009 mng_info->image=image;
6010 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6011 GetBlobSize(image));
6013 if (status == MagickFalse)
6016 if (term_chunk_found)
6018 image->start_loop=MagickTrue;
6019 term_chunk_found=MagickFalse;
6023 image->start_loop=MagickFalse;
6025 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6027 image->delay=frame_delay;
6028 frame_delay=default_frame_delay;
6034 image->page.width=mng_info->mng_width;
6035 image->page.height=mng_info->mng_height;
6036 image->page.x=mng_info->x_off[object_id];
6037 image->page.y=mng_info->y_off[object_id];
6038 image->iterations=mng_iterations;
6041 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6044 if (logging != MagickFalse)
6045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6046 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6049 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6052 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6056 mng_info->image=image;
6057 mng_info->mng_type=mng_type;
6058 mng_info->object_id=object_id;
6060 if (memcmp(type,mng_IHDR,4) == 0)
6061 image=ReadOnePNGImage(mng_info,image_info,exception);
6063 #if defined(JNG_SUPPORTED)
6065 image=ReadOneJNGImage(mng_info,image_info,exception);
6068 if (image == (Image *) NULL)
6070 if (IsImageObject(previous) != MagickFalse)
6072 (void) DestroyImageList(previous);
6073 (void) CloseBlob(previous);
6076 MngInfoFreeStruct(mng_info,&have_mng_structure);
6077 return((Image *) NULL);
6080 if (image->columns == 0 || image->rows == 0)
6082 (void) CloseBlob(image);
6083 image=DestroyImageList(image);
6084 MngInfoFreeStruct(mng_info,&have_mng_structure);
6085 return((Image *) NULL);
6088 mng_info->image=image;
6095 if (mng_info->magn_methx || mng_info->magn_methy)
6101 if (logging != MagickFalse)
6102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6103 " Processing MNG MAGN chunk");
6105 if (mng_info->magn_methx == 1)
6107 magnified_width=mng_info->magn_ml;
6109 if (image->columns > 1)
6110 magnified_width += mng_info->magn_mr;
6112 if (image->columns > 2)
6113 magnified_width += (png_uint_32)
6114 ((image->columns-2)*(mng_info->magn_mx));
6119 magnified_width=(png_uint_32) image->columns;
6121 if (image->columns > 1)
6122 magnified_width += mng_info->magn_ml-1;
6124 if (image->columns > 2)
6125 magnified_width += mng_info->magn_mr-1;
6127 if (image->columns > 3)
6128 magnified_width += (png_uint_32)
6129 ((image->columns-3)*(mng_info->magn_mx-1));
6132 if (mng_info->magn_methy == 1)
6134 magnified_height=mng_info->magn_mt;
6136 if (image->rows > 1)
6137 magnified_height += mng_info->magn_mb;
6139 if (image->rows > 2)
6140 magnified_height += (png_uint_32)
6141 ((image->rows-2)*(mng_info->magn_my));
6146 magnified_height=(png_uint_32) image->rows;
6148 if (image->rows > 1)
6149 magnified_height += mng_info->magn_mt-1;
6151 if (image->rows > 2)
6152 magnified_height += mng_info->magn_mb-1;
6154 if (image->rows > 3)
6155 magnified_height += (png_uint_32)
6156 ((image->rows-3)*(mng_info->magn_my-1));
6159 if (magnified_height > image->rows ||
6160 magnified_width > image->columns)
6187 /* Allocate next image structure. */
6189 if (logging != MagickFalse)
6190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6191 " Allocate magnified image");
6193 AcquireNextImage(image_info,image,exception);
6195 if (GetNextImageInList(image) == (Image *) NULL)
6197 image=DestroyImageList(image);
6198 MngInfoFreeStruct(mng_info,&have_mng_structure);
6199 return((Image *) NULL);
6202 large_image=SyncNextImageInList(image);
6204 large_image->columns=magnified_width;
6205 large_image->rows=magnified_height;
6207 magn_methx=mng_info->magn_methx;
6208 magn_methy=mng_info->magn_methy;
6210 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6211 #define QM unsigned short
6212 if (magn_methx != 1 || magn_methy != 1)
6215 Scale pixels to unsigned shorts to prevent
6216 overflow of intermediate values of interpolations
6218 for (y=0; y < (ssize_t) image->rows; y++)
6220 q=GetAuthenticPixels(image,0,y,image->columns,1,
6223 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6225 SetPixelRed(image,ScaleQuantumToShort(
6226 GetPixelRed(image,q)),q);
6227 SetPixelGreen(image,ScaleQuantumToShort(
6228 GetPixelGreen(image,q)),q);
6229 SetPixelBlue(image,ScaleQuantumToShort(
6230 GetPixelBlue(image,q)),q);
6231 SetPixelAlpha(image,ScaleQuantumToShort(
6232 GetPixelAlpha(image,q)),q);
6233 q+=GetPixelChannels(image);
6236 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6244 if (image->matte != MagickFalse)
6245 (void) SetImageBackgroundColor(large_image,exception);
6249 large_image->background_color.alpha=OpaqueAlpha;
6250 (void) SetImageBackgroundColor(large_image,exception);
6252 if (magn_methx == 4)
6255 if (magn_methx == 5)
6258 if (magn_methy == 4)
6261 if (magn_methy == 5)
6265 /* magnify the rows into the right side of the large image */
6267 if (logging != MagickFalse)
6268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6269 " Magnify the rows to %.20g",(double) large_image->rows);
6270 m=(ssize_t) mng_info->magn_mt;
6272 length=(size_t) image->columns;
6273 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6274 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6276 if ((prev == (Quantum *) NULL) ||
6277 (next == (Quantum *) NULL))
6279 image=DestroyImageList(image);
6280 MngInfoFreeStruct(mng_info,&have_mng_structure);
6281 ThrowReaderException(ResourceLimitError,
6282 "MemoryAllocationFailed");
6285 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6286 (void) CopyMagickMemory(next,n,length);
6288 for (y=0; y < (ssize_t) image->rows; y++)
6291 m=(ssize_t) mng_info->magn_mt;
6293 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6294 m=(ssize_t) mng_info->magn_mb;
6296 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6297 m=(ssize_t) mng_info->magn_mb;
6299 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6303 m=(ssize_t) mng_info->magn_my;
6309 if (y < (ssize_t) image->rows-1)
6311 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6313 (void) CopyMagickMemory(next,n,length);
6316 for (i=0; i < m; i++, yy++)
6321 assert(yy < (ssize_t) large_image->rows);
6324 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6326 q+=(large_image->columns-image->columns);
6328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6330 /* To do: get color as function of indexes[x] */
6332 if (image->storage_class == PseudoClass)
6337 if (magn_methy <= 1)
6339 /* replicate previous */
6340 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6341 SetPixelGreen(large_image,GetPixelGreen(image,
6343 SetPixelBlue(large_image,GetPixelBlue(image,
6345 SetPixelAlpha(large_image,GetPixelAlpha(image,
6349 else if (magn_methy == 2 || magn_methy == 4)
6353 SetPixelRed(large_image,GetPixelRed(image,
6355 SetPixelGreen(large_image,GetPixelGreen(image,
6357 SetPixelBlue(large_image,GetPixelBlue(image,
6359 SetPixelAlpha(large_image,GetPixelAlpha(image,
6366 SetPixelRed(large_image,((QM) (((ssize_t)
6367 (2*i*(GetPixelRed(image,n)
6368 -GetPixelRed(image,pixels)+m))/
6370 +GetPixelRed(image,pixels)))),q);
6371 SetPixelGreen(large_image,((QM) (((ssize_t)
6372 (2*i*(GetPixelGreen(image,n)
6373 -GetPixelGreen(image,pixels)+m))/
6375 +GetPixelGreen(image,pixels)))),q);
6376 SetPixelBlue(large_image,((QM) (((ssize_t)
6377 (2*i*(GetPixelBlue(image,n)
6378 -GetPixelBlue(image,pixels)+m))/
6380 +GetPixelBlue(image,pixels)))),q);
6382 if (image->matte != MagickFalse)
6383 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6384 (2*i*(GetPixelAlpha(image,n)
6385 -GetPixelAlpha(image,pixels)+m))
6387 GetPixelAlpha(image,pixels)))),q);
6390 if (magn_methy == 4)
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
6394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6397 SetPixelAlpha(large_image,GetPixelAlpha(image,
6402 else /* if (magn_methy == 3 || magn_methy == 5) */
6404 /* Replicate nearest */
6405 if (i <= ((m+1) << 1))
6407 SetPixelRed(large_image,GetPixelRed(image,
6409 SetPixelGreen(large_image,GetPixelGreen(image,
6411 SetPixelBlue(large_image,GetPixelBlue(image,
6413 SetPixelAlpha(large_image,GetPixelAlpha(image,
6419 SetPixelRed(large_image,GetPixelRed(image,n),q);
6420 SetPixelGreen(large_image,GetPixelGreen(image,n),
6422 SetPixelBlue(large_image,GetPixelBlue(image,n),
6424 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6428 if (magn_methy == 5)
6430 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6431 (GetPixelAlpha(image,n)
6432 -GetPixelAlpha(image,pixels))
6433 +m))/((ssize_t) (m*2))
6434 +GetPixelAlpha(image,pixels)),q);
6437 n+=GetPixelChannels(image);
6438 q+=GetPixelChannels(large_image);
6439 pixels+=GetPixelChannels(image);
6442 if (SyncAuthenticPixels(large_image,exception) == 0)
6448 prev=(Quantum *) RelinquishMagickMemory(prev);
6449 next=(Quantum *) RelinquishMagickMemory(next);
6451 length=image->columns;
6453 if (logging != MagickFalse)
6454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6455 " Delete original image");
6457 DeleteImageFromList(&image);
6461 mng_info->image=image;
6463 /* magnify the columns */
6464 if (logging != MagickFalse)
6465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6466 " Magnify the columns to %.20g",(double) image->columns);
6468 for (y=0; y < (ssize_t) image->rows; y++)
6473 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6474 pixels=q+(image->columns-length)*GetPixelChannels(image);
6475 n=pixels+GetPixelChannels(image);
6477 for (x=(ssize_t) (image->columns-length);
6478 x < (ssize_t) image->columns; x++)
6480 /* To do: Rewrite using Get/Set***PixelChannel() */
6482 if (x == (ssize_t) (image->columns-length))
6483 m=(ssize_t) mng_info->magn_ml;
6485 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6486 m=(ssize_t) mng_info->magn_mr;
6488 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6489 m=(ssize_t) mng_info->magn_mr;
6491 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6495 m=(ssize_t) mng_info->magn_mx;
6497 for (i=0; i < m; i++)
6499 if (magn_methx <= 1)
6501 /* replicate previous */
6502 SetPixelRed(image,GetPixelRed(image,pixels),q);
6503 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6504 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6505 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6508 else if (magn_methx == 2 || magn_methx == 4)
6512 SetPixelRed(image,GetPixelRed(image,pixels),q);
6513 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6514 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6515 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6518 /* To do: Rewrite using Get/Set***PixelChannel() */
6522 SetPixelRed(image,(QM) ((2*i*(
6523 GetPixelRed(image,n)
6524 -GetPixelRed(image,pixels))+m)
6526 GetPixelRed(image,pixels)),q);
6528 SetPixelGreen(image,(QM) ((2*i*(
6529 GetPixelGreen(image,n)
6530 -GetPixelGreen(image,pixels))+m)
6532 GetPixelGreen(image,pixels)),q);
6534 SetPixelBlue(image,(QM) ((2*i*(
6535 GetPixelBlue(image,n)
6536 -GetPixelBlue(image,pixels))+m)
6538 GetPixelBlue(image,pixels)),q);
6539 if (image->matte != MagickFalse)
6540 SetPixelAlpha(image,(QM) ((2*i*(
6541 GetPixelAlpha(image,n)
6542 -GetPixelAlpha(image,pixels))+m)
6544 GetPixelAlpha(image,pixels)),q);
6547 if (magn_methx == 4)
6549 /* Replicate nearest */
6550 if (i <= ((m+1) << 1))
6552 SetPixelAlpha(image,
6553 GetPixelAlpha(image,pixels)+0,q);
6557 SetPixelAlpha(image,
6558 GetPixelAlpha(image,n)+0,q);
6563 else /* if (magn_methx == 3 || magn_methx == 5) */
6565 /* Replicate nearest */
6566 if (i <= ((m+1) << 1))
6568 SetPixelRed(image,GetPixelRed(image,pixels),q);
6569 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6570 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6571 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6576 SetPixelRed(image,GetPixelRed(image,n),q);
6577 SetPixelGreen(image,GetPixelGreen(image,n),q);
6578 SetPixelBlue(image,GetPixelBlue(image,n),q);
6579 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6582 if (magn_methx == 5)
6585 SetPixelAlpha(image,
6586 (QM) ((2*i*( GetPixelAlpha(image,n)
6587 -GetPixelAlpha(image,pixels))+m)/
6589 +GetPixelAlpha(image,pixels)),q);
6592 q+=GetPixelChannels(image);
6594 n+=GetPixelChannels(image);
6595 p+=GetPixelChannels(image);
6598 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6601 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6602 if (magn_methx != 1 || magn_methy != 1)
6605 Rescale pixels to Quantum
6607 for (y=0; y < (ssize_t) image->rows; y++)
6609 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6611 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6613 SetPixelRed(image,ScaleShortToQuantum(
6614 GetPixelRed(image,q)),q);
6615 SetPixelGreen(image,ScaleShortToQuantum(
6616 GetPixelGreen(image,q)),q);
6617 SetPixelBlue(image,ScaleShortToQuantum(
6618 GetPixelBlue(image,q)),q);
6619 SetPixelAlpha(image,ScaleShortToQuantum(
6620 GetPixelAlpha(image,q)),q);
6621 q+=GetPixelChannels(image);
6624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6629 if (logging != MagickFalse)
6630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6631 " Finished MAGN processing");
6636 Crop_box is with respect to the upper left corner of the MNG.
6638 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6639 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6640 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6641 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6642 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6643 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6644 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6645 if ((crop_box.left != (mng_info->image_box.left
6646 +mng_info->x_off[object_id])) ||
6647 (crop_box.right != (mng_info->image_box.right
6648 +mng_info->x_off[object_id])) ||
6649 (crop_box.top != (mng_info->image_box.top
6650 +mng_info->y_off[object_id])) ||
6651 (crop_box.bottom != (mng_info->image_box.bottom
6652 +mng_info->y_off[object_id])))
6654 if (logging != MagickFalse)
6655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6656 " Crop the PNG image");
6658 if ((crop_box.left < crop_box.right) &&
6659 (crop_box.top < crop_box.bottom))
6668 Crop_info is with respect to the upper left corner of
6671 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6672 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6673 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6674 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6675 image->page.width=image->columns;
6676 image->page.height=image->rows;
6679 im=CropImage(image,&crop_info,exception);
6681 if (im != (Image *) NULL)
6683 image->columns=im->columns;
6684 image->rows=im->rows;
6685 im=DestroyImage(im);
6686 image->page.width=image->columns;
6687 image->page.height=image->rows;
6688 image->page.x=crop_box.left;
6689 image->page.y=crop_box.top;
6696 No pixels in crop area. The MNG spec still requires
6697 a layer, though, so make a single transparent pixel in
6698 the top left corner.
6703 (void) SetImageBackgroundColor(image,exception);
6704 image->page.width=1;
6705 image->page.height=1;
6710 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6711 image=mng_info->image;
6715 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6716 /* PNG does not handle depths greater than 16 so reduce it even
6719 if (image->depth > 16)
6723 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6724 if (LosslessReduceDepthOK(image) != MagickFalse)
6728 GetImageException(image,exception);
6730 if (image_info->number_scenes != 0)
6732 if (mng_info->scenes_found >
6733 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6737 if (logging != MagickFalse)
6738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6739 " Finished reading image datastream.");
6741 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6743 (void) CloseBlob(image);
6745 if (logging != MagickFalse)
6746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6747 " Finished reading all image datastreams.");
6749 #if defined(MNG_INSERT_LAYERS)
6750 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6751 (mng_info->mng_height))
6754 Insert a background layer if nothing else was found.
6756 if (logging != MagickFalse)
6757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6758 " No images found. Inserting a background layer.");
6760 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6763 Allocate next image structure.
6765 AcquireNextImage(image_info,image,exception);
6766 if (GetNextImageInList(image) == (Image *) NULL)
6768 image=DestroyImageList(image);
6769 MngInfoFreeStruct(mng_info,&have_mng_structure);
6771 if (logging != MagickFalse)
6772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6773 " Allocation failed, returning NULL.");
6775 return((Image *) NULL);
6777 image=SyncNextImageInList(image);
6779 image->columns=mng_info->mng_width;
6780 image->rows=mng_info->mng_height;
6781 image->page.width=mng_info->mng_width;
6782 image->page.height=mng_info->mng_height;
6785 image->background_color=mng_background_color;
6786 image->matte=MagickFalse;
6788 if (image_info->ping == MagickFalse)
6789 (void) SetImageBackgroundColor(image,exception);
6791 mng_info->image_found++;
6794 image->iterations=mng_iterations;
6796 if (mng_iterations == 1)
6797 image->start_loop=MagickTrue;
6799 while (GetPreviousImageInList(image) != (Image *) NULL)
6802 if (image_count > 10*mng_info->image_found)
6804 if (logging != MagickFalse)
6805 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6807 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6808 CoderError,"Linked list is corrupted, beginning of list not found",
6809 "`%s'",image_info->filename);
6811 return((Image *) NULL);
6814 image=GetPreviousImageInList(image);
6816 if (GetNextImageInList(image) == (Image *) NULL)
6818 if (logging != MagickFalse)
6819 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6821 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6822 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6823 image_info->filename);
6827 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6828 GetNextImageInList(image) ==
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6833 " First image null");
6835 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6836 CoderError,"image->next for first image is NULL but shouldn't be.",
6837 "`%s'",image_info->filename);
6840 if (mng_info->image_found == 0)
6842 if (logging != MagickFalse)
6843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6844 " No visible images found.");
6846 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6847 CoderError,"No visible images in file","`%s'",image_info->filename);
6849 if (image != (Image *) NULL)
6850 image=DestroyImageList(image);
6852 MngInfoFreeStruct(mng_info,&have_mng_structure);
6853 return((Image *) NULL);
6856 if (mng_info->ticks_per_second)
6857 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6858 final_delay/mng_info->ticks_per_second;
6861 image->start_loop=MagickTrue;
6863 /* Find final nonzero image delay */
6864 final_image_delay=0;
6866 while (GetNextImageInList(image) != (Image *) NULL)
6869 final_image_delay=image->delay;
6871 image=GetNextImageInList(image);
6874 if (final_delay < final_image_delay)
6875 final_delay=final_image_delay;
6877 image->delay=final_delay;
6879 if (logging != MagickFalse)
6880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6882 (double) final_delay);
6884 if (logging != MagickFalse)
6890 image=GetFirstImageInList(image);
6892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6893 " Before coalesce:");
6895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6896 " scene 0 delay=%.20g",(double) image->delay);
6898 while (GetNextImageInList(image) != (Image *) NULL)
6900 image=GetNextImageInList(image);
6901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6902 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6906 image=GetFirstImageInList(image);
6907 #ifdef MNG_COALESCE_LAYERS
6917 if (logging != MagickFalse)
6918 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6921 next_image=CoalesceImages(image,&image->exception);
6923 if (next_image == (Image *) NULL)
6924 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6926 image=DestroyImageList(image);
6929 for (next=image; next != (Image *) NULL; next=next_image)
6931 next->page.width=mng_info->mng_width;
6932 next->page.height=mng_info->mng_height;
6935 next->scene=scene++;
6936 next_image=GetNextImageInList(next);
6938 if (next_image == (Image *) NULL)
6941 if (next->delay == 0)
6944 next_image->previous=GetPreviousImageInList(next);
6945 if (GetPreviousImageInList(next) == (Image *) NULL)
6948 next->previous->next=next_image;
6949 next=DestroyImage(next);
6955 while (GetNextImageInList(image) != (Image *) NULL)
6956 image=GetNextImageInList(image);
6958 image->dispose=BackgroundDispose;
6960 if (logging != MagickFalse)
6966 image=GetFirstImageInList(image);
6968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6969 " After coalesce:");
6971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6972 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6973 (double) image->dispose);
6975 while (GetNextImageInList(image) != (Image *) NULL)
6977 image=GetNextImageInList(image);
6979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6980 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6981 (double) image->delay,(double) image->dispose);
6985 image=GetFirstImageInList(image);
6986 MngInfoFreeStruct(mng_info,&have_mng_structure);
6987 have_mng_structure=MagickFalse;
6989 if (logging != MagickFalse)
6990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6992 return(GetFirstImageInList(image));
6994 #else /* PNG_LIBPNG_VER > 10011 */
6995 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6997 printf("Your PNG library is too old: You have libpng-%s\n",
6998 PNG_LIBPNG_VER_STRING);
7000 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7001 "PNG library is too old","`%s'",image_info->filename);
7003 return(Image *) NULL;
7006 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7008 return(ReadPNGImage(image_info,exception));
7010 #endif /* PNG_LIBPNG_VER > 10011 */
7014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7018 % R e g i s t e r P N G I m a g e %
7022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7024 % RegisterPNGImage() adds properties for the PNG image format to
7025 % the list of supported formats. The properties include the image format
7026 % tag, a method to read and/or write the format, whether the format
7027 % supports the saving of more than one frame to the same file or blob,
7028 % whether the format supports native in-memory I/O, and a brief
7029 % description of the format.
7031 % The format of the RegisterPNGImage method is:
7033 % size_t RegisterPNGImage(void)
7036 ModuleExport size_t RegisterPNGImage(void)
7039 version[MaxTextExtent];
7047 "See http://www.libpng.org/ for details about the PNG format."
7052 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7058 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7064 #if defined(PNG_LIBPNG_VER_STRING)
7065 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7066 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7068 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7070 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7071 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7076 entry=SetMagickInfo("MNG");
7077 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7079 #if defined(MAGICKCORE_PNG_DELEGATE)
7080 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7081 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7084 entry->magick=(IsImageFormatHandler *) IsMNG;
7085 entry->description=ConstantString("Multiple-image Network Graphics");
7087 if (*version != '\0')
7088 entry->version=ConstantString(version);
7090 entry->module=ConstantString("PNG");
7091 entry->note=ConstantString(MNGNote);
7092 (void) RegisterMagickInfo(entry);
7094 entry=SetMagickInfo("PNG");
7096 #if defined(MAGICKCORE_PNG_DELEGATE)
7097 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7098 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7101 entry->magick=(IsImageFormatHandler *) IsPNG;
7102 entry->adjoin=MagickFalse;
7103 entry->description=ConstantString("Portable Network Graphics");
7104 entry->module=ConstantString("PNG");
7106 if (*version != '\0')
7107 entry->version=ConstantString(version);
7109 entry->note=ConstantString(PNGNote);
7110 (void) RegisterMagickInfo(entry);
7112 entry=SetMagickInfo("PNG8");
7114 #if defined(MAGICKCORE_PNG_DELEGATE)
7115 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7116 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7119 entry->magick=(IsImageFormatHandler *) IsPNG;
7120 entry->adjoin=MagickFalse;
7121 entry->description=ConstantString(
7122 "8-bit indexed with optional binary transparency");
7123 entry->module=ConstantString("PNG");
7124 (void) RegisterMagickInfo(entry);
7126 entry=SetMagickInfo("PNG24");
7129 #if defined(ZLIB_VERSION)
7130 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7131 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7133 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7135 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7136 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7140 if (*version != '\0')
7141 entry->version=ConstantString(version);
7143 #if defined(MAGICKCORE_PNG_DELEGATE)
7144 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7145 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7148 entry->magick=(IsImageFormatHandler *) IsPNG;
7149 entry->adjoin=MagickFalse;
7150 entry->description=ConstantString("opaque 24-bit RGB");
7151 entry->module=ConstantString("PNG");
7152 (void) RegisterMagickInfo(entry);
7154 entry=SetMagickInfo("PNG32");
7156 #if defined(MAGICKCORE_PNG_DELEGATE)
7157 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7158 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7161 entry->magick=(IsImageFormatHandler *) IsPNG;
7162 entry->adjoin=MagickFalse;
7163 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7164 entry->module=ConstantString("PNG");
7165 (void) RegisterMagickInfo(entry);
7167 entry=SetMagickInfo("JNG");
7169 #if defined(JNG_SUPPORTED)
7170 #if defined(MAGICKCORE_PNG_DELEGATE)
7171 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7172 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7176 entry->magick=(IsImageFormatHandler *) IsJNG;
7177 entry->adjoin=MagickFalse;
7178 entry->description=ConstantString("JPEG Network Graphics");
7179 entry->module=ConstantString("PNG");
7180 entry->note=ConstantString(JNGNote);
7181 (void) RegisterMagickInfo(entry);
7183 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7184 ping_semaphore=AllocateSemaphoreInfo();
7187 return(MagickImageCoderSignature);
7191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7195 % U n r e g i s t e r P N G I m a g e %
7199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7201 % UnregisterPNGImage() removes format registrations made by the
7202 % PNG module from the list of supported formats.
7204 % The format of the UnregisterPNGImage method is:
7206 % UnregisterPNGImage(void)
7209 ModuleExport void UnregisterPNGImage(void)
7211 (void) UnregisterMagickInfo("MNG");
7212 (void) UnregisterMagickInfo("PNG");
7213 (void) UnregisterMagickInfo("PNG8");
7214 (void) UnregisterMagickInfo("PNG24");
7215 (void) UnregisterMagickInfo("PNG32");
7216 (void) UnregisterMagickInfo("JNG");
7218 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7219 if (ping_semaphore != (SemaphoreInfo *) NULL)
7220 DestroySemaphoreInfo(&ping_semaphore);
7224 #if defined(MAGICKCORE_PNG_DELEGATE)
7225 #if PNG_LIBPNG_VER > 10011
7227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7231 % W r i t e M N G I m a g e %
7235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7237 % WriteMNGImage() writes an image in the Portable Network Graphics
7238 % Group's "Multiple-image Network Graphics" encoded image format.
7240 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7242 % The format of the WriteMNGImage method is:
7244 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7245 % Image *image,ExceptionInfo *exception)
7247 % A description of each parameter follows.
7249 % o image_info: the image info.
7251 % o image: The image.
7253 % o exception: return any errors or warnings in this structure.
7255 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7256 % "To do" under ReadPNGImage):
7258 % Preserve all unknown and not-yet-handled known chunks found in input
7259 % PNG file and copy them into output PNG files according to the PNG
7262 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7264 % Improve selection of color type (use indexed-colour or indexed-colour
7265 % with tRNS when 256 or fewer unique RGBA values are present).
7267 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7268 % This will be complicated if we limit ourselves to generating MNG-LC
7269 % files. For now we ignore disposal method 3 and simply overlay the next
7272 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7273 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7274 % [mostly done 15 June 1999 but still need to take care of tRNS]
7276 % Check for identical sRGB and replace with a global sRGB (and remove
7277 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7278 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7279 % local gAMA/cHRM with local sRGB if appropriate).
7281 % Check for identical sBIT chunks and write global ones.
7283 % Provide option to skip writing the signature tEXt chunks.
7285 % Use signatures to detect identical objects and reuse the first
7286 % instance of such objects instead of writing duplicate objects.
7288 % Use a smaller-than-32k value of compression window size when
7291 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7292 % ancillary text chunks and save profiles.
7294 % Provide an option to force LC files (to ensure exact framing rate)
7297 % Provide an option to force VLC files instead of LC, even when offsets
7298 % are present. This will involve expanding the embedded images with a
7299 % transparent region at the top and/or left.
7303 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7304 png_info *ping_info, unsigned char *profile_type, unsigned char
7305 *profile_description, unsigned char *profile_data, png_uint_32 length)
7324 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7326 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7329 if (image_info->verbose)
7331 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7332 (char *) profile_type, (double) length);
7335 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7336 description_length=(png_uint_32) strlen((const char *) profile_description);
7337 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7338 + description_length);
7339 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7340 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7341 text[0].key[0]='\0';
7342 (void) ConcatenateMagickString(text[0].key,
7343 "Raw profile type ",MaxTextExtent);
7344 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7348 (void) CopyMagickString(dp,(const char *) profile_description,
7350 dp+=description_length;
7352 (void) FormatLocaleString(dp,allocated_length-
7353 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7356 for (i=0; i < (ssize_t) length; i++)
7360 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7361 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7366 text[0].text_length=(png_size_t) (dp-text[0].text);
7367 text[0].compression=image_info->compression == NoCompression ||
7368 (image_info->compression == UndefinedCompression &&
7369 text[0].text_length < 128) ? -1 : 0;
7371 if (text[0].text_length <= allocated_length)
7372 png_set_text(ping,ping_info,text,1);
7374 png_free(ping,text[0].text);
7375 png_free(ping,text[0].key);
7376 png_free(ping,text);
7379 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7380 const char *string, MagickBooleanType logging)
7393 ResetImageProfileIterator(image);
7395 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7397 profile=GetImageProfile(image,name);
7399 if (profile != (const StringInfo *) NULL)
7404 if (LocaleNCompare(name,string,11) == 0)
7406 if (logging != MagickFalse)
7407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7408 " Found %s profile",name);
7410 ping_profile=CloneStringInfo(profile);
7411 data=GetStringInfoDatum(ping_profile),
7412 length=(png_uint_32) GetStringInfoLength(ping_profile);
7417 (void) WriteBlobMSBULong(image,length-5); /* data length */
7418 (void) WriteBlob(image,length-1,data+1);
7419 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7420 ping_profile=DestroyStringInfo(ping_profile);
7424 name=GetNextImageProfile(image);
7431 /* Write one PNG image */
7432 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7433 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7457 ping_trans_alpha[256];
7485 ping_have_cheap_transparency,
7496 /* ping_exclude_EXIF, */
7499 /* ping_exclude_iTXt, */
7504 /* ping_exclude_tRNS, */
7506 ping_exclude_zCCP, /* hex-encoded iCCP */
7509 ping_preserve_colormap,
7510 ping_need_colortype_warning,
7531 ping_interlace_method,
7532 ping_compression_method,
7549 number_semitransparent,
7551 ping_pHYs_unit_type;
7554 ping_pHYs_x_resolution,
7555 ping_pHYs_y_resolution;
7557 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7558 " Enter WriteOnePNGImage()");
7560 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7561 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7562 if (image_info == (ImageInfo *) NULL)
7563 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7565 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7566 LockSemaphoreInfo(ping_semaphore);
7569 /* Initialize some stuff */
7572 ping_interlace_method=0,
7573 ping_compression_method=0,
7574 ping_filter_method=0,
7577 ping_background.red = 0;
7578 ping_background.green = 0;
7579 ping_background.blue = 0;
7580 ping_background.gray = 0;
7581 ping_background.index = 0;
7583 ping_trans_color.red=0;
7584 ping_trans_color.green=0;
7585 ping_trans_color.blue=0;
7586 ping_trans_color.gray=0;
7588 ping_pHYs_unit_type = 0;
7589 ping_pHYs_x_resolution = 0;
7590 ping_pHYs_y_resolution = 0;
7592 ping_have_blob=MagickFalse;
7593 ping_have_color=MagickTrue;
7594 ping_have_non_bw=MagickTrue;
7595 ping_have_PLTE=MagickFalse;
7596 ping_have_bKGD=MagickFalse;
7597 ping_have_pHYs=MagickFalse;
7598 ping_have_tRNS=MagickFalse;
7600 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7601 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7602 ping_exclude_date=mng_info->ping_exclude_date;
7603 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7604 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7605 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7606 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7607 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7608 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7609 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7610 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7611 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7612 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7613 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7614 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7616 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7617 ping_need_colortype_warning = MagickFalse;
7619 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7620 * i.e., eliminate the ICC profile and set image->rendering_intent.
7621 * Note that this will not involve any changes to the actual pixels
7622 * but merely passes information to applications that read the resulting
7625 if (ping_exclude_sRGB == MagickFalse)
7633 ResetImageProfileIterator(image);
7634 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7636 profile=GetImageProfile(image,name);
7638 if (profile != (StringInfo *) NULL)
7640 if ((LocaleCompare(name,"ICC") == 0) ||
7641 (LocaleCompare(name,"ICM") == 0))
7646 /* 0: not a known sRGB profile
7647 * 1: HP-Microsoft sRGB v2
7648 * 2: ICC sRGB v4 perceptual
7649 * 3: ICC sRGB v2 perceptual no black-compensation
7652 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7653 check_len[4] = {0, 3144, 60960, 3052};
7662 length=(png_uint_32) GetStringInfoLength(profile);
7664 for (icheck=3; icheck > 0; icheck--)
7666 if (length == check_len[icheck])
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " Got a %lu-byte ICC profile (potentially sRGB)",
7670 (unsigned long) length);
7672 data=GetStringInfoDatum(profile);
7673 profile_crc=crc32(0,data,length);
7675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7676 " with crc=%8x",(unsigned int) profile_crc);
7678 if (profile_crc == check_crc[icheck])
7680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7682 if (image->rendering_intent==UndefinedIntent)
7683 image->rendering_intent=PerceptualIntent;
7689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7690 " Got a %lu-byte ICC profile",
7691 (unsigned long) length);
7694 name=GetNextImageProfile(image);
7699 number_semitransparent = 0;
7700 number_transparent = 0;
7702 if (logging != MagickFalse)
7704 if (image->storage_class == UndefinedClass)
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " storage_class=UndefinedClass");
7707 if (image->storage_class == DirectClass)
7708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7709 " storage_class=DirectClass");
7710 if (image->storage_class == PseudoClass)
7711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 " storage_class=PseudoClass");
7715 if (image->storage_class == PseudoClass &&
7716 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7717 (mng_info->write_png_colortype != 0 &&
7718 mng_info->write_png_colortype != 4)))
7720 (void) SyncImage(image,exception);
7721 image->storage_class = DirectClass;
7724 if (ping_preserve_colormap == MagickFalse)
7726 if (image->storage_class != PseudoClass && image->colormap != NULL)
7728 /* Free the bogus colormap; it can cause trouble later */
7729 if (logging != MagickFalse)
7730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7731 " Freeing bogus colormap");
7732 (void) RelinquishMagickMemory(image->colormap);
7733 image->colormap=NULL;
7737 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7738 (void) TransformImageColorspace(image,RGBColorspace,exception);
7741 Sometimes we get PseudoClass images whose RGB values don't match
7742 the colors in the colormap. This code syncs the RGB values.
7744 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7745 (void) SyncImage(image,exception);
7747 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7748 if (image->depth > 8)
7750 if (logging != MagickFalse)
7751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7752 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7758 /* Respect the -depth option */
7759 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7767 exception=(&image->exception);
7769 if (image->depth > 8)
7771 #if MAGICKCORE_QUANTUM_DEPTH > 16
7772 /* Scale to 16-bit */
7773 LBR16PacketRGBO(image->background_color);
7775 for (y=0; y < (ssize_t) image->rows; y++)
7777 r=GetAuthenticPixels(image,0,y,image->columns,1,
7780 if (r == (Quantum *) NULL)
7783 for (x=0; x < (ssize_t) image->columns; x++)
7789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7793 if (image->storage_class == PseudoClass && image->colormap != NULL)
7795 for (i=0; i < (ssize_t) image->colors; i++)
7797 LBR16PacketRGBO(image->colormap[i]);
7800 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7803 else if (image->depth > 4)
7805 #if MAGICKCORE_QUANTUM_DEPTH > 8
7806 /* Scale to 8-bit */
7807 LBR08PacketRGBO(image->background_color);
7809 for (y=0; y < (ssize_t) image->rows; y++)
7811 r=GetAuthenticPixels(image,0,y,image->columns,1,
7814 if (r == (Quantum *) NULL)
7817 for (x=0; x < (ssize_t) image->columns; x++)
7823 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7827 if (image->storage_class == PseudoClass && image->colormap != NULL)
7829 for (i=0; i < (ssize_t) image->colors; i++)
7831 LBR08PacketRGBO(image->colormap[i]);
7834 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7837 if (image->depth > 2)
7839 /* Scale to 4-bit */
7840 LBR04PacketRGBO(image->background_color);
7842 for (y=0; y < (ssize_t) image->rows; y++)
7844 r=GetAuthenticPixels(image,0,y,image->columns,1,
7847 if (r == (Quantum *) NULL)
7850 for (x=0; x < (ssize_t) image->columns; x++)
7856 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7860 if (image->storage_class == PseudoClass && image->colormap != NULL)
7862 for (i=0; i < (ssize_t) image->colors; i++)
7864 LBR04PacketRGBO(image->colormap[i]);
7869 else if (image->depth > 1)
7871 /* Scale to 2-bit */
7872 LBR02PacketRGBO(image->background_color);
7874 for (y=0; y < (ssize_t) image->rows; y++)
7876 r=GetAuthenticPixels(image,0,y,image->columns,1,
7879 if (r == (Quantum *) NULL)
7882 for (x=0; x < (ssize_t) image->columns; x++)
7888 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7892 if (image->storage_class == PseudoClass && image->colormap != NULL)
7894 for (i=0; i < (ssize_t) image->colors; i++)
7896 LBR02PacketRGBO(image->colormap[i]);
7902 /* Scale to 1-bit */
7903 LBR01PacketRGBO(image->background_color);
7905 for (y=0; y < (ssize_t) image->rows; y++)
7907 r=GetAuthenticPixels(image,0,y,image->columns,1,
7910 if (r == (Quantum *) NULL)
7913 for (x=0; x < (ssize_t) image->columns; x++)
7919 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7923 if (image->storage_class == PseudoClass && image->colormap != NULL)
7925 for (i=0; i < (ssize_t) image->colors; i++)
7927 LBR01PacketRGBO(image->colormap[i]);
7933 /* To do: set to next higher multiple of 8 */
7934 if (image->depth < 8)
7937 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7938 /* PNG does not handle depths greater than 16 so reduce it even
7941 if (image->depth > 8)
7945 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7946 if (image->depth == 16 && mng_info->write_png_depth != 16)
7947 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7951 /* Normally we run this just once, but in the case of writing PNG8
7952 * we reduce the transparency to binary and run again, then if there
7953 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7954 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7955 * palette. Then (To do) we take care of a final reduction that is only
7956 * needed if there are still 256 colors present and one of them has both
7957 * transparent and opaque instances.
7960 tried_332 = MagickFalse;
7961 tried_333 = MagickFalse;
7962 tried_444 = MagickFalse;
7968 * Sometimes we get DirectClass images that have 256 colors or fewer.
7969 * This code will build a colormap.
7971 * Also, sometimes we get PseudoClass images with an out-of-date
7972 * colormap. This code will replace the colormap with a new one.
7973 * Sometimes we get PseudoClass images that have more than 256 colors.
7974 * This code will delete the colormap and change the image to
7977 * If image->matte is MagickFalse, we ignore the alpha channel
7978 * even though it sometimes contains left-over non-opaque values.
7980 * Also we gather some information (number of opaque, transparent,
7981 * and semitransparent pixels, and whether the image has any non-gray
7982 * pixels or only black-and-white pixels) that we might need later.
7984 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7985 * we need to check for bogus non-opaque values, at least.
7996 semitransparent[260],
7999 register const Quantum
8006 if (logging != MagickFalse)
8007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8008 " Enter BUILD_PALETTE:");
8010 if (logging != MagickFalse)
8012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8013 " image->columns=%.20g",(double) image->columns);
8014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8015 " image->rows=%.20g",(double) image->rows);
8016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8017 " image->matte=%.20g",(double) image->matte);
8018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8019 " image->depth=%.20g",(double) image->depth);
8021 if (image->storage_class == PseudoClass && image->colormap != NULL)
8023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8024 " Original colormap:");
8025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8026 " i (red,green,blue,alpha)");
8028 for (i=0; i < 256; i++)
8030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " %d (%d,%d,%d,%d)",
8033 (int) image->colormap[i].red,
8034 (int) image->colormap[i].green,
8035 (int) image->colormap[i].blue,
8036 (int) image->colormap[i].alpha);
8039 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8044 " %d (%d,%d,%d,%d)",
8046 (int) image->colormap[i].red,
8047 (int) image->colormap[i].green,
8048 (int) image->colormap[i].blue,
8049 (int) image->colormap[i].alpha);
8054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8055 " image->colors=%d",(int) image->colors);
8057 if (image->colors == 0)
8058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8059 " (zero means unknown)");
8061 if (ping_preserve_colormap == MagickFalse)
8062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8063 " Regenerate the colormap");
8066 exception=(&image->exception);
8070 number_semitransparent = 0;
8071 number_transparent = 0;
8073 for (y=0; y < (ssize_t) image->rows; y++)
8075 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8077 if (q == (Quantum *) NULL)
8080 for (x=0; x < (ssize_t) image->columns; x++)
8082 if (image->matte == MagickFalse ||
8083 GetPixelAlpha(image,q) == OpaqueAlpha)
8085 if (number_opaque < 259)
8087 if (number_opaque == 0)
8089 GetPixelInfoPixel(image, q, opaque);
8090 opaque[0].alpha=OpaqueAlpha;
8094 for (i=0; i< (ssize_t) number_opaque; i++)
8096 if (IsPixelEquivalent(image,q, opaque+i))
8100 if (i == (ssize_t) number_opaque &&
8101 number_opaque < 259)
8104 GetPixelInfoPixel(image, q, opaque+i);
8105 opaque[i].alpha=OpaqueAlpha;
8109 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8111 if (number_transparent < 259)
8113 if (number_transparent == 0)
8115 GetPixelInfoPixel(image, q, transparent);
8116 ping_trans_color.red=(unsigned short)
8117 GetPixelRed(image,q);
8118 ping_trans_color.green=(unsigned short)
8119 GetPixelGreen(image,q);
8120 ping_trans_color.blue=(unsigned short)
8121 GetPixelBlue(image,q);
8122 ping_trans_color.gray=(unsigned short)
8123 GetPixelRed(image,q);
8124 number_transparent = 1;
8127 for (i=0; i< (ssize_t) number_transparent; i++)
8129 if (IsPixelEquivalent(image,q, transparent+i))
8133 if (i == (ssize_t) number_transparent &&
8134 number_transparent < 259)
8136 number_transparent++;
8137 GetPixelInfoPixel(image,q,transparent+i);
8143 if (number_semitransparent < 259)
8145 if (number_semitransparent == 0)
8147 GetPixelInfoPixel(image,q,semitransparent);
8148 number_semitransparent = 1;
8151 for (i=0; i< (ssize_t) number_semitransparent; i++)
8153 if (IsPixelEquivalent(image,q, semitransparent+i)
8154 && GetPixelAlpha(image,q) ==
8155 semitransparent[i].alpha)
8159 if (i == (ssize_t) number_semitransparent &&
8160 number_semitransparent < 259)
8162 number_semitransparent++;
8163 GetPixelInfoPixel(image, q, semitransparent+i);
8167 q+=GetPixelChannels(image);
8171 if (mng_info->write_png8 == MagickFalse &&
8172 ping_exclude_bKGD == MagickFalse)
8174 /* Add the background color to the palette, if it
8175 * isn't already there.
8177 if (logging != MagickFalse)
8179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8180 " Check colormap for background (%d,%d,%d)",
8181 (int) image->background_color.red,
8182 (int) image->background_color.green,
8183 (int) image->background_color.blue);
8185 for (i=0; i<number_opaque; i++)
8187 if (opaque[i].red == image->background_color.red &&
8188 opaque[i].green == image->background_color.green &&
8189 opaque[i].blue == image->background_color.blue)
8192 if (number_opaque < 259 && i == number_opaque)
8194 opaque[i] = image->background_color;
8195 ping_background.index = i;
8196 if (logging != MagickFalse)
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " background_color index is %d",(int) i);
8203 else if (logging != MagickFalse)
8204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8205 " No room in the colormap to add background color");
8208 image_colors=number_opaque+number_transparent+number_semitransparent;
8210 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8212 /* No room for the background color; remove it. */
8217 if (logging != MagickFalse)
8219 if (image_colors > 256)
8220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8221 " image has more than 256 colors");
8224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8225 " image has %d colors",image_colors);
8228 if (ping_preserve_colormap != MagickFalse)
8231 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8233 ping_have_color=MagickFalse;
8234 ping_have_non_bw=MagickFalse;
8236 if(image_colors > 256)
8238 for (y=0; y < (ssize_t) image->rows; y++)
8240 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8242 if (q == (Quantum *) NULL)
8246 for (x=0; x < (ssize_t) image->columns; x++)
8248 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8249 GetPixelRed(image,s) != GetPixelBlue(image,s))
8251 ping_have_color=MagickTrue;
8252 ping_have_non_bw=MagickTrue;
8255 s+=GetPixelChannels(image);
8258 if (ping_have_color != MagickFalse)
8261 /* Worst case is black-and-white; we are looking at every
8265 if (ping_have_non_bw == MagickFalse)
8268 for (x=0; x < (ssize_t) image->columns; x++)
8270 if (GetPixelRed(image,s) != 0 &&
8271 GetPixelRed(image,s) != QuantumRange)
8273 ping_have_non_bw=MagickTrue;
8276 s+=GetPixelChannels(image);
8283 if (image_colors < 257)
8289 * Initialize image colormap.
8292 if (logging != MagickFalse)
8293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " Sort the new colormap");
8296 /* Sort palette, transparent first */;
8300 for (i=0; i<number_transparent; i++)
8301 colormap[n++] = transparent[i];
8303 for (i=0; i<number_semitransparent; i++)
8304 colormap[n++] = semitransparent[i];
8306 for (i=0; i<number_opaque; i++)
8307 colormap[n++] = opaque[i];
8309 ping_background.index +=
8310 (number_transparent + number_semitransparent);
8312 /* image_colors < 257; search the colormap instead of the pixels
8313 * to get ping_have_color and ping_have_non_bw
8317 if (ping_have_color == MagickFalse)
8319 if (colormap[i].red != colormap[i].green ||
8320 colormap[i].red != colormap[i].blue)
8322 ping_have_color=MagickTrue;
8323 ping_have_non_bw=MagickTrue;
8328 if (ping_have_non_bw == MagickFalse)
8330 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8331 ping_have_non_bw=MagickTrue;
8335 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8336 (number_transparent == 0 && number_semitransparent == 0)) &&
8337 (((mng_info->write_png_colortype-1) ==
8338 PNG_COLOR_TYPE_PALETTE) ||
8339 (mng_info->write_png_colortype == 0)))
8341 if (logging != MagickFalse)
8343 if (n != (ssize_t) image_colors)
8344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " image_colors (%d) and n (%d) don't match",
8348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8349 " AcquireImageColormap");
8352 image->colors = image_colors;
8354 if (AcquireImageColormap(image,image_colors,exception) ==
8356 ThrowWriterException(ResourceLimitError,
8357 "MemoryAllocationFailed");
8359 for (i=0; i< (ssize_t) image_colors; i++)
8360 image->colormap[i] = colormap[i];
8362 if (logging != MagickFalse)
8364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365 " image->colors=%d (%d)",
8366 (int) image->colors, image_colors);
8368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8369 " Update the pixel indexes");
8372 /* Sync the pixel indices with the new colormap */
8374 for (y=0; y < (ssize_t) image->rows; y++)
8376 q=GetAuthenticPixels(image,0,y,image->columns,1,
8379 if (q == (Quantum *) NULL)
8383 for (x=0; x < (ssize_t) image->columns; x++)
8385 for (i=0; i< (ssize_t) image_colors; i++)
8387 if ((image->matte == MagickFalse ||
8388 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8389 image->colormap[i].red == GetPixelRed(image,q) &&
8390 image->colormap[i].green == GetPixelGreen(image,q) &&
8391 image->colormap[i].blue == GetPixelBlue(image,q))
8393 SetPixelIndex(image,i,q);
8397 q+=GetPixelChannels(image);
8400 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8406 if (logging != MagickFalse)
8408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " image->colors=%d", (int) image->colors);
8411 if (image->colormap != NULL)
8413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8414 " i (red,green,blue,alpha)");
8416 for (i=0; i < (ssize_t) image->colors; i++)
8418 if (i < 300 || i >= (ssize_t) image->colors - 10)
8420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8421 " %d (%d,%d,%d,%d)",
8423 (int) image->colormap[i].red,
8424 (int) image->colormap[i].green,
8425 (int) image->colormap[i].blue,
8426 (int) image->colormap[i].alpha);
8431 if (number_transparent < 257)
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " number_transparent = %d",
8434 number_transparent);
8437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " number_transparent > 256");
8440 if (number_opaque < 257)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " number_opaque = %d",
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " number_opaque > 256");
8449 if (number_semitransparent < 257)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " number_semitransparent = %d",
8452 number_semitransparent);
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " number_semitransparent > 256");
8458 if (ping_have_non_bw == MagickFalse)
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " All pixels and the background are black or white");
8462 else if (ping_have_color == MagickFalse)
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " All pixels and the background are gray");
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " At least one pixel or the background is non-gray");
8470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8471 " Exit BUILD_PALETTE:");
8474 if (mng_info->write_png8 == MagickFalse)
8477 /* Make any reductions necessary for the PNG8 format */
8478 if (image_colors <= 256 &&
8479 image_colors != 0 && image->colormap != NULL &&
8480 number_semitransparent == 0 &&
8481 number_transparent <= 1)
8484 /* PNG8 can't have semitransparent colors so we threshold the
8485 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8486 * transparent color so if more than one is transparent we merge
8487 * them into image->background_color.
8489 if (number_semitransparent != 0 || number_transparent > 1)
8491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8492 " Thresholding the alpha channel to binary");
8494 for (y=0; y < (ssize_t) image->rows; y++)
8496 r=GetAuthenticPixels(image,0,y,image->columns,1,
8499 if (r == (Quantum *) NULL)
8502 for (x=0; x < (ssize_t) image->columns; x++)
8504 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8506 SetPixelPixelInfo(image,&image->background_color,r);
8507 SetPixelAlpha(image,TransparentAlpha,r);
8510 SetPixelAlpha(image,OpaqueAlpha,r);
8511 r+=GetPixelChannels(image);
8514 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8517 if (image_colors != 0 && image_colors <= 256 &&
8518 image->colormap != NULL)
8519 for (i=0; i<image_colors; i++)
8520 image->colormap[i].alpha =
8521 (image->colormap[i].alpha > TransparentAlpha/2 ?
8522 TransparentAlpha : OpaqueAlpha);
8527 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8528 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8529 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8532 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8534 if (logging != MagickFalse)
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " Quantizing the background color to 4-4-4");
8538 tried_444 = MagickTrue;
8540 LBR04PacketRGB(image->background_color);
8542 if (logging != MagickFalse)
8543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8544 " Quantizing the pixel colors to 4-4-4");
8546 if (image->colormap == NULL)
8548 for (y=0; y < (ssize_t) image->rows; y++)
8550 r=GetAuthenticPixels(image,0,y,image->columns,1,
8553 if (r == (Quantum *) NULL)
8556 for (x=0; x < (ssize_t) image->columns; x++)
8558 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8563 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8568 else /* Should not reach this; colormap already exists and
8571 if (logging != MagickFalse)
8572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8573 " Quantizing the colormap to 4-4-4");
8575 for (i=0; i<image_colors; i++)
8577 LBR04PacketRGB(image->colormap[i]);
8583 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8585 if (logging != MagickFalse)
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8587 " Quantizing the background color to 3-3-3");
8589 tried_333 = MagickTrue;
8591 LBR03PacketRGB(image->background_color);
8593 if (logging != MagickFalse)
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595 " Quantizing the pixel colors to 3-3-3-1");
8597 if (image->colormap == NULL)
8599 for (y=0; y < (ssize_t) image->rows; y++)
8601 r=GetAuthenticPixels(image,0,y,image->columns,1,
8604 if (r == (Quantum *) NULL)
8607 for (x=0; x < (ssize_t) image->columns; x++)
8609 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8614 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8619 else /* Should not reach this; colormap already exists and
8622 if (logging != MagickFalse)
8623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8624 " Quantizing the colormap to 3-3-3-1");
8625 for (i=0; i<image_colors; i++)
8627 LBR03PacketRGB(image->colormap[i]);
8633 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8635 if (logging != MagickFalse)
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637 " Quantizing the background color to 3-3-2");
8639 tried_332 = MagickTrue;
8641 /* Red and green were already done so we only quantize the blue
8645 LBR02PacketBlue(image->background_color);
8647 if (logging != MagickFalse)
8648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8649 " Quantizing the pixel colors to 3-3-2-1");
8651 if (image->colormap == NULL)
8653 for (y=0; y < (ssize_t) image->rows; y++)
8655 r=GetAuthenticPixels(image,0,y,image->columns,1,
8658 if (r == (Quantum *) NULL)
8661 for (x=0; x < (ssize_t) image->columns; x++)
8663 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8668 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8673 else /* Should not reach this; colormap already exists and
8676 if (logging != MagickFalse)
8677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678 " Quantizing the colormap to 3-3-2-1");
8679 for (i=0; i<image_colors; i++)
8681 LBR02PacketBlue(image->colormap[i]);
8688 if (image_colors == 0 || image_colors > 256)
8690 /* Take care of special case with 256 colors + 1 transparent
8691 * color. We don't need to quantize to 2-3-2-1; we only need to
8692 * eliminate one color, so we'll merge the two darkest red
8693 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8695 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8696 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8697 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8699 image->background_color.red=ScaleCharToQuantum(0x24);
8702 if (image->colormap == NULL)
8704 for (y=0; y < (ssize_t) image->rows; y++)
8706 r=GetAuthenticPixels(image,0,y,image->columns,1,
8709 if (r == (Quantum *) NULL)
8712 for (x=0; x < (ssize_t) image->columns; x++)
8714 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8715 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8716 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8717 GetPixelAlpha(image,r) == OpaqueAlpha)
8719 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8721 r+=GetPixelChannels(image);
8724 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8732 for (i=0; i<image_colors; i++)
8734 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8735 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8736 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8738 image->colormap[i].red=ScaleCharToQuantum(0x24);
8744 /* END OF BUILD_PALETTE */
8746 /* If we are excluding the tRNS chunk and there is transparency,
8747 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8750 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8751 (number_transparent != 0 || number_semitransparent != 0))
8753 unsigned int colortype=mng_info->write_png_colortype;
8755 if (ping_have_color == MagickFalse)
8756 mng_info->write_png_colortype = 5;
8759 mng_info->write_png_colortype = 7;
8761 if (colortype != 0 &&
8762 mng_info->write_png_colortype != colortype)
8763 ping_need_colortype_warning=MagickTrue;
8767 /* See if cheap transparency is possible. It is only possible
8768 * when there is a single transparent color, no semitransparent
8769 * color, and no opaque color that has the same RGB components
8770 * as the transparent color. We only need this information if
8771 * we are writing a PNG with colortype 0 or 2, and we have not
8772 * excluded the tRNS chunk.
8774 if (number_transparent == 1 &&
8775 mng_info->write_png_colortype < 4)
8777 ping_have_cheap_transparency = MagickTrue;
8779 if (number_semitransparent != 0)
8780 ping_have_cheap_transparency = MagickFalse;
8782 else if (image_colors == 0 || image_colors > 256 ||
8783 image->colormap == NULL)
8788 register const Quantum
8791 exception=(&image->exception);
8793 for (y=0; y < (ssize_t) image->rows; y++)
8795 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8797 if (q == (Quantum *) NULL)
8800 for (x=0; x < (ssize_t) image->columns; x++)
8802 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8803 (unsigned short) GetPixelRed(image,q) ==
8804 ping_trans_color.red &&
8805 (unsigned short) GetPixelGreen(image,q) ==
8806 ping_trans_color.green &&
8807 (unsigned short) GetPixelBlue(image,q) ==
8808 ping_trans_color.blue)
8810 ping_have_cheap_transparency = MagickFalse;
8814 q+=GetPixelChannels(image);
8817 if (ping_have_cheap_transparency == MagickFalse)
8823 /* Assuming that image->colormap[0] is the one transparent color
8824 * and that all others are opaque.
8826 if (image_colors > 1)
8827 for (i=1; i<image_colors; i++)
8828 if (image->colormap[i].red == image->colormap[0].red &&
8829 image->colormap[i].green == image->colormap[0].green &&
8830 image->colormap[i].blue == image->colormap[0].blue)
8832 ping_have_cheap_transparency = MagickFalse;
8837 if (logging != MagickFalse)
8839 if (ping_have_cheap_transparency == MagickFalse)
8840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8841 " Cheap transparency is not possible.");
8844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8845 " Cheap transparency is possible.");
8849 ping_have_cheap_transparency = MagickFalse;
8851 image_depth=image->depth;
8853 quantum_info = (QuantumInfo *) NULL;
8855 image_colors=(int) image->colors;
8856 image_matte=image->matte;
8858 mng_info->IsPalette=image->storage_class == PseudoClass &&
8859 image_colors <= 256 && image->colormap != NULL;
8861 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8862 (image->colors == 0 || image->colormap == NULL))
8864 image_info=DestroyImageInfo(image_info);
8865 image=DestroyImage(image);
8866 (void) ThrowMagickException(&IMimage->exception,
8867 GetMagickModule(),CoderError,
8868 "Cannot write PNG8 or color-type 3; colormap is NULL",
8869 "`%s'",IMimage->filename);
8870 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8871 UnlockSemaphoreInfo(ping_semaphore);
8873 return(MagickFalse);
8877 Allocate the PNG structures
8879 #ifdef PNG_USER_MEM_SUPPORTED
8880 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8881 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8882 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8885 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8886 MagickPNGErrorHandler,MagickPNGWarningHandler);
8889 if (ping == (png_struct *) NULL)
8890 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8892 ping_info=png_create_info_struct(ping);
8894 if (ping_info == (png_info *) NULL)
8896 png_destroy_write_struct(&ping,(png_info **) NULL);
8897 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8900 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8901 ping_pixels=(unsigned char *) NULL;
8903 if (setjmp(png_jmpbuf(ping)))
8909 if (image_info->verbose)
8910 (void) printf("PNG write has failed.\n");
8912 png_destroy_write_struct(&ping,&ping_info);
8913 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8914 UnlockSemaphoreInfo(ping_semaphore);
8916 if (ping_have_blob != MagickFalse)
8917 (void) CloseBlob(image);
8918 image_info=DestroyImageInfo(image_info);
8919 image=DestroyImage(image);
8920 return(MagickFalse);
8923 Prepare PNG for writing.
8925 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8926 if (mng_info->write_mng)
8927 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8930 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8931 if (mng_info->write_mng)
8932 png_permit_empty_plte(ping,MagickTrue);
8939 ping_width=(png_uint_32) image->columns;
8940 ping_height=(png_uint_32) image->rows;
8942 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8945 if (mng_info->write_png_depth != 0)
8946 image_depth=mng_info->write_png_depth;
8948 /* Adjust requested depth to next higher valid depth if necessary */
8949 if (image_depth > 8)
8952 if ((image_depth > 4) && (image_depth < 8))
8955 if (image_depth == 3)
8958 if (logging != MagickFalse)
8960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8961 " width=%.20g",(double) ping_width);
8962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963 " height=%.20g",(double) ping_height);
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8965 " image_matte=%.20g",(double) image->matte);
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967 " image->depth=%.20g",(double) image->depth);
8968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8969 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8972 save_image_depth=image_depth;
8973 ping_bit_depth=(png_byte) save_image_depth;
8976 #if defined(PNG_pHYs_SUPPORTED)
8977 if (ping_exclude_pHYs == MagickFalse)
8979 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8980 (!mng_info->write_mng || !mng_info->equal_physs))
8982 if (logging != MagickFalse)
8983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984 " Setting up pHYs chunk");
8986 if (image->units == PixelsPerInchResolution)
8988 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8989 ping_pHYs_x_resolution=
8990 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8991 ping_pHYs_y_resolution=
8992 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8995 else if (image->units == PixelsPerCentimeterResolution)
8997 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8998 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8999 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
9004 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9005 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
9006 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
9009 if (logging != MagickFalse)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9012 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9013 (int) ping_pHYs_unit_type);
9014 ping_have_pHYs = MagickTrue;
9019 if (ping_exclude_bKGD == MagickFalse)
9021 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9027 if (ping_bit_depth == 8)
9030 if (ping_bit_depth == 4)
9033 if (ping_bit_depth == 2)
9036 if (ping_bit_depth == 1)
9039 ping_background.red=(png_uint_16)
9040 (ScaleQuantumToShort(image->background_color.red) & mask);
9042 ping_background.green=(png_uint_16)
9043 (ScaleQuantumToShort(image->background_color.green) & mask);
9045 ping_background.blue=(png_uint_16)
9046 (ScaleQuantumToShort(image->background_color.blue) & mask);
9048 ping_background.gray=(png_uint_16) ping_background.green;
9051 if (logging != MagickFalse)
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " Setting up bKGD chunk (1)");
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " background_color index is %d",
9057 (int) ping_background.index);
9059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " ping_bit_depth=%d",ping_bit_depth);
9063 ping_have_bKGD = MagickTrue;
9067 Select the color type.
9072 if (mng_info->IsPalette && mng_info->write_png8)
9075 /* To do: make this a function cause it's used twice, except
9076 for reducing the sample depth from 8. */
9078 number_colors=image_colors;
9080 ping_have_tRNS=MagickFalse;
9085 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9087 if (logging != MagickFalse)
9088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9089 " Setting up PLTE chunk with %d colors (%d)",
9090 number_colors, image_colors);
9092 for (i=0; i < (ssize_t) number_colors; i++)
9094 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9095 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9096 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9097 if (logging != MagickFalse)
9098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9099 #if MAGICKCORE_QUANTUM_DEPTH == 8
9100 " %3ld (%3d,%3d,%3d)",
9102 " %5ld (%5d,%5d,%5d)",
9104 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9108 ping_have_PLTE=MagickTrue;
9109 image_depth=ping_bit_depth;
9112 if (matte != MagickFalse)
9115 Identify which colormap entry is transparent.
9117 assert(number_colors <= 256);
9118 assert(image->colormap != NULL);
9120 for (i=0; i < (ssize_t) number_transparent; i++)
9121 ping_trans_alpha[i]=0;
9124 ping_num_trans=(unsigned short) (number_transparent +
9125 number_semitransparent);
9127 if (ping_num_trans == 0)
9128 ping_have_tRNS=MagickFalse;
9131 ping_have_tRNS=MagickTrue;
9134 if (ping_exclude_bKGD == MagickFalse)
9137 * Identify which colormap entry is the background color.
9140 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9141 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9144 ping_background.index=(png_byte) i;
9146 if (logging != MagickFalse)
9148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149 " background_color index is %d",
9150 (int) ping_background.index);
9153 } /* end of write_png8 */
9155 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9157 image_matte=MagickFalse;
9158 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9161 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9163 image_matte=MagickTrue;
9164 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9167 else /* mng_info->write_pngNN not specified */
9169 image_depth=ping_bit_depth;
9171 if (mng_info->write_png_colortype != 0)
9173 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9175 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9176 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9177 image_matte=MagickTrue;
9180 image_matte=MagickFalse;
9182 if (logging != MagickFalse)
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184 " PNG colortype %d was specified:",(int) ping_color_type);
9187 else /* write_png_colortype not specified */
9189 if (logging != MagickFalse)
9190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9191 " Selecting PNG colortype:");
9193 ping_color_type=(png_byte) ((matte != MagickFalse)?
9194 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9196 if (image_info->type == TrueColorType)
9198 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9199 image_matte=MagickFalse;
9202 if (image_info->type == TrueColorMatteType)
9204 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9205 image_matte=MagickTrue;
9208 if (image_info->type == PaletteType ||
9209 image_info->type == PaletteMatteType)
9210 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9212 if (mng_info->write_png_colortype == 0 &&
9213 (image_info->type == UndefinedType ||
9214 image_info->type == OptimizeType))
9216 if (ping_have_color == MagickFalse)
9218 if (image_matte == MagickFalse)
9220 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9221 image_matte=MagickFalse;
9226 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9227 image_matte=MagickTrue;
9232 if (image_matte == MagickFalse)
9234 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9235 image_matte=MagickFalse;
9240 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9241 image_matte=MagickTrue;
9248 if (logging != MagickFalse)
9249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9250 " Selected PNG colortype=%d",ping_color_type);
9252 if (ping_bit_depth < 8)
9254 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9255 ping_color_type == PNG_COLOR_TYPE_RGB ||
9256 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9260 old_bit_depth=ping_bit_depth;
9262 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9264 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9268 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9273 if (image->colors == 0)
9276 (void) ThrowMagickException(&image->exception,
9277 GetMagickModule(),CoderError,
9278 "image has 0 colors", "`%s'","");
9281 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9282 ping_bit_depth <<= 1;
9285 if (logging != MagickFalse)
9287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9288 " Number of colors: %.20g",(double) image_colors);
9290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9291 " Tentative PNG bit depth: %d",ping_bit_depth);
9294 if (ping_bit_depth < (int) mng_info->write_png_depth)
9295 ping_bit_depth = mng_info->write_png_depth;
9298 image_depth=ping_bit_depth;
9300 if (logging != MagickFalse)
9302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9303 " Tentative PNG color type: %.20g",(double) ping_color_type);
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306 " image_info->type: %.20g",(double) image_info->type);
9308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9309 " image_depth: %.20g",(double) image_depth);
9311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9313 " image->depth: %.20g",(double) image->depth);
9315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9316 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9319 if (matte != MagickFalse)
9321 if (mng_info->IsPalette)
9323 if (mng_info->write_png_colortype == 0)
9325 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9327 if (ping_have_color != MagickFalse)
9328 ping_color_type=PNG_COLOR_TYPE_RGBA;
9332 * Determine if there is any transparent color.
9334 if (number_transparent + number_semitransparent == 0)
9337 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9340 image_matte=MagickFalse;
9342 if (mng_info->write_png_colortype == 0)
9343 ping_color_type&=0x03;
9353 if (ping_bit_depth == 8)
9356 if (ping_bit_depth == 4)
9359 if (ping_bit_depth == 2)
9362 if (ping_bit_depth == 1)
9365 ping_trans_color.red=(png_uint_16)
9366 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9368 ping_trans_color.green=(png_uint_16)
9369 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9371 ping_trans_color.blue=(png_uint_16)
9372 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9374 ping_trans_color.gray=(png_uint_16)
9375 (ScaleQuantumToShort(GetPixelInfoIntensity(
9376 image->colormap)) & mask);
9378 ping_trans_color.index=(png_byte) 0;
9380 ping_have_tRNS=MagickTrue;
9383 if (ping_have_tRNS != MagickFalse)
9386 * Determine if there is one and only one transparent color
9387 * and if so if it is fully transparent.
9389 if (ping_have_cheap_transparency == MagickFalse)
9390 ping_have_tRNS=MagickFalse;
9393 if (ping_have_tRNS != MagickFalse)
9395 if (mng_info->write_png_colortype == 0)
9396 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9398 if (image_depth == 8)
9400 ping_trans_color.red&=0xff;
9401 ping_trans_color.green&=0xff;
9402 ping_trans_color.blue&=0xff;
9403 ping_trans_color.gray&=0xff;
9409 if (image_depth == 8)
9411 ping_trans_color.red&=0xff;
9412 ping_trans_color.green&=0xff;
9413 ping_trans_color.blue&=0xff;
9414 ping_trans_color.gray&=0xff;
9421 if (ping_have_tRNS != MagickFalse)
9422 image_matte=MagickFalse;
9424 if ((mng_info->IsPalette) &&
9425 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9426 ping_have_color == MagickFalse &&
9427 (image_matte == MagickFalse || image_depth >= 8))
9431 if (image_matte != MagickFalse)
9432 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9434 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9436 ping_color_type=PNG_COLOR_TYPE_GRAY;
9438 if (save_image_depth == 16 && image_depth == 8)
9440 if (logging != MagickFalse)
9442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9443 " Scaling ping_trans_color (0)");
9445 ping_trans_color.gray*=0x0101;
9449 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9450 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9452 if ((image_colors == 0) ||
9453 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9454 image_colors=(int) (one << image_depth);
9456 if (image_depth > 8)
9462 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9464 if(!mng_info->write_png_depth)
9468 while ((int) (one << ping_bit_depth)
9469 < (ssize_t) image_colors)
9470 ping_bit_depth <<= 1;
9474 else if (ping_color_type ==
9475 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9476 mng_info->IsPalette)
9478 /* Check if grayscale is reducible */
9481 depth_4_ok=MagickTrue,
9482 depth_2_ok=MagickTrue,
9483 depth_1_ok=MagickTrue;
9485 for (i=0; i < (ssize_t) image_colors; i++)
9490 intensity=ScaleQuantumToChar(image->colormap[i].red);
9492 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9493 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9494 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9495 depth_2_ok=depth_1_ok=MagickFalse;
9496 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9497 depth_1_ok=MagickFalse;
9500 if (depth_1_ok && mng_info->write_png_depth <= 1)
9503 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9506 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9511 image_depth=ping_bit_depth;
9516 if (mng_info->IsPalette)
9518 number_colors=image_colors;
9520 if (image_depth <= 8)
9525 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9527 if (mng_info->have_write_global_plte && matte == MagickFalse)
9529 png_set_PLTE(ping,ping_info,NULL,0);
9531 if (logging != MagickFalse)
9532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9533 " Setting up empty PLTE chunk");
9538 for (i=0; i < (ssize_t) number_colors; i++)
9540 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9541 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9542 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9545 if (logging != MagickFalse)
9546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9547 " Setting up PLTE chunk with %d colors",
9550 ping_have_PLTE=MagickTrue;
9553 /* color_type is PNG_COLOR_TYPE_PALETTE */
9554 if (mng_info->write_png_depth == 0)
9562 while ((one << ping_bit_depth) < (size_t) number_colors)
9563 ping_bit_depth <<= 1;
9568 if (matte != MagickFalse)
9571 * Set up trans_colors array.
9573 assert(number_colors <= 256);
9575 ping_num_trans=(unsigned short) (number_transparent +
9576 number_semitransparent);
9578 if (ping_num_trans == 0)
9579 ping_have_tRNS=MagickFalse;
9583 if (logging != MagickFalse)
9585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9586 " Scaling ping_trans_color (1)");
9588 ping_have_tRNS=MagickTrue;
9590 for (i=0; i < ping_num_trans; i++)
9592 ping_trans_alpha[i]= (png_byte)
9593 ScaleQuantumToChar(image->colormap[i].alpha);
9603 if (image_depth < 8)
9606 if ((save_image_depth == 16) && (image_depth == 8))
9608 if (logging != MagickFalse)
9610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9611 " Scaling ping_trans_color from (%d,%d,%d)",
9612 (int) ping_trans_color.red,
9613 (int) ping_trans_color.green,
9614 (int) ping_trans_color.blue);
9617 ping_trans_color.red*=0x0101;
9618 ping_trans_color.green*=0x0101;
9619 ping_trans_color.blue*=0x0101;
9620 ping_trans_color.gray*=0x0101;
9622 if (logging != MagickFalse)
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9626 (int) ping_trans_color.red,
9627 (int) ping_trans_color.green,
9628 (int) ping_trans_color.blue);
9633 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9634 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9637 Adjust background and transparency samples in sub-8-bit grayscale files.
9639 if (ping_bit_depth < 8 && ping_color_type ==
9640 PNG_COLOR_TYPE_GRAY)
9648 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9650 if (ping_exclude_bKGD == MagickFalse)
9653 ping_background.gray=(png_uint_16)
9654 ((maxval/255.)*((GetPixelInfoIntensity(&image->background_color)))
9657 if (logging != MagickFalse)
9658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9659 " Setting up bKGD chunk (2)");
9660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9661 " background_color index is %d",
9662 (int) ping_background.index);
9664 ping_have_bKGD = MagickTrue;
9667 if (logging != MagickFalse)
9668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9669 " Scaling ping_trans_color.gray from %d",
9670 (int)ping_trans_color.gray);
9672 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9673 ping_trans_color.gray)+.5);
9675 if (logging != MagickFalse)
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " to %d", (int)ping_trans_color.gray);
9680 if (ping_exclude_bKGD == MagickFalse)
9682 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9685 Identify which colormap entry is the background color.
9688 number_colors=image_colors;
9690 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9691 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9694 ping_background.index=(png_byte) i;
9696 if (logging != MagickFalse)
9698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9699 " Setting up bKGD chunk with index=%d",(int) i);
9702 if (i < (ssize_t) number_colors)
9704 ping_have_bKGD = MagickTrue;
9706 if (logging != MagickFalse)
9708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9709 " background =(%d,%d,%d)",
9710 (int) ping_background.red,
9711 (int) ping_background.green,
9712 (int) ping_background.blue);
9716 else /* Can't happen */
9718 if (logging != MagickFalse)
9719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720 " No room in PLTE to add bKGD color");
9721 ping_have_bKGD = MagickFalse;
9726 if (logging != MagickFalse)
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9728 " PNG color type: %d",ping_color_type);
9730 Initialize compression level and filtering.
9732 if (logging != MagickFalse)
9734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9735 " Setting up deflate compression");
9737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " Compression buffer size: 32768");
9741 png_set_compression_buffer_size(ping,32768L);
9743 if (logging != MagickFalse)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " Compression mem level: 9");
9747 png_set_compression_mem_level(ping, 9);
9749 /* Untangle the "-quality" setting:
9751 Undefined is 0; the default is used.
9756 0: Use Z_HUFFMAN_ONLY strategy with the
9757 zlib default compression level
9759 1-9: the zlib compression level
9763 0-4: the PNG filter method
9765 5: libpng adaptive filtering if compression level > 5
9766 libpng filter type "none" if compression level <= 5
9767 or if image is grayscale or palette
9769 6: libpng adaptive filtering
9771 7: "LOCO" filtering (intrapixel differing) if writing
9772 a MNG, othewise "none". Did not work in IM-6.7.0-9
9773 and earlier because of a missing "else".
9775 8: Z_RLE strategy, all filters
9776 Unused prior to IM-6.7.0-10, was same as 6
9778 9: Z_RLE strategy, no PNG filters
9779 Unused prior to IM-6.7.0-10, was same as 6
9781 Note that using the -quality option, not all combinations of
9782 PNG filter type, zlib compression level, and zlib compression
9783 strategy are possible. This will be addressed soon in a
9784 release that accomodates "-define PNG:compression-strategy", etc.
9788 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9793 if (mng_info->write_png_compression_strategy == 0)
9794 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9797 else if (mng_info->write_png_compression_level == 0)
9802 level=(int) MagickMin((ssize_t) quality/10,9);
9804 mng_info->write_png_compression_level = level+1;
9807 if (mng_info->write_png_compression_strategy == 0)
9809 if ((quality %10) == 8 || (quality %10) == 9)
9810 mng_info->write_png_compression_strategy=Z_RLE;
9813 if (mng_info->write_png_compression_filter == 0)
9814 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9816 if (logging != MagickFalse)
9818 if (mng_info->write_png_compression_level)
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820 " Compression level: %d",
9821 (int) mng_info->write_png_compression_level-1);
9823 if (mng_info->write_png_compression_strategy)
9824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825 " Compression strategy: %d",
9826 (int) mng_info->write_png_compression_strategy-1);
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Setting up filtering");
9831 if (mng_info->write_png_compression_filter == 6)
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Base filter method: ADAPTIVE");
9834 else if (mng_info->write_png_compression_filter == 0 ||
9835 mng_info->write_png_compression_filter == 1)
9836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Base filter method: NONE");
9839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840 " Base filter method: %d",
9841 (int) mng_info->write_png_compression_filter-1);
9844 if (mng_info->write_png_compression_level != 0)
9845 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9847 if (mng_info->write_png_compression_filter == 6)
9849 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9850 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9852 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9854 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9856 else if (mng_info->write_png_compression_filter == 7 ||
9857 mng_info->write_png_compression_filter == 10)
9858 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9860 else if (mng_info->write_png_compression_filter == 8)
9862 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9863 if (mng_info->write_mng)
9865 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9866 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9867 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9870 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9873 else if (mng_info->write_png_compression_filter == 9)
9874 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9876 else if (mng_info->write_png_compression_filter != 0)
9877 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9878 mng_info->write_png_compression_filter-1);
9880 if (mng_info->write_png_compression_strategy != 0)
9881 png_set_compression_strategy(ping,
9882 mng_info->write_png_compression_strategy-1);
9884 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9885 if (ping_exclude_sRGB != MagickFalse ||
9886 (image->rendering_intent == UndefinedIntent))
9888 if ((ping_exclude_tEXt == MagickFalse ||
9889 ping_exclude_zTXt == MagickFalse) &&
9890 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9892 ResetImageProfileIterator(image);
9893 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9895 profile=GetImageProfile(image,name);
9897 if (profile != (StringInfo *) NULL)
9899 #ifdef PNG_WRITE_iCCP_SUPPORTED
9900 if ((LocaleCompare(name,"ICC") == 0) ||
9901 (LocaleCompare(name,"ICM") == 0))
9904 if (ping_exclude_iCCP == MagickFalse)
9906 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9907 #if (PNG_LIBPNG_VER < 10500)
9908 (png_charp) GetStringInfoDatum(profile),
9910 (png_const_bytep) GetStringInfoDatum(profile),
9912 (png_uint_32) GetStringInfoLength(profile));
9918 if (ping_exclude_zCCP == MagickFalse)
9920 Magick_png_write_raw_profile(image_info,ping,ping_info,
9921 (unsigned char *) name,(unsigned char *) name,
9922 GetStringInfoDatum(profile),
9923 (png_uint_32) GetStringInfoLength(profile));
9927 if (logging != MagickFalse)
9928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9929 " Setting up text chunk with %s profile",name);
9931 name=GetNextImageProfile(image);
9936 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9937 if ((mng_info->have_write_global_srgb == 0) &&
9938 ((image->rendering_intent != UndefinedIntent) ||
9939 (image->colorspace == sRGBColorspace)))
9941 if (ping_exclude_sRGB == MagickFalse)
9944 Note image rendering intent.
9946 if (logging != MagickFalse)
9947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9948 " Setting up sRGB chunk");
9950 (void) png_set_sRGB(ping,ping_info,(
9951 Magick_RenderingIntent_to_PNG_RenderingIntent(
9952 image->rendering_intent)));
9956 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9959 if (ping_exclude_gAMA == MagickFalse &&
9960 (ping_exclude_sRGB == MagickFalse ||
9961 (image->gamma < .45 || image->gamma > .46)))
9963 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9967 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9969 if (logging != MagickFalse)
9970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9971 " Setting up gAMA chunk");
9973 png_set_gAMA(ping,ping_info,image->gamma);
9977 if (ping_exclude_cHRM == MagickFalse)
9979 if ((mng_info->have_write_global_chrm == 0) &&
9980 (image->chromaticity.red_primary.x != 0.0))
9983 Note image chromaticity.
9984 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9992 wp=image->chromaticity.white_point;
9993 rp=image->chromaticity.red_primary;
9994 gp=image->chromaticity.green_primary;
9995 bp=image->chromaticity.blue_primary;
9997 if (logging != MagickFalse)
9998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9999 " Setting up cHRM chunk");
10001 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10007 ping_interlace_method=image_info->interlace != NoInterlace;
10009 if (mng_info->write_mng)
10010 png_set_sig_bytes(ping,8);
10012 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10014 if (mng_info->write_png_colortype != 0)
10016 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10017 if (ping_have_color != MagickFalse)
10019 ping_color_type = PNG_COLOR_TYPE_RGB;
10021 if (ping_bit_depth < 8)
10025 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10026 if (ping_have_color != MagickFalse)
10027 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10030 if (ping_need_colortype_warning != MagickFalse ||
10031 ((mng_info->write_png_depth &&
10032 (int) mng_info->write_png_depth != ping_bit_depth) ||
10033 (mng_info->write_png_colortype &&
10034 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10035 mng_info->write_png_colortype != 7 &&
10036 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10038 if (logging != MagickFalse)
10040 if (ping_need_colortype_warning != MagickFalse)
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " Image has transparency but tRNS chunk was excluded");
10046 if (mng_info->write_png_depth)
10048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10049 " Defined PNG:bit-depth=%u, Computed depth=%u",
10050 mng_info->write_png_depth,
10054 if (mng_info->write_png_colortype)
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Defined PNG:color-type=%u, Computed color type=%u",
10058 mng_info->write_png_colortype-1,
10064 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10067 if (image_matte != MagickFalse && image->matte == MagickFalse)
10069 /* Add an opaque matte channel */
10070 image->matte = MagickTrue;
10071 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10073 if (logging != MagickFalse)
10074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10075 " Added an opaque matte channel");
10078 if (number_transparent != 0 || number_semitransparent != 0)
10080 if (ping_color_type < 4)
10082 ping_have_tRNS=MagickTrue;
10083 if (logging != MagickFalse)
10084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10085 " Setting ping_have_tRNS=MagickTrue.");
10089 if (logging != MagickFalse)
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Writing PNG header chunks");
10093 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10094 ping_bit_depth,ping_color_type,
10095 ping_interlace_method,ping_compression_method,
10096 ping_filter_method);
10098 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10100 png_set_PLTE(ping,ping_info,palette,number_colors);
10102 if (logging != MagickFalse)
10104 for (i=0; i< (ssize_t) number_colors; i++)
10106 if (i < ping_num_trans)
10107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10108 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10110 (int) palette[i].red,
10111 (int) palette[i].green,
10112 (int) palette[i].blue,
10114 (int) ping_trans_alpha[i]);
10116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10117 " PLTE[%d] = (%d,%d,%d)",
10119 (int) palette[i].red,
10120 (int) palette[i].green,
10121 (int) palette[i].blue);
10126 if (ping_exclude_bKGD == MagickFalse)
10128 if (ping_have_bKGD != MagickFalse)
10130 png_set_bKGD(ping,ping_info,&ping_background);
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10134 " Setting up bKGD chunk");
10135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10136 " background color = (%d,%d,%d)",
10137 (int) ping_background.red,
10138 (int) ping_background.green,
10139 (int) ping_background.blue);
10140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141 " index = %d, gray=%d",
10142 (int) ping_background.index,
10143 (int) ping_background.gray);
10148 if (ping_exclude_pHYs == MagickFalse)
10150 if (ping_have_pHYs != MagickFalse)
10152 png_set_pHYs(ping,ping_info,
10153 ping_pHYs_x_resolution,
10154 ping_pHYs_y_resolution,
10155 ping_pHYs_unit_type);
10159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10160 " Setting up pHYs chunk");
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " x_resolution=%lu",
10163 (unsigned long) ping_pHYs_x_resolution);
10164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10165 " y_resolution=%lu",
10166 (unsigned long) ping_pHYs_y_resolution);
10167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10169 (unsigned long) ping_pHYs_unit_type);
10174 #if defined(PNG_oFFs_SUPPORTED)
10175 if (ping_exclude_oFFs == MagickFalse)
10177 if (image->page.x || image->page.y)
10179 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10180 (png_int_32) image->page.y, 0);
10182 if (logging != MagickFalse)
10183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10184 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10185 (int) image->page.x, (int) image->page.y);
10190 if (mng_info->need_blob != MagickFalse)
10192 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10194 png_error(ping,"WriteBlob Failed");
10196 ping_have_blob=MagickTrue;
10199 png_write_info_before_PLTE(ping, ping_info);
10201 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10203 if (logging != MagickFalse)
10205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10206 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10209 if (ping_color_type == 3)
10210 (void) png_set_tRNS(ping, ping_info,
10217 (void) png_set_tRNS(ping, ping_info,
10220 &ping_trans_color);
10222 if (logging != MagickFalse)
10224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10225 " tRNS color =(%d,%d,%d)",
10226 (int) ping_trans_color.red,
10227 (int) ping_trans_color.green,
10228 (int) ping_trans_color.blue);
10233 /* write any png-chunk-b profiles */
10234 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10236 png_write_info(ping,ping_info);
10238 /* write any PNG-chunk-m profiles */
10239 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10241 if (ping_exclude_vpAg == MagickFalse)
10243 if ((image->page.width != 0 && image->page.width != image->columns) ||
10244 (image->page.height != 0 && image->page.height != image->rows))
10249 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10250 PNGType(chunk,mng_vpAg);
10251 LogPNGChunk(logging,mng_vpAg,9L);
10252 PNGLong(chunk+4,(png_uint_32) image->page.width);
10253 PNGLong(chunk+8,(png_uint_32) image->page.height);
10254 chunk[12]=0; /* unit = pixels */
10255 (void) WriteBlob(image,13,chunk);
10256 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10260 #if (PNG_LIBPNG_VER == 10206)
10261 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10262 #define PNG_HAVE_IDAT 0x04
10263 ping->mode |= PNG_HAVE_IDAT;
10264 #undef PNG_HAVE_IDAT
10267 png_set_packing(ping);
10271 rowbytes=image->columns;
10272 if (image_depth > 8)
10274 switch (ping_color_type)
10276 case PNG_COLOR_TYPE_RGB:
10280 case PNG_COLOR_TYPE_GRAY_ALPHA:
10284 case PNG_COLOR_TYPE_RGBA:
10292 if (logging != MagickFalse)
10294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10295 " Writing PNG image data");
10297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10298 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10300 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10301 sizeof(*ping_pixels));
10303 if (ping_pixels == (unsigned char *) NULL)
10304 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10307 Initialize image scanlines.
10309 if (setjmp(png_jmpbuf(ping)))
10315 if (image_info->verbose)
10316 (void) printf("PNG write has failed.\n");
10318 png_destroy_write_struct(&ping,&ping_info);
10319 if (quantum_info != (QuantumInfo *) NULL)
10320 quantum_info=DestroyQuantumInfo(quantum_info);
10321 if (ping_pixels != (unsigned char *) NULL)
10322 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10323 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10324 UnlockSemaphoreInfo(ping_semaphore);
10326 if (ping_have_blob != MagickFalse)
10327 (void) CloseBlob(image);
10328 image_info=DestroyImageInfo(image_info);
10329 image=DestroyImage(image);
10330 return(MagickFalse);
10332 quantum_info=AcquireQuantumInfo(image_info,image);
10333 if (quantum_info == (QuantumInfo *) NULL)
10334 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10335 quantum_info->format=UndefinedQuantumFormat;
10336 quantum_info->depth=image_depth;
10337 num_passes=png_set_interlace_handling(ping);
10339 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10340 !mng_info->write_png32) &&
10341 (mng_info->IsPalette ||
10342 (image_info->type == BilevelType)) &&
10343 image_matte == MagickFalse &&
10344 ping_have_non_bw == MagickFalse)
10346 /* Palette, Bilevel, or Opaque Monochrome */
10347 register const Quantum
10350 quantum_info->depth=8;
10351 for (pass=0; pass < num_passes; pass++)
10354 Convert PseudoClass image to a PNG monochrome image.
10356 for (y=0; y < (ssize_t) image->rows; y++)
10358 if (logging != MagickFalse && y == 0)
10359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " Writing row of pixels (0)");
10362 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10364 if (p == (const Quantum *) NULL)
10367 if (mng_info->IsPalette)
10369 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10370 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10371 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10372 mng_info->write_png_depth &&
10373 mng_info->write_png_depth != old_bit_depth)
10375 /* Undo pixel scaling */
10376 for (i=0; i < (ssize_t) image->columns; i++)
10377 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10378 >> (8-old_bit_depth));
10384 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10385 quantum_info,RedQuantum,ping_pixels,&image->exception);
10388 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10389 for (i=0; i < (ssize_t) image->columns; i++)
10390 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10393 if (logging != MagickFalse && y == 0)
10394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10395 " Writing row of pixels (1)");
10397 png_write_row(ping,ping_pixels);
10399 if (image->previous == (Image *) NULL)
10401 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10402 if (status == MagickFalse)
10408 else /* Not Palette, Bilevel, or Opaque Monochrome */
10410 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10411 !mng_info->write_png32) &&
10412 (image_matte != MagickFalse ||
10413 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10414 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10416 register const Quantum
10419 for (pass=0; pass < num_passes; pass++)
10422 for (y=0; y < (ssize_t) image->rows; y++)
10424 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10426 if (p == (const Quantum *) NULL)
10429 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10431 if (mng_info->IsPalette)
10432 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10433 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10436 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10437 quantum_info,RedQuantum,ping_pixels,&image->exception);
10439 if (logging != MagickFalse && y == 0)
10440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10441 " Writing GRAY PNG pixels (2)");
10444 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10446 if (logging != MagickFalse && y == 0)
10447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448 " Writing GRAY_ALPHA PNG pixels (2)");
10450 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10451 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
10454 if (logging != MagickFalse && y == 0)
10455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10456 " Writing row of pixels (2)");
10458 png_write_row(ping,ping_pixels);
10461 if (image->previous == (Image *) NULL)
10463 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10464 if (status == MagickFalse)
10472 register const Quantum
10475 for (pass=0; pass < num_passes; pass++)
10477 if ((image_depth > 8) || (mng_info->write_png24 ||
10478 mng_info->write_png32 ||
10479 (!mng_info->write_png8 && !mng_info->IsPalette)))
10481 for (y=0; y < (ssize_t) image->rows; y++)
10483 p=GetVirtualPixels(image,0,y,image->columns,1,
10484 &image->exception);
10486 if (p == (const Quantum *) NULL)
10489 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10491 if (image->storage_class == DirectClass)
10492 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10493 quantum_info,RedQuantum,ping_pixels,&image->exception);
10496 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10497 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10500 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10502 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10503 quantum_info,GrayAlphaQuantum,ping_pixels,
10504 &image->exception);
10506 if (logging != MagickFalse && y == 0)
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " Writing GRAY_ALPHA PNG pixels (3)");
10511 else if (image_matte != MagickFalse)
10512 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10513 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
10516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10517 quantum_info,RGBQuantum,ping_pixels,&image->exception);
10519 if (logging != MagickFalse && y == 0)
10520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10521 " Writing row of pixels (3)");
10523 png_write_row(ping,ping_pixels);
10528 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10529 mng_info->write_png32 ||
10530 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10532 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10533 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10535 if (logging != MagickFalse)
10536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10539 quantum_info->depth=8;
10543 for (y=0; y < (ssize_t) image->rows; y++)
10545 if (logging != MagickFalse && y == 0)
10546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10547 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10549 p=GetVirtualPixels(image,0,y,image->columns,1,
10550 &image->exception);
10552 if (p == (const Quantum *) NULL)
10555 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10557 quantum_info->depth=image->depth;
10559 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10560 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10563 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10565 if (logging != MagickFalse && y == 0)
10566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567 " Writing GRAY_ALPHA PNG pixels (4)");
10569 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10570 quantum_info,GrayAlphaQuantum,ping_pixels,
10571 &image->exception);
10576 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10577 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10579 if (logging != MagickFalse && y <= 2)
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10582 " Writing row of non-gray pixels (4)");
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10586 (int)ping_pixels[0],(int)ping_pixels[1]);
10589 png_write_row(ping,ping_pixels);
10593 if (image->previous == (Image *) NULL)
10595 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10596 if (status == MagickFalse)
10603 if (quantum_info != (QuantumInfo *) NULL)
10604 quantum_info=DestroyQuantumInfo(quantum_info);
10606 if (logging != MagickFalse)
10608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609 " Wrote PNG image data");
10611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10612 " Width: %.20g",(double) ping_width);
10614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10615 " Height: %.20g",(double) ping_height);
10617 if (mng_info->write_png_depth)
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " PNG bit-depth written: %d",ping_bit_depth);
10626 if (mng_info->write_png_colortype)
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " PNG color-type written: %d",ping_color_type);
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " PNG Interlace method: %d",ping_interlace_method);
10639 Generate text chunks after IDAT.
10641 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10643 ResetImagePropertyIterator(image);
10644 property=GetNextImageProperty(image);
10645 while (property != (const char *) NULL)
10650 value=GetImageProperty(image,property,exception);
10652 /* Don't write any "png:" properties; those are just for "identify" */
10653 if (LocaleNCompare(property,"png:",4) != 0 &&
10655 /* Suppress density and units if we wrote a pHYs chunk */
10656 (ping_exclude_pHYs != MagickFalse ||
10657 LocaleCompare(property,"density") != 0 ||
10658 LocaleCompare(property,"units") != 0) &&
10660 /* Suppress the IM-generated Date:create and Date:modify */
10661 (ping_exclude_date == MagickFalse ||
10662 LocaleNCompare(property, "Date:",5) != 0))
10664 if (value != (const char *) NULL)
10666 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10667 text[0].key=(char *) property;
10668 text[0].text=(char *) value;
10669 text[0].text_length=strlen(value);
10671 if (ping_exclude_tEXt != MagickFalse)
10672 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10674 else if (ping_exclude_zTXt != MagickFalse)
10675 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10679 text[0].compression=image_info->compression == NoCompression ||
10680 (image_info->compression == UndefinedCompression &&
10681 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10682 PNG_TEXT_COMPRESSION_zTXt ;
10685 if (logging != MagickFalse)
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " Setting up text chunk");
10690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10691 " keyword: %s",text[0].key);
10694 png_set_text(ping,ping_info,text,1);
10695 png_free(ping,text);
10698 property=GetNextImageProperty(image);
10702 /* write any PNG-chunk-e profiles */
10703 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10705 if (logging != MagickFalse)
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " Writing PNG end info");
10709 png_write_end(ping,ping_info);
10711 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10713 if (mng_info->page.x || mng_info->page.y ||
10714 (ping_width != mng_info->page.width) ||
10715 (ping_height != mng_info->page.height))
10721 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10723 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10724 PNGType(chunk,mng_FRAM);
10725 LogPNGChunk(logging,mng_FRAM,27L);
10727 chunk[5]=0; /* frame name separator (no name) */
10728 chunk[6]=1; /* flag for changing delay, for next frame only */
10729 chunk[7]=0; /* flag for changing frame timeout */
10730 chunk[8]=1; /* flag for changing frame clipping for next frame */
10731 chunk[9]=0; /* flag for changing frame sync_id */
10732 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10733 chunk[14]=0; /* clipping boundaries delta type */
10734 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10736 (png_uint_32) (mng_info->page.x + ping_width));
10737 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10739 (png_uint_32) (mng_info->page.y + ping_height));
10740 (void) WriteBlob(image,31,chunk);
10741 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10742 mng_info->old_framing_mode=4;
10743 mng_info->framing_mode=1;
10747 mng_info->framing_mode=3;
10749 if (mng_info->write_mng && !mng_info->need_fram &&
10750 ((int) image->dispose == 3))
10751 (void) ThrowMagickException(&image->exception,GetMagickModule(),
10752 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10753 "`%s'",image->filename);
10756 Free PNG resources.
10759 png_destroy_write_struct(&ping,&ping_info);
10761 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10763 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10764 UnlockSemaphoreInfo(ping_semaphore);
10767 if (ping_have_blob != MagickFalse)
10768 (void) CloseBlob(image);
10770 image_info=DestroyImageInfo(image_info);
10771 image=DestroyImage(image);
10773 /* Store bit depth actually written */
10774 s[0]=(char) ping_bit_depth;
10777 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10779 if (logging != MagickFalse)
10780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10781 " exit WriteOnePNGImage()");
10783 return(MagickTrue);
10784 /* End write one PNG image */
10788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10792 % W r i t e P N G I m a g e %
10796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10798 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10799 % Multiple-image Network Graphics (MNG) image file.
10801 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10803 % The format of the WritePNGImage method is:
10805 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10806 % Image *image,ExceptionInfo *exception)
10808 % A description of each parameter follows:
10810 % o image_info: the image info.
10812 % o image: The image.
10814 % o exception: return any errors or warnings in this structure.
10816 % Returns MagickTrue on success, MagickFalse on failure.
10818 % Communicating with the PNG encoder:
10820 % While the datastream written is always in PNG format and normally would
10821 % be given the "png" file extension, this method also writes the following
10822 % pseudo-formats which are subsets of PNG:
10824 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10825 % a depth greater than 8, the depth is reduced. If transparency
10826 % is present, the tRNS chunk must only have values 0 and 255
10827 % (i.e., transparency is binary: fully opaque or fully
10828 % transparent). If other values are present they will be
10829 % 50%-thresholded to binary transparency. If more than 256
10830 % colors are present, they will be quantized to the 4-4-4-1,
10831 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10832 % of any resulting fully-transparent pixels is changed to
10833 % the image's background color.
10835 % If you want better quantization or dithering of the colors
10836 % or alpha than that, you need to do it before calling the
10837 % PNG encoder. The pixels contain 8-bit indices even if
10838 % they could be represented with 1, 2, or 4 bits. Grayscale
10839 % images will be written as indexed PNG files even though the
10840 % PNG grayscale type might be slightly more efficient. Please
10841 % note that writing to the PNG8 format may result in loss
10842 % of color and alpha data.
10844 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10845 % chunk can be present to convey binary transparency by naming
10846 % one of the colors as transparent. The only loss incurred
10847 % is reduction of sample depth to 8. If the image has more
10848 % than one transparent color, has semitransparent pixels, or
10849 % has an opaque pixel with the same RGB components as the
10850 % transparent color, an image is not written.
10852 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10853 % transparency is permitted, i.e., the alpha sample for
10854 % each pixel can have any value from 0 to 255. The alpha
10855 % channel is present even if the image is fully opaque.
10856 % The only loss in data is the reduction of the sample depth
10859 % o -define: For more precise control of the PNG output, you can use the
10860 % Image options "png:bit-depth" and "png:color-type". These
10861 % can be set from the commandline with "-define" and also
10862 % from the application programming interfaces. The options
10863 % are case-independent and are converted to lowercase before
10864 % being passed to this encoder.
10866 % png:color-type can be 0, 2, 3, 4, or 6.
10868 % When png:color-type is 0 (Grayscale), png:bit-depth can
10869 % be 1, 2, 4, 8, or 16.
10871 % When png:color-type is 2 (RGB), png:bit-depth can
10874 % When png:color-type is 3 (Indexed), png:bit-depth can
10875 % be 1, 2, 4, or 8. This refers to the number of bits
10876 % used to store the index. The color samples always have
10877 % bit-depth 8 in indexed PNG files.
10879 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10880 % png:bit-depth can be 8 or 16.
10882 % If the image cannot be written without loss with the requested bit-depth
10883 % and color-type, a PNG file will not be written, and the encoder will
10884 % return MagickFalse.
10886 % Since image encoders should not be responsible for the "heavy lifting",
10887 % the user should make sure that ImageMagick has already reduced the
10888 % image depth and number of colors and limit transparency to binary
10889 % transparency prior to attempting to write the image with depth, color,
10890 % or transparency limitations.
10892 % Note that another definition, "png:bit-depth-written" exists, but it
10893 % is not intended for external use. It is only used internally by the
10894 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10896 % It is possible to request that the PNG encoder write previously-formatted
10897 % ancillary chunks in the output PNG file, using the "-profile" commandline
10898 % option as shown below or by setting the profile via a programming
10901 % -profile PNG-chunk-x:<file>
10903 % where x is a location flag and <file> is a file containing the chunk
10904 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10905 % This encoder will compute the chunk length and CRC, so those must not
10906 % be included in the file.
10908 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10909 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10910 % of the same type, then add a short unique string after the "x" to prevent
10911 % subsequent profiles from overwriting the preceding ones, e.g.,
10913 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10915 % As of version 6.6.6 the following optimizations are always done:
10917 % o 32-bit depth is reduced to 16.
10918 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10919 % high byte and low byte are identical.
10920 % o Palette is sorted to remove unused entries and to put a
10921 % transparent color first, if BUILD_PNG_PALETTE is defined.
10922 % o Opaque matte channel is removed when writing an indexed PNG.
10923 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10924 % this can be done without loss and a larger bit depth N was not
10925 % requested via the "-define PNG:bit-depth=N" option.
10926 % o If matte channel is present but only one transparent color is
10927 % present, RGB+tRNS is written instead of RGBA
10928 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10929 % was requested when converting an opaque image).
10931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10933 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10934 Image *image,ExceptionInfo *exception)
10939 have_mng_structure,
10955 assert(image_info != (const ImageInfo *) NULL);
10956 assert(image_info->signature == MagickSignature);
10957 assert(image != (Image *) NULL);
10958 assert(image->signature == MagickSignature);
10959 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10960 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10962 Allocate a MngInfo structure.
10964 have_mng_structure=MagickFalse;
10965 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10967 if (mng_info == (MngInfo *) NULL)
10968 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10971 Initialize members of the MngInfo structure.
10973 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10974 mng_info->image=image;
10975 mng_info->equal_backgrounds=MagickTrue;
10976 have_mng_structure=MagickTrue;
10978 /* See if user has requested a specific PNG subformat */
10980 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10981 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10982 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10984 if (mng_info->write_png8)
10986 mng_info->write_png_colortype = /* 3 */ 4;
10987 mng_info->write_png_depth = 8;
10991 if (mng_info->write_png24)
10993 mng_info->write_png_colortype = /* 2 */ 3;
10994 mng_info->write_png_depth = 8;
10997 if (image->matte == MagickTrue)
10998 (void) SetImageType(image,TrueColorMatteType,exception);
11001 (void) SetImageType(image,TrueColorType,exception);
11003 (void) SyncImage(image,exception);
11006 if (mng_info->write_png32)
11008 mng_info->write_png_colortype = /* 6 */ 7;
11009 mng_info->write_png_depth = 8;
11012 if (image->matte == MagickTrue)
11013 (void) SetImageType(image,TrueColorMatteType,exception);
11016 (void) SetImageType(image,TrueColorType,exception);
11018 (void) SyncImage(image,exception);
11021 value=GetImageOption(image_info,"png:bit-depth");
11023 if (value != (char *) NULL)
11025 if (LocaleCompare(value,"1") == 0)
11026 mng_info->write_png_depth = 1;
11028 else if (LocaleCompare(value,"2") == 0)
11029 mng_info->write_png_depth = 2;
11031 else if (LocaleCompare(value,"4") == 0)
11032 mng_info->write_png_depth = 4;
11034 else if (LocaleCompare(value,"8") == 0)
11035 mng_info->write_png_depth = 8;
11037 else if (LocaleCompare(value,"16") == 0)
11038 mng_info->write_png_depth = 16;
11041 (void) ThrowMagickException(&image->exception,
11042 GetMagickModule(),CoderWarning,
11043 "ignoring invalid defined png:bit-depth",
11046 if (logging != MagickFalse)
11047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11048 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11051 value=GetImageOption(image_info,"png:color-type");
11053 if (value != (char *) NULL)
11055 /* We must store colortype+1 because 0 is a valid colortype */
11056 if (LocaleCompare(value,"0") == 0)
11057 mng_info->write_png_colortype = 1;
11059 else if (LocaleCompare(value,"2") == 0)
11060 mng_info->write_png_colortype = 3;
11062 else if (LocaleCompare(value,"3") == 0)
11063 mng_info->write_png_colortype = 4;
11065 else if (LocaleCompare(value,"4") == 0)
11066 mng_info->write_png_colortype = 5;
11068 else if (LocaleCompare(value,"6") == 0)
11069 mng_info->write_png_colortype = 7;
11072 (void) ThrowMagickException(&image->exception,
11073 GetMagickModule(),CoderWarning,
11074 "ignoring invalid defined png:color-type",
11077 if (logging != MagickFalse)
11078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11079 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11082 /* Check for chunks to be excluded:
11084 * The default is to not exclude any known chunks except for any
11085 * listed in the "unused_chunks" array, above.
11087 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11088 * define (in the image properties or in the image artifacts)
11089 * or via a mng_info member. For convenience, in addition
11090 * to or instead of a comma-separated list of chunks, the
11091 * "exclude-chunk" string can be simply "all" or "none".
11093 * The exclude-chunk define takes priority over the mng_info.
11095 * A "PNG:include-chunk" define takes priority over both the
11096 * mng_info and the "PNG:exclude-chunk" define. Like the
11097 * "exclude-chunk" string, it can define "all" or "none" as
11098 * well as a comma-separated list. Chunks that are unknown to
11099 * ImageMagick are always excluded, regardless of their "copy-safe"
11100 * status according to the PNG specification, and even if they
11101 * appear in the "include-chunk" list.
11103 * Finally, all chunks listed in the "unused_chunks" array are
11104 * automatically excluded, regardless of the other instructions
11107 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11108 * will not be written and the gAMA chunk will only be written if it
11109 * is not between .45 and .46, or approximately (1.0/2.2).
11111 * If you exclude tRNS and the image has transparency, the colortype
11112 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11114 * The -strip option causes StripImage() to set the png:include-chunk
11115 * artifact to "none,trns,gama".
11118 mng_info->ping_exclude_bKGD=MagickFalse;
11119 mng_info->ping_exclude_cHRM=MagickFalse;
11120 mng_info->ping_exclude_date=MagickFalse;
11121 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11122 mng_info->ping_exclude_gAMA=MagickFalse;
11123 mng_info->ping_exclude_iCCP=MagickFalse;
11124 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11125 mng_info->ping_exclude_oFFs=MagickFalse;
11126 mng_info->ping_exclude_pHYs=MagickFalse;
11127 mng_info->ping_exclude_sRGB=MagickFalse;
11128 mng_info->ping_exclude_tEXt=MagickFalse;
11129 mng_info->ping_exclude_tRNS=MagickFalse;
11130 mng_info->ping_exclude_vpAg=MagickFalse;
11131 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11132 mng_info->ping_exclude_zTXt=MagickFalse;
11134 mng_info->ping_preserve_colormap=MagickFalse;
11136 value=GetImageArtifact(image,"png:preserve-colormap");
11138 value=GetImageOption(image_info,"png:preserve-colormap");
11140 mng_info->ping_preserve_colormap=MagickTrue;
11142 /* Thes compression-level, compression-strategy, and compression-filter
11143 * defines take precedence over values from the -quality option.
11145 value=GetImageArtifact(image,"png:compression-level");
11147 value=GetImageOption(image_info,"png:compression-level");
11150 /* We have to add 1 to everything because 0 is a valid input,
11151 * and we want to use 0 (the default) to mean undefined.
11153 if (LocaleCompare(value,"0") == 0)
11154 mng_info->write_png_compression_level = 1;
11156 if (LocaleCompare(value,"1") == 0)
11157 mng_info->write_png_compression_level = 2;
11159 else if (LocaleCompare(value,"2") == 0)
11160 mng_info->write_png_compression_level = 3;
11162 else if (LocaleCompare(value,"3") == 0)
11163 mng_info->write_png_compression_level = 4;
11165 else if (LocaleCompare(value,"4") == 0)
11166 mng_info->write_png_compression_level = 5;
11168 else if (LocaleCompare(value,"5") == 0)
11169 mng_info->write_png_compression_level = 6;
11171 else if (LocaleCompare(value,"6") == 0)
11172 mng_info->write_png_compression_level = 7;
11174 else if (LocaleCompare(value,"7") == 0)
11175 mng_info->write_png_compression_level = 8;
11177 else if (LocaleCompare(value,"8") == 0)
11178 mng_info->write_png_compression_level = 9;
11180 else if (LocaleCompare(value,"9") == 0)
11181 mng_info->write_png_compression_level = 10;
11184 (void) ThrowMagickException(&image->exception,
11185 GetMagickModule(),CoderWarning,
11186 "ignoring invalid defined png:compression-level",
11190 value=GetImageArtifact(image,"png:compression-strategy");
11192 value=GetImageOption(image_info,"png:compression-strategy");
11196 if (LocaleCompare(value,"0") == 0)
11197 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11199 else if (LocaleCompare(value,"1") == 0)
11200 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11202 else if (LocaleCompare(value,"2") == 0)
11203 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11205 else if (LocaleCompare(value,"3") == 0)
11206 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11207 mng_info->write_png_compression_strategy = Z_RLE+1;
11209 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11212 else if (LocaleCompare(value,"4") == 0)
11213 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11214 mng_info->write_png_compression_strategy = Z_FIXED+1;
11216 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11220 (void) ThrowMagickException(&image->exception,
11221 GetMagickModule(),CoderWarning,
11222 "ignoring invalid defined png:compression-strategy",
11226 value=GetImageArtifact(image,"png:compression-filter");
11228 value=GetImageOption(image_info,"png:compression-filter");
11232 /* To do: combinations of filters allowed by libpng
11233 * masks 0x08 through 0xf8
11235 * Implement this as a comma-separated list of 0,1,2,3,4,5
11236 * where 5 is a special case meaning PNG_ALL_FILTERS.
11239 if (LocaleCompare(value,"0") == 0)
11240 mng_info->write_png_compression_filter = 1;
11242 if (LocaleCompare(value,"1") == 0)
11243 mng_info->write_png_compression_filter = 2;
11245 else if (LocaleCompare(value,"2") == 0)
11246 mng_info->write_png_compression_filter = 3;
11248 else if (LocaleCompare(value,"3") == 0)
11249 mng_info->write_png_compression_filter = 4;
11251 else if (LocaleCompare(value,"4") == 0)
11252 mng_info->write_png_compression_filter = 5;
11254 else if (LocaleCompare(value,"5") == 0)
11255 mng_info->write_png_compression_filter = 6;
11258 (void) ThrowMagickException(&image->exception,
11259 GetMagickModule(),CoderWarning,
11260 "ignoring invalid defined png:compression-filter",
11264 excluding=MagickFalse;
11266 for (source=0; source<1; source++)
11270 value=GetImageArtifact(image,"png:exclude-chunk");
11273 value=GetImageArtifact(image,"png:exclude-chunks");
11277 value=GetImageOption(image_info,"png:exclude-chunk");
11280 value=GetImageOption(image_info,"png:exclude-chunks");
11289 excluding=MagickTrue;
11291 if (logging != MagickFalse)
11294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11295 " png:exclude-chunk=%s found in image artifacts.\n", value);
11297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11298 " png:exclude-chunk=%s found in image properties.\n", value);
11301 last=strlen(value);
11303 for (i=0; i<(int) last; i+=5)
11306 if (LocaleNCompare(value+i,"all",3) == 0)
11308 mng_info->ping_exclude_bKGD=MagickTrue;
11309 mng_info->ping_exclude_cHRM=MagickTrue;
11310 mng_info->ping_exclude_date=MagickTrue;
11311 mng_info->ping_exclude_EXIF=MagickTrue;
11312 mng_info->ping_exclude_gAMA=MagickTrue;
11313 mng_info->ping_exclude_iCCP=MagickTrue;
11314 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11315 mng_info->ping_exclude_oFFs=MagickTrue;
11316 mng_info->ping_exclude_pHYs=MagickTrue;
11317 mng_info->ping_exclude_sRGB=MagickTrue;
11318 mng_info->ping_exclude_tEXt=MagickTrue;
11319 mng_info->ping_exclude_tRNS=MagickTrue;
11320 mng_info->ping_exclude_vpAg=MagickTrue;
11321 mng_info->ping_exclude_zCCP=MagickTrue;
11322 mng_info->ping_exclude_zTXt=MagickTrue;
11326 if (LocaleNCompare(value+i,"none",4) == 0)
11328 mng_info->ping_exclude_bKGD=MagickFalse;
11329 mng_info->ping_exclude_cHRM=MagickFalse;
11330 mng_info->ping_exclude_date=MagickFalse;
11331 mng_info->ping_exclude_EXIF=MagickFalse;
11332 mng_info->ping_exclude_gAMA=MagickFalse;
11333 mng_info->ping_exclude_iCCP=MagickFalse;
11334 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11335 mng_info->ping_exclude_oFFs=MagickFalse;
11336 mng_info->ping_exclude_pHYs=MagickFalse;
11337 mng_info->ping_exclude_sRGB=MagickFalse;
11338 mng_info->ping_exclude_tEXt=MagickFalse;
11339 mng_info->ping_exclude_tRNS=MagickFalse;
11340 mng_info->ping_exclude_vpAg=MagickFalse;
11341 mng_info->ping_exclude_zCCP=MagickFalse;
11342 mng_info->ping_exclude_zTXt=MagickFalse;
11345 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11346 mng_info->ping_exclude_bKGD=MagickTrue;
11348 if (LocaleNCompare(value+i,"chrm",4) == 0)
11349 mng_info->ping_exclude_cHRM=MagickTrue;
11351 if (LocaleNCompare(value+i,"date",4) == 0)
11352 mng_info->ping_exclude_date=MagickTrue;
11354 if (LocaleNCompare(value+i,"exif",4) == 0)
11355 mng_info->ping_exclude_EXIF=MagickTrue;
11357 if (LocaleNCompare(value+i,"gama",4) == 0)
11358 mng_info->ping_exclude_gAMA=MagickTrue;
11360 if (LocaleNCompare(value+i,"iccp",4) == 0)
11361 mng_info->ping_exclude_iCCP=MagickTrue;
11364 if (LocaleNCompare(value+i,"itxt",4) == 0)
11365 mng_info->ping_exclude_iTXt=MagickTrue;
11368 if (LocaleNCompare(value+i,"gama",4) == 0)
11369 mng_info->ping_exclude_gAMA=MagickTrue;
11371 if (LocaleNCompare(value+i,"offs",4) == 0)
11372 mng_info->ping_exclude_oFFs=MagickTrue;
11374 if (LocaleNCompare(value+i,"phys",4) == 0)
11375 mng_info->ping_exclude_pHYs=MagickTrue;
11377 if (LocaleNCompare(value+i,"srgb",4) == 0)
11378 mng_info->ping_exclude_sRGB=MagickTrue;
11380 if (LocaleNCompare(value+i,"text",4) == 0)
11381 mng_info->ping_exclude_tEXt=MagickTrue;
11383 if (LocaleNCompare(value+i,"trns",4) == 0)
11384 mng_info->ping_exclude_tRNS=MagickTrue;
11386 if (LocaleNCompare(value+i,"vpag",4) == 0)
11387 mng_info->ping_exclude_vpAg=MagickTrue;
11389 if (LocaleNCompare(value+i,"zccp",4) == 0)
11390 mng_info->ping_exclude_zCCP=MagickTrue;
11392 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11393 mng_info->ping_exclude_zTXt=MagickTrue;
11399 for (source=0; source<1; source++)
11403 value=GetImageArtifact(image,"png:include-chunk");
11406 value=GetImageArtifact(image,"png:include-chunks");
11410 value=GetImageOption(image_info,"png:include-chunk");
11413 value=GetImageOption(image_info,"png:include-chunks");
11421 excluding=MagickTrue;
11423 if (logging != MagickFalse)
11426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11427 " png:include-chunk=%s found in image artifacts.\n", value);
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " png:include-chunk=%s found in image properties.\n", value);
11433 last=strlen(value);
11435 for (i=0; i<(int) last; i+=5)
11437 if (LocaleNCompare(value+i,"all",3) == 0)
11439 mng_info->ping_exclude_bKGD=MagickFalse;
11440 mng_info->ping_exclude_cHRM=MagickFalse;
11441 mng_info->ping_exclude_date=MagickFalse;
11442 mng_info->ping_exclude_EXIF=MagickFalse;
11443 mng_info->ping_exclude_gAMA=MagickFalse;
11444 mng_info->ping_exclude_iCCP=MagickFalse;
11445 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11446 mng_info->ping_exclude_oFFs=MagickFalse;
11447 mng_info->ping_exclude_pHYs=MagickFalse;
11448 mng_info->ping_exclude_sRGB=MagickFalse;
11449 mng_info->ping_exclude_tEXt=MagickFalse;
11450 mng_info->ping_exclude_tRNS=MagickFalse;
11451 mng_info->ping_exclude_vpAg=MagickFalse;
11452 mng_info->ping_exclude_zCCP=MagickFalse;
11453 mng_info->ping_exclude_zTXt=MagickFalse;
11457 if (LocaleNCompare(value+i,"none",4) == 0)
11459 mng_info->ping_exclude_bKGD=MagickTrue;
11460 mng_info->ping_exclude_cHRM=MagickTrue;
11461 mng_info->ping_exclude_date=MagickTrue;
11462 mng_info->ping_exclude_EXIF=MagickTrue;
11463 mng_info->ping_exclude_gAMA=MagickTrue;
11464 mng_info->ping_exclude_iCCP=MagickTrue;
11465 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11466 mng_info->ping_exclude_oFFs=MagickTrue;
11467 mng_info->ping_exclude_pHYs=MagickTrue;
11468 mng_info->ping_exclude_sRGB=MagickTrue;
11469 mng_info->ping_exclude_tEXt=MagickTrue;
11470 mng_info->ping_exclude_tRNS=MagickTrue;
11471 mng_info->ping_exclude_vpAg=MagickTrue;
11472 mng_info->ping_exclude_zCCP=MagickTrue;
11473 mng_info->ping_exclude_zTXt=MagickTrue;
11476 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11477 mng_info->ping_exclude_bKGD=MagickFalse;
11479 if (LocaleNCompare(value+i,"chrm",4) == 0)
11480 mng_info->ping_exclude_cHRM=MagickFalse;
11482 if (LocaleNCompare(value+i,"date",4) == 0)
11483 mng_info->ping_exclude_date=MagickFalse;
11485 if (LocaleNCompare(value+i,"exif",4) == 0)
11486 mng_info->ping_exclude_EXIF=MagickFalse;
11488 if (LocaleNCompare(value+i,"gama",4) == 0)
11489 mng_info->ping_exclude_gAMA=MagickFalse;
11491 if (LocaleNCompare(value+i,"iccp",4) == 0)
11492 mng_info->ping_exclude_iCCP=MagickFalse;
11495 if (LocaleNCompare(value+i,"itxt",4) == 0)
11496 mng_info->ping_exclude_iTXt=MagickFalse;
11499 if (LocaleNCompare(value+i,"gama",4) == 0)
11500 mng_info->ping_exclude_gAMA=MagickFalse;
11502 if (LocaleNCompare(value+i,"offs",4) == 0)
11503 mng_info->ping_exclude_oFFs=MagickFalse;
11505 if (LocaleNCompare(value+i,"phys",4) == 0)
11506 mng_info->ping_exclude_pHYs=MagickFalse;
11508 if (LocaleNCompare(value+i,"srgb",4) == 0)
11509 mng_info->ping_exclude_sRGB=MagickFalse;
11511 if (LocaleNCompare(value+i,"text",4) == 0)
11512 mng_info->ping_exclude_tEXt=MagickFalse;
11514 if (LocaleNCompare(value+i,"trns",4) == 0)
11515 mng_info->ping_exclude_tRNS=MagickFalse;
11517 if (LocaleNCompare(value+i,"vpag",4) == 0)
11518 mng_info->ping_exclude_vpAg=MagickFalse;
11520 if (LocaleNCompare(value+i,"zccp",4) == 0)
11521 mng_info->ping_exclude_zCCP=MagickFalse;
11523 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11524 mng_info->ping_exclude_zTXt=MagickFalse;
11530 if (excluding != MagickFalse && logging != MagickFalse)
11532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11533 " Chunks to be excluded from the output PNG:");
11534 if (mng_info->ping_exclude_bKGD != MagickFalse)
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11537 if (mng_info->ping_exclude_cHRM != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11540 if (mng_info->ping_exclude_date != MagickFalse)
11541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543 if (mng_info->ping_exclude_EXIF != MagickFalse)
11544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11546 if (mng_info->ping_exclude_gAMA != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11549 if (mng_info->ping_exclude_iCCP != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 if (mng_info->ping_exclude_iTXt != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 if (mng_info->ping_exclude_oFFs != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11560 if (mng_info->ping_exclude_pHYs != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11563 if (mng_info->ping_exclude_sRGB != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11566 if (mng_info->ping_exclude_tEXt != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 if (mng_info->ping_exclude_tRNS != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11572 if (mng_info->ping_exclude_vpAg != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 if (mng_info->ping_exclude_zCCP != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 if (mng_info->ping_exclude_zTXt != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11583 mng_info->need_blob = MagickTrue;
11585 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11587 MngInfoFreeStruct(mng_info,&have_mng_structure);
11589 if (logging != MagickFalse)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11595 #if defined(JNG_SUPPORTED)
11597 /* Write one JNG image */
11598 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11599 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11620 jng_alpha_compression_method,
11621 jng_alpha_sample_depth,
11628 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11629 " Enter WriteOneJNGImage()");
11631 blob=(unsigned char *) NULL;
11632 jpeg_image=(Image *) NULL;
11633 jpeg_image_info=(ImageInfo *) NULL;
11636 transparent=image_info->type==GrayscaleMatteType ||
11637 image_info->type==TrueColorMatteType;
11639 jng_alpha_sample_depth=0;
11640 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11641 jng_alpha_compression_method=0;
11643 if (image->matte != MagickFalse)
11645 /* if any pixels are transparent */
11646 transparent=MagickTrue;
11647 if (image_info->compression==JPEGCompression)
11648 jng_alpha_compression_method=8;
11658 /* Create JPEG blob, image, and image_info */
11659 if (logging != MagickFalse)
11660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11661 " Creating jpeg_image_info for alpha.");
11663 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11665 if (jpeg_image_info == (ImageInfo *) NULL)
11666 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11668 if (logging != MagickFalse)
11669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670 " Creating jpeg_image.");
11672 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11674 if (jpeg_image == (Image *) NULL)
11675 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11677 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11678 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
11679 status=SeparateImage(jpeg_image,exception);
11680 (void) SetPixelChannelMap(jpeg_image,channel_mask);
11681 jpeg_image->matte=MagickFalse;
11683 if (jng_quality >= 1000)
11684 jpeg_image_info->quality=jng_quality/1000;
11687 jpeg_image_info->quality=jng_quality;
11689 jpeg_image_info->type=GrayscaleType;
11690 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11691 (void) AcquireUniqueFilename(jpeg_image->filename);
11692 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11693 "%s",jpeg_image->filename);
11696 /* To do: check bit depth of PNG alpha channel */
11698 /* Check if image is grayscale. */
11699 if (image_info->type != TrueColorMatteType && image_info->type !=
11700 TrueColorType && ImageIsGray(image))
11705 if (jng_alpha_compression_method==0)
11710 /* Encode alpha as a grayscale PNG blob */
11711 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11712 &image->exception);
11713 if (logging != MagickFalse)
11714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11715 " Creating PNG blob.");
11718 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11719 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11720 jpeg_image_info->interlace=NoInterlace;
11722 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11723 &image->exception);
11725 /* Retrieve sample depth used */
11726 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11727 if (value != (char *) NULL)
11728 jng_alpha_sample_depth= (unsigned int) value[0];
11732 /* Encode alpha as a grayscale JPEG blob */
11734 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11735 &image->exception);
11737 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11738 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11739 jpeg_image_info->interlace=NoInterlace;
11740 if (logging != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " Creating blob.");
11743 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11744 &image->exception);
11745 jng_alpha_sample_depth=8;
11747 if (logging != MagickFalse)
11748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11749 " Successfully read jpeg_image into a blob, length=%.20g.",
11753 /* Destroy JPEG image and image_info */
11754 jpeg_image=DestroyImage(jpeg_image);
11755 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11756 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11759 /* Write JHDR chunk */
11760 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11761 PNGType(chunk,mng_JHDR);
11762 LogPNGChunk(logging,mng_JHDR,16L);
11763 PNGLong(chunk+4,(png_uint_32) image->columns);
11764 PNGLong(chunk+8,(png_uint_32) image->rows);
11765 chunk[12]=jng_color_type;
11766 chunk[13]=8; /* sample depth */
11767 chunk[14]=8; /*jng_image_compression_method */
11768 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11769 chunk[16]=jng_alpha_sample_depth;
11770 chunk[17]=jng_alpha_compression_method;
11771 chunk[18]=0; /*jng_alpha_filter_method */
11772 chunk[19]=0; /*jng_alpha_interlace_method */
11773 (void) WriteBlob(image,20,chunk);
11774 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11775 if (logging != MagickFalse)
11777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11778 " JNG width:%15lu",(unsigned long) image->columns);
11780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11781 " JNG height:%14lu",(unsigned long) image->rows);
11783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11784 " JNG color type:%10d",jng_color_type);
11786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11787 " JNG sample depth:%8d",8);
11789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11790 " JNG compression:%9d",8);
11792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11793 " JNG interlace:%11d",0);
11795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11796 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11799 " JNG alpha compression:%3d",jng_alpha_compression_method);
11801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11802 " JNG alpha filter:%8d",0);
11804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11805 " JNG alpha interlace:%5d",0);
11808 /* Write any JNG-chunk-b profiles */
11809 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11812 Write leading ancillary chunks
11818 Write JNG bKGD chunk
11829 if (jng_color_type == 8 || jng_color_type == 12)
11833 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11834 PNGType(chunk,mng_bKGD);
11835 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11836 red=ScaleQuantumToChar(image->background_color.red);
11837 green=ScaleQuantumToChar(image->background_color.green);
11838 blue=ScaleQuantumToChar(image->background_color.blue);
11845 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11846 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11849 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11852 Write JNG sRGB chunk
11854 (void) WriteBlobMSBULong(image,1L);
11855 PNGType(chunk,mng_sRGB);
11856 LogPNGChunk(logging,mng_sRGB,1L);
11858 if (image->rendering_intent != UndefinedIntent)
11859 chunk[4]=(unsigned char)
11860 Magick_RenderingIntent_to_PNG_RenderingIntent(
11861 (image->rendering_intent));
11864 chunk[4]=(unsigned char)
11865 Magick_RenderingIntent_to_PNG_RenderingIntent(
11866 (PerceptualIntent));
11868 (void) WriteBlob(image,5,chunk);
11869 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11873 if (image->gamma != 0.0)
11876 Write JNG gAMA chunk
11878 (void) WriteBlobMSBULong(image,4L);
11879 PNGType(chunk,mng_gAMA);
11880 LogPNGChunk(logging,mng_gAMA,4L);
11881 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11882 (void) WriteBlob(image,8,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11886 if ((mng_info->equal_chrms == MagickFalse) &&
11887 (image->chromaticity.red_primary.x != 0.0))
11893 Write JNG cHRM chunk
11895 (void) WriteBlobMSBULong(image,32L);
11896 PNGType(chunk,mng_cHRM);
11897 LogPNGChunk(logging,mng_cHRM,32L);
11898 primary=image->chromaticity.white_point;
11899 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11900 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11901 primary=image->chromaticity.red_primary;
11902 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11903 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11904 primary=image->chromaticity.green_primary;
11905 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11906 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11907 primary=image->chromaticity.blue_primary;
11908 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11909 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11910 (void) WriteBlob(image,36,chunk);
11911 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11915 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11918 Write JNG pHYs chunk
11920 (void) WriteBlobMSBULong(image,9L);
11921 PNGType(chunk,mng_pHYs);
11922 LogPNGChunk(logging,mng_pHYs,9L);
11923 if (image->units == PixelsPerInchResolution)
11925 PNGLong(chunk+4,(png_uint_32)
11926 (image->x_resolution*100.0/2.54+0.5));
11928 PNGLong(chunk+8,(png_uint_32)
11929 (image->y_resolution*100.0/2.54+0.5));
11936 if (image->units == PixelsPerCentimeterResolution)
11938 PNGLong(chunk+4,(png_uint_32)
11939 (image->x_resolution*100.0+0.5));
11941 PNGLong(chunk+8,(png_uint_32)
11942 (image->y_resolution*100.0+0.5));
11949 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11950 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11954 (void) WriteBlob(image,13,chunk);
11955 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11958 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11961 Write JNG oFFs chunk
11963 (void) WriteBlobMSBULong(image,9L);
11964 PNGType(chunk,mng_oFFs);
11965 LogPNGChunk(logging,mng_oFFs,9L);
11966 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11967 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11969 (void) WriteBlob(image,13,chunk);
11970 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11972 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11974 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11975 PNGType(chunk,mng_vpAg);
11976 LogPNGChunk(logging,mng_vpAg,9L);
11977 PNGLong(chunk+4,(png_uint_32) image->page.width);
11978 PNGLong(chunk+8,(png_uint_32) image->page.height);
11979 chunk[12]=0; /* unit = pixels */
11980 (void) WriteBlob(image,13,chunk);
11981 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11987 if (jng_alpha_compression_method==0)
11995 /* Write IDAT chunk header */
11996 if (logging != MagickFalse)
11997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11998 " Write IDAT chunks from blob, length=%.20g.",(double)
12001 /* Copy IDAT chunks */
12004 for (i=8; i<(ssize_t) length; i+=len+12)
12006 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12009 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12011 /* Found an IDAT chunk. */
12012 (void) WriteBlobMSBULong(image,(size_t) len);
12013 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12014 (void) WriteBlob(image,(size_t) len+4,p);
12015 (void) WriteBlobMSBULong(image,
12016 crc32(0,p,(uInt) len+4));
12021 if (logging != MagickFalse)
12022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12023 " Skipping %c%c%c%c chunk, length=%.20g.",
12024 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12031 /* Write JDAA chunk header */
12032 if (logging != MagickFalse)
12033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12034 " Write JDAA chunk, length=%.20g.",(double) length);
12035 (void) WriteBlobMSBULong(image,(size_t) length);
12036 PNGType(chunk,mng_JDAA);
12037 LogPNGChunk(logging,mng_JDAA,length);
12038 /* Write JDAT chunk(s) data */
12039 (void) WriteBlob(image,4,chunk);
12040 (void) WriteBlob(image,length,blob);
12041 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12044 blob=(unsigned char *) RelinquishMagickMemory(blob);
12047 /* Encode image as a JPEG blob */
12048 if (logging != MagickFalse)
12049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12050 " Creating jpeg_image_info.");
12051 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12052 if (jpeg_image_info == (ImageInfo *) NULL)
12053 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12055 if (logging != MagickFalse)
12056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12057 " Creating jpeg_image.");
12059 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12060 if (jpeg_image == (Image *) NULL)
12061 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12062 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12064 (void) AcquireUniqueFilename(jpeg_image->filename);
12065 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12066 jpeg_image->filename);
12068 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12069 &image->exception);
12071 if (logging != MagickFalse)
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12073 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12074 (double) jpeg_image->rows);
12076 if (jng_color_type == 8 || jng_color_type == 12)
12077 jpeg_image_info->type=GrayscaleType;
12079 jpeg_image_info->quality=jng_quality % 1000;
12080 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12081 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12083 if (logging != MagickFalse)
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 " Creating blob.");
12087 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
12089 if (logging != MagickFalse)
12091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12092 " Successfully read jpeg_image into a blob, length=%.20g.",
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12096 " Write JDAT chunk, length=%.20g.",(double) length);
12099 /* Write JDAT chunk(s) */
12100 (void) WriteBlobMSBULong(image,(size_t) length);
12101 PNGType(chunk,mng_JDAT);
12102 LogPNGChunk(logging,mng_JDAT,length);
12103 (void) WriteBlob(image,4,chunk);
12104 (void) WriteBlob(image,length,blob);
12105 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12107 jpeg_image=DestroyImage(jpeg_image);
12108 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12109 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12110 blob=(unsigned char *) RelinquishMagickMemory(blob);
12112 /* Write any JNG-chunk-e profiles */
12113 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12115 /* Write IEND chunk */
12116 (void) WriteBlobMSBULong(image,0L);
12117 PNGType(chunk,mng_IEND);
12118 LogPNGChunk(logging,mng_IEND,0);
12119 (void) WriteBlob(image,4,chunk);
12120 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12122 if (logging != MagickFalse)
12123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12124 " exit WriteOneJNGImage()");
12131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12135 % W r i t e J N G I m a g e %
12139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12141 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12143 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12145 % The format of the WriteJNGImage method is:
12147 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12148 % Image *image,ExceptionInfo *exception)
12150 % A description of each parameter follows:
12152 % o image_info: the image info.
12154 % o image: The image.
12156 % o exception: return any errors or warnings in this structure.
12158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12160 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12161 ExceptionInfo *exception)
12164 have_mng_structure,
12174 assert(image_info != (const ImageInfo *) NULL);
12175 assert(image_info->signature == MagickSignature);
12176 assert(image != (Image *) NULL);
12177 assert(image->signature == MagickSignature);
12178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12179 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12180 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12181 if (status == MagickFalse)
12185 Allocate a MngInfo structure.
12187 have_mng_structure=MagickFalse;
12188 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12189 if (mng_info == (MngInfo *) NULL)
12190 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12192 Initialize members of the MngInfo structure.
12194 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12195 mng_info->image=image;
12196 have_mng_structure=MagickTrue;
12198 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12200 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12201 (void) CloseBlob(image);
12203 (void) CatchImageException(image);
12204 MngInfoFreeStruct(mng_info,&have_mng_structure);
12205 if (logging != MagickFalse)
12206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12211 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12212 ExceptionInfo *exception)
12221 have_mng_structure,
12224 volatile MagickBooleanType
12236 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12237 defined(PNG_MNG_FEATURES_SUPPORTED)
12240 all_images_are_gray,
12250 volatile unsigned int
12261 #if (PNG_LIBPNG_VER < 10200)
12262 if (image_info->verbose)
12263 printf("Your PNG library (libpng-%s) is rather old.\n",
12264 PNG_LIBPNG_VER_STRING);
12270 assert(image_info != (const ImageInfo *) NULL);
12271 assert(image_info->signature == MagickSignature);
12272 assert(image != (Image *) NULL);
12273 assert(image->signature == MagickSignature);
12274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12275 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12276 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12277 if (status == MagickFalse)
12281 Allocate a MngInfo structure.
12283 have_mng_structure=MagickFalse;
12284 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12285 if (mng_info == (MngInfo *) NULL)
12286 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12288 Initialize members of the MngInfo structure.
12290 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12291 mng_info->image=image;
12292 have_mng_structure=MagickTrue;
12293 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12296 * See if user has requested a specific PNG subformat to be used
12297 * for all of the PNGs in the MNG being written, e.g.,
12299 * convert *.png png8:animation.mng
12301 * To do: check -define png:bit_depth and png:color_type as well,
12302 * or perhaps use mng:bit_depth and mng:color_type instead for
12306 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12307 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12308 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12310 write_jng=MagickFalse;
12311 if (image_info->compression == JPEGCompression)
12312 write_jng=MagickTrue;
12314 mng_info->adjoin=image_info->adjoin &&
12315 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12317 if (logging != MagickFalse)
12319 /* Log some info about the input */
12323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 " Checking input image(s)");
12326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12327 " Image_info depth: %.20g",(double) image_info->depth);
12329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12330 " Type: %d",image_info->type);
12333 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12336 " Scene: %.20g",(double) scene++);
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " Image depth: %.20g",(double) p->depth);
12342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12349 if (p->storage_class == PseudoClass)
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " Storage class: PseudoClass");
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355 " Storage class: DirectClass");
12358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12359 " Number of colors: %.20g",(double) p->colors);
12362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12363 " Number of colors: unspecified");
12365 if (mng_info->adjoin == MagickFalse)
12370 use_global_plte=MagickFalse;
12371 all_images_are_gray=MagickFalse;
12372 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12373 need_local_plte=MagickTrue;
12375 need_defi=MagickFalse;
12376 need_matte=MagickFalse;
12377 mng_info->framing_mode=1;
12378 mng_info->old_framing_mode=1;
12381 if (image_info->page != (char *) NULL)
12384 Determine image bounding box.
12386 SetGeometry(image,&mng_info->page);
12387 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12388 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12400 mng_info->page=image->page;
12401 need_geom=MagickTrue;
12402 if (mng_info->page.width || mng_info->page.height)
12403 need_geom=MagickFalse;
12405 Check all the scenes.
12407 initial_delay=image->delay;
12408 need_iterations=MagickFalse;
12409 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12410 mng_info->equal_physs=MagickTrue,
12411 mng_info->equal_gammas=MagickTrue;
12412 mng_info->equal_srgbs=MagickTrue;
12413 mng_info->equal_backgrounds=MagickTrue;
12415 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12416 defined(PNG_MNG_FEATURES_SUPPORTED)
12417 all_images_are_gray=MagickTrue;
12418 mng_info->equal_palettes=MagickFalse;
12419 need_local_plte=MagickFalse;
12421 for (next_image=image; next_image != (Image *) NULL; )
12425 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12426 mng_info->page.width=next_image->columns+next_image->page.x;
12428 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12429 mng_info->page.height=next_image->rows+next_image->page.y;
12432 if (next_image->page.x || next_image->page.y)
12433 need_defi=MagickTrue;
12435 if (next_image->matte)
12436 need_matte=MagickTrue;
12438 if ((int) next_image->dispose >= BackgroundDispose)
12439 if (next_image->matte || next_image->page.x || next_image->page.y ||
12440 ((next_image->columns < mng_info->page.width) &&
12441 (next_image->rows < mng_info->page.height)))
12442 mng_info->need_fram=MagickTrue;
12444 if (next_image->iterations)
12445 need_iterations=MagickTrue;
12447 final_delay=next_image->delay;
12449 if (final_delay != initial_delay || final_delay > 1UL*
12450 next_image->ticks_per_second)
12451 mng_info->need_fram=1;
12453 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12454 defined(PNG_MNG_FEATURES_SUPPORTED)
12456 check for global palette possibility.
12458 if (image->matte != MagickFalse)
12459 need_local_plte=MagickTrue;
12461 if (need_local_plte == 0)
12463 if (ImageIsGray(image) == MagickFalse)
12464 all_images_are_gray=MagickFalse;
12465 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12466 if (use_global_plte == 0)
12467 use_global_plte=mng_info->equal_palettes;
12468 need_local_plte=!mng_info->equal_palettes;
12471 if (GetNextImageInList(next_image) != (Image *) NULL)
12473 if (next_image->background_color.red !=
12474 next_image->next->background_color.red ||
12475 next_image->background_color.green !=
12476 next_image->next->background_color.green ||
12477 next_image->background_color.blue !=
12478 next_image->next->background_color.blue)
12479 mng_info->equal_backgrounds=MagickFalse;
12481 if (next_image->gamma != next_image->next->gamma)
12482 mng_info->equal_gammas=MagickFalse;
12484 if (next_image->rendering_intent !=
12485 next_image->next->rendering_intent)
12486 mng_info->equal_srgbs=MagickFalse;
12488 if ((next_image->units != next_image->next->units) ||
12489 (next_image->x_resolution != next_image->next->x_resolution) ||
12490 (next_image->y_resolution != next_image->next->y_resolution))
12491 mng_info->equal_physs=MagickFalse;
12493 if (mng_info->equal_chrms)
12495 if (next_image->chromaticity.red_primary.x !=
12496 next_image->next->chromaticity.red_primary.x ||
12497 next_image->chromaticity.red_primary.y !=
12498 next_image->next->chromaticity.red_primary.y ||
12499 next_image->chromaticity.green_primary.x !=
12500 next_image->next->chromaticity.green_primary.x ||
12501 next_image->chromaticity.green_primary.y !=
12502 next_image->next->chromaticity.green_primary.y ||
12503 next_image->chromaticity.blue_primary.x !=
12504 next_image->next->chromaticity.blue_primary.x ||
12505 next_image->chromaticity.blue_primary.y !=
12506 next_image->next->chromaticity.blue_primary.y ||
12507 next_image->chromaticity.white_point.x !=
12508 next_image->next->chromaticity.white_point.x ||
12509 next_image->chromaticity.white_point.y !=
12510 next_image->next->chromaticity.white_point.y)
12511 mng_info->equal_chrms=MagickFalse;
12515 next_image=GetNextImageInList(next_image);
12517 if (image_count < 2)
12519 mng_info->equal_backgrounds=MagickFalse;
12520 mng_info->equal_chrms=MagickFalse;
12521 mng_info->equal_gammas=MagickFalse;
12522 mng_info->equal_srgbs=MagickFalse;
12523 mng_info->equal_physs=MagickFalse;
12524 use_global_plte=MagickFalse;
12525 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12526 need_local_plte=MagickTrue;
12528 need_iterations=MagickFalse;
12531 if (mng_info->need_fram == MagickFalse)
12534 Only certain framing rates 100/n are exactly representable without
12535 the FRAM chunk but we'll allow some slop in VLC files
12537 if (final_delay == 0)
12539 if (need_iterations != MagickFalse)
12542 It's probably a GIF with loop; don't run it *too* fast.
12544 if (mng_info->adjoin)
12547 (void) ThrowMagickException(&image->exception,
12548 GetMagickModule(),CoderWarning,
12549 "input has zero delay between all frames; assuming",
12554 mng_info->ticks_per_second=0;
12556 if (final_delay != 0)
12557 mng_info->ticks_per_second=(png_uint_32)
12558 (image->ticks_per_second/final_delay);
12559 if (final_delay > 50)
12560 mng_info->ticks_per_second=2;
12562 if (final_delay > 75)
12563 mng_info->ticks_per_second=1;
12565 if (final_delay > 125)
12566 mng_info->need_fram=MagickTrue;
12568 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12569 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12570 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12571 1UL*image->ticks_per_second))
12572 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12575 if (mng_info->need_fram != MagickFalse)
12576 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12578 If pseudocolor, we should also check to see if all the
12579 palettes are identical and write a global PLTE if they are.
12583 Write the MNG version 1.0 signature and MHDR chunk.
12585 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12586 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12587 PNGType(chunk,mng_MHDR);
12588 LogPNGChunk(logging,mng_MHDR,28L);
12589 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12590 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12591 PNGLong(chunk+12,mng_info->ticks_per_second);
12592 PNGLong(chunk+16,0L); /* layer count=unknown */
12593 PNGLong(chunk+20,0L); /* frame count=unknown */
12594 PNGLong(chunk+24,0L); /* play time=unknown */
12599 if (need_defi || mng_info->need_fram || use_global_plte)
12600 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12603 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12608 if (need_defi || mng_info->need_fram || use_global_plte)
12609 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12612 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12620 if (need_defi || mng_info->need_fram || use_global_plte)
12621 PNGLong(chunk+28,11L); /* simplicity=LC */
12624 PNGLong(chunk+28,9L); /* simplicity=VLC */
12629 if (need_defi || mng_info->need_fram || use_global_plte)
12630 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12633 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12636 (void) WriteBlob(image,32,chunk);
12637 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12638 option=GetImageOption(image_info,"mng:need-cacheoff");
12639 if (option != (const char *) NULL)
12645 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12647 PNGType(chunk,mng_nEED);
12648 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12649 (void) WriteBlobMSBULong(image,(size_t) length);
12650 LogPNGChunk(logging,mng_nEED,(size_t) length);
12652 (void) WriteBlob(image,length,chunk);
12653 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12655 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12656 (GetNextImageInList(image) != (Image *) NULL) &&
12657 (image->iterations != 1))
12660 Write MNG TERM chunk
12662 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12663 PNGType(chunk,mng_TERM);
12664 LogPNGChunk(logging,mng_TERM,10L);
12665 chunk[4]=3; /* repeat animation */
12666 chunk[5]=0; /* show last frame when done */
12667 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12668 final_delay/MagickMax(image->ticks_per_second,1)));
12670 if (image->iterations == 0)
12671 PNGLong(chunk+10,PNG_UINT_31_MAX);
12674 PNGLong(chunk+10,(png_uint_32) image->iterations);
12676 if (logging != MagickFalse)
12678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12679 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12680 final_delay/MagickMax(image->ticks_per_second,1)));
12682 if (image->iterations == 0)
12683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12684 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12688 " Image iterations: %.20g",(double) image->iterations);
12690 (void) WriteBlob(image,14,chunk);
12691 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12694 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12696 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12697 mng_info->equal_srgbs)
12700 Write MNG sRGB chunk
12702 (void) WriteBlobMSBULong(image,1L);
12703 PNGType(chunk,mng_sRGB);
12704 LogPNGChunk(logging,mng_sRGB,1L);
12706 if (image->rendering_intent != UndefinedIntent)
12707 chunk[4]=(unsigned char)
12708 Magick_RenderingIntent_to_PNG_RenderingIntent(
12709 (image->rendering_intent));
12712 chunk[4]=(unsigned char)
12713 Magick_RenderingIntent_to_PNG_RenderingIntent(
12714 (PerceptualIntent));
12716 (void) WriteBlob(image,5,chunk);
12717 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12718 mng_info->have_write_global_srgb=MagickTrue;
12723 if (image->gamma && mng_info->equal_gammas)
12726 Write MNG gAMA chunk
12728 (void) WriteBlobMSBULong(image,4L);
12729 PNGType(chunk,mng_gAMA);
12730 LogPNGChunk(logging,mng_gAMA,4L);
12731 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12732 (void) WriteBlob(image,8,chunk);
12733 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12734 mng_info->have_write_global_gama=MagickTrue;
12736 if (mng_info->equal_chrms)
12742 Write MNG cHRM chunk
12744 (void) WriteBlobMSBULong(image,32L);
12745 PNGType(chunk,mng_cHRM);
12746 LogPNGChunk(logging,mng_cHRM,32L);
12747 primary=image->chromaticity.white_point;
12748 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12749 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12750 primary=image->chromaticity.red_primary;
12751 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12752 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12753 primary=image->chromaticity.green_primary;
12754 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12755 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12756 primary=image->chromaticity.blue_primary;
12757 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12758 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12759 (void) WriteBlob(image,36,chunk);
12760 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12761 mng_info->have_write_global_chrm=MagickTrue;
12764 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12767 Write MNG pHYs chunk
12769 (void) WriteBlobMSBULong(image,9L);
12770 PNGType(chunk,mng_pHYs);
12771 LogPNGChunk(logging,mng_pHYs,9L);
12773 if (image->units == PixelsPerInchResolution)
12775 PNGLong(chunk+4,(png_uint_32)
12776 (image->x_resolution*100.0/2.54+0.5));
12778 PNGLong(chunk+8,(png_uint_32)
12779 (image->y_resolution*100.0/2.54+0.5));
12786 if (image->units == PixelsPerCentimeterResolution)
12788 PNGLong(chunk+4,(png_uint_32)
12789 (image->x_resolution*100.0+0.5));
12791 PNGLong(chunk+8,(png_uint_32)
12792 (image->y_resolution*100.0+0.5));
12799 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12800 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
12804 (void) WriteBlob(image,13,chunk);
12805 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12808 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12809 or does not cover the entire frame.
12811 if (write_mng && (image->matte || image->page.x > 0 ||
12812 image->page.y > 0 || (image->page.width &&
12813 (image->page.width+image->page.x < mng_info->page.width))
12814 || (image->page.height && (image->page.height+image->page.y
12815 < mng_info->page.height))))
12817 (void) WriteBlobMSBULong(image,6L);
12818 PNGType(chunk,mng_BACK);
12819 LogPNGChunk(logging,mng_BACK,6L);
12820 red=ScaleQuantumToShort(image->background_color.red);
12821 green=ScaleQuantumToShort(image->background_color.green);
12822 blue=ScaleQuantumToShort(image->background_color.blue);
12823 PNGShort(chunk+4,red);
12824 PNGShort(chunk+6,green);
12825 PNGShort(chunk+8,blue);
12826 (void) WriteBlob(image,10,chunk);
12827 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12828 if (mng_info->equal_backgrounds)
12830 (void) WriteBlobMSBULong(image,6L);
12831 PNGType(chunk,mng_bKGD);
12832 LogPNGChunk(logging,mng_bKGD,6L);
12833 (void) WriteBlob(image,10,chunk);
12834 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12838 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12839 if ((need_local_plte == MagickFalse) &&
12840 (image->storage_class == PseudoClass) &&
12841 (all_images_are_gray == MagickFalse))
12847 Write MNG PLTE chunk
12849 data_length=3*image->colors;
12850 (void) WriteBlobMSBULong(image,data_length);
12851 PNGType(chunk,mng_PLTE);
12852 LogPNGChunk(logging,mng_PLTE,data_length);
12854 for (i=0; i < (ssize_t) image->colors; i++)
12856 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12857 image->colormap[i].red) & 0xff);
12858 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12859 image->colormap[i].green) & 0xff);
12860 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12861 image->colormap[i].blue) & 0xff);
12864 (void) WriteBlob(image,data_length+4,chunk);
12865 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12866 mng_info->have_write_global_plte=MagickTrue;
12872 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12873 defined(PNG_MNG_FEATURES_SUPPORTED)
12874 mng_info->equal_palettes=MagickFalse;
12878 if (mng_info->adjoin)
12880 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12881 defined(PNG_MNG_FEATURES_SUPPORTED)
12883 If we aren't using a global palette for the entire MNG, check to
12884 see if we can use one for two or more consecutive images.
12886 if (need_local_plte && use_global_plte && !all_images_are_gray)
12888 if (mng_info->IsPalette)
12891 When equal_palettes is true, this image has the same palette
12892 as the previous PseudoClass image
12894 mng_info->have_write_global_plte=mng_info->equal_palettes;
12895 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12896 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12899 Write MNG PLTE chunk
12904 data_length=3*image->colors;
12905 (void) WriteBlobMSBULong(image,data_length);
12906 PNGType(chunk,mng_PLTE);
12907 LogPNGChunk(logging,mng_PLTE,data_length);
12909 for (i=0; i < (ssize_t) image->colors; i++)
12911 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12912 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12913 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12916 (void) WriteBlob(image,data_length+4,chunk);
12917 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12918 (uInt) (data_length+4)));
12919 mng_info->have_write_global_plte=MagickTrue;
12923 mng_info->have_write_global_plte=MagickFalse;
12934 previous_x=mng_info->page.x;
12935 previous_y=mng_info->page.y;
12942 mng_info->page=image->page;
12943 if ((mng_info->page.x != previous_x) ||
12944 (mng_info->page.y != previous_y))
12946 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12947 PNGType(chunk,mng_DEFI);
12948 LogPNGChunk(logging,mng_DEFI,12L);
12949 chunk[4]=0; /* object 0 MSB */
12950 chunk[5]=0; /* object 0 LSB */
12951 chunk[6]=0; /* visible */
12952 chunk[7]=0; /* abstract */
12953 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12954 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12955 (void) WriteBlob(image,16,chunk);
12956 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12961 mng_info->write_mng=write_mng;
12963 if ((int) image->dispose >= 3)
12964 mng_info->framing_mode=3;
12966 if (mng_info->need_fram && mng_info->adjoin &&
12967 ((image->delay != mng_info->delay) ||
12968 (mng_info->framing_mode != mng_info->old_framing_mode)))
12970 if (image->delay == mng_info->delay)
12973 Write a MNG FRAM chunk with the new framing mode.
12975 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12976 PNGType(chunk,mng_FRAM);
12977 LogPNGChunk(logging,mng_FRAM,1L);
12978 chunk[4]=(unsigned char) mng_info->framing_mode;
12979 (void) WriteBlob(image,5,chunk);
12980 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12985 Write a MNG FRAM chunk with the delay.
12987 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12988 PNGType(chunk,mng_FRAM);
12989 LogPNGChunk(logging,mng_FRAM,10L);
12990 chunk[4]=(unsigned char) mng_info->framing_mode;
12991 chunk[5]=0; /* frame name separator (no name) */
12992 chunk[6]=2; /* flag for changing default delay */
12993 chunk[7]=0; /* flag for changing frame timeout */
12994 chunk[8]=0; /* flag for changing frame clipping */
12995 chunk[9]=0; /* flag for changing frame sync_id */
12996 PNGLong(chunk+10,(png_uint_32)
12997 ((mng_info->ticks_per_second*
12998 image->delay)/MagickMax(image->ticks_per_second,1)));
12999 (void) WriteBlob(image,14,chunk);
13000 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13001 mng_info->delay=(png_uint_32) image->delay;
13003 mng_info->old_framing_mode=mng_info->framing_mode;
13006 #if defined(JNG_SUPPORTED)
13007 if (image_info->compression == JPEGCompression)
13012 if (logging != MagickFalse)
13013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13014 " Writing JNG object.");
13015 /* To do: specify the desired alpha compression method. */
13016 write_info=CloneImageInfo(image_info);
13017 write_info->compression=UndefinedCompression;
13018 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13019 write_info=DestroyImageInfo(write_info);
13024 if (logging != MagickFalse)
13025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13026 " Writing PNG object.");
13028 mng_info->need_blob = MagickFalse;
13029 mng_info->ping_preserve_colormap = MagickFalse;
13031 /* We don't want any ancillary chunks written */
13032 mng_info->ping_exclude_bKGD=MagickTrue;
13033 mng_info->ping_exclude_cHRM=MagickTrue;
13034 mng_info->ping_exclude_date=MagickTrue;
13035 mng_info->ping_exclude_EXIF=MagickTrue;
13036 mng_info->ping_exclude_gAMA=MagickTrue;
13037 mng_info->ping_exclude_iCCP=MagickTrue;
13038 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13039 mng_info->ping_exclude_oFFs=MagickTrue;
13040 mng_info->ping_exclude_pHYs=MagickTrue;
13041 mng_info->ping_exclude_sRGB=MagickTrue;
13042 mng_info->ping_exclude_tEXt=MagickTrue;
13043 mng_info->ping_exclude_tRNS=MagickTrue;
13044 mng_info->ping_exclude_vpAg=MagickTrue;
13045 mng_info->ping_exclude_zCCP=MagickTrue;
13046 mng_info->ping_exclude_zTXt=MagickTrue;
13048 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13051 if (status == MagickFalse)
13053 MngInfoFreeStruct(mng_info,&have_mng_structure);
13054 (void) CloseBlob(image);
13055 return(MagickFalse);
13057 (void) CatchImageException(image);
13058 if (GetNextImageInList(image) == (Image *) NULL)
13060 image=SyncNextImageInList(image);
13061 status=SetImageProgress(image,SaveImagesTag,scene++,
13062 GetImageListLength(image));
13064 if (status == MagickFalse)
13067 } while (mng_info->adjoin);
13071 while (GetPreviousImageInList(image) != (Image *) NULL)
13072 image=GetPreviousImageInList(image);
13074 Write the MEND chunk.
13076 (void) WriteBlobMSBULong(image,0x00000000L);
13077 PNGType(chunk,mng_MEND);
13078 LogPNGChunk(logging,mng_MEND,0L);
13079 (void) WriteBlob(image,4,chunk);
13080 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13083 Relinquish resources.
13085 (void) CloseBlob(image);
13086 MngInfoFreeStruct(mng_info,&have_mng_structure);
13088 if (logging != MagickFalse)
13089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13091 return(MagickTrue);
13093 #else /* PNG_LIBPNG_VER > 10011 */
13095 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13098 printf("Your PNG library is too old: You have libpng-%s\n",
13099 PNG_LIBPNG_VER_STRING);
13101 ThrowBinaryException(CoderError,"PNG library is too old",
13102 image_info->filename);
13105 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13107 return(WritePNGImage(image_info,image));
13109 #endif /* PNG_LIBPNG_VER > 10011 */