2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/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 mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
674 #if defined(JNG_SUPPORTED)
675 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680 static png_byte 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 mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692 static png_byte 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,ExceptionInfo *exception)
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,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,ExceptionInfo *exception)
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,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 typedef struct _PNGErrorInfo
1732 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
1753 #if (PNG_LIBPNG_VER < 10500)
1754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1757 longjmp(ping->jmpbuf,1);
1759 png_longjmp(ping,1);
1763 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
1777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1784 message,"`%s'",image->filename);
1787 #ifdef PNG_USER_MEM_SUPPORTED
1788 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1790 #if (PNG_LIBPNG_VER < 10011)
1795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1798 png_error("Insufficient memory.");
1803 return((png_voidp) AcquireMagickMemory((size_t) size));
1808 Free a pointer. It is removed from the list at the same time.
1810 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1818 #if defined(__cplusplus) || defined(c_plusplus)
1823 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1824 png_textp text,int ii,ExceptionInfo *exception)
1829 register unsigned char
1843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1851 /* look for newline */
1855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1859 length=(png_uint_32) StringToLong(sp);
1861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1864 while (*sp != ' ' && *sp != '\n')
1867 /* allocate space */
1870 (void) ThrowMagickException(exception,GetMagickModule(),
1871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1875 profile=BlobToStringInfo((const void *) NULL,length);
1877 if (profile == (StringInfo *) NULL)
1879 (void) ThrowMagickException(exception,GetMagickModule(),
1880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1889 for (i=0; i < (ssize_t) nibbles; i++)
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1895 (void) ThrowMagickException(exception,GetMagickModule(),
1896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1907 (*dp++)+=unhex[(int) *sp++];
1910 We have already read "Raw profile type.
1912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1913 profile=DestroyStringInfo(profile);
1915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1921 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1928 /* The unknown chunk structure contains the chunk data:
1933 Note that libpng has already taken care of the CRC handling.
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1941 /* recognized vpAg */
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1951 image->page.width=(size_t) ((chunk->data[0] << 24) |
1952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1954 image->page.height=(size_t) ((chunk->data[4] << 24) |
1955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1972 % R e a d O n e P N G I m a g e %
1976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1978 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979 % (minus the 8-byte signature) and returns it. It allocates the memory
1980 % necessary for the new Image structure and returns a pointer to the new
1983 % The format of the ReadOnePNGImage method is:
1985 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986 % ExceptionInfo *exception)
1988 % A description of each parameter follows:
1990 % o mng_info: Specifies a pointer to a MngInfo structure.
1992 % o image_info: the image info.
1994 % o exception: return any errors or warnings in this structure.
1997 static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
2000 /* Read one PNG image */
2002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2017 ping_interlace_method,
2018 ping_compression_method,
2069 register unsigned char
2086 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2099 " Enter ReadOnePNGImage()");
2101 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2102 LockSemaphoreInfo(ping_semaphore);
2105 #if (PNG_LIBPNG_VER < 10200)
2106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2111 #if (PNG_LIBPNG_VER >= 10400)
2112 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2123 quantum_info = (QuantumInfo *) NULL;
2124 image=mng_info->image;
2126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
2134 transparent_color.alpha=65537;
2138 num_raw_profiles = 0;
2141 Allocate the PNG structures
2143 #ifdef PNG_USER_MEM_SUPPORTED
2144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2151 MagickPNGErrorHandler,MagickPNGWarningHandler);
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2156 ping_info=png_create_info_struct(ping);
2158 if (ping_info == (png_info *) NULL)
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2164 end_info=png_create_info_struct(ping);
2166 if (end_info == (png_info *) NULL)
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2172 ping_pixels=(unsigned char *) NULL;
2174 if (setjmp(png_jmpbuf(ping)))
2177 PNG image is corrupt.
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2181 UnlockSemaphoreInfo(ping_semaphore);
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
2187 if (image != (Image *) NULL)
2189 InheritException(exception,exception);
2193 return(GetFirstImageInList(image));
2196 Prepare PNG for reading.
2199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
2202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2204 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2208 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2222 png_set_read_fn(ping,image,png_get_data);
2224 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2234 #if (PNG_LIBPNG_VER < 10400)
2235 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2253 png_read_info(ping,ping_info);
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2265 if (ping_bit_depth < 8)
2267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2269 png_set_packing(ping);
2274 image->depth=ping_bit_depth;
2275 image->depth=GetImageQuantumDepth(image,MagickFalse);
2276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2277 if (logging != MagickFalse)
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
2283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
2285 ping_color_type, ping_bit_depth);
2287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
2289 ping_compression_method);
2291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
2293 ping_interlace_method,ping_filter_method);
2296 #ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2302 #if (PNG_LIBPNG_VER < 10500)
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2319 if (profile_length != 0)
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
2327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2330 (void) ThrowMagickException(exception,GetMagickModule(),
2331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
2333 return((Image *) NULL);
2335 SetStringInfoDatum(profile,(const unsigned char *) info);
2336 (void) SetImageProfile(image,"icc",profile,exception);
2337 profile=DestroyStringInfo(profile);
2341 #if defined(PNG_READ_sRGB_SUPPORTED)
2343 if (mng_info->have_global_srgb)
2345 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2346 (mng_info->global_srgb_intent);
2349 if (png_get_sRGB(ping,ping_info,&intent))
2351 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2354 if (logging != MagickFalse)
2355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2356 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2361 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2362 if (mng_info->have_global_gama)
2363 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2365 if (png_get_gAMA(ping,ping_info,&file_gamma))
2367 image->gamma=(float) file_gamma;
2368 if (logging != MagickFalse)
2369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2370 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2373 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2375 if (mng_info->have_global_chrm != MagickFalse)
2377 (void) png_set_cHRM(ping,ping_info,
2378 mng_info->global_chrm.white_point.x,
2379 mng_info->global_chrm.white_point.y,
2380 mng_info->global_chrm.red_primary.x,
2381 mng_info->global_chrm.red_primary.y,
2382 mng_info->global_chrm.green_primary.x,
2383 mng_info->global_chrm.green_primary.y,
2384 mng_info->global_chrm.blue_primary.x,
2385 mng_info->global_chrm.blue_primary.y);
2389 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2391 (void) png_get_cHRM(ping,ping_info,
2392 &image->chromaticity.white_point.x,
2393 &image->chromaticity.white_point.y,
2394 &image->chromaticity.red_primary.x,
2395 &image->chromaticity.red_primary.y,
2396 &image->chromaticity.green_primary.x,
2397 &image->chromaticity.green_primary.y,
2398 &image->chromaticity.blue_primary.x,
2399 &image->chromaticity.blue_primary.y);
2401 if (logging != MagickFalse)
2402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2403 " Reading PNG cHRM chunk.");
2406 if (image->rendering_intent != UndefinedIntent)
2408 png_set_sRGB(ping,ping_info,
2409 Magick_RenderingIntent_to_PNG_RenderingIntent
2410 (image->rendering_intent));
2411 png_set_gAMA(ping,ping_info,0.45455f);
2412 png_set_cHRM(ping,ping_info,
2413 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2414 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2416 #if defined(PNG_oFFs_SUPPORTED)
2417 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2419 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2420 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2422 if (logging != MagickFalse)
2423 if (image->page.x || image->page.y)
2424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2425 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2426 image->page.x,(double) image->page.y);
2429 #if defined(PNG_pHYs_SUPPORTED)
2430 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2432 if (mng_info->have_global_phys)
2434 png_set_pHYs(ping,ping_info,
2435 mng_info->global_x_pixels_per_unit,
2436 mng_info->global_y_pixels_per_unit,
2437 mng_info->global_phys_unit_type);
2441 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2444 Set image resolution.
2446 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2448 image->resolution.x=(double) x_resolution;
2449 image->resolution.y=(double) y_resolution;
2451 if (unit_type == PNG_RESOLUTION_METER)
2453 image->units=PixelsPerCentimeterResolution;
2454 image->resolution.x=(double) x_resolution/100.0;
2455 image->resolution.y=(double) y_resolution/100.0;
2458 if (logging != MagickFalse)
2459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2460 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2461 (double) x_resolution,(double) y_resolution,unit_type);
2465 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2473 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2475 if ((number_colors == 0) &&
2476 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2478 if (mng_info->global_plte_length)
2480 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2481 (int) mng_info->global_plte_length);
2483 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2484 if (mng_info->global_trns_length)
2486 if (mng_info->global_trns_length >
2487 mng_info->global_plte_length)
2488 (void) ThrowMagickException(exception,
2489 GetMagickModule(),CoderError,
2490 "global tRNS has more entries than global PLTE",
2491 "`%s'",image_info->filename);
2492 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2493 (int) mng_info->global_trns_length,NULL);
2495 #ifdef PNG_READ_bKGD_SUPPORTED
2497 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2498 mng_info->have_saved_bkgd_index ||
2500 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2505 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2506 if (mng_info->have_saved_bkgd_index)
2507 background.index=mng_info->saved_bkgd_index;
2509 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2510 background.index=ping_background->index;
2512 background.red=(png_uint_16)
2513 mng_info->global_plte[background.index].red;
2515 background.green=(png_uint_16)
2516 mng_info->global_plte[background.index].green;
2518 background.blue=(png_uint_16)
2519 mng_info->global_plte[background.index].blue;
2521 background.gray=(png_uint_16)
2522 mng_info->global_plte[background.index].green;
2524 png_set_bKGD(ping,ping_info,&background);
2529 (void) ThrowMagickException(exception,GetMagickModule(),
2530 CoderError,"No global PLTE in file","`%s'",
2531 image_info->filename);
2535 #ifdef PNG_READ_bKGD_SUPPORTED
2536 if (mng_info->have_global_bkgd &&
2537 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2538 image->background_color=mng_info->mng_global_bkgd;
2540 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2546 Set image background color.
2548 if (logging != MagickFalse)
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550 " Reading PNG bKGD chunk.");
2552 /* Scale background components to 16-bit, then scale
2555 if (logging != MagickFalse)
2556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557 " raw ping_background=(%d,%d,%d).",ping_background->red,
2558 ping_background->green,ping_background->blue);
2562 if (ping_bit_depth == 1)
2565 else if (ping_bit_depth == 2)
2568 else if (ping_bit_depth == 4)
2571 if (ping_bit_depth <= 8)
2574 ping_background->red *= bkgd_scale;
2575 ping_background->green *= bkgd_scale;
2576 ping_background->blue *= bkgd_scale;
2578 if (logging != MagickFalse)
2580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2581 " bkgd_scale=%d.",bkgd_scale);
2583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2584 " ping_background=(%d,%d,%d).",ping_background->red,
2585 ping_background->green,ping_background->blue);
2588 image->background_color.red=
2589 ScaleShortToQuantum(ping_background->red);
2591 image->background_color.green=
2592 ScaleShortToQuantum(ping_background->green);
2594 image->background_color.blue=
2595 ScaleShortToQuantum(ping_background->blue);
2597 image->background_color.alpha=OpaqueAlpha;
2599 if (logging != MagickFalse)
2600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2601 " image->background_color=(%.20g,%.20g,%.20g).",
2602 (double) image->background_color.red,
2603 (double) image->background_color.green,
2604 (double) image->background_color.blue);
2606 #endif /* PNG_READ_bKGD_SUPPORTED */
2608 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2611 Image has a tRNS chunk.
2619 if (logging != MagickFalse)
2620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2621 " Reading PNG tRNS chunk.");
2623 max_sample = (int) ((one << ping_bit_depth) - 1);
2625 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2626 (int)ping_trans_color->gray > max_sample) ||
2627 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2628 ((int)ping_trans_color->red > max_sample ||
2629 (int)ping_trans_color->green > max_sample ||
2630 (int)ping_trans_color->blue > max_sample)))
2632 if (logging != MagickFalse)
2633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2634 " Ignoring PNG tRNS chunk with out-of-range sample.");
2635 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2636 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2637 image->matte=MagickFalse;
2644 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2646 /* Scale transparent_color to short */
2647 transparent_color.red= scale_to_short*ping_trans_color->red;
2648 transparent_color.green= scale_to_short*ping_trans_color->green;
2649 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2650 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2652 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2654 if (logging != MagickFalse)
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2660 " scaled graylevel is %.20g.",transparent_color.alpha);
2662 transparent_color.red=transparent_color.alpha;
2663 transparent_color.green=transparent_color.alpha;
2664 transparent_color.blue=transparent_color.alpha;
2668 #if defined(PNG_READ_sBIT_SUPPORTED)
2669 if (mng_info->have_global_sbit)
2671 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2672 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2675 num_passes=png_set_interlace_handling(ping);
2677 png_read_update_info(ping,ping_info);
2679 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2682 Initialize image structure.
2684 mng_info->image_box.left=0;
2685 mng_info->image_box.right=(ssize_t) ping_width;
2686 mng_info->image_box.top=0;
2687 mng_info->image_box.bottom=(ssize_t) ping_height;
2688 if (mng_info->mng_type == 0)
2690 mng_info->mng_width=ping_width;
2691 mng_info->mng_height=ping_height;
2692 mng_info->frame=mng_info->image_box;
2693 mng_info->clip=mng_info->image_box;
2698 image->page.y=mng_info->y_off[mng_info->object_id];
2701 image->compression=ZipCompression;
2702 image->columns=ping_width;
2703 image->rows=ping_height;
2704 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2705 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2706 image->colorspace=GRAYColorspace;
2707 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2708 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2713 image->storage_class=PseudoClass;
2715 image->colors=one << ping_bit_depth;
2716 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2717 if (image->colors > 256)
2720 if (image->colors > 65536L)
2721 image->colors=65536L;
2723 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2731 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2732 image->colors=(size_t) number_colors;
2734 if (logging != MagickFalse)
2735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2736 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2740 if (image->storage_class == PseudoClass)
2743 Initialize image colormap.
2745 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2746 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2748 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2756 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2758 for (i=0; i < (ssize_t) number_colors; i++)
2760 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2761 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2762 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2765 for ( ; i < (ssize_t) image->colors; i++)
2767 image->colormap[i].red=0;
2768 image->colormap[i].green=0;
2769 image->colormap[i].blue=0;
2778 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2783 for (i=0; i < (ssize_t) image->colors; i++)
2785 image->colormap[i].red=(Quantum) (i*scale);
2786 image->colormap[i].green=(Quantum) (i*scale);
2787 image->colormap[i].blue=(Quantum) (i*scale);
2792 /* Set some properties for reporting by "identify" */
2797 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2798 ping_interlace_method in value */
2800 (void) FormatLocaleString(msg,MaxTextExtent,
2801 "%d, %d",(int) ping_width, (int) ping_height);
2802 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2804 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2805 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2807 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2808 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2810 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2811 (int) ping_interlace_method);
2812 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2816 Read image scanlines.
2818 if (image->delay != 0)
2819 mng_info->scenes_found++;
2821 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2822 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2823 (image_info->first_scene+image_info->number_scenes))))
2825 if (logging != MagickFalse)
2826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2827 " Skipping PNG image data for scene %.20g",(double)
2828 mng_info->scenes_found-1);
2829 png_destroy_read_struct(&ping,&ping_info,&end_info);
2830 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2831 UnlockSemaphoreInfo(ping_semaphore);
2833 if (logging != MagickFalse)
2834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2835 " exit ReadOnePNGImage().");
2840 if (logging != MagickFalse)
2841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2842 " Reading PNG IDAT chunk(s)");
2845 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2846 ping_rowbytes*sizeof(*ping_pixels));
2849 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2850 sizeof(*ping_pixels));
2852 if (ping_pixels == (unsigned char *) NULL)
2853 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2855 if (logging != MagickFalse)
2856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2857 " Converting PNG pixels to pixel packets");
2859 Convert PNG pixels to pixel packets.
2861 if (setjmp(png_jmpbuf(ping)))
2864 PNG image is corrupt.
2866 png_destroy_read_struct(&ping,&ping_info,&end_info);
2867 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2868 UnlockSemaphoreInfo(ping_semaphore);
2870 if (quantum_info != (QuantumInfo *) NULL)
2871 quantum_info = DestroyQuantumInfo(quantum_info);
2873 if (ping_pixels != (unsigned char *) NULL)
2874 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2876 if (logging != MagickFalse)
2877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2878 " exit ReadOnePNGImage() with error.");
2880 if (image != (Image *) NULL)
2882 InheritException(exception,exception);
2886 return(GetFirstImageInList(image));
2889 quantum_info=AcquireQuantumInfo(image_info,image);
2891 if (quantum_info == (QuantumInfo *) NULL)
2892 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2897 found_transparent_pixel;
2899 found_transparent_pixel=MagickFalse;
2901 if (image->storage_class == DirectClass)
2903 for (pass=0; pass < num_passes; pass++)
2906 Convert image to DirectClass pixel packets.
2908 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2912 depth=(ssize_t) ping_bit_depth;
2914 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2915 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2916 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2917 MagickTrue : MagickFalse;
2919 for (y=0; y < (ssize_t) image->rows; y++)
2922 row_offset=ping_rowbytes*y;
2927 png_read_row(ping,ping_pixels+row_offset,NULL);
2928 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2930 if (q == (Quantum *) NULL)
2933 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2934 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2935 GrayQuantum,ping_pixels+row_offset,exception);
2937 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2938 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2939 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2941 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2942 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2943 RGBAQuantum,ping_pixels+row_offset,exception);
2945 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2946 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2947 IndexQuantum,ping_pixels+row_offset,exception);
2949 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2950 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2951 RGBQuantum,ping_pixels+row_offset,exception);
2953 if (found_transparent_pixel == MagickFalse)
2955 /* Is there a transparent pixel in the row? */
2956 if (y== 0 && logging != MagickFalse)
2957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2958 " Looking for cheap transparent pixel");
2960 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2962 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2963 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2964 (GetPixelAlpha(image,q) != OpaqueAlpha))
2966 if (logging != MagickFalse)
2967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2970 found_transparent_pixel = MagickTrue;
2973 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2974 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2975 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2976 transparent_color.red &&
2977 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2978 transparent_color.green &&
2979 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2980 transparent_color.blue))
2982 if (logging != MagickFalse)
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2985 found_transparent_pixel = MagickTrue;
2988 q+=GetPixelChannels(image);
2992 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2994 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2997 if (status == MagickFalse)
3000 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3004 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3006 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3007 if (status == MagickFalse)
3013 else /* image->storage_class != DirectClass */
3015 for (pass=0; pass < num_passes; pass++)
3024 Convert grayscale image to PseudoClass pixel packets.
3026 if (logging != MagickFalse)
3027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3028 " Converting grayscale pixels to pixel packets");
3030 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3031 MagickTrue : MagickFalse;
3033 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3034 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
3036 if (quantum_scanline == (Quantum *) NULL)
3037 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3039 for (y=0; y < (ssize_t) image->rows; y++)
3042 row_offset=ping_rowbytes*y;
3047 png_read_row(ping,ping_pixels+row_offset,NULL);
3048 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3050 if (q == (Quantum *) NULL)
3053 p=ping_pixels+row_offset;
3056 switch (ping_bit_depth)
3063 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3065 for (bit=7; bit >= 0; bit--)
3066 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3070 if ((image->columns % 8) != 0)
3072 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3073 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3081 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3083 *r++=(*p >> 6) & 0x03;
3084 *r++=(*p >> 4) & 0x03;
3085 *r++=(*p >> 2) & 0x03;
3089 if ((image->columns % 4) != 0)
3091 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3092 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3100 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3102 *r++=(*p >> 4) & 0x0f;
3106 if ((image->columns % 2) != 0)
3107 *r++=(*p++ >> 4) & 0x0f;
3114 if (ping_color_type == 4)
3115 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3118 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3119 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3120 found_transparent_pixel = MagickTrue;
3121 q+=GetPixelChannels(image);
3125 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3133 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3135 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3139 if (image->colors > 256)
3140 quantum=((*p++) << 8);
3146 *r=ScaleShortToQuantum(quantum);
3149 if (ping_color_type == 4)
3151 if (image->colors > 256)
3152 quantum=((*p++) << 8);
3157 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3158 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3159 found_transparent_pixel = MagickTrue;
3160 q+=GetPixelChannels(image);
3163 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3165 p++; /* strip low byte */
3167 if (ping_color_type == 4)
3169 SetPixelAlpha(image,*p++,q);
3170 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3171 found_transparent_pixel = MagickTrue;
3173 q+=GetPixelChannels(image);
3186 Transfer image scanline.
3190 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3192 if (q == (Quantum *) NULL)
3194 for (x=0; x < (ssize_t) image->columns; x++)
3196 SetPixelIndex(image,*r++,q);
3197 q+=GetPixelChannels(image);
3200 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3203 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3205 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3208 if (status == MagickFalse)
3213 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3215 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3217 if (status == MagickFalse)
3221 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3224 image->matte=found_transparent_pixel;
3226 if (logging != MagickFalse)
3228 if (found_transparent_pixel != MagickFalse)
3229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3230 " Found transparent pixel");
3233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3234 " No transparent pixel was found");
3236 ping_color_type&=0x03;
3241 if (quantum_info != (QuantumInfo *) NULL)
3242 quantum_info=DestroyQuantumInfo(quantum_info);
3244 if (image->storage_class == PseudoClass)
3250 image->matte=MagickFalse;
3251 (void) SyncImage(image,exception);
3255 png_read_end(ping,end_info);
3257 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3258 (ssize_t) image_info->first_scene && image->delay != 0)
3260 png_destroy_read_struct(&ping,&ping_info,&end_info);
3261 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3263 (void) SetImageBackgroundColor(image,exception);
3264 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3265 UnlockSemaphoreInfo(ping_semaphore);
3267 if (logging != MagickFalse)
3268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3269 " exit ReadOnePNGImage() early.");
3273 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3279 Image has a transparent background.
3281 storage_class=image->storage_class;
3282 image->matte=MagickTrue;
3284 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3286 if (storage_class == PseudoClass)
3288 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3290 for (x=0; x < ping_num_trans; x++)
3292 image->colormap[x].matte=MagickTrue;
3293 image->colormap[x].alpha =
3294 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3298 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3300 for (x=0; x < (int) image->colors; x++)
3302 if (ScaleQuantumToShort(image->colormap[x].red) ==
3303 transparent_color.alpha)
3305 image->colormap[x].matte=MagickTrue;
3306 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3310 (void) SyncImage(image,exception);
3313 #if 1 /* Should have already been done above, but glennrp problem P10
3318 for (y=0; y < (ssize_t) image->rows; y++)
3320 image->storage_class=storage_class;
3321 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3323 if (q == (Quantum *) NULL)
3327 /* Caution: on a Q8 build, this does not distinguish between
3328 * 16-bit colors that differ only in the low byte
3330 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3332 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3333 transparent_color.red &&
3334 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3335 transparent_color.green &&
3336 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3337 transparent_color.blue)
3339 SetPixelAlpha(image,TransparentAlpha,q);
3342 #if 0 /* I have not found a case where this is needed. */
3345 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3349 q+=GetPixelChannels(image);
3352 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3358 image->storage_class=DirectClass;
3361 for (j = 0; j < 2; j++)
3364 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3365 MagickTrue : MagickFalse;
3367 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3368 MagickTrue : MagickFalse;
3370 if (status != MagickFalse)
3371 for (i=0; i < (ssize_t) num_text; i++)
3373 /* Check for a profile */
3375 if (logging != MagickFalse)
3376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3377 " Reading PNG text chunk");
3379 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3381 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3391 length=text[i].text_length;
3392 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3394 if (value == (char *) NULL)
3396 (void) ThrowMagickException(exception,GetMagickModule(),
3397 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3402 (void) ConcatenateMagickString(value,text[i].text,length+2);
3404 /* Don't save "density" or "units" property if we have a pHYs
3407 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3408 (LocaleCompare(text[i].key,"density") != 0 &&
3409 LocaleCompare(text[i].key,"units") != 0))
3410 (void) SetImageProperty(image,text[i].key,value,exception);
3412 if (logging != MagickFalse)
3414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415 " length: %lu",(unsigned long) length);
3416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3417 " Keyword: %s",text[i].key);
3420 value=DestroyString(value);
3423 num_text_total += num_text;
3426 #ifdef MNG_OBJECT_BUFFERS
3428 Store the object if necessary.
3430 if (object_id && !mng_info->frozen[object_id])
3432 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3435 create a new object buffer.
3437 mng_info->ob[object_id]=(MngBuffer *)
3438 AcquireMagickMemory(sizeof(MngBuffer));
3440 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3442 mng_info->ob[object_id]->image=(Image *) NULL;
3443 mng_info->ob[object_id]->reference_count=1;
3447 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3448 mng_info->ob[object_id]->frozen)
3450 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3451 (void) ThrowMagickException(exception,GetMagickModule(),
3452 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3455 if (mng_info->ob[object_id]->frozen)
3456 (void) ThrowMagickException(exception,GetMagickModule(),
3457 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3458 "`%s'",image->filename);
3464 if (mng_info->ob[object_id]->image != (Image *) NULL)
3465 mng_info->ob[object_id]->image=DestroyImage
3466 (mng_info->ob[object_id]->image);
3468 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3471 if (mng_info->ob[object_id]->image != (Image *) NULL)
3472 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3475 (void) ThrowMagickException(exception,GetMagickModule(),
3476 ResourceLimitError,"Cloning image for object buffer failed",
3477 "`%s'",image->filename);
3479 if (ping_width > 250000L || ping_height > 250000L)
3480 png_error(ping,"PNG Image dimensions are too large.");
3482 mng_info->ob[object_id]->width=ping_width;
3483 mng_info->ob[object_id]->height=ping_height;
3484 mng_info->ob[object_id]->color_type=ping_color_type;
3485 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3486 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3487 mng_info->ob[object_id]->compression_method=
3488 ping_compression_method;
3489 mng_info->ob[object_id]->filter_method=ping_filter_method;
3491 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3500 Copy the PLTE to the object buffer.
3502 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3503 mng_info->ob[object_id]->plte_length=number_colors;
3505 for (i=0; i < number_colors; i++)
3507 mng_info->ob[object_id]->plte[i]=plte[i];
3512 mng_info->ob[object_id]->plte_length=0;
3517 /* Set image->matte to MagickTrue if the input colortype supports
3518 * alpha or if a valid tRNS chunk is present, no matter whether there
3519 * is actual transparency present.
3521 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3522 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3523 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3524 MagickTrue : MagickFalse;
3526 /* Set more properties for identify to retrieve */
3531 if (num_text_total != 0)
3533 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3534 (void) FormatLocaleString(msg,MaxTextExtent,
3535 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3536 (void) SetImageProperty(image,"png:text ",msg,
3540 if (num_raw_profiles != 0)
3542 (void) FormatLocaleString(msg,MaxTextExtent,
3543 "%d were found", num_raw_profiles);
3544 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3548 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3550 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3551 "chunk was found (see Chromaticity, above)");
3552 (void) SetImageProperty(image,"png:cHRM ",msg,
3556 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3558 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3559 "chunk was found (see Background color, above)");
3560 (void) SetImageProperty(image,"png:bKGD ",msg,
3564 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3567 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3568 (void) SetImageProperty(image,"png:iCCP ",msg,
3571 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3572 (void) SetImageProperty(image,"png:tRNS ",msg,
3575 #if defined(PNG_sRGB_SUPPORTED)
3576 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3578 (void) FormatLocaleString(msg,MaxTextExtent,
3579 "intent=%d (See Rendering intent)", (int) intent);
3580 (void) SetImageProperty(image,"png:sRGB ",msg,
3585 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3587 (void) FormatLocaleString(msg,MaxTextExtent,
3588 "gamma=%.8g (See Gamma, above)",
3590 (void) SetImageProperty(image,"png:gAMA ",msg,
3594 #if defined(PNG_pHYs_SUPPORTED)
3595 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3597 (void) FormatLocaleString(msg,MaxTextExtent,
3598 "x_res=%.10g, y_res=%.10g, units=%d",
3599 (double) x_resolution,(double) y_resolution, unit_type);
3600 (void) SetImageProperty(image,"png:pHYs ",msg,
3605 #if defined(PNG_oFFs_SUPPORTED)
3606 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3608 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3609 (double) image->page.x,(double) image->page.y);
3610 (void) SetImageProperty(image,"png:oFFs ",msg,
3615 if ((image->page.width != 0 && image->page.width != image->columns) ||
3616 (image->page.height != 0 && image->page.height != image->rows))
3618 (void) FormatLocaleString(msg,MaxTextExtent,
3619 "width=%.20g, height=%.20g",
3620 (double) image->page.width,(double) image->page.height);
3621 (void) SetImageProperty(image,"png:vpAg ",msg,
3627 Relinquish resources.
3629 png_destroy_read_struct(&ping,&ping_info,&end_info);
3631 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3632 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3633 UnlockSemaphoreInfo(ping_semaphore);
3636 if (logging != MagickFalse)
3637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3638 " exit ReadOnePNGImage()");
3642 /* end of reading one PNG image */
3645 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3660 magic_number[MaxTextExtent];
3668 assert(image_info != (const ImageInfo *) NULL);
3669 assert(image_info->signature == MagickSignature);
3671 if (image_info->debug != MagickFalse)
3672 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3673 image_info->filename);
3675 assert(exception != (ExceptionInfo *) NULL);
3676 assert(exception->signature == MagickSignature);
3677 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3678 image=AcquireImage(image_info,exception);
3679 mng_info=(MngInfo *) NULL;
3680 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3682 if (status == MagickFalse)
3683 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3686 Verify PNG signature.
3688 count=ReadBlob(image,8,(unsigned char *) magic_number);
3690 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3691 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3694 Allocate a MngInfo structure.
3696 have_mng_structure=MagickFalse;
3697 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3699 if (mng_info == (MngInfo *) NULL)
3700 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3703 Initialize members of the MngInfo structure.
3705 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3706 mng_info->image=image;
3707 have_mng_structure=MagickTrue;
3710 image=ReadOnePNGImage(mng_info,image_info,exception);
3711 MngInfoFreeStruct(mng_info,&have_mng_structure);
3713 if (image == (Image *) NULL)
3715 if (previous != (Image *) NULL)
3717 if (previous->signature != MagickSignature)
3718 ThrowReaderException(CorruptImageError,"CorruptImage");
3720 (void) CloseBlob(previous);
3721 (void) DestroyImageList(previous);
3724 if (logging != MagickFalse)
3725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3726 "exit ReadPNGImage() with error");
3728 return((Image *) NULL);
3731 (void) CloseBlob(image);
3733 if ((image->columns == 0) || (image->rows == 0))
3735 if (logging != MagickFalse)
3736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3737 "exit ReadPNGImage() with error.");
3739 ThrowReaderException(CorruptImageError,"CorruptImage");
3742 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3744 (void) SetImageType(image,TrueColorType,exception);
3745 image->matte=MagickFalse;
3748 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3749 (void) SetImageType(image,TrueColorMatteType,exception);
3751 if (logging != MagickFalse)
3752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3753 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3754 (double) image->page.width,(double) image->page.height,
3755 (double) image->page.x,(double) image->page.y);
3757 if (logging != MagickFalse)
3758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3765 #if defined(JNG_SUPPORTED)
3767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771 % R e a d O n e J N G I m a g e %
3775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3777 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3778 % (minus the 8-byte signature) and returns it. It allocates the memory
3779 % necessary for the new Image structure and returns a pointer to the new
3782 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3784 % The format of the ReadOneJNGImage method is:
3786 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3787 % ExceptionInfo *exception)
3789 % A description of each parameter follows:
3791 % o mng_info: Specifies a pointer to a MngInfo structure.
3793 % o image_info: the image info.
3795 % o exception: return any errors or warnings in this structure.
3798 static Image *ReadOneJNGImage(MngInfo *mng_info,
3799 const ImageInfo *image_info, ExceptionInfo *exception)
3826 jng_image_sample_depth,
3827 jng_image_compression_method,
3828 jng_image_interlace_method,
3829 jng_alpha_sample_depth,
3830 jng_alpha_compression_method,
3831 jng_alpha_filter_method,
3832 jng_alpha_interlace_method;
3834 register const Quantum
3844 register unsigned char
3855 jng_alpha_compression_method=0;
3856 jng_alpha_sample_depth=8;
3860 alpha_image=(Image *) NULL;
3861 color_image=(Image *) NULL;
3862 alpha_image_info=(ImageInfo *) NULL;
3863 color_image_info=(ImageInfo *) NULL;
3865 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3866 " Enter ReadOneJNGImage()");
3868 image=mng_info->image;
3870 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3873 Allocate next image structure.
3875 if (logging != MagickFalse)
3876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3877 " AcquireNextImage()");
3879 AcquireNextImage(image_info,image,exception);
3881 if (GetNextImageInList(image) == (Image *) NULL)
3882 return((Image *) NULL);
3884 image=SyncNextImageInList(image);
3886 mng_info->image=image;
3889 Signature bytes have already been read.
3892 read_JSEP=MagickFalse;
3893 reading_idat=MagickFalse;
3894 skip_to_iend=MagickFalse;
3898 type[MaxTextExtent];
3907 Read a new JNG chunk.
3909 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3910 2*GetBlobSize(image));
3912 if (status == MagickFalse)
3916 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3917 length=ReadBlobMSBLong(image);
3918 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3920 if (logging != MagickFalse)
3921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3922 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3923 type[0],type[1],type[2],type[3],(double) length);
3925 if (length > PNG_UINT_31_MAX || count == 0)
3926 ThrowReaderException(CorruptImageError,"CorruptImage");
3929 chunk=(unsigned char *) NULL;
3933 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3935 if (chunk == (unsigned char *) NULL)
3936 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3938 for (i=0; i < (ssize_t) length; i++)
3939 chunk[i]=(unsigned char) ReadBlobByte(image);
3944 (void) ReadBlobMSBLong(image); /* read crc word */
3949 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3954 if (memcmp(type,mng_JHDR,4) == 0)
3958 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3959 (p[2] << 8) | p[3]);
3960 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3961 (p[6] << 8) | p[7]);
3962 jng_color_type=p[8];
3963 jng_image_sample_depth=p[9];
3964 jng_image_compression_method=p[10];
3965 jng_image_interlace_method=p[11];
3967 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3970 jng_alpha_sample_depth=p[12];
3971 jng_alpha_compression_method=p[13];
3972 jng_alpha_filter_method=p[14];
3973 jng_alpha_interlace_method=p[15];
3975 if (logging != MagickFalse)
3977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3978 " jng_width: %16lu",(unsigned long) jng_width);
3980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3981 " jng_width: %16lu",(unsigned long) jng_height);
3983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3984 " jng_color_type: %16d",jng_color_type);
3986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " jng_image_sample_depth: %3d",
3988 jng_image_sample_depth);
3990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3991 " jng_image_compression_method:%3d",
3992 jng_image_compression_method);
3994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3995 " jng_image_interlace_method: %3d",
3996 jng_image_interlace_method);
3998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999 " jng_alpha_sample_depth: %3d",
4000 jng_alpha_sample_depth);
4002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4003 " jng_alpha_compression_method:%3d",
4004 jng_alpha_compression_method);
4006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4007 " jng_alpha_filter_method: %3d",
4008 jng_alpha_filter_method);
4010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4011 " jng_alpha_interlace_method: %3d",
4012 jng_alpha_interlace_method);
4017 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4023 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4024 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4025 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4028 o create color_image
4029 o open color_blob, attached to color_image
4030 o if (color type has alpha)
4031 open alpha_blob, attached to alpha_image
4034 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4036 if (color_image_info == (ImageInfo *) NULL)
4037 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4039 GetImageInfo(color_image_info);
4040 color_image=AcquireImage(color_image_info,exception);
4042 if (color_image == (Image *) NULL)
4043 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4045 if (logging != MagickFalse)
4046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4047 " Creating color_blob.");
4049 (void) AcquireUniqueFilename(color_image->filename);
4050 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4053 if (status == MagickFalse)
4054 return((Image *) NULL);
4056 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4058 alpha_image_info=(ImageInfo *)
4059 AcquireMagickMemory(sizeof(ImageInfo));
4061 if (alpha_image_info == (ImageInfo *) NULL)
4062 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4064 GetImageInfo(alpha_image_info);
4065 alpha_image=AcquireImage(alpha_image_info,exception);
4067 if (alpha_image == (Image *) NULL)
4069 alpha_image=DestroyImage(alpha_image);
4070 ThrowReaderException(ResourceLimitError,
4071 "MemoryAllocationFailed");
4074 if (logging != MagickFalse)
4075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4076 " Creating alpha_blob.");
4078 (void) AcquireUniqueFilename(alpha_image->filename);
4079 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4082 if (status == MagickFalse)
4083 return((Image *) NULL);
4085 if (jng_alpha_compression_method == 0)
4090 if (logging != MagickFalse)
4091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4092 " Writing IHDR chunk to alpha_blob.");
4094 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4095 "\211PNG\r\n\032\n");
4097 (void) WriteBlobMSBULong(alpha_image,13L);
4098 PNGType(data,mng_IHDR);
4099 LogPNGChunk(logging,mng_IHDR,13L);
4100 PNGLong(data+4,jng_width);
4101 PNGLong(data+8,jng_height);
4102 data[12]=jng_alpha_sample_depth;
4103 data[13]=0; /* color_type gray */
4104 data[14]=0; /* compression method 0 */
4105 data[15]=0; /* filter_method 0 */
4106 data[16]=0; /* interlace_method 0 */
4107 (void) WriteBlob(alpha_image,17,data);
4108 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4111 reading_idat=MagickTrue;
4114 if (memcmp(type,mng_JDAT,4) == 0)
4116 /* Copy chunk to color_image->blob */
4118 if (logging != MagickFalse)
4119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4120 " Copying JDAT chunk data to color_blob.");
4122 (void) WriteBlob(color_image,length,chunk);
4125 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4130 if (memcmp(type,mng_IDAT,4) == 0)
4135 /* Copy IDAT header and chunk data to alpha_image->blob */
4137 if (image_info->ping == MagickFalse)
4139 if (logging != MagickFalse)
4140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4141 " Copying IDAT chunk data to alpha_blob.");
4143 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4144 PNGType(data,mng_IDAT);
4145 LogPNGChunk(logging,mng_IDAT,length);
4146 (void) WriteBlob(alpha_image,4,data);
4147 (void) WriteBlob(alpha_image,length,chunk);
4148 (void) WriteBlobMSBULong(alpha_image,
4149 crc32(crc32(0,data,4),chunk,(uInt) length));
4153 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4158 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4160 /* Copy chunk data to alpha_image->blob */
4162 if (image_info->ping == MagickFalse)
4164 if (logging != MagickFalse)
4165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4166 " Copying JDAA chunk data to alpha_blob.");
4168 (void) WriteBlob(alpha_image,length,chunk);
4172 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4177 if (memcmp(type,mng_JSEP,4) == 0)
4179 read_JSEP=MagickTrue;
4182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4187 if (memcmp(type,mng_bKGD,4) == 0)
4191 image->background_color.red=ScaleCharToQuantum(p[1]);
4192 image->background_color.green=image->background_color.red;
4193 image->background_color.blue=image->background_color.red;
4198 image->background_color.red=ScaleCharToQuantum(p[1]);
4199 image->background_color.green=ScaleCharToQuantum(p[3]);
4200 image->background_color.blue=ScaleCharToQuantum(p[5]);
4203 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4207 if (memcmp(type,mng_gAMA,4) == 0)
4210 image->gamma=((float) mng_get_long(p))*0.00001;
4212 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4216 if (memcmp(type,mng_cHRM,4) == 0)
4220 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4221 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4222 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4223 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4224 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4225 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4226 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4227 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4230 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4234 if (memcmp(type,mng_sRGB,4) == 0)
4238 image->rendering_intent=
4239 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4240 image->gamma=0.45455f;
4241 image->chromaticity.red_primary.x=0.6400f;
4242 image->chromaticity.red_primary.y=0.3300f;
4243 image->chromaticity.green_primary.x=0.3000f;
4244 image->chromaticity.green_primary.y=0.6000f;
4245 image->chromaticity.blue_primary.x=0.1500f;
4246 image->chromaticity.blue_primary.y=0.0600f;
4247 image->chromaticity.white_point.x=0.3127f;
4248 image->chromaticity.white_point.y=0.3290f;
4251 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4255 if (memcmp(type,mng_oFFs,4) == 0)
4259 image->page.x=(ssize_t) mng_get_long(p);
4260 image->page.y=(ssize_t) mng_get_long(&p[4]);
4262 if ((int) p[8] != 0)
4264 image->page.x/=10000;
4265 image->page.y/=10000;
4270 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4275 if (memcmp(type,mng_pHYs,4) == 0)
4279 image->resolution.x=(double) mng_get_long(p);
4280 image->resolution.y=(double) mng_get_long(&p[4]);
4281 if ((int) p[8] == PNG_RESOLUTION_METER)
4283 image->units=PixelsPerCentimeterResolution;
4284 image->resolution.x=image->resolution.x/100.0f;
4285 image->resolution.y=image->resolution.y/100.0f;
4289 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4294 if (memcmp(type,mng_iCCP,4) == 0)
4298 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4305 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4307 if (memcmp(type,mng_IEND,4))
4317 Finish up reading image data:
4319 o read main image from color_blob.
4323 o if (color_type has alpha)
4324 if alpha_encoding is PNG
4325 read secondary image from alpha_blob via ReadPNG
4326 if alpha_encoding is JPEG
4327 read secondary image from alpha_blob via ReadJPEG
4331 o copy intensity of secondary image into
4332 alpha samples of main image.
4334 o destroy the secondary image.
4337 (void) CloseBlob(color_image);
4339 if (logging != MagickFalse)
4340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4341 " Reading jng_image from color_blob.");
4343 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4344 color_image->filename);
4346 color_image_info->ping=MagickFalse; /* To do: avoid this */
4347 jng_image=ReadImage(color_image_info,exception);
4349 if (jng_image == (Image *) NULL)
4350 return((Image *) NULL);
4352 (void) RelinquishUniqueFileResource(color_image->filename);
4353 color_image=DestroyImage(color_image);
4354 color_image_info=DestroyImageInfo(color_image_info);
4356 if (jng_image == (Image *) NULL)
4357 return((Image *) NULL);
4359 if (logging != MagickFalse)
4360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4361 " Copying jng_image pixels to main image.");
4363 image->rows=jng_height;
4364 image->columns=jng_width;
4366 for (y=0; y < (ssize_t) image->rows; y++)
4368 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4369 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4370 for (x=(ssize_t) image->columns; x != 0; x--)
4372 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4373 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4374 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4375 q+=GetPixelChannels(image);
4376 s+=GetPixelChannels(jng_image);
4379 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4383 jng_image=DestroyImage(jng_image);
4385 if (image_info->ping == MagickFalse)
4387 if (jng_color_type >= 12)
4389 if (jng_alpha_compression_method == 0)
4393 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4394 PNGType(data,mng_IEND);
4395 LogPNGChunk(logging,mng_IEND,0L);
4396 (void) WriteBlob(alpha_image,4,data);
4397 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4400 (void) CloseBlob(alpha_image);
4402 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " Reading alpha from alpha_blob.");
4406 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4407 "%s",alpha_image->filename);
4409 jng_image=ReadImage(alpha_image_info,exception);
4411 if (jng_image != (Image *) NULL)
4412 for (y=0; y < (ssize_t) image->rows; y++)
4414 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4416 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4418 if (image->matte != MagickFalse)
4419 for (x=(ssize_t) image->columns; x != 0; x--)
4421 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4422 q+=GetPixelChannels(image);
4423 s+=GetPixelChannels(jng_image);
4427 for (x=(ssize_t) image->columns; x != 0; x--)
4429 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4430 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4431 image->matte=MagickTrue;
4432 q+=GetPixelChannels(image);
4433 s+=GetPixelChannels(jng_image);
4436 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4439 (void) RelinquishUniqueFileResource(alpha_image->filename);
4440 alpha_image=DestroyImage(alpha_image);
4441 alpha_image_info=DestroyImageInfo(alpha_image_info);
4442 if (jng_image != (Image *) NULL)
4443 jng_image=DestroyImage(jng_image);
4447 /* Read the JNG image. */
4449 if (mng_info->mng_type == 0)
4451 mng_info->mng_width=jng_width;
4452 mng_info->mng_height=jng_height;
4455 if (image->page.width == 0 && image->page.height == 0)
4457 image->page.width=jng_width;
4458 image->page.height=jng_height;
4461 if (image->page.x == 0 && image->page.y == 0)
4463 image->page.x=mng_info->x_off[mng_info->object_id];
4464 image->page.y=mng_info->y_off[mng_info->object_id];
4469 image->page.y=mng_info->y_off[mng_info->object_id];
4472 mng_info->image_found++;
4473 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4474 2*GetBlobSize(image));
4476 if (logging != MagickFalse)
4477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4478 " exit ReadOneJNGImage()");
4484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4488 % R e a d J N G I m a g e %
4492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4494 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4495 % (including the 8-byte signature) and returns it. It allocates the memory
4496 % necessary for the new Image structure and returns a pointer to the new
4499 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4501 % The format of the ReadJNGImage method is:
4503 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4506 % A description of each parameter follows:
4508 % o image_info: the image info.
4510 % o exception: return any errors or warnings in this structure.
4514 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4529 magic_number[MaxTextExtent];
4537 assert(image_info != (const ImageInfo *) NULL);
4538 assert(image_info->signature == MagickSignature);
4539 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4540 assert(exception != (ExceptionInfo *) NULL);
4541 assert(exception->signature == MagickSignature);
4542 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4543 image=AcquireImage(image_info,exception);
4544 mng_info=(MngInfo *) NULL;
4545 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4547 if (status == MagickFalse)
4548 return((Image *) NULL);
4550 if (LocaleCompare(image_info->magick,"JNG") != 0)
4551 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4553 /* Verify JNG signature. */
4555 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4557 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4558 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4560 /* Allocate a MngInfo structure. */
4562 have_mng_structure=MagickFalse;
4563 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4565 if (mng_info == (MngInfo *) NULL)
4566 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4568 /* Initialize members of the MngInfo structure. */
4570 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4571 have_mng_structure=MagickTrue;
4573 mng_info->image=image;
4575 image=ReadOneJNGImage(mng_info,image_info,exception);
4576 MngInfoFreeStruct(mng_info,&have_mng_structure);
4578 if (image == (Image *) NULL)
4580 if (IsImageObject(previous) != MagickFalse)
4582 (void) CloseBlob(previous);
4583 (void) DestroyImageList(previous);
4586 if (logging != MagickFalse)
4587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4588 "exit ReadJNGImage() with error");
4590 return((Image *) NULL);
4592 (void) CloseBlob(image);
4594 if (image->columns == 0 || image->rows == 0)
4596 if (logging != MagickFalse)
4597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4598 "exit ReadJNGImage() with error");
4600 ThrowReaderException(CorruptImageError,"CorruptImage");
4603 if (logging != MagickFalse)
4604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4610 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4613 page_geometry[MaxTextExtent];
4646 #if defined(MNG_INSERT_LAYERS)
4648 mng_background_color;
4651 register unsigned char
4666 #if defined(MNG_INSERT_LAYERS)
4671 volatile unsigned int
4672 #ifdef MNG_OBJECT_BUFFERS
4673 mng_background_object=0,
4675 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4678 default_frame_timeout,
4680 #if defined(MNG_INSERT_LAYERS)
4686 /* These delays are all measured in image ticks_per_second,
4687 * not in MNG ticks_per_second
4690 default_frame_delay,
4694 #if defined(MNG_INSERT_LAYERS)
4703 previous_fb.bottom=0;
4705 previous_fb.right=0;
4707 default_fb.bottom=0;
4711 /* Open image file. */
4713 assert(image_info != (const ImageInfo *) NULL);
4714 assert(image_info->signature == MagickSignature);
4715 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4716 assert(exception != (ExceptionInfo *) NULL);
4717 assert(exception->signature == MagickSignature);
4718 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4719 image=AcquireImage(image_info,exception);
4720 mng_info=(MngInfo *) NULL;
4721 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4723 if (status == MagickFalse)
4724 return((Image *) NULL);
4726 first_mng_object=MagickFalse;
4728 have_mng_structure=MagickFalse;
4730 /* Allocate a MngInfo structure. */
4732 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4734 if (mng_info == (MngInfo *) NULL)
4735 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4737 /* Initialize members of the MngInfo structure. */
4739 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4740 mng_info->image=image;
4741 have_mng_structure=MagickTrue;
4743 if (LocaleCompare(image_info->magick,"MNG") == 0)
4746 magic_number[MaxTextExtent];
4748 /* Verify MNG signature. */
4749 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4750 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4751 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4753 /* Initialize some nonzero members of the MngInfo structure. */
4754 for (i=0; i < MNG_MAX_OBJECTS; i++)
4756 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4757 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4759 mng_info->exists[0]=MagickTrue;
4762 first_mng_object=MagickTrue;
4764 #if defined(MNG_INSERT_LAYERS)
4765 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4767 default_frame_delay=0;
4768 default_frame_timeout=0;
4771 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4773 skip_to_iend=MagickFalse;
4774 term_chunk_found=MagickFalse;
4775 mng_info->framing_mode=1;
4776 #if defined(MNG_INSERT_LAYERS)
4777 mandatory_back=MagickFalse;
4779 #if defined(MNG_INSERT_LAYERS)
4780 mng_background_color=image->background_color;
4782 default_fb=mng_info->frame;
4783 previous_fb=mng_info->frame;
4787 type[MaxTextExtent];
4789 if (LocaleCompare(image_info->magick,"MNG") == 0)
4798 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4799 length=ReadBlobMSBLong(image);
4800 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4802 if (logging != MagickFalse)
4803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4804 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4805 type[0],type[1],type[2],type[3],(double) length);
4807 if (length > PNG_UINT_31_MAX)
4811 ThrowReaderException(CorruptImageError,"CorruptImage");
4814 chunk=(unsigned char *) NULL;
4818 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4820 if (chunk == (unsigned char *) NULL)
4821 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4823 for (i=0; i < (ssize_t) length; i++)
4824 chunk[i]=(unsigned char) ReadBlobByte(image);
4829 (void) ReadBlobMSBLong(image); /* read crc word */
4831 #if !defined(JNG_SUPPORTED)
4832 if (memcmp(type,mng_JHDR,4) == 0)
4834 skip_to_iend=MagickTrue;
4836 if (mng_info->jhdr_warning == 0)
4837 (void) ThrowMagickException(exception,GetMagickModule(),
4838 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4840 mng_info->jhdr_warning++;
4843 if (memcmp(type,mng_DHDR,4) == 0)
4845 skip_to_iend=MagickTrue;
4847 if (mng_info->dhdr_warning == 0)
4848 (void) ThrowMagickException(exception,GetMagickModule(),
4849 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4851 mng_info->dhdr_warning++;
4853 if (memcmp(type,mng_MEND,4) == 0)
4858 if (memcmp(type,mng_IEND,4) == 0)
4859 skip_to_iend=MagickFalse;
4862 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4864 if (logging != MagickFalse)
4865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4871 if (memcmp(type,mng_MHDR,4) == 0)
4873 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4874 (p[2] << 8) | p[3]);
4876 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4877 (p[6] << 8) | p[7]);
4879 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4882 " MNG width: %.20g",(double) mng_info->mng_width);
4883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4884 " MNG height: %.20g",(double) mng_info->mng_height);
4888 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4890 if (mng_info->ticks_per_second == 0)
4891 default_frame_delay=0;
4894 default_frame_delay=1UL*image->ticks_per_second/
4895 mng_info->ticks_per_second;
4897 frame_delay=default_frame_delay;
4903 simplicity=(size_t) mng_get_long(p);
4906 mng_type=1; /* Full MNG */
4908 if ((simplicity != 0) && ((simplicity | 11) == 11))
4909 mng_type=2; /* LC */
4911 if ((simplicity != 0) && ((simplicity | 9) == 9))
4912 mng_type=3; /* VLC */
4914 #if defined(MNG_INSERT_LAYERS)
4916 insert_layers=MagickTrue;
4918 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4920 /* Allocate next image structure. */
4921 AcquireNextImage(image_info,image,exception);
4923 if (GetNextImageInList(image) == (Image *) NULL)
4924 return((Image *) NULL);
4926 image=SyncNextImageInList(image);
4927 mng_info->image=image;
4930 if ((mng_info->mng_width > 65535L) ||
4931 (mng_info->mng_height > 65535L))
4932 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4934 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4935 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4936 mng_info->mng_height);
4938 mng_info->frame.left=0;
4939 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4940 mng_info->frame.top=0;
4941 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4942 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4944 for (i=0; i < MNG_MAX_OBJECTS; i++)
4945 mng_info->object_clip[i]=mng_info->frame;
4947 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4951 if (memcmp(type,mng_TERM,4) == 0)
4962 final_delay=(png_uint_32) mng_get_long(&p[2]);
4963 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4965 if (mng_iterations == PNG_UINT_31_MAX)
4968 image->iterations=mng_iterations;
4969 term_chunk_found=MagickTrue;
4972 if (logging != MagickFalse)
4974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4975 " repeat=%d",repeat);
4977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4978 " final_delay=%.20g",(double) final_delay);
4980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4981 " image->iterations=%.20g",(double) image->iterations);
4984 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4987 if (memcmp(type,mng_DEFI,4) == 0)
4990 (void) ThrowMagickException(exception,GetMagickModule(),
4991 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4994 object_id=(p[0] << 8) | p[1];
4996 if (mng_type == 2 && object_id != 0)
4997 (void) ThrowMagickException(exception,GetMagickModule(),
4998 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5001 if (object_id > MNG_MAX_OBJECTS)
5004 Instead ofsuing a warning we should allocate a larger
5005 MngInfo structure and continue.
5007 (void) ThrowMagickException(exception,GetMagickModule(),
5008 CoderError,"object id too large","`%s'",image->filename);
5009 object_id=MNG_MAX_OBJECTS;
5012 if (mng_info->exists[object_id])
5013 if (mng_info->frozen[object_id])
5015 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5016 (void) ThrowMagickException(exception,
5017 GetMagickModule(),CoderError,
5018 "DEFI cannot redefine a frozen MNG object","`%s'",
5023 mng_info->exists[object_id]=MagickTrue;
5026 mng_info->invisible[object_id]=p[2];
5029 Extract object offset info.
5033 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5034 (p[5] << 16) | (p[6] << 8) | p[7]);
5036 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5037 (p[9] << 16) | (p[10] << 8) | p[11]);
5039 if (logging != MagickFalse)
5041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5042 " x_off[%d]: %.20g",object_id,(double)
5043 mng_info->x_off[object_id]);
5045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5046 " y_off[%d]: %.20g",object_id,(double)
5047 mng_info->y_off[object_id]);
5052 Extract object clipping info.
5055 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5058 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5061 if (memcmp(type,mng_bKGD,4) == 0)
5063 mng_info->have_global_bkgd=MagickFalse;
5067 mng_info->mng_global_bkgd.red=
5068 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5070 mng_info->mng_global_bkgd.green=
5071 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5073 mng_info->mng_global_bkgd.blue=
5074 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5076 mng_info->have_global_bkgd=MagickTrue;
5079 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5082 if (memcmp(type,mng_BACK,4) == 0)
5084 #if defined(MNG_INSERT_LAYERS)
5086 mandatory_back=p[6];
5091 if (mandatory_back && length > 5)
5093 mng_background_color.red=
5094 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5096 mng_background_color.green=
5097 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5099 mng_background_color.blue=
5100 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5102 mng_background_color.alpha=OpaqueAlpha;
5105 #ifdef MNG_OBJECT_BUFFERS
5107 mng_background_object=(p[7] << 8) | p[8];
5110 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5114 if (memcmp(type,mng_PLTE,4) == 0)
5116 /* Read global PLTE. */
5118 if (length && (length < 769))
5120 if (mng_info->global_plte == (png_colorp) NULL)
5121 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5122 sizeof(*mng_info->global_plte));
5124 for (i=0; i < (ssize_t) (length/3); i++)
5126 mng_info->global_plte[i].red=p[3*i];
5127 mng_info->global_plte[i].green=p[3*i+1];
5128 mng_info->global_plte[i].blue=p[3*i+2];
5131 mng_info->global_plte_length=(unsigned int) (length/3);
5134 for ( ; i < 256; i++)
5136 mng_info->global_plte[i].red=i;
5137 mng_info->global_plte[i].green=i;
5138 mng_info->global_plte[i].blue=i;
5142 mng_info->global_plte_length=256;
5145 mng_info->global_plte_length=0;
5147 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5151 if (memcmp(type,mng_tRNS,4) == 0)
5153 /* read global tRNS */
5156 for (i=0; i < (ssize_t) length; i++)
5157 mng_info->global_trns[i]=p[i];
5160 for ( ; i < 256; i++)
5161 mng_info->global_trns[i]=255;
5163 mng_info->global_trns_length=(unsigned int) length;
5164 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5167 if (memcmp(type,mng_gAMA,4) == 0)
5174 igamma=mng_get_long(p);
5175 mng_info->global_gamma=((float) igamma)*0.00001;
5176 mng_info->have_global_gama=MagickTrue;
5180 mng_info->have_global_gama=MagickFalse;
5182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5186 if (memcmp(type,mng_cHRM,4) == 0)
5188 /* Read global cHRM */
5192 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5193 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5194 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5195 mng_info->global_chrm.red_primary.y=0.00001*
5196 mng_get_long(&p[12]);
5197 mng_info->global_chrm.green_primary.x=0.00001*
5198 mng_get_long(&p[16]);
5199 mng_info->global_chrm.green_primary.y=0.00001*
5200 mng_get_long(&p[20]);
5201 mng_info->global_chrm.blue_primary.x=0.00001*
5202 mng_get_long(&p[24]);
5203 mng_info->global_chrm.blue_primary.y=0.00001*
5204 mng_get_long(&p[28]);
5205 mng_info->have_global_chrm=MagickTrue;
5208 mng_info->have_global_chrm=MagickFalse;
5210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5214 if (memcmp(type,mng_sRGB,4) == 0)
5221 mng_info->global_srgb_intent=
5222 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5223 mng_info->have_global_srgb=MagickTrue;
5226 mng_info->have_global_srgb=MagickFalse;
5228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5232 if (memcmp(type,mng_iCCP,4) == 0)
5240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5245 if (memcmp(type,mng_FRAM,4) == 0)
5248 (void) ThrowMagickException(exception,GetMagickModule(),
5249 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5252 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5253 image->delay=frame_delay;
5255 frame_delay=default_frame_delay;
5256 frame_timeout=default_frame_timeout;
5261 mng_info->framing_mode=p[0];
5263 if (logging != MagickFalse)
5264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5265 " Framing_mode=%d",mng_info->framing_mode);
5269 /* Note the delay and frame clipping boundaries. */
5271 p++; /* framing mode */
5273 while (*p && ((p-chunk) < (ssize_t) length))
5274 p++; /* frame name */
5276 p++; /* frame name terminator */
5278 if ((p-chunk) < (ssize_t) (length-4))
5285 change_delay=(*p++);
5286 change_timeout=(*p++);
5287 change_clipping=(*p++);
5288 p++; /* change_sync */
5292 frame_delay=1UL*image->ticks_per_second*
5295 if (mng_info->ticks_per_second != 0)
5296 frame_delay/=mng_info->ticks_per_second;
5299 frame_delay=PNG_UINT_31_MAX;
5301 if (change_delay == 2)
5302 default_frame_delay=frame_delay;
5306 if (logging != MagickFalse)
5307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5308 " Framing_delay=%.20g",(double) frame_delay);
5313 frame_timeout=1UL*image->ticks_per_second*
5316 if (mng_info->ticks_per_second != 0)
5317 frame_timeout/=mng_info->ticks_per_second;
5320 frame_timeout=PNG_UINT_31_MAX;
5322 if (change_delay == 2)
5323 default_frame_timeout=frame_timeout;
5327 if (logging != MagickFalse)
5328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5329 " Framing_timeout=%.20g",(double) frame_timeout);
5332 if (change_clipping)
5334 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5338 if (logging != MagickFalse)
5339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5340 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5341 (double) fb.left,(double) fb.right,(double) fb.top,
5342 (double) fb.bottom);
5344 if (change_clipping == 2)
5350 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5352 subframe_width=(size_t) (mng_info->clip.right
5353 -mng_info->clip.left);
5355 subframe_height=(size_t) (mng_info->clip.bottom
5356 -mng_info->clip.top);
5358 Insert a background layer behind the frame if framing_mode is 4.
5360 #if defined(MNG_INSERT_LAYERS)
5361 if (logging != MagickFalse)
5362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5363 " subframe_width=%.20g, subframe_height=%.20g",(double)
5364 subframe_width,(double) subframe_height);
5366 if (insert_layers && (mng_info->framing_mode == 4) &&
5367 (subframe_width) && (subframe_height))
5369 /* Allocate next image structure. */
5370 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5372 AcquireNextImage(image_info,image,exception);
5374 if (GetNextImageInList(image) == (Image *) NULL)
5376 image=DestroyImageList(image);
5377 MngInfoFreeStruct(mng_info,&have_mng_structure);
5378 return((Image *) NULL);
5381 image=SyncNextImageInList(image);
5384 mng_info->image=image;
5386 if (term_chunk_found)
5388 image->start_loop=MagickTrue;
5389 image->iterations=mng_iterations;
5390 term_chunk_found=MagickFalse;
5394 image->start_loop=MagickFalse;
5396 image->columns=subframe_width;
5397 image->rows=subframe_height;
5398 image->page.width=subframe_width;
5399 image->page.height=subframe_height;
5400 image->page.x=mng_info->clip.left;
5401 image->page.y=mng_info->clip.top;
5402 image->background_color=mng_background_color;
5403 image->matte=MagickFalse;
5405 (void) SetImageBackgroundColor(image,exception);
5407 if (logging != MagickFalse)
5408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5409 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5410 (double) mng_info->clip.left,(double) mng_info->clip.right,
5411 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5414 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5417 if (memcmp(type,mng_CLIP,4) == 0)
5426 first_object=(p[0] << 8) | p[1];
5427 last_object=(p[2] << 8) | p[3];
5429 for (i=(int) first_object; i <= (int) last_object; i++)
5431 if (mng_info->exists[i] && !mng_info->frozen[i])
5436 box=mng_info->object_clip[i];
5437 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5444 if (memcmp(type,mng_SAVE,4) == 0)
5446 for (i=1; i < MNG_MAX_OBJECTS; i++)
5447 if (mng_info->exists[i])
5449 mng_info->frozen[i]=MagickTrue;
5450 #ifdef MNG_OBJECT_BUFFERS
5451 if (mng_info->ob[i] != (MngBuffer *) NULL)
5452 mng_info->ob[i]->frozen=MagickTrue;
5457 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5462 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5464 /* Read DISC or SEEK. */
5466 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5468 for (i=1; i < MNG_MAX_OBJECTS; i++)
5469 MngInfoDiscardObject(mng_info,i);
5477 for (j=0; j < (ssize_t) length; j+=2)
5479 i=p[j] << 8 | p[j+1];
5480 MngInfoDiscardObject(mng_info,i);
5485 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5490 if (memcmp(type,mng_MOVE,4) == 0)
5498 first_object=(p[0] << 8) | p[1];
5499 last_object=(p[2] << 8) | p[3];
5500 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5502 if (mng_info->exists[i] && !mng_info->frozen[i])
5510 old_pair.a=mng_info->x_off[i];
5511 old_pair.b=mng_info->y_off[i];
5512 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5513 mng_info->x_off[i]=new_pair.a;
5514 mng_info->y_off[i]=new_pair.b;
5518 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5522 if (memcmp(type,mng_LOOP,4) == 0)
5524 ssize_t loop_iters=1;
5525 loop_level=chunk[0];
5526 mng_info->loop_active[loop_level]=1; /* mark loop active */
5528 /* Record starting point. */
5529 loop_iters=mng_get_long(&chunk[1]);
5531 if (logging != MagickFalse)
5532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5533 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5534 (double) loop_iters);
5536 if (loop_iters == 0)
5537 skipping_loop=loop_level;
5541 mng_info->loop_jump[loop_level]=TellBlob(image);
5542 mng_info->loop_count[loop_level]=loop_iters;
5545 mng_info->loop_iteration[loop_level]=0;
5546 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5550 if (memcmp(type,mng_ENDL,4) == 0)
5552 loop_level=chunk[0];
5554 if (skipping_loop > 0)
5556 if (skipping_loop == loop_level)
5559 Found end of zero-iteration loop.
5562 mng_info->loop_active[loop_level]=0;
5568 if (mng_info->loop_active[loop_level] == 1)
5570 mng_info->loop_count[loop_level]--;
5571 mng_info->loop_iteration[loop_level]++;
5573 if (logging != MagickFalse)
5574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5575 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5576 (double) loop_level,(double)
5577 mng_info->loop_count[loop_level]);
5579 if (mng_info->loop_count[loop_level] != 0)
5581 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5585 ThrowReaderException(CorruptImageError,
5586 "ImproperImageHeader");
5597 mng_info->loop_active[loop_level]=0;
5599 for (i=0; i < loop_level; i++)
5600 if (mng_info->loop_active[i] == 1)
5601 last_level=(short) i;
5602 loop_level=last_level;
5607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5611 if (memcmp(type,mng_CLON,4) == 0)
5613 if (mng_info->clon_warning == 0)
5614 (void) ThrowMagickException(exception,GetMagickModule(),
5615 CoderError,"CLON is not implemented yet","`%s'",
5618 mng_info->clon_warning++;
5621 if (memcmp(type,mng_MAGN,4) == 0)
5636 magn_first=(p[0] << 8) | p[1];
5642 magn_last=(p[2] << 8) | p[3];
5645 magn_last=magn_first;
5646 #ifndef MNG_OBJECT_BUFFERS
5647 if (magn_first || magn_last)
5648 if (mng_info->magn_warning == 0)
5650 (void) ThrowMagickException(exception,
5651 GetMagickModule(),CoderError,
5652 "MAGN is not implemented yet for nonzero objects",
5653 "`%s'",image->filename);
5655 mng_info->magn_warning++;
5665 magn_mx=(p[5] << 8) | p[6];
5674 magn_my=(p[7] << 8) | p[8];
5683 magn_ml=(p[9] << 8) | p[10];
5692 magn_mr=(p[11] << 8) | p[12];
5701 magn_mt=(p[13] << 8) | p[14];
5710 magn_mb=(p[15] << 8) | p[16];
5722 magn_methy=magn_methx;
5725 if (magn_methx > 5 || magn_methy > 5)
5726 if (mng_info->magn_warning == 0)
5728 (void) ThrowMagickException(exception,
5729 GetMagickModule(),CoderError,
5730 "Unknown MAGN method in MNG datastream","`%s'",
5733 mng_info->magn_warning++;
5735 #ifdef MNG_OBJECT_BUFFERS
5736 /* Magnify existing objects in the range magn_first to magn_last */
5738 if (magn_first == 0 || magn_last == 0)
5740 /* Save the magnification factors for object 0 */
5741 mng_info->magn_mb=magn_mb;
5742 mng_info->magn_ml=magn_ml;
5743 mng_info->magn_mr=magn_mr;
5744 mng_info->magn_mt=magn_mt;
5745 mng_info->magn_mx=magn_mx;
5746 mng_info->magn_my=magn_my;
5747 mng_info->magn_methx=magn_methx;
5748 mng_info->magn_methy=magn_methy;
5752 if (memcmp(type,mng_PAST,4) == 0)
5754 if (mng_info->past_warning == 0)
5755 (void) ThrowMagickException(exception,GetMagickModule(),
5756 CoderError,"PAST is not implemented yet","`%s'",
5759 mng_info->past_warning++;
5762 if (memcmp(type,mng_SHOW,4) == 0)
5764 if (mng_info->show_warning == 0)
5765 (void) ThrowMagickException(exception,GetMagickModule(),
5766 CoderError,"SHOW is not implemented yet","`%s'",
5769 mng_info->show_warning++;
5772 if (memcmp(type,mng_sBIT,4) == 0)
5775 mng_info->have_global_sbit=MagickFalse;
5779 mng_info->global_sbit.gray=p[0];
5780 mng_info->global_sbit.red=p[0];
5781 mng_info->global_sbit.green=p[1];
5782 mng_info->global_sbit.blue=p[2];
5783 mng_info->global_sbit.alpha=p[3];
5784 mng_info->have_global_sbit=MagickTrue;
5787 if (memcmp(type,mng_pHYs,4) == 0)
5791 mng_info->global_x_pixels_per_unit=
5792 (size_t) mng_get_long(p);
5793 mng_info->global_y_pixels_per_unit=
5794 (size_t) mng_get_long(&p[4]);
5795 mng_info->global_phys_unit_type=p[8];
5796 mng_info->have_global_phys=MagickTrue;
5800 mng_info->have_global_phys=MagickFalse;
5802 if (memcmp(type,mng_pHYg,4) == 0)
5804 if (mng_info->phyg_warning == 0)
5805 (void) ThrowMagickException(exception,GetMagickModule(),
5806 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5808 mng_info->phyg_warning++;
5810 if (memcmp(type,mng_BASI,4) == 0)
5812 skip_to_iend=MagickTrue;
5814 if (mng_info->basi_warning == 0)
5815 (void) ThrowMagickException(exception,GetMagickModule(),
5816 CoderError,"BASI is not implemented yet","`%s'",
5819 mng_info->basi_warning++;
5820 #ifdef MNG_BASI_SUPPORTED
5821 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5822 (p[2] << 8) | p[3]);
5823 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5824 (p[6] << 8) | p[7]);
5825 basi_color_type=p[8];
5826 basi_compression_method=p[9];
5827 basi_filter_type=p[10];
5828 basi_interlace_method=p[11];
5830 basi_red=(p[12] << 8) & p[13];
5836 basi_green=(p[14] << 8) & p[15];
5842 basi_blue=(p[16] << 8) & p[17];
5848 basi_alpha=(p[18] << 8) & p[19];
5852 if (basi_sample_depth == 16)
5859 basi_viewable=p[20];
5865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5869 if (memcmp(type,mng_IHDR,4)
5870 #if defined(JNG_SUPPORTED)
5871 && memcmp(type,mng_JHDR,4)
5875 /* Not an IHDR or JHDR chunk */
5877 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5882 if (logging != MagickFalse)
5883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5884 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5886 mng_info->exists[object_id]=MagickTrue;
5887 mng_info->viewable[object_id]=MagickTrue;
5889 if (mng_info->invisible[object_id])
5891 if (logging != MagickFalse)
5892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5893 " Skipping invisible object");
5895 skip_to_iend=MagickTrue;
5896 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5899 #if defined(MNG_INSERT_LAYERS)
5901 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5903 image_width=(size_t) mng_get_long(p);
5904 image_height=(size_t) mng_get_long(&p[4]);
5906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5909 Insert a transparent background layer behind the entire animation
5910 if it is not full screen.
5912 #if defined(MNG_INSERT_LAYERS)
5913 if (insert_layers && mng_type && first_mng_object)
5915 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5916 (image_width < mng_info->mng_width) ||
5917 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5918 (image_height < mng_info->mng_height) ||
5919 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5921 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5924 Allocate next image structure.
5926 AcquireNextImage(image_info,image,exception);
5928 if (GetNextImageInList(image) == (Image *) NULL)
5930 image=DestroyImageList(image);
5931 MngInfoFreeStruct(mng_info,&have_mng_structure);
5932 return((Image *) NULL);
5935 image=SyncNextImageInList(image);
5937 mng_info->image=image;
5939 if (term_chunk_found)
5941 image->start_loop=MagickTrue;
5942 image->iterations=mng_iterations;
5943 term_chunk_found=MagickFalse;
5947 image->start_loop=MagickFalse;
5949 /* Make a background rectangle. */
5952 image->columns=mng_info->mng_width;
5953 image->rows=mng_info->mng_height;
5954 image->page.width=mng_info->mng_width;
5955 image->page.height=mng_info->mng_height;
5958 image->background_color=mng_background_color;
5959 (void) SetImageBackgroundColor(image,exception);
5960 if (logging != MagickFalse)
5961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5962 " Inserted transparent background layer, W=%.20g, H=%.20g",
5963 (double) mng_info->mng_width,(double) mng_info->mng_height);
5967 Insert a background layer behind the upcoming image if
5968 framing_mode is 3, and we haven't already inserted one.
5970 if (insert_layers && (mng_info->framing_mode == 3) &&
5971 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5972 (simplicity & 0x08)))
5974 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5977 Allocate next image structure.
5979 AcquireNextImage(image_info,image,exception);
5981 if (GetNextImageInList(image) == (Image *) NULL)
5983 image=DestroyImageList(image);
5984 MngInfoFreeStruct(mng_info,&have_mng_structure);
5985 return((Image *) NULL);
5988 image=SyncNextImageInList(image);
5991 mng_info->image=image;
5993 if (term_chunk_found)
5995 image->start_loop=MagickTrue;
5996 image->iterations=mng_iterations;
5997 term_chunk_found=MagickFalse;
6001 image->start_loop=MagickFalse;
6004 image->columns=subframe_width;
6005 image->rows=subframe_height;
6006 image->page.width=subframe_width;
6007 image->page.height=subframe_height;
6008 image->page.x=mng_info->clip.left;
6009 image->page.y=mng_info->clip.top;
6010 image->background_color=mng_background_color;
6011 image->matte=MagickFalse;
6012 (void) SetImageBackgroundColor(image,exception);
6014 if (logging != MagickFalse)
6015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6016 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6017 (double) mng_info->clip.left,(double) mng_info->clip.right,
6018 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6020 #endif /* MNG_INSERT_LAYERS */
6021 first_mng_object=MagickFalse;
6023 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6026 Allocate next image structure.
6028 AcquireNextImage(image_info,image,exception);
6030 if (GetNextImageInList(image) == (Image *) NULL)
6032 image=DestroyImageList(image);
6033 MngInfoFreeStruct(mng_info,&have_mng_structure);
6034 return((Image *) NULL);
6037 image=SyncNextImageInList(image);
6039 mng_info->image=image;
6040 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6041 GetBlobSize(image));
6043 if (status == MagickFalse)
6046 if (term_chunk_found)
6048 image->start_loop=MagickTrue;
6049 term_chunk_found=MagickFalse;
6053 image->start_loop=MagickFalse;
6055 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6057 image->delay=frame_delay;
6058 frame_delay=default_frame_delay;
6064 image->page.width=mng_info->mng_width;
6065 image->page.height=mng_info->mng_height;
6066 image->page.x=mng_info->x_off[object_id];
6067 image->page.y=mng_info->y_off[object_id];
6068 image->iterations=mng_iterations;
6071 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6074 if (logging != MagickFalse)
6075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6076 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6079 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6082 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6086 mng_info->image=image;
6087 mng_info->mng_type=mng_type;
6088 mng_info->object_id=object_id;
6090 if (memcmp(type,mng_IHDR,4) == 0)
6091 image=ReadOnePNGImage(mng_info,image_info,exception);
6093 #if defined(JNG_SUPPORTED)
6095 image=ReadOneJNGImage(mng_info,image_info,exception);
6098 if (image == (Image *) NULL)
6100 if (IsImageObject(previous) != MagickFalse)
6102 (void) DestroyImageList(previous);
6103 (void) CloseBlob(previous);
6106 MngInfoFreeStruct(mng_info,&have_mng_structure);
6107 return((Image *) NULL);
6110 if (image->columns == 0 || image->rows == 0)
6112 (void) CloseBlob(image);
6113 image=DestroyImageList(image);
6114 MngInfoFreeStruct(mng_info,&have_mng_structure);
6115 return((Image *) NULL);
6118 mng_info->image=image;
6125 if (mng_info->magn_methx || mng_info->magn_methy)
6131 if (logging != MagickFalse)
6132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6133 " Processing MNG MAGN chunk");
6135 if (mng_info->magn_methx == 1)
6137 magnified_width=mng_info->magn_ml;
6139 if (image->columns > 1)
6140 magnified_width += mng_info->magn_mr;
6142 if (image->columns > 2)
6143 magnified_width += (png_uint_32)
6144 ((image->columns-2)*(mng_info->magn_mx));
6149 magnified_width=(png_uint_32) image->columns;
6151 if (image->columns > 1)
6152 magnified_width += mng_info->magn_ml-1;
6154 if (image->columns > 2)
6155 magnified_width += mng_info->magn_mr-1;
6157 if (image->columns > 3)
6158 magnified_width += (png_uint_32)
6159 ((image->columns-3)*(mng_info->magn_mx-1));
6162 if (mng_info->magn_methy == 1)
6164 magnified_height=mng_info->magn_mt;
6166 if (image->rows > 1)
6167 magnified_height += mng_info->magn_mb;
6169 if (image->rows > 2)
6170 magnified_height += (png_uint_32)
6171 ((image->rows-2)*(mng_info->magn_my));
6176 magnified_height=(png_uint_32) image->rows;
6178 if (image->rows > 1)
6179 magnified_height += mng_info->magn_mt-1;
6181 if (image->rows > 2)
6182 magnified_height += mng_info->magn_mb-1;
6184 if (image->rows > 3)
6185 magnified_height += (png_uint_32)
6186 ((image->rows-3)*(mng_info->magn_my-1));
6189 if (magnified_height > image->rows ||
6190 magnified_width > image->columns)
6217 /* Allocate next image structure. */
6219 if (logging != MagickFalse)
6220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6221 " Allocate magnified image");
6223 AcquireNextImage(image_info,image,exception);
6225 if (GetNextImageInList(image) == (Image *) NULL)
6227 image=DestroyImageList(image);
6228 MngInfoFreeStruct(mng_info,&have_mng_structure);
6229 return((Image *) NULL);
6232 large_image=SyncNextImageInList(image);
6234 large_image->columns=magnified_width;
6235 large_image->rows=magnified_height;
6237 magn_methx=mng_info->magn_methx;
6238 magn_methy=mng_info->magn_methy;
6240 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6241 #define QM unsigned short
6242 if (magn_methx != 1 || magn_methy != 1)
6245 Scale pixels to unsigned shorts to prevent
6246 overflow of intermediate values of interpolations
6248 for (y=0; y < (ssize_t) image->rows; y++)
6250 q=GetAuthenticPixels(image,0,y,image->columns,1,
6253 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6255 SetPixelRed(image,ScaleQuantumToShort(
6256 GetPixelRed(image,q)),q);
6257 SetPixelGreen(image,ScaleQuantumToShort(
6258 GetPixelGreen(image,q)),q);
6259 SetPixelBlue(image,ScaleQuantumToShort(
6260 GetPixelBlue(image,q)),q);
6261 SetPixelAlpha(image,ScaleQuantumToShort(
6262 GetPixelAlpha(image,q)),q);
6263 q+=GetPixelChannels(image);
6266 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6274 if (image->matte != MagickFalse)
6275 (void) SetImageBackgroundColor(large_image,exception);
6279 large_image->background_color.alpha=OpaqueAlpha;
6280 (void) SetImageBackgroundColor(large_image,exception);
6282 if (magn_methx == 4)
6285 if (magn_methx == 5)
6288 if (magn_methy == 4)
6291 if (magn_methy == 5)
6295 /* magnify the rows into the right side of the large image */
6297 if (logging != MagickFalse)
6298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6299 " Magnify the rows to %.20g",(double) large_image->rows);
6300 m=(ssize_t) mng_info->magn_mt;
6302 length=(size_t) image->columns*GetPixelChannels(image);
6303 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6304 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6306 if ((prev == (Quantum *) NULL) ||
6307 (next == (Quantum *) NULL))
6309 image=DestroyImageList(image);
6310 MngInfoFreeStruct(mng_info,&have_mng_structure);
6311 ThrowReaderException(ResourceLimitError,
6312 "MemoryAllocationFailed");
6315 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6316 (void) CopyMagickMemory(next,n,length);
6318 for (y=0; y < (ssize_t) image->rows; y++)
6321 m=(ssize_t) mng_info->magn_mt;
6323 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6324 m=(ssize_t) mng_info->magn_mb;
6326 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6327 m=(ssize_t) mng_info->magn_mb;
6329 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6333 m=(ssize_t) mng_info->magn_my;
6339 if (y < (ssize_t) image->rows-1)
6341 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6343 (void) CopyMagickMemory(next,n,length);
6346 for (i=0; i < m; i++, yy++)
6351 assert(yy < (ssize_t) large_image->rows);
6354 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6356 q+=(large_image->columns-image->columns)*
6357 GetPixelChannels(large_image);
6359 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6361 /* To do: get color as function of indexes[x] */
6363 if (image->storage_class == PseudoClass)
6368 if (magn_methy <= 1)
6370 /* replicate previous */
6371 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6372 SetPixelGreen(large_image,GetPixelGreen(image,
6374 SetPixelBlue(large_image,GetPixelBlue(image,
6376 SetPixelAlpha(large_image,GetPixelAlpha(image,
6380 else if (magn_methy == 2 || magn_methy == 4)
6384 SetPixelRed(large_image,GetPixelRed(image,
6386 SetPixelGreen(large_image,GetPixelGreen(image,
6388 SetPixelBlue(large_image,GetPixelBlue(image,
6390 SetPixelAlpha(large_image,GetPixelAlpha(image,
6397 SetPixelRed(large_image,((QM) (((ssize_t)
6398 (2*i*(GetPixelRed(image,n)
6399 -GetPixelRed(image,pixels)+m))/
6401 +GetPixelRed(image,pixels)))),q);
6402 SetPixelGreen(large_image,((QM) (((ssize_t)
6403 (2*i*(GetPixelGreen(image,n)
6404 -GetPixelGreen(image,pixels)+m))/
6406 +GetPixelGreen(image,pixels)))),q);
6407 SetPixelBlue(large_image,((QM) (((ssize_t)
6408 (2*i*(GetPixelBlue(image,n)
6409 -GetPixelBlue(image,pixels)+m))/
6411 +GetPixelBlue(image,pixels)))),q);
6413 if (image->matte != MagickFalse)
6414 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6415 (2*i*(GetPixelAlpha(image,n)
6416 -GetPixelAlpha(image,pixels)+m))
6418 GetPixelAlpha(image,pixels)))),q);
6421 if (magn_methy == 4)
6423 /* Replicate nearest */
6424 if (i <= ((m+1) << 1))
6425 SetPixelAlpha(large_image,GetPixelAlpha(image,
6428 SetPixelAlpha(large_image,GetPixelAlpha(image,
6433 else /* if (magn_methy == 3 || magn_methy == 5) */
6435 /* Replicate nearest */
6436 if (i <= ((m+1) << 1))
6438 SetPixelRed(large_image,GetPixelRed(image,
6440 SetPixelGreen(large_image,GetPixelGreen(image,
6442 SetPixelBlue(large_image,GetPixelBlue(image,
6444 SetPixelAlpha(large_image,GetPixelAlpha(image,
6450 SetPixelRed(large_image,GetPixelRed(image,n),q);
6451 SetPixelGreen(large_image,GetPixelGreen(image,n),
6453 SetPixelBlue(large_image,GetPixelBlue(image,n),
6455 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6459 if (magn_methy == 5)
6461 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6462 (GetPixelAlpha(image,n)
6463 -GetPixelAlpha(image,pixels))
6464 +m))/((ssize_t) (m*2))
6465 +GetPixelAlpha(image,pixels)),q);
6468 n+=GetPixelChannels(image);
6469 q+=GetPixelChannels(large_image);
6470 pixels+=GetPixelChannels(image);
6473 if (SyncAuthenticPixels(large_image,exception) == 0)
6479 prev=(Quantum *) RelinquishMagickMemory(prev);
6480 next=(Quantum *) RelinquishMagickMemory(next);
6482 length=image->columns;
6484 if (logging != MagickFalse)
6485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6486 " Delete original image");
6488 DeleteImageFromList(&image);
6492 mng_info->image=image;
6494 /* magnify the columns */
6495 if (logging != MagickFalse)
6496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6497 " Magnify the columns to %.20g",(double) image->columns);
6499 for (y=0; y < (ssize_t) image->rows; y++)
6504 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6505 pixels=q+(image->columns-length)*GetPixelChannels(image);
6506 n=pixels+GetPixelChannels(image);
6508 for (x=(ssize_t) (image->columns-length);
6509 x < (ssize_t) image->columns; x++)
6511 /* To do: Rewrite using Get/Set***PixelChannel() */
6513 if (x == (ssize_t) (image->columns-length))
6514 m=(ssize_t) mng_info->magn_ml;
6516 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6517 m=(ssize_t) mng_info->magn_mr;
6519 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6520 m=(ssize_t) mng_info->magn_mr;
6522 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6526 m=(ssize_t) mng_info->magn_mx;
6528 for (i=0; i < m; i++)
6530 if (magn_methx <= 1)
6532 /* replicate previous */
6533 SetPixelRed(image,GetPixelRed(image,pixels),q);
6534 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6535 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6536 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6539 else if (magn_methx == 2 || magn_methx == 4)
6543 SetPixelRed(image,GetPixelRed(image,pixels),q);
6544 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6545 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6546 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6549 /* To do: Rewrite using Get/Set***PixelChannel() */
6553 SetPixelRed(image,(QM) ((2*i*(
6554 GetPixelRed(image,n)
6555 -GetPixelRed(image,pixels))+m)
6557 GetPixelRed(image,pixels)),q);
6559 SetPixelGreen(image,(QM) ((2*i*(
6560 GetPixelGreen(image,n)
6561 -GetPixelGreen(image,pixels))+m)
6563 GetPixelGreen(image,pixels)),q);
6565 SetPixelBlue(image,(QM) ((2*i*(
6566 GetPixelBlue(image,n)
6567 -GetPixelBlue(image,pixels))+m)
6569 GetPixelBlue(image,pixels)),q);
6570 if (image->matte != MagickFalse)
6571 SetPixelAlpha(image,(QM) ((2*i*(
6572 GetPixelAlpha(image,n)
6573 -GetPixelAlpha(image,pixels))+m)
6575 GetPixelAlpha(image,pixels)),q);
6578 if (magn_methx == 4)
6580 /* Replicate nearest */
6581 if (i <= ((m+1) << 1))
6583 SetPixelAlpha(image,
6584 GetPixelAlpha(image,pixels)+0,q);
6588 SetPixelAlpha(image,
6589 GetPixelAlpha(image,n)+0,q);
6594 else /* if (magn_methx == 3 || magn_methx == 5) */
6596 /* Replicate nearest */
6597 if (i <= ((m+1) << 1))
6599 SetPixelRed(image,GetPixelRed(image,pixels),q);
6600 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6601 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6602 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6607 SetPixelRed(image,GetPixelRed(image,n),q);
6608 SetPixelGreen(image,GetPixelGreen(image,n),q);
6609 SetPixelBlue(image,GetPixelBlue(image,n),q);
6610 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6613 if (magn_methx == 5)
6616 SetPixelAlpha(image,
6617 (QM) ((2*i*( GetPixelAlpha(image,n)
6618 -GetPixelAlpha(image,pixels))+m)/
6620 +GetPixelAlpha(image,pixels)),q);
6623 q+=GetPixelChannels(image);
6625 n+=GetPixelChannels(image);
6626 p+=GetPixelChannels(image);
6629 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6632 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6633 if (magn_methx != 1 || magn_methy != 1)
6636 Rescale pixels to Quantum
6638 for (y=0; y < (ssize_t) image->rows; y++)
6640 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6642 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6644 SetPixelRed(image,ScaleShortToQuantum(
6645 GetPixelRed(image,q)),q);
6646 SetPixelGreen(image,ScaleShortToQuantum(
6647 GetPixelGreen(image,q)),q);
6648 SetPixelBlue(image,ScaleShortToQuantum(
6649 GetPixelBlue(image,q)),q);
6650 SetPixelAlpha(image,ScaleShortToQuantum(
6651 GetPixelAlpha(image,q)),q);
6652 q+=GetPixelChannels(image);
6655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6660 if (logging != MagickFalse)
6661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6662 " Finished MAGN processing");
6667 Crop_box is with respect to the upper left corner of the MNG.
6669 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6670 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6671 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6672 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6673 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6674 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6675 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6676 if ((crop_box.left != (mng_info->image_box.left
6677 +mng_info->x_off[object_id])) ||
6678 (crop_box.right != (mng_info->image_box.right
6679 +mng_info->x_off[object_id])) ||
6680 (crop_box.top != (mng_info->image_box.top
6681 +mng_info->y_off[object_id])) ||
6682 (crop_box.bottom != (mng_info->image_box.bottom
6683 +mng_info->y_off[object_id])))
6685 if (logging != MagickFalse)
6686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687 " Crop the PNG image");
6689 if ((crop_box.left < crop_box.right) &&
6690 (crop_box.top < crop_box.bottom))
6699 Crop_info is with respect to the upper left corner of
6702 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6703 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6704 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6705 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6706 image->page.width=image->columns;
6707 image->page.height=image->rows;
6710 im=CropImage(image,&crop_info,exception);
6712 if (im != (Image *) NULL)
6714 image->columns=im->columns;
6715 image->rows=im->rows;
6716 im=DestroyImage(im);
6717 image->page.width=image->columns;
6718 image->page.height=image->rows;
6719 image->page.x=crop_box.left;
6720 image->page.y=crop_box.top;
6727 No pixels in crop area. The MNG spec still requires
6728 a layer, though, so make a single transparent pixel in
6729 the top left corner.
6734 (void) SetImageBackgroundColor(image,exception);
6735 image->page.width=1;
6736 image->page.height=1;
6741 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6742 image=mng_info->image;
6746 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6747 /* PNG does not handle depths greater than 16 so reduce it even
6750 if (image->depth > 16)
6754 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6755 if (image->depth > 8)
6757 /* To do: fill low byte properly */
6761 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6765 if (image_info->number_scenes != 0)
6767 if (mng_info->scenes_found >
6768 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6772 if (logging != MagickFalse)
6773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6774 " Finished reading image datastream.");
6776 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6778 (void) CloseBlob(image);
6780 if (logging != MagickFalse)
6781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6782 " Finished reading all image datastreams.");
6784 #if defined(MNG_INSERT_LAYERS)
6785 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6786 (mng_info->mng_height))
6789 Insert a background layer if nothing else was found.
6791 if (logging != MagickFalse)
6792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793 " No images found. Inserting a background layer.");
6795 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6798 Allocate next image structure.
6800 AcquireNextImage(image_info,image,exception);
6801 if (GetNextImageInList(image) == (Image *) NULL)
6803 image=DestroyImageList(image);
6804 MngInfoFreeStruct(mng_info,&have_mng_structure);
6806 if (logging != MagickFalse)
6807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6808 " Allocation failed, returning NULL.");
6810 return((Image *) NULL);
6812 image=SyncNextImageInList(image);
6814 image->columns=mng_info->mng_width;
6815 image->rows=mng_info->mng_height;
6816 image->page.width=mng_info->mng_width;
6817 image->page.height=mng_info->mng_height;
6820 image->background_color=mng_background_color;
6821 image->matte=MagickFalse;
6823 if (image_info->ping == MagickFalse)
6824 (void) SetImageBackgroundColor(image,exception);
6826 mng_info->image_found++;
6829 image->iterations=mng_iterations;
6831 if (mng_iterations == 1)
6832 image->start_loop=MagickTrue;
6834 while (GetPreviousImageInList(image) != (Image *) NULL)
6837 if (image_count > 10*mng_info->image_found)
6839 if (logging != MagickFalse)
6840 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6842 (void) ThrowMagickException(exception,GetMagickModule(),
6843 CoderError,"Linked list is corrupted, beginning of list not found",
6844 "`%s'",image_info->filename);
6846 return((Image *) NULL);
6849 image=GetPreviousImageInList(image);
6851 if (GetNextImageInList(image) == (Image *) NULL)
6853 if (logging != MagickFalse)
6854 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6856 (void) ThrowMagickException(exception,GetMagickModule(),
6857 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6858 image_info->filename);
6862 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6863 GetNextImageInList(image) ==
6866 if (logging != MagickFalse)
6867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6868 " First image null");
6870 (void) ThrowMagickException(exception,GetMagickModule(),
6871 CoderError,"image->next for first image is NULL but shouldn't be.",
6872 "`%s'",image_info->filename);
6875 if (mng_info->image_found == 0)
6877 if (logging != MagickFalse)
6878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6879 " No visible images found.");
6881 (void) ThrowMagickException(exception,GetMagickModule(),
6882 CoderError,"No visible images in file","`%s'",image_info->filename);
6884 if (image != (Image *) NULL)
6885 image=DestroyImageList(image);
6887 MngInfoFreeStruct(mng_info,&have_mng_structure);
6888 return((Image *) NULL);
6891 if (mng_info->ticks_per_second)
6892 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6893 final_delay/mng_info->ticks_per_second;
6896 image->start_loop=MagickTrue;
6898 /* Find final nonzero image delay */
6899 final_image_delay=0;
6901 while (GetNextImageInList(image) != (Image *) NULL)
6904 final_image_delay=image->delay;
6906 image=GetNextImageInList(image);
6909 if (final_delay < final_image_delay)
6910 final_delay=final_image_delay;
6912 image->delay=final_delay;
6914 if (logging != MagickFalse)
6915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6916 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6917 (double) final_delay);
6919 if (logging != MagickFalse)
6925 image=GetFirstImageInList(image);
6927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6928 " Before coalesce:");
6930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6931 " scene 0 delay=%.20g",(double) image->delay);
6933 while (GetNextImageInList(image) != (Image *) NULL)
6935 image=GetNextImageInList(image);
6936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6937 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6941 image=GetFirstImageInList(image);
6942 #ifdef MNG_COALESCE_LAYERS
6952 if (logging != MagickFalse)
6953 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6956 next_image=CoalesceImages(image,exception);
6958 if (next_image == (Image *) NULL)
6959 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6961 image=DestroyImageList(image);
6964 for (next=image; next != (Image *) NULL; next=next_image)
6966 next->page.width=mng_info->mng_width;
6967 next->page.height=mng_info->mng_height;
6970 next->scene=scene++;
6971 next_image=GetNextImageInList(next);
6973 if (next_image == (Image *) NULL)
6976 if (next->delay == 0)
6979 next_image->previous=GetPreviousImageInList(next);
6980 if (GetPreviousImageInList(next) == (Image *) NULL)
6983 next->previous->next=next_image;
6984 next=DestroyImage(next);
6990 while (GetNextImageInList(image) != (Image *) NULL)
6991 image=GetNextImageInList(image);
6993 image->dispose=BackgroundDispose;
6995 if (logging != MagickFalse)
7001 image=GetFirstImageInList(image);
7003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7004 " After coalesce:");
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7008 (double) image->dispose);
7010 while (GetNextImageInList(image) != (Image *) NULL)
7012 image=GetNextImageInList(image);
7014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7015 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7016 (double) image->delay,(double) image->dispose);
7020 image=GetFirstImageInList(image);
7021 MngInfoFreeStruct(mng_info,&have_mng_structure);
7022 have_mng_structure=MagickFalse;
7024 if (logging != MagickFalse)
7025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7027 return(GetFirstImageInList(image));
7029 #else /* PNG_LIBPNG_VER > 10011 */
7030 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7032 printf("Your PNG library is too old: You have libpng-%s\n",
7033 PNG_LIBPNG_VER_STRING);
7035 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7036 "PNG library is too old","`%s'",image_info->filename);
7038 return(Image *) NULL;
7041 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7043 return(ReadPNGImage(image_info,exception));
7045 #endif /* PNG_LIBPNG_VER > 10011 */
7049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7053 % R e g i s t e r P N G I m a g e %
7057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7059 % RegisterPNGImage() adds properties for the PNG image format to
7060 % the list of supported formats. The properties include the image format
7061 % tag, a method to read and/or write the format, whether the format
7062 % supports the saving of more than one frame to the same file or blob,
7063 % whether the format supports native in-memory I/O, and a brief
7064 % description of the format.
7066 % The format of the RegisterPNGImage method is:
7068 % size_t RegisterPNGImage(void)
7071 ModuleExport size_t RegisterPNGImage(void)
7074 version[MaxTextExtent];
7082 "See http://www.libpng.org/ for details about the PNG format."
7087 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7093 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7099 #if defined(PNG_LIBPNG_VER_STRING)
7100 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7101 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7103 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7105 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7106 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7111 entry=SetMagickInfo("MNG");
7112 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7114 #if defined(MAGICKCORE_PNG_DELEGATE)
7115 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7116 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7119 entry->magick=(IsImageFormatHandler *) IsMNG;
7120 entry->description=ConstantString("Multiple-image Network Graphics");
7122 if (*version != '\0')
7123 entry->version=ConstantString(version);
7125 entry->module=ConstantString("PNG");
7126 entry->note=ConstantString(MNGNote);
7127 (void) RegisterMagickInfo(entry);
7129 entry=SetMagickInfo("PNG");
7131 #if defined(MAGICKCORE_PNG_DELEGATE)
7132 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7133 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7136 entry->magick=(IsImageFormatHandler *) IsPNG;
7137 entry->adjoin=MagickFalse;
7138 entry->description=ConstantString("Portable Network Graphics");
7139 entry->module=ConstantString("PNG");
7141 if (*version != '\0')
7142 entry->version=ConstantString(version);
7144 entry->note=ConstantString(PNGNote);
7145 (void) RegisterMagickInfo(entry);
7147 entry=SetMagickInfo("PNG8");
7149 #if defined(MAGICKCORE_PNG_DELEGATE)
7150 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7151 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7154 entry->magick=(IsImageFormatHandler *) IsPNG;
7155 entry->adjoin=MagickFalse;
7156 entry->description=ConstantString(
7157 "8-bit indexed with optional binary transparency");
7158 entry->module=ConstantString("PNG");
7159 (void) RegisterMagickInfo(entry);
7161 entry=SetMagickInfo("PNG24");
7164 #if defined(ZLIB_VERSION)
7165 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7166 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7168 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7170 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7171 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7175 if (*version != '\0')
7176 entry->version=ConstantString(version);
7178 #if defined(MAGICKCORE_PNG_DELEGATE)
7179 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7180 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7183 entry->magick=(IsImageFormatHandler *) IsPNG;
7184 entry->adjoin=MagickFalse;
7185 entry->description=ConstantString("opaque 24-bit RGB");
7186 entry->module=ConstantString("PNG");
7187 (void) RegisterMagickInfo(entry);
7189 entry=SetMagickInfo("PNG32");
7191 #if defined(MAGICKCORE_PNG_DELEGATE)
7192 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7193 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7196 entry->magick=(IsImageFormatHandler *) IsPNG;
7197 entry->adjoin=MagickFalse;
7198 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7199 entry->module=ConstantString("PNG");
7200 (void) RegisterMagickInfo(entry);
7202 entry=SetMagickInfo("JNG");
7204 #if defined(JNG_SUPPORTED)
7205 #if defined(MAGICKCORE_PNG_DELEGATE)
7206 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7207 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7211 entry->magick=(IsImageFormatHandler *) IsJNG;
7212 entry->adjoin=MagickFalse;
7213 entry->description=ConstantString("JPEG Network Graphics");
7214 entry->module=ConstantString("PNG");
7215 entry->note=ConstantString(JNGNote);
7216 (void) RegisterMagickInfo(entry);
7218 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7219 ping_semaphore=AllocateSemaphoreInfo();
7222 return(MagickImageCoderSignature);
7226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7230 % U n r e g i s t e r P N G I m a g e %
7234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7236 % UnregisterPNGImage() removes format registrations made by the
7237 % PNG module from the list of supported formats.
7239 % The format of the UnregisterPNGImage method is:
7241 % UnregisterPNGImage(void)
7244 ModuleExport void UnregisterPNGImage(void)
7246 (void) UnregisterMagickInfo("MNG");
7247 (void) UnregisterMagickInfo("PNG");
7248 (void) UnregisterMagickInfo("PNG8");
7249 (void) UnregisterMagickInfo("PNG24");
7250 (void) UnregisterMagickInfo("PNG32");
7251 (void) UnregisterMagickInfo("JNG");
7253 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7254 if (ping_semaphore != (SemaphoreInfo *) NULL)
7255 DestroySemaphoreInfo(&ping_semaphore);
7259 #if defined(MAGICKCORE_PNG_DELEGATE)
7260 #if PNG_LIBPNG_VER > 10011
7262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7266 % W r i t e M N G I m a g e %
7270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7272 % WriteMNGImage() writes an image in the Portable Network Graphics
7273 % Group's "Multiple-image Network Graphics" encoded image format.
7275 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7277 % The format of the WriteMNGImage method is:
7279 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7280 % Image *image,ExceptionInfo *exception)
7282 % A description of each parameter follows.
7284 % o image_info: the image info.
7286 % o image: The image.
7288 % o exception: return any errors or warnings in this structure.
7290 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7291 % "To do" under ReadPNGImage):
7293 % Preserve all unknown and not-yet-handled known chunks found in input
7294 % PNG file and copy them into output PNG files according to the PNG
7297 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7299 % Improve selection of color type (use indexed-colour or indexed-colour
7300 % with tRNS when 256 or fewer unique RGBA values are present).
7302 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7303 % This will be complicated if we limit ourselves to generating MNG-LC
7304 % files. For now we ignore disposal method 3 and simply overlay the next
7307 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7308 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7309 % [mostly done 15 June 1999 but still need to take care of tRNS]
7311 % Check for identical sRGB and replace with a global sRGB (and remove
7312 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7313 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7314 % local gAMA/cHRM with local sRGB if appropriate).
7316 % Check for identical sBIT chunks and write global ones.
7318 % Provide option to skip writing the signature tEXt chunks.
7320 % Use signatures to detect identical objects and reuse the first
7321 % instance of such objects instead of writing duplicate objects.
7323 % Use a smaller-than-32k value of compression window size when
7326 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7327 % ancillary text chunks and save profiles.
7329 % Provide an option to force LC files (to ensure exact framing rate)
7332 % Provide an option to force VLC files instead of LC, even when offsets
7333 % are present. This will involve expanding the embedded images with a
7334 % transparent region at the top and/or left.
7338 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7339 png_info *ping_info, unsigned char *profile_type, unsigned char
7340 *profile_description, unsigned char *profile_data, png_uint_32 length)
7359 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7361 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7364 if (image_info->verbose)
7366 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7367 (char *) profile_type, (double) length);
7370 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7371 description_length=(png_uint_32) strlen((const char *) profile_description);
7372 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7373 + description_length);
7374 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7375 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7376 text[0].key[0]='\0';
7377 (void) ConcatenateMagickString(text[0].key,
7378 "Raw profile type ",MaxTextExtent);
7379 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7383 (void) CopyMagickString(dp,(const char *) profile_description,
7385 dp+=description_length;
7387 (void) FormatLocaleString(dp,allocated_length-
7388 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7391 for (i=0; i < (ssize_t) length; i++)
7395 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7396 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7401 text[0].text_length=(png_size_t) (dp-text[0].text);
7402 text[0].compression=image_info->compression == NoCompression ||
7403 (image_info->compression == UndefinedCompression &&
7404 text[0].text_length < 128) ? -1 : 0;
7406 if (text[0].text_length <= allocated_length)
7407 png_set_text(ping,ping_info,text,1);
7409 png_free(ping,text[0].text);
7410 png_free(ping,text[0].key);
7411 png_free(ping,text);
7414 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7415 const char *string, MagickBooleanType logging)
7428 ResetImageProfileIterator(image);
7430 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7432 profile=GetImageProfile(image,name);
7434 if (profile != (const StringInfo *) NULL)
7439 if (LocaleNCompare(name,string,11) == 0)
7441 if (logging != MagickFalse)
7442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7443 " Found %s profile",name);
7445 ping_profile=CloneStringInfo(profile);
7446 data=GetStringInfoDatum(ping_profile),
7447 length=(png_uint_32) GetStringInfoLength(ping_profile);
7452 (void) WriteBlobMSBULong(image,length-5); /* data length */
7453 (void) WriteBlob(image,length-1,data+1);
7454 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7455 ping_profile=DestroyStringInfo(ping_profile);
7459 name=GetNextImageProfile(image);
7466 /* Write one PNG image */
7467 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7468 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7492 ping_trans_alpha[256];
7520 ping_have_cheap_transparency,
7531 /* ping_exclude_EXIF, */
7534 /* ping_exclude_iTXt, */
7539 /* ping_exclude_tRNS, */
7541 ping_exclude_zCCP, /* hex-encoded iCCP */
7544 ping_preserve_colormap,
7545 ping_need_colortype_warning,
7569 ping_interlace_method,
7570 ping_compression_method,
7587 number_semitransparent,
7589 ping_pHYs_unit_type;
7592 ping_pHYs_x_resolution,
7593 ping_pHYs_y_resolution;
7595 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7596 " Enter WriteOnePNGImage()");
7598 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7599 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7600 if (image_info == (ImageInfo *) NULL)
7601 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7603 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7604 LockSemaphoreInfo(ping_semaphore);
7607 /* Initialize some stuff */
7610 ping_interlace_method=0,
7611 ping_compression_method=0,
7612 ping_filter_method=0,
7615 ping_background.red = 0;
7616 ping_background.green = 0;
7617 ping_background.blue = 0;
7618 ping_background.gray = 0;
7619 ping_background.index = 0;
7621 ping_trans_color.red=0;
7622 ping_trans_color.green=0;
7623 ping_trans_color.blue=0;
7624 ping_trans_color.gray=0;
7626 ping_pHYs_unit_type = 0;
7627 ping_pHYs_x_resolution = 0;
7628 ping_pHYs_y_resolution = 0;
7630 ping_have_blob=MagickFalse;
7631 ping_have_color=MagickTrue;
7632 ping_have_non_bw=MagickTrue;
7633 ping_have_PLTE=MagickFalse;
7634 ping_have_bKGD=MagickFalse;
7635 ping_have_pHYs=MagickFalse;
7636 ping_have_tRNS=MagickFalse;
7638 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7639 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7640 ping_exclude_date=mng_info->ping_exclude_date;
7641 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7642 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7643 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7644 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7645 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7646 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7647 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7648 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7649 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7650 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7651 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7652 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7654 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7655 ping_need_colortype_warning = MagickFalse;
7657 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7658 * i.e., eliminate the ICC profile and set image->rendering_intent.
7659 * Note that this will not involve any changes to the actual pixels
7660 * but merely passes information to applications that read the resulting
7663 if (ping_exclude_sRGB == MagickFalse)
7671 ResetImageProfileIterator(image);
7672 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7674 profile=GetImageProfile(image,name);
7676 if (profile != (StringInfo *) NULL)
7678 if ((LocaleCompare(name,"ICC") == 0) ||
7679 (LocaleCompare(name,"ICM") == 0))
7684 /* 0: not a known sRGB profile
7685 * 1: HP-Microsoft sRGB v2
7686 * 2: ICC sRGB v4 perceptual
7687 * 3: ICC sRGB v2 perceptual no black-compensation
7690 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7691 check_len[4] = {0, 3144, 60960, 3052};
7700 length=(png_uint_32) GetStringInfoLength(profile);
7702 for (icheck=3; icheck > 0; icheck--)
7704 if (length == check_len[icheck])
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " Got a %lu-byte ICC profile (potentially sRGB)",
7708 (unsigned long) length);
7710 data=GetStringInfoDatum(profile);
7711 profile_crc=crc32(0,data,length);
7713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7714 " with crc=%8x",(unsigned int) profile_crc);
7716 if (profile_crc == check_crc[icheck])
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7720 if (image->rendering_intent==UndefinedIntent)
7721 image->rendering_intent=PerceptualIntent;
7727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7728 " Got a %lu-byte ICC profile",
7729 (unsigned long) length);
7732 name=GetNextImageProfile(image);
7737 number_semitransparent = 0;
7738 number_transparent = 0;
7740 if (logging != MagickFalse)
7742 if (image->storage_class == UndefinedClass)
7743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7744 " storage_class=UndefinedClass");
7745 if (image->storage_class == DirectClass)
7746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7747 " storage_class=DirectClass");
7748 if (image->storage_class == PseudoClass)
7749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7750 " storage_class=PseudoClass");
7753 if (image->storage_class == PseudoClass &&
7754 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7755 (mng_info->write_png_colortype != 0 &&
7756 mng_info->write_png_colortype != 4)))
7758 (void) SyncImage(image,exception);
7759 image->storage_class = DirectClass;
7762 if (ping_preserve_colormap == MagickFalse)
7764 if (image->storage_class != PseudoClass && image->colormap != NULL)
7766 /* Free the bogus colormap; it can cause trouble later */
7767 if (logging != MagickFalse)
7768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7769 " Freeing bogus colormap");
7770 (void) RelinquishMagickMemory(image->colormap);
7771 image->colormap=NULL;
7775 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7776 (void) TransformImageColorspace(image,sRGBColorspace,exception);
7779 Sometimes we get PseudoClass images whose RGB values don't match
7780 the colors in the colormap. This code syncs the RGB values.
7782 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7783 (void) SyncImage(image,exception);
7785 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7786 if (image->depth > 8)
7788 if (logging != MagickFalse)
7789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7790 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7796 /* Respect the -depth option */
7797 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7802 if (image->depth > 8)
7804 #if MAGICKCORE_QUANTUM_DEPTH > 16
7805 /* Scale to 16-bit */
7806 LBR16PacketRGBO(image->background_color);
7808 for (y=0; y < (ssize_t) image->rows; y++)
7810 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7812 if (r == (Quantum *) NULL)
7815 for (x=0; x < (ssize_t) image->columns; x++)
7818 r+=GetPixelChannels(image);
7821 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7825 if (image->storage_class == PseudoClass && image->colormap != NULL)
7827 for (i=0; i < (ssize_t) image->colors; i++)
7829 LBR16PacketRGBO(image->colormap[i]);
7832 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7835 else if (image->depth > 4)
7837 #if MAGICKCORE_QUANTUM_DEPTH > 8
7838 /* Scale to 8-bit */
7839 LBR08PacketRGBO(image->background_color);
7841 for (y=0; y < (ssize_t) image->rows; y++)
7843 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7845 if (r == (Quantum *) NULL)
7848 for (x=0; x < (ssize_t) image->columns; x++)
7851 r+=GetPixelChannels(image);
7854 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7858 if (image->storage_class == PseudoClass && image->colormap != NULL)
7860 for (i=0; i < (ssize_t) image->colors; i++)
7862 LBR08PacketRGBO(image->colormap[i]);
7865 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7868 if (image->depth > 2)
7870 /* Scale to 4-bit */
7871 LBR04PacketRGBO(image->background_color);
7873 for (y=0; y < (ssize_t) image->rows; y++)
7875 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7877 if (r == (Quantum *) NULL)
7880 for (x=0; x < (ssize_t) image->columns; x++)
7883 r+=GetPixelChannels(image);
7886 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7890 if (image->storage_class == PseudoClass && image->colormap != NULL)
7892 for (i=0; i < (ssize_t) image->colors; i++)
7894 LBR04PacketRGBO(image->colormap[i]);
7899 else if (image->depth > 1)
7901 /* Scale to 2-bit */
7902 LBR02PacketRGBO(image->background_color);
7904 for (y=0; y < (ssize_t) image->rows; y++)
7906 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7908 if (r == (Quantum *) NULL)
7911 for (x=0; x < (ssize_t) image->columns; x++)
7914 r+=GetPixelChannels(image);
7917 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7921 if (image->storage_class == PseudoClass && image->colormap != NULL)
7923 for (i=0; i < (ssize_t) image->colors; i++)
7925 LBR02PacketRGBO(image->colormap[i]);
7931 /* Scale to 1-bit */
7932 LBR01PacketRGBO(image->background_color);
7934 for (y=0; y < (ssize_t) image->rows; y++)
7936 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7938 if (r == (Quantum *) NULL)
7941 for (x=0; x < (ssize_t) image->columns; x++)
7944 r+=GetPixelChannels(image);
7947 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7951 if (image->storage_class == PseudoClass && image->colormap != NULL)
7953 for (i=0; i < (ssize_t) image->colors; i++)
7955 LBR01PacketRGBO(image->colormap[i]);
7961 /* To do: set to next higher multiple of 8 */
7962 if (image->depth < 8)
7965 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7966 /* PNG does not handle depths greater than 16 so reduce it even
7969 if (image->depth > 8)
7973 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7974 if (image->depth > 8)
7976 /* To do: fill low byte properly */
7980 if (image->depth == 16 && mng_info->write_png_depth != 16)
7981 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
7985 /* Normally we run this just once, but in the case of writing PNG8
7986 * we reduce the transparency to binary and run again, then if there
7987 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7988 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7989 * palette. Then (To do) we take care of a final reduction that is only
7990 * needed if there are still 256 colors present and one of them has both
7991 * transparent and opaque instances.
7994 tried_332 = MagickFalse;
7995 tried_333 = MagickFalse;
7996 tried_444 = MagickFalse;
8002 * Sometimes we get DirectClass images that have 256 colors or fewer.
8003 * This code will build a colormap.
8005 * Also, sometimes we get PseudoClass images with an out-of-date
8006 * colormap. This code will replace the colormap with a new one.
8007 * Sometimes we get PseudoClass images that have more than 256 colors.
8008 * This code will delete the colormap and change the image to
8011 * If image->matte is MagickFalse, we ignore the alpha channel
8012 * even though it sometimes contains left-over non-opaque values.
8014 * Also we gather some information (number of opaque, transparent,
8015 * and semitransparent pixels, and whether the image has any non-gray
8016 * pixels or only black-and-white pixels) that we might need later.
8018 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8019 * we need to check for bogus non-opaque values, at least.
8027 semitransparent[260],
8030 register const Quantum
8037 if (logging != MagickFalse)
8038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8039 " Enter BUILD_PALETTE:");
8041 if (logging != MagickFalse)
8043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8044 " image->columns=%.20g",(double) image->columns);
8045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8046 " image->rows=%.20g",(double) image->rows);
8047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " image->matte=%.20g",(double) image->matte);
8049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8050 " image->depth=%.20g",(double) image->depth);
8052 if (image->storage_class == PseudoClass && image->colormap != NULL)
8054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8055 " Original colormap:");
8056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8057 " i (red,green,blue,alpha)");
8059 for (i=0; i < 256; i++)
8061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8062 " %d (%d,%d,%d,%d)",
8064 (int) image->colormap[i].red,
8065 (int) image->colormap[i].green,
8066 (int) image->colormap[i].blue,
8067 (int) image->colormap[i].alpha);
8070 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8075 " %d (%d,%d,%d,%d)",
8077 (int) image->colormap[i].red,
8078 (int) image->colormap[i].green,
8079 (int) image->colormap[i].blue,
8080 (int) image->colormap[i].alpha);
8085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8086 " image->colors=%d",(int) image->colors);
8088 if (image->colors == 0)
8089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8090 " (zero means unknown)");
8092 if (ping_preserve_colormap == MagickFalse)
8093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " Regenerate the colormap");
8099 number_semitransparent = 0;
8100 number_transparent = 0;
8102 for (y=0; y < (ssize_t) image->rows; y++)
8104 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8106 if (q == (Quantum *) NULL)
8109 for (x=0; x < (ssize_t) image->columns; x++)
8111 if (image->matte == MagickFalse ||
8112 GetPixelAlpha(image,q) == OpaqueAlpha)
8114 if (number_opaque < 259)
8116 if (number_opaque == 0)
8118 GetPixelInfoPixel(image, q, opaque);
8119 opaque[0].alpha=OpaqueAlpha;
8123 for (i=0; i< (ssize_t) number_opaque; i++)
8125 if (IsPixelEquivalent(image,q, opaque+i))
8129 if (i == (ssize_t) number_opaque && number_opaque < 259)
8132 GetPixelInfoPixel(image, q, opaque+i);
8133 opaque[i].alpha=OpaqueAlpha;
8137 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8139 if (number_transparent < 259)
8141 if (number_transparent == 0)
8143 GetPixelInfoPixel(image, q, transparent);
8144 ping_trans_color.red=(unsigned short)
8145 GetPixelRed(image,q);
8146 ping_trans_color.green=(unsigned short)
8147 GetPixelGreen(image,q);
8148 ping_trans_color.blue=(unsigned short)
8149 GetPixelBlue(image,q);
8150 ping_trans_color.gray=(unsigned short)
8151 GetPixelRed(image,q);
8152 number_transparent = 1;
8155 for (i=0; i< (ssize_t) number_transparent; i++)
8157 if (IsPixelEquivalent(image,q, transparent+i))
8161 if (i == (ssize_t) number_transparent &&
8162 number_transparent < 259)
8164 number_transparent++;
8165 GetPixelInfoPixel(image,q,transparent+i);
8171 if (number_semitransparent < 259)
8173 if (number_semitransparent == 0)
8175 GetPixelInfoPixel(image,q,semitransparent);
8176 number_semitransparent = 1;
8179 for (i=0; i< (ssize_t) number_semitransparent; i++)
8181 if (IsPixelEquivalent(image,q, semitransparent+i)
8182 && GetPixelAlpha(image,q) ==
8183 semitransparent[i].alpha)
8187 if (i == (ssize_t) number_semitransparent &&
8188 number_semitransparent < 259)
8190 number_semitransparent++;
8191 GetPixelInfoPixel(image, q, semitransparent+i);
8195 q+=GetPixelChannels(image);
8199 if (mng_info->write_png8 == MagickFalse &&
8200 ping_exclude_bKGD == MagickFalse)
8202 /* Add the background color to the palette, if it
8203 * isn't already there.
8205 if (logging != MagickFalse)
8207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8208 " Check colormap for background (%d,%d,%d)",
8209 (int) image->background_color.red,
8210 (int) image->background_color.green,
8211 (int) image->background_color.blue);
8213 for (i=0; i<number_opaque; i++)
8215 if (opaque[i].red == image->background_color.red &&
8216 opaque[i].green == image->background_color.green &&
8217 opaque[i].blue == image->background_color.blue)
8220 if (number_opaque < 259 && i == number_opaque)
8222 opaque[i] = image->background_color;
8223 ping_background.index = i;
8224 if (logging != MagickFalse)
8226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8227 " background_color index is %d",(int) i);
8231 else if (logging != MagickFalse)
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " No room in the colormap to add background color");
8236 image_colors=number_opaque+number_transparent+number_semitransparent;
8238 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8240 /* No room for the background color; remove it. */
8245 if (logging != MagickFalse)
8247 if (image_colors > 256)
8248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8249 " image has more than 256 colors");
8252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8253 " image has %d colors",image_colors);
8256 if (ping_preserve_colormap != MagickFalse)
8259 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8261 ping_have_color=MagickFalse;
8262 ping_have_non_bw=MagickFalse;
8264 if(image_colors > 256)
8266 for (y=0; y < (ssize_t) image->rows; y++)
8268 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8270 if (q == (Quantum *) NULL)
8274 for (x=0; x < (ssize_t) image->columns; x++)
8276 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8277 GetPixelRed(image,s) != GetPixelBlue(image,s))
8279 ping_have_color=MagickTrue;
8280 ping_have_non_bw=MagickTrue;
8283 s+=GetPixelChannels(image);
8286 if (ping_have_color != MagickFalse)
8289 /* Worst case is black-and-white; we are looking at every
8293 if (ping_have_non_bw == MagickFalse)
8296 for (x=0; x < (ssize_t) image->columns; x++)
8298 if (GetPixelRed(image,s) != 0 &&
8299 GetPixelRed(image,s) != QuantumRange)
8301 ping_have_non_bw=MagickTrue;
8304 s+=GetPixelChannels(image);
8311 if (image_colors < 257)
8317 * Initialize image colormap.
8320 if (logging != MagickFalse)
8321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8322 " Sort the new colormap");
8324 /* Sort palette, transparent first */;
8328 for (i=0; i<number_transparent; i++)
8329 colormap[n++] = transparent[i];
8331 for (i=0; i<number_semitransparent; i++)
8332 colormap[n++] = semitransparent[i];
8334 for (i=0; i<number_opaque; i++)
8335 colormap[n++] = opaque[i];
8337 ping_background.index +=
8338 (number_transparent + number_semitransparent);
8340 /* image_colors < 257; search the colormap instead of the pixels
8341 * to get ping_have_color and ping_have_non_bw
8345 if (ping_have_color == MagickFalse)
8347 if (colormap[i].red != colormap[i].green ||
8348 colormap[i].red != colormap[i].blue)
8350 ping_have_color=MagickTrue;
8351 ping_have_non_bw=MagickTrue;
8356 if (ping_have_non_bw == MagickFalse)
8358 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8359 ping_have_non_bw=MagickTrue;
8363 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8364 (number_transparent == 0 && number_semitransparent == 0)) &&
8365 (((mng_info->write_png_colortype-1) ==
8366 PNG_COLOR_TYPE_PALETTE) ||
8367 (mng_info->write_png_colortype == 0)))
8369 if (logging != MagickFalse)
8371 if (n != (ssize_t) image_colors)
8372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8373 " image_colors (%d) and n (%d) don't match",
8376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8377 " AcquireImageColormap");
8380 image->colors = image_colors;
8382 if (AcquireImageColormap(image,image_colors,exception) ==
8384 ThrowWriterException(ResourceLimitError,
8385 "MemoryAllocationFailed");
8387 for (i=0; i< (ssize_t) image_colors; i++)
8388 image->colormap[i] = colormap[i];
8390 if (logging != MagickFalse)
8392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8393 " image->colors=%d (%d)",
8394 (int) image->colors, image_colors);
8396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " Update the pixel indexes");
8400 /* Sync the pixel indices with the new colormap */
8402 for (y=0; y < (ssize_t) image->rows; y++)
8404 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8406 if (q == (Quantum *) NULL)
8409 for (x=0; x < (ssize_t) image->columns; x++)
8411 for (i=0; i< (ssize_t) image_colors; i++)
8413 if ((image->matte == MagickFalse ||
8414 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8415 image->colormap[i].red == GetPixelRed(image,q) &&
8416 image->colormap[i].green == GetPixelGreen(image,q) &&
8417 image->colormap[i].blue == GetPixelBlue(image,q))
8419 SetPixelIndex(image,i,q);
8423 q+=GetPixelChannels(image);
8426 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8432 if (logging != MagickFalse)
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " image->colors=%d", (int) image->colors);
8437 if (image->colormap != NULL)
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8440 " i (red,green,blue,alpha)");
8442 for (i=0; i < (ssize_t) image->colors; i++)
8444 if (i < 300 || i >= (ssize_t) image->colors - 10)
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " %d (%d,%d,%d,%d)",
8449 (int) image->colormap[i].red,
8450 (int) image->colormap[i].green,
8451 (int) image->colormap[i].blue,
8452 (int) image->colormap[i].alpha);
8457 if (number_transparent < 257)
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " number_transparent = %d",
8460 number_transparent);
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " number_transparent > 256");
8466 if (number_opaque < 257)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " number_opaque = %d",
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " number_opaque > 256");
8475 if (number_semitransparent < 257)
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477 " number_semitransparent = %d",
8478 number_semitransparent);
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " number_semitransparent > 256");
8484 if (ping_have_non_bw == MagickFalse)
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " All pixels and the background are black or white");
8488 else if (ping_have_color == MagickFalse)
8489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8490 " All pixels and the background are gray");
8493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8494 " At least one pixel or the background is non-gray");
8496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " Exit BUILD_PALETTE:");
8500 if (mng_info->write_png8 == MagickFalse)
8503 /* Make any reductions necessary for the PNG8 format */
8504 if (image_colors <= 256 &&
8505 image_colors != 0 && image->colormap != NULL &&
8506 number_semitransparent == 0 &&
8507 number_transparent <= 1)
8510 /* PNG8 can't have semitransparent colors so we threshold the
8511 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8512 * transparent color so if more than one is transparent we merge
8513 * them into image->background_color.
8515 if (number_semitransparent != 0 || number_transparent > 1)
8517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " Thresholding the alpha channel to binary");
8520 for (y=0; y < (ssize_t) image->rows; y++)
8522 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8524 if (r == (Quantum *) NULL)
8527 for (x=0; x < (ssize_t) image->columns; x++)
8529 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8531 SetPixelInfoPixel(image,&image->background_color,r);
8532 SetPixelAlpha(image,TransparentAlpha,r);
8535 SetPixelAlpha(image,OpaqueAlpha,r);
8536 r+=GetPixelChannels(image);
8539 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8542 if (image_colors != 0 && image_colors <= 256 &&
8543 image->colormap != NULL)
8544 for (i=0; i<image_colors; i++)
8545 image->colormap[i].alpha =
8546 (image->colormap[i].alpha > TransparentAlpha/2 ?
8547 TransparentAlpha : OpaqueAlpha);
8552 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8553 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8554 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8557 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8559 if (logging != MagickFalse)
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8561 " Quantizing the background color to 4-4-4");
8563 tried_444 = MagickTrue;
8565 LBR04PacketRGB(image->background_color);
8567 if (logging != MagickFalse)
8568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8569 " Quantizing the pixel colors to 4-4-4");
8571 if (image->colormap == NULL)
8573 for (y=0; y < (ssize_t) image->rows; y++)
8575 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8577 if (r == (Quantum *) NULL)
8580 for (x=0; x < (ssize_t) image->columns; x++)
8582 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8584 r+=GetPixelChannels(image);
8587 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8592 else /* Should not reach this; colormap already exists and
8595 if (logging != MagickFalse)
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " Quantizing the colormap to 4-4-4");
8599 for (i=0; i<image_colors; i++)
8601 LBR04PacketRGB(image->colormap[i]);
8607 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Quantizing the background color to 3-3-3");
8613 tried_333 = MagickTrue;
8615 LBR03PacketRGB(image->background_color);
8617 if (logging != MagickFalse)
8618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8619 " Quantizing the pixel colors to 3-3-3-1");
8621 if (image->colormap == NULL)
8623 for (y=0; y < (ssize_t) image->rows; y++)
8625 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8627 if (r == (Quantum *) NULL)
8630 for (x=0; x < (ssize_t) image->columns; x++)
8632 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8634 r+=GetPixelChannels(image);
8637 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8642 else /* Should not reach this; colormap already exists and
8645 if (logging != MagickFalse)
8646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8647 " Quantizing the colormap to 3-3-3-1");
8648 for (i=0; i<image_colors; i++)
8650 LBR03PacketRGB(image->colormap[i]);
8656 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8658 if (logging != MagickFalse)
8659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8660 " Quantizing the background color to 3-3-2");
8662 tried_332 = MagickTrue;
8664 /* Red and green were already done so we only quantize the blue
8668 LBR02PacketBlue(image->background_color);
8670 if (logging != MagickFalse)
8671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8672 " Quantizing the pixel colors to 3-3-2-1");
8674 if (image->colormap == NULL)
8676 for (y=0; y < (ssize_t) image->rows; y++)
8678 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8680 if (r == (Quantum *) NULL)
8683 for (x=0; x < (ssize_t) image->columns; x++)
8685 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8687 r+=GetPixelChannels(image);
8690 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8695 else /* Should not reach this; colormap already exists and
8698 if (logging != MagickFalse)
8699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8700 " Quantizing the colormap to 3-3-2-1");
8701 for (i=0; i<image_colors; i++)
8703 LBR02PacketBlue(image->colormap[i]);
8710 if (image_colors == 0 || image_colors > 256)
8712 /* Take care of special case with 256 colors + 1 transparent
8713 * color. We don't need to quantize to 2-3-2-1; we only need to
8714 * eliminate one color, so we'll merge the two darkest red
8715 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8717 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8718 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8719 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8721 image->background_color.red=ScaleCharToQuantum(0x24);
8724 if (image->colormap == NULL)
8726 for (y=0; y < (ssize_t) image->rows; y++)
8728 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8730 if (r == (Quantum *) NULL)
8733 for (x=0; x < (ssize_t) image->columns; x++)
8735 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8736 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8737 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8738 GetPixelAlpha(image,r) == OpaqueAlpha)
8740 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8742 r+=GetPixelChannels(image);
8745 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8753 for (i=0; i<image_colors; i++)
8755 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8756 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8757 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8759 image->colormap[i].red=ScaleCharToQuantum(0x24);
8765 /* END OF BUILD_PALETTE */
8767 /* If we are excluding the tRNS chunk and there is transparency,
8768 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8771 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8772 (number_transparent != 0 || number_semitransparent != 0))
8774 unsigned int colortype=mng_info->write_png_colortype;
8776 if (ping_have_color == MagickFalse)
8777 mng_info->write_png_colortype = 5;
8780 mng_info->write_png_colortype = 7;
8782 if (colortype != 0 &&
8783 mng_info->write_png_colortype != colortype)
8784 ping_need_colortype_warning=MagickTrue;
8788 /* See if cheap transparency is possible. It is only possible
8789 * when there is a single transparent color, no semitransparent
8790 * color, and no opaque color that has the same RGB components
8791 * as the transparent color. We only need this information if
8792 * we are writing a PNG with colortype 0 or 2, and we have not
8793 * excluded the tRNS chunk.
8795 if (number_transparent == 1 &&
8796 mng_info->write_png_colortype < 4)
8798 ping_have_cheap_transparency = MagickTrue;
8800 if (number_semitransparent != 0)
8801 ping_have_cheap_transparency = MagickFalse;
8803 else if (image_colors == 0 || image_colors > 256 ||
8804 image->colormap == NULL)
8806 register const Quantum
8809 for (y=0; y < (ssize_t) image->rows; y++)
8811 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8813 if (q == (Quantum *) NULL)
8816 for (x=0; x < (ssize_t) image->columns; x++)
8818 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8819 (unsigned short) GetPixelRed(image,q) ==
8820 ping_trans_color.red &&
8821 (unsigned short) GetPixelGreen(image,q) ==
8822 ping_trans_color.green &&
8823 (unsigned short) GetPixelBlue(image,q) ==
8824 ping_trans_color.blue)
8826 ping_have_cheap_transparency = MagickFalse;
8830 q+=GetPixelChannels(image);
8833 if (ping_have_cheap_transparency == MagickFalse)
8839 /* Assuming that image->colormap[0] is the one transparent color
8840 * and that all others are opaque.
8842 if (image_colors > 1)
8843 for (i=1; i<image_colors; i++)
8844 if (image->colormap[i].red == image->colormap[0].red &&
8845 image->colormap[i].green == image->colormap[0].green &&
8846 image->colormap[i].blue == image->colormap[0].blue)
8848 ping_have_cheap_transparency = MagickFalse;
8853 if (logging != MagickFalse)
8855 if (ping_have_cheap_transparency == MagickFalse)
8856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857 " Cheap transparency is not possible.");
8860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8861 " Cheap transparency is possible.");
8865 ping_have_cheap_transparency = MagickFalse;
8867 image_depth=image->depth;
8869 quantum_info = (QuantumInfo *) NULL;
8871 image_colors=(int) image->colors;
8872 image_matte=image->matte;
8874 mng_info->IsPalette=image->storage_class == PseudoClass &&
8875 image_colors <= 256 && image->colormap != NULL;
8877 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8878 (image->colors == 0 || image->colormap == NULL))
8880 image_info=DestroyImageInfo(image_info);
8881 image=DestroyImage(image);
8882 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8883 "Cannot write PNG8 or color-type 3; colormap is NULL",
8884 "`%s'",IMimage->filename);
8885 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8886 UnlockSemaphoreInfo(ping_semaphore);
8888 return(MagickFalse);
8892 Allocate the PNG structures
8894 #ifdef PNG_USER_MEM_SUPPORTED
8895 error_info.image=image;
8896 error_info.exception=exception;
8897 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
8898 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8899 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8902 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
8903 MagickPNGErrorHandler,MagickPNGWarningHandler);
8906 if (ping == (png_struct *) NULL)
8907 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8909 ping_info=png_create_info_struct(ping);
8911 if (ping_info == (png_info *) NULL)
8913 png_destroy_write_struct(&ping,(png_info **) NULL);
8914 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8917 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8918 ping_pixels=(unsigned char *) NULL;
8920 if (setjmp(png_jmpbuf(ping)))
8926 if (image_info->verbose)
8927 (void) printf("PNG write has failed.\n");
8929 png_destroy_write_struct(&ping,&ping_info);
8930 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8931 UnlockSemaphoreInfo(ping_semaphore);
8933 if (ping_have_blob != MagickFalse)
8934 (void) CloseBlob(image);
8935 image_info=DestroyImageInfo(image_info);
8936 image=DestroyImage(image);
8937 return(MagickFalse);
8940 Prepare PNG for writing.
8942 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8943 if (mng_info->write_mng)
8944 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8947 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8948 if (mng_info->write_mng)
8949 png_permit_empty_plte(ping,MagickTrue);
8956 ping_width=(png_uint_32) image->columns;
8957 ping_height=(png_uint_32) image->rows;
8959 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8962 if (mng_info->write_png_depth != 0)
8963 image_depth=mng_info->write_png_depth;
8965 /* Adjust requested depth to next higher valid depth if necessary */
8966 if (image_depth > 8)
8969 if ((image_depth > 4) && (image_depth < 8))
8972 if (image_depth == 3)
8975 if (logging != MagickFalse)
8977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8978 " width=%.20g",(double) ping_width);
8979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8980 " height=%.20g",(double) ping_height);
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " image_matte=%.20g",(double) image->matte);
8983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984 " image->depth=%.20g",(double) image->depth);
8985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8986 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8989 save_image_depth=image_depth;
8990 ping_bit_depth=(png_byte) save_image_depth;
8993 #if defined(PNG_pHYs_SUPPORTED)
8994 if (ping_exclude_pHYs == MagickFalse)
8996 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
8997 (!mng_info->write_mng || !mng_info->equal_physs))
8999 if (logging != MagickFalse)
9000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001 " Setting up pHYs chunk");
9003 if (image->units == PixelsPerInchResolution)
9005 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9006 ping_pHYs_x_resolution=
9007 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9008 ping_pHYs_y_resolution=
9009 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9012 else if (image->units == PixelsPerCentimeterResolution)
9014 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9015 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9016 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9021 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9022 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9023 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9026 if (logging != MagickFalse)
9027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9028 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9029 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9030 (int) ping_pHYs_unit_type);
9031 ping_have_pHYs = MagickTrue;
9036 if (ping_exclude_bKGD == MagickFalse)
9038 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9044 if (ping_bit_depth == 8)
9047 if (ping_bit_depth == 4)
9050 if (ping_bit_depth == 2)
9053 if (ping_bit_depth == 1)
9056 ping_background.red=(png_uint_16)
9057 (ScaleQuantumToShort(image->background_color.red) & mask);
9059 ping_background.green=(png_uint_16)
9060 (ScaleQuantumToShort(image->background_color.green) & mask);
9062 ping_background.blue=(png_uint_16)
9063 (ScaleQuantumToShort(image->background_color.blue) & mask);
9065 ping_background.gray=(png_uint_16) ping_background.green;
9068 if (logging != MagickFalse)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Setting up bKGD chunk (1)");
9072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073 " background_color index is %d",
9074 (int) ping_background.index);
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " ping_bit_depth=%d",ping_bit_depth);
9080 ping_have_bKGD = MagickTrue;
9084 Select the color type.
9089 if (mng_info->IsPalette && mng_info->write_png8)
9092 /* To do: make this a function cause it's used twice, except
9093 for reducing the sample depth from 8. */
9095 number_colors=image_colors;
9097 ping_have_tRNS=MagickFalse;
9102 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9104 if (logging != MagickFalse)
9105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106 " Setting up PLTE chunk with %d colors (%d)",
9107 number_colors, image_colors);
9109 for (i=0; i < (ssize_t) number_colors; i++)
9111 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9112 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9113 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9114 if (logging != MagickFalse)
9115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9116 #if MAGICKCORE_QUANTUM_DEPTH == 8
9117 " %3ld (%3d,%3d,%3d)",
9119 " %5ld (%5d,%5d,%5d)",
9121 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9125 ping_have_PLTE=MagickTrue;
9126 image_depth=ping_bit_depth;
9129 if (matte != MagickFalse)
9132 Identify which colormap entry is transparent.
9134 assert(number_colors <= 256);
9135 assert(image->colormap != NULL);
9137 for (i=0; i < (ssize_t) number_transparent; i++)
9138 ping_trans_alpha[i]=0;
9141 ping_num_trans=(unsigned short) (number_transparent +
9142 number_semitransparent);
9144 if (ping_num_trans == 0)
9145 ping_have_tRNS=MagickFalse;
9148 ping_have_tRNS=MagickTrue;
9151 if (ping_exclude_bKGD == MagickFalse)
9154 * Identify which colormap entry is the background color.
9157 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9158 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9161 ping_background.index=(png_byte) i;
9163 if (logging != MagickFalse)
9165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9166 " background_color index is %d",
9167 (int) ping_background.index);
9170 } /* end of write_png8 */
9172 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9174 image_matte=MagickFalse;
9175 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9178 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9180 image_matte=MagickTrue;
9181 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9184 else /* mng_info->write_pngNN not specified */
9186 image_depth=ping_bit_depth;
9188 if (mng_info->write_png_colortype != 0)
9190 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9192 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9193 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9194 image_matte=MagickTrue;
9197 image_matte=MagickFalse;
9199 if (logging != MagickFalse)
9200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201 " PNG colortype %d was specified:",(int) ping_color_type);
9204 else /* write_png_colortype not specified */
9206 if (logging != MagickFalse)
9207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9208 " Selecting PNG colortype:");
9210 ping_color_type=(png_byte) ((matte != MagickFalse)?
9211 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9213 if (image_info->type == TrueColorType)
9215 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9216 image_matte=MagickFalse;
9219 if (image_info->type == TrueColorMatteType)
9221 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9222 image_matte=MagickTrue;
9225 if (image_info->type == PaletteType ||
9226 image_info->type == PaletteMatteType)
9227 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9229 if (mng_info->write_png_colortype == 0 &&
9230 (image_info->type == UndefinedType ||
9231 image_info->type == OptimizeType))
9233 if (ping_have_color == MagickFalse)
9235 if (image_matte == MagickFalse)
9237 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9238 image_matte=MagickFalse;
9243 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9244 image_matte=MagickTrue;
9249 if (image_matte == MagickFalse)
9251 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9252 image_matte=MagickFalse;
9257 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9258 image_matte=MagickTrue;
9265 if (logging != MagickFalse)
9266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9267 " Selected PNG colortype=%d",ping_color_type);
9269 if (ping_bit_depth < 8)
9271 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9272 ping_color_type == PNG_COLOR_TYPE_RGB ||
9273 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9277 old_bit_depth=ping_bit_depth;
9279 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9281 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9285 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9290 if (image->colors == 0)
9293 (void) ThrowMagickException(exception,
9294 GetMagickModule(),CoderError,
9295 "image has 0 colors", "`%s'","");
9298 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9299 ping_bit_depth <<= 1;
9302 if (logging != MagickFalse)
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305 " Number of colors: %.20g",(double) image_colors);
9307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9308 " Tentative PNG bit depth: %d",ping_bit_depth);
9311 if (ping_bit_depth < (int) mng_info->write_png_depth)
9312 ping_bit_depth = mng_info->write_png_depth;
9315 image_depth=ping_bit_depth;
9317 if (logging != MagickFalse)
9319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9320 " Tentative PNG color type: %.20g",(double) ping_color_type);
9322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9323 " image_info->type: %.20g",(double) image_info->type);
9325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9326 " image_depth: %.20g",(double) image_depth);
9328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9330 " image->depth: %.20g",(double) image->depth);
9332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9333 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9336 if (matte != MagickFalse)
9338 if (mng_info->IsPalette)
9340 if (mng_info->write_png_colortype == 0)
9342 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9344 if (ping_have_color != MagickFalse)
9345 ping_color_type=PNG_COLOR_TYPE_RGBA;
9349 * Determine if there is any transparent color.
9351 if (number_transparent + number_semitransparent == 0)
9354 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9357 image_matte=MagickFalse;
9359 if (mng_info->write_png_colortype == 0)
9360 ping_color_type&=0x03;
9370 if (ping_bit_depth == 8)
9373 if (ping_bit_depth == 4)
9376 if (ping_bit_depth == 2)
9379 if (ping_bit_depth == 1)
9382 ping_trans_color.red=(png_uint_16)
9383 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9385 ping_trans_color.green=(png_uint_16)
9386 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9388 ping_trans_color.blue=(png_uint_16)
9389 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9391 ping_trans_color.gray=(png_uint_16)
9392 (ScaleQuantumToShort(GetPixelInfoIntensity(
9393 image->colormap)) & mask);
9395 ping_trans_color.index=(png_byte) 0;
9397 ping_have_tRNS=MagickTrue;
9400 if (ping_have_tRNS != MagickFalse)
9403 * Determine if there is one and only one transparent color
9404 * and if so if it is fully transparent.
9406 if (ping_have_cheap_transparency == MagickFalse)
9407 ping_have_tRNS=MagickFalse;
9410 if (ping_have_tRNS != MagickFalse)
9412 if (mng_info->write_png_colortype == 0)
9413 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9415 if (image_depth == 8)
9417 ping_trans_color.red&=0xff;
9418 ping_trans_color.green&=0xff;
9419 ping_trans_color.blue&=0xff;
9420 ping_trans_color.gray&=0xff;
9426 if (image_depth == 8)
9428 ping_trans_color.red&=0xff;
9429 ping_trans_color.green&=0xff;
9430 ping_trans_color.blue&=0xff;
9431 ping_trans_color.gray&=0xff;
9438 if (ping_have_tRNS != MagickFalse)
9439 image_matte=MagickFalse;
9441 if ((mng_info->IsPalette) &&
9442 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9443 ping_have_color == MagickFalse &&
9444 (image_matte == MagickFalse || image_depth >= 8))
9448 if (image_matte != MagickFalse)
9449 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9451 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9453 ping_color_type=PNG_COLOR_TYPE_GRAY;
9455 if (save_image_depth == 16 && image_depth == 8)
9457 if (logging != MagickFalse)
9459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9460 " Scaling ping_trans_color (0)");
9462 ping_trans_color.gray*=0x0101;
9466 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9467 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9469 if ((image_colors == 0) ||
9470 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9471 image_colors=(int) (one << image_depth);
9473 if (image_depth > 8)
9479 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9481 if(!mng_info->write_png_depth)
9485 while ((int) (one << ping_bit_depth)
9486 < (ssize_t) image_colors)
9487 ping_bit_depth <<= 1;
9491 else if (ping_color_type ==
9492 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9493 mng_info->IsPalette)
9495 /* Check if grayscale is reducible */
9498 depth_4_ok=MagickTrue,
9499 depth_2_ok=MagickTrue,
9500 depth_1_ok=MagickTrue;
9502 for (i=0; i < (ssize_t) image_colors; i++)
9507 intensity=ScaleQuantumToChar(image->colormap[i].red);
9509 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9510 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9511 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9512 depth_2_ok=depth_1_ok=MagickFalse;
9513 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9514 depth_1_ok=MagickFalse;
9517 if (depth_1_ok && mng_info->write_png_depth <= 1)
9520 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9523 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9528 image_depth=ping_bit_depth;
9533 if (mng_info->IsPalette)
9535 number_colors=image_colors;
9537 if (image_depth <= 8)
9542 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9544 if (mng_info->have_write_global_plte && matte == MagickFalse)
9546 png_set_PLTE(ping,ping_info,NULL,0);
9548 if (logging != MagickFalse)
9549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9550 " Setting up empty PLTE chunk");
9555 for (i=0; i < (ssize_t) number_colors; i++)
9557 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9558 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9559 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9562 if (logging != MagickFalse)
9563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9564 " Setting up PLTE chunk with %d colors",
9567 ping_have_PLTE=MagickTrue;
9570 /* color_type is PNG_COLOR_TYPE_PALETTE */
9571 if (mng_info->write_png_depth == 0)
9579 while ((one << ping_bit_depth) < (size_t) number_colors)
9580 ping_bit_depth <<= 1;
9585 if (matte != MagickFalse)
9588 * Set up trans_colors array.
9590 assert(number_colors <= 256);
9592 ping_num_trans=(unsigned short) (number_transparent +
9593 number_semitransparent);
9595 if (ping_num_trans == 0)
9596 ping_have_tRNS=MagickFalse;
9600 if (logging != MagickFalse)
9602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Scaling ping_trans_color (1)");
9605 ping_have_tRNS=MagickTrue;
9607 for (i=0; i < ping_num_trans; i++)
9609 ping_trans_alpha[i]= (png_byte)
9610 ScaleQuantumToChar(image->colormap[i].alpha);
9620 if (image_depth < 8)
9623 if ((save_image_depth == 16) && (image_depth == 8))
9625 if (logging != MagickFalse)
9627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628 " Scaling ping_trans_color from (%d,%d,%d)",
9629 (int) ping_trans_color.red,
9630 (int) ping_trans_color.green,
9631 (int) ping_trans_color.blue);
9634 ping_trans_color.red*=0x0101;
9635 ping_trans_color.green*=0x0101;
9636 ping_trans_color.blue*=0x0101;
9637 ping_trans_color.gray*=0x0101;
9639 if (logging != MagickFalse)
9641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9643 (int) ping_trans_color.red,
9644 (int) ping_trans_color.green,
9645 (int) ping_trans_color.blue);
9650 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9651 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9654 Adjust background and transparency samples in sub-8-bit grayscale files.
9656 if (ping_bit_depth < 8 && ping_color_type ==
9657 PNG_COLOR_TYPE_GRAY)
9665 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9667 if (ping_exclude_bKGD == MagickFalse)
9670 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9671 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9672 &image->background_color))) +.5)));
9674 if (logging != MagickFalse)
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676 " Setting up bKGD chunk (2)");
9677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9678 " background_color index is %d",
9679 (int) ping_background.index);
9681 ping_have_bKGD = MagickTrue;
9684 if (logging != MagickFalse)
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " Scaling ping_trans_color.gray from %d",
9687 (int)ping_trans_color.gray);
9689 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9690 ping_trans_color.gray)+.5);
9692 if (logging != MagickFalse)
9693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694 " to %d", (int)ping_trans_color.gray);
9697 if (ping_exclude_bKGD == MagickFalse)
9699 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9702 Identify which colormap entry is the background color.
9705 number_colors=image_colors;
9707 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9708 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9711 ping_background.index=(png_byte) i;
9713 if (logging != MagickFalse)
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 " Setting up bKGD chunk with index=%d",(int) i);
9719 if (i < (ssize_t) number_colors)
9721 ping_have_bKGD = MagickTrue;
9723 if (logging != MagickFalse)
9725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9726 " background =(%d,%d,%d)",
9727 (int) ping_background.red,
9728 (int) ping_background.green,
9729 (int) ping_background.blue);
9733 else /* Can't happen */
9735 if (logging != MagickFalse)
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " No room in PLTE to add bKGD color");
9738 ping_have_bKGD = MagickFalse;
9743 if (logging != MagickFalse)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " PNG color type: %d",ping_color_type);
9747 Initialize compression level and filtering.
9749 if (logging != MagickFalse)
9751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9752 " Setting up deflate compression");
9754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755 " Compression buffer size: 32768");
9758 png_set_compression_buffer_size(ping,32768L);
9760 if (logging != MagickFalse)
9761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9762 " Compression mem level: 9");
9764 png_set_compression_mem_level(ping, 9);
9766 /* Untangle the "-quality" setting:
9768 Undefined is 0; the default is used.
9773 0: Use Z_HUFFMAN_ONLY strategy with the
9774 zlib default compression level
9776 1-9: the zlib compression level
9780 0-4: the PNG filter method
9782 5: libpng adaptive filtering if compression level > 5
9783 libpng filter type "none" if compression level <= 5
9784 or if image is grayscale or palette
9786 6: libpng adaptive filtering
9788 7: "LOCO" filtering (intrapixel differing) if writing
9789 a MNG, othewise "none". Did not work in IM-6.7.0-9
9790 and earlier because of a missing "else".
9792 8: Z_RLE strategy, all filters
9793 Unused prior to IM-6.7.0-10, was same as 6
9795 9: Z_RLE strategy, no PNG filters
9796 Unused prior to IM-6.7.0-10, was same as 6
9798 Note that using the -quality option, not all combinations of
9799 PNG filter type, zlib compression level, and zlib compression
9800 strategy are possible. This will be addressed soon in a
9801 release that accomodates "-define png:compression-strategy", etc.
9805 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9810 if (mng_info->write_png_compression_strategy == 0)
9811 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9814 else if (mng_info->write_png_compression_level == 0)
9819 level=(int) MagickMin((ssize_t) quality/10,9);
9821 mng_info->write_png_compression_level = level+1;
9824 if (mng_info->write_png_compression_strategy == 0)
9826 if ((quality %10) == 8 || (quality %10) == 9)
9827 mng_info->write_png_compression_strategy=Z_RLE;
9830 if (mng_info->write_png_compression_filter == 0)
9831 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9833 if (logging != MagickFalse)
9835 if (mng_info->write_png_compression_level)
9836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Compression level: %d",
9838 (int) mng_info->write_png_compression_level-1);
9840 if (mng_info->write_png_compression_strategy)
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842 " Compression strategy: %d",
9843 (int) mng_info->write_png_compression_strategy-1);
9845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " Setting up filtering");
9848 if (mng_info->write_png_compression_filter == 6)
9849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9850 " Base filter method: ADAPTIVE");
9851 else if (mng_info->write_png_compression_filter == 0 ||
9852 mng_info->write_png_compression_filter == 1)
9853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 " Base filter method: NONE");
9856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9857 " Base filter method: %d",
9858 (int) mng_info->write_png_compression_filter-1);
9861 if (mng_info->write_png_compression_level != 0)
9862 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9864 if (mng_info->write_png_compression_filter == 6)
9866 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9867 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9869 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9871 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9873 else if (mng_info->write_png_compression_filter == 7 ||
9874 mng_info->write_png_compression_filter == 10)
9875 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9877 else if (mng_info->write_png_compression_filter == 8)
9879 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9880 if (mng_info->write_mng)
9882 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9883 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9884 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9887 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9890 else if (mng_info->write_png_compression_filter == 9)
9891 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9893 else if (mng_info->write_png_compression_filter != 0)
9894 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9895 mng_info->write_png_compression_filter-1);
9897 if (mng_info->write_png_compression_strategy != 0)
9898 png_set_compression_strategy(ping,
9899 mng_info->write_png_compression_strategy-1);
9901 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9902 if (ping_exclude_sRGB != MagickFalse ||
9903 (image->rendering_intent == UndefinedIntent))
9905 if ((ping_exclude_tEXt == MagickFalse ||
9906 ping_exclude_zTXt == MagickFalse) &&
9907 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9909 ResetImageProfileIterator(image);
9910 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9912 profile=GetImageProfile(image,name);
9914 if (profile != (StringInfo *) NULL)
9916 #ifdef PNG_WRITE_iCCP_SUPPORTED
9917 if ((LocaleCompare(name,"ICC") == 0) ||
9918 (LocaleCompare(name,"ICM") == 0))
9921 if (ping_exclude_iCCP == MagickFalse)
9923 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9924 #if (PNG_LIBPNG_VER < 10500)
9925 (png_charp) GetStringInfoDatum(profile),
9927 (png_const_bytep) GetStringInfoDatum(profile),
9929 (png_uint_32) GetStringInfoLength(profile));
9935 if (ping_exclude_zCCP == MagickFalse)
9937 Magick_png_write_raw_profile(image_info,ping,ping_info,
9938 (unsigned char *) name,(unsigned char *) name,
9939 GetStringInfoDatum(profile),
9940 (png_uint_32) GetStringInfoLength(profile));
9944 if (logging != MagickFalse)
9945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9946 " Setting up text chunk with %s profile",name);
9948 name=GetNextImageProfile(image);
9953 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9954 if ((mng_info->have_write_global_srgb == 0) &&
9955 ((image->rendering_intent != UndefinedIntent) ||
9956 (image->colorspace == sRGBColorspace)))
9958 if (ping_exclude_sRGB == MagickFalse)
9961 Note image rendering intent.
9963 if (logging != MagickFalse)
9964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9965 " Setting up sRGB chunk");
9967 (void) png_set_sRGB(ping,ping_info,(
9968 Magick_RenderingIntent_to_PNG_RenderingIntent(
9969 image->rendering_intent)));
9973 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9976 if (ping_exclude_gAMA == MagickFalse &&
9977 (ping_exclude_sRGB == MagickFalse ||
9978 (image->gamma < .45 || image->gamma > .46)))
9980 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9984 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9986 if (logging != MagickFalse)
9987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9988 " Setting up gAMA chunk");
9990 png_set_gAMA(ping,ping_info,image->gamma);
9994 if (ping_exclude_cHRM == MagickFalse)
9996 if ((mng_info->have_write_global_chrm == 0) &&
9997 (image->chromaticity.red_primary.x != 0.0))
10000 Note image chromaticity.
10001 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10009 wp=image->chromaticity.white_point;
10010 rp=image->chromaticity.red_primary;
10011 gp=image->chromaticity.green_primary;
10012 bp=image->chromaticity.blue_primary;
10014 if (logging != MagickFalse)
10015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10016 " Setting up cHRM chunk");
10018 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10024 ping_interlace_method=image_info->interlace != NoInterlace;
10026 if (mng_info->write_mng)
10027 png_set_sig_bytes(ping,8);
10029 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10031 if (mng_info->write_png_colortype != 0)
10033 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10034 if (ping_have_color != MagickFalse)
10036 ping_color_type = PNG_COLOR_TYPE_RGB;
10038 if (ping_bit_depth < 8)
10042 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10043 if (ping_have_color != MagickFalse)
10044 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10047 if (ping_need_colortype_warning != MagickFalse ||
10048 ((mng_info->write_png_depth &&
10049 (int) mng_info->write_png_depth != ping_bit_depth) ||
10050 (mng_info->write_png_colortype &&
10051 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10052 mng_info->write_png_colortype != 7 &&
10053 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10055 if (logging != MagickFalse)
10057 if (ping_need_colortype_warning != MagickFalse)
10059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10060 " Image has transparency but tRNS chunk was excluded");
10063 if (mng_info->write_png_depth)
10065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10066 " Defined png:bit-depth=%u, Computed depth=%u",
10067 mng_info->write_png_depth,
10071 if (mng_info->write_png_colortype)
10073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10074 " Defined png:color-type=%u, Computed color type=%u",
10075 mng_info->write_png_colortype-1,
10081 "Cannot write image with defined png:bit-depth or png:color-type.");
10084 if (image_matte != MagickFalse && image->matte == MagickFalse)
10086 /* Add an opaque matte channel */
10087 image->matte = MagickTrue;
10088 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10090 if (logging != MagickFalse)
10091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10092 " Added an opaque matte channel");
10095 if (number_transparent != 0 || number_semitransparent != 0)
10097 if (ping_color_type < 4)
10099 ping_have_tRNS=MagickTrue;
10100 if (logging != MagickFalse)
10101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10102 " Setting ping_have_tRNS=MagickTrue.");
10106 if (logging != MagickFalse)
10107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10108 " Writing PNG header chunks");
10110 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10111 ping_bit_depth,ping_color_type,
10112 ping_interlace_method,ping_compression_method,
10113 ping_filter_method);
10115 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10117 png_set_PLTE(ping,ping_info,palette,number_colors);
10119 if (logging != MagickFalse)
10121 for (i=0; i< (ssize_t) number_colors; i++)
10123 if (i < ping_num_trans)
10124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10125 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10127 (int) palette[i].red,
10128 (int) palette[i].green,
10129 (int) palette[i].blue,
10131 (int) ping_trans_alpha[i]);
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10134 " PLTE[%d] = (%d,%d,%d)",
10136 (int) palette[i].red,
10137 (int) palette[i].green,
10138 (int) palette[i].blue);
10143 if (ping_exclude_bKGD == MagickFalse)
10145 if (ping_have_bKGD != MagickFalse)
10147 png_set_bKGD(ping,ping_info,&ping_background);
10150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10151 " Setting up bKGD chunk");
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153 " background color = (%d,%d,%d)",
10154 (int) ping_background.red,
10155 (int) ping_background.green,
10156 (int) ping_background.blue);
10157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10158 " index = %d, gray=%d",
10159 (int) ping_background.index,
10160 (int) ping_background.gray);
10165 if (ping_exclude_pHYs == MagickFalse)
10167 if (ping_have_pHYs != MagickFalse)
10169 png_set_pHYs(ping,ping_info,
10170 ping_pHYs_x_resolution,
10171 ping_pHYs_y_resolution,
10172 ping_pHYs_unit_type);
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10177 " Setting up pHYs chunk");
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179 " x_resolution=%lu",
10180 (unsigned long) ping_pHYs_x_resolution);
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182 " y_resolution=%lu",
10183 (unsigned long) ping_pHYs_y_resolution);
10184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10186 (unsigned long) ping_pHYs_unit_type);
10191 #if defined(PNG_oFFs_SUPPORTED)
10192 if (ping_exclude_oFFs == MagickFalse)
10194 if (image->page.x || image->page.y)
10196 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10197 (png_int_32) image->page.y, 0);
10199 if (logging != MagickFalse)
10200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10201 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10202 (int) image->page.x, (int) image->page.y);
10207 if (mng_info->need_blob != MagickFalse)
10209 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10211 png_error(ping,"WriteBlob Failed");
10213 ping_have_blob=MagickTrue;
10216 png_write_info_before_PLTE(ping, ping_info);
10218 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10220 if (logging != MagickFalse)
10222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10223 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10226 if (ping_color_type == 3)
10227 (void) png_set_tRNS(ping, ping_info,
10234 (void) png_set_tRNS(ping, ping_info,
10237 &ping_trans_color);
10239 if (logging != MagickFalse)
10241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10242 " tRNS color =(%d,%d,%d)",
10243 (int) ping_trans_color.red,
10244 (int) ping_trans_color.green,
10245 (int) ping_trans_color.blue);
10250 /* write any png-chunk-b profiles */
10251 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10253 png_write_info(ping,ping_info);
10255 /* write any PNG-chunk-m profiles */
10256 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10258 if (ping_exclude_vpAg == MagickFalse)
10260 if ((image->page.width != 0 && image->page.width != image->columns) ||
10261 (image->page.height != 0 && image->page.height != image->rows))
10266 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10267 PNGType(chunk,mng_vpAg);
10268 LogPNGChunk(logging,mng_vpAg,9L);
10269 PNGLong(chunk+4,(png_uint_32) image->page.width);
10270 PNGLong(chunk+8,(png_uint_32) image->page.height);
10271 chunk[12]=0; /* unit = pixels */
10272 (void) WriteBlob(image,13,chunk);
10273 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10277 #if (PNG_LIBPNG_VER == 10206)
10278 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10279 #define PNG_HAVE_IDAT 0x04
10280 ping->mode |= PNG_HAVE_IDAT;
10281 #undef PNG_HAVE_IDAT
10284 png_set_packing(ping);
10288 rowbytes=image->columns;
10289 if (image_depth > 8)
10291 switch (ping_color_type)
10293 case PNG_COLOR_TYPE_RGB:
10297 case PNG_COLOR_TYPE_GRAY_ALPHA:
10301 case PNG_COLOR_TYPE_RGBA:
10309 if (logging != MagickFalse)
10311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10312 " Writing PNG image data");
10314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10315 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10317 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10318 sizeof(*ping_pixels));
10320 if (ping_pixels == (unsigned char *) NULL)
10321 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10324 Initialize image scanlines.
10326 if (setjmp(png_jmpbuf(ping)))
10332 if (image_info->verbose)
10333 (void) printf("PNG write has failed.\n");
10335 png_destroy_write_struct(&ping,&ping_info);
10336 if (quantum_info != (QuantumInfo *) NULL)
10337 quantum_info=DestroyQuantumInfo(quantum_info);
10338 if (ping_pixels != (unsigned char *) NULL)
10339 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10340 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10341 UnlockSemaphoreInfo(ping_semaphore);
10343 if (ping_have_blob != MagickFalse)
10344 (void) CloseBlob(image);
10345 image_info=DestroyImageInfo(image_info);
10346 image=DestroyImage(image);
10347 return(MagickFalse);
10349 quantum_info=AcquireQuantumInfo(image_info,image);
10350 if (quantum_info == (QuantumInfo *) NULL)
10351 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10352 quantum_info->format=UndefinedQuantumFormat;
10353 quantum_info->depth=image_depth;
10354 num_passes=png_set_interlace_handling(ping);
10356 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10357 !mng_info->write_png32) &&
10358 (mng_info->IsPalette ||
10359 (image_info->type == BilevelType)) &&
10360 image_matte == MagickFalse &&
10361 ping_have_non_bw == MagickFalse)
10363 /* Palette, Bilevel, or Opaque Monochrome */
10364 register const Quantum
10367 quantum_info->depth=8;
10368 for (pass=0; pass < num_passes; pass++)
10371 Convert PseudoClass image to a PNG monochrome image.
10373 for (y=0; y < (ssize_t) image->rows; y++)
10375 if (logging != MagickFalse && y == 0)
10376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377 " Writing row of pixels (0)");
10379 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10381 if (p == (const Quantum *) NULL)
10384 if (mng_info->IsPalette)
10386 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10387 quantum_info,GrayQuantum,ping_pixels,exception);
10388 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10389 mng_info->write_png_depth &&
10390 mng_info->write_png_depth != old_bit_depth)
10392 /* Undo pixel scaling */
10393 for (i=0; i < (ssize_t) image->columns; i++)
10394 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10395 >> (8-old_bit_depth));
10401 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10402 quantum_info,RedQuantum,ping_pixels,exception);
10405 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10406 for (i=0; i < (ssize_t) image->columns; i++)
10407 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10410 if (logging != MagickFalse && y == 0)
10411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10412 " Writing row of pixels (1)");
10414 png_write_row(ping,ping_pixels);
10416 if (image->previous == (Image *) NULL)
10418 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10419 if (status == MagickFalse)
10425 else /* Not Palette, Bilevel, or Opaque Monochrome */
10427 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10428 !mng_info->write_png32) &&
10429 (image_matte != MagickFalse ||
10430 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10431 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10433 register const Quantum
10436 for (pass=0; pass < num_passes; pass++)
10439 for (y=0; y < (ssize_t) image->rows; y++)
10441 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10443 if (p == (const Quantum *) NULL)
10446 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10448 if (mng_info->IsPalette)
10449 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10450 quantum_info,GrayQuantum,ping_pixels,exception);
10453 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10454 quantum_info,RedQuantum,ping_pixels,exception);
10456 if (logging != MagickFalse && y == 0)
10457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10458 " Writing GRAY PNG pixels (2)");
10461 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10463 if (logging != MagickFalse && y == 0)
10464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10465 " Writing GRAY_ALPHA PNG pixels (2)");
10467 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10468 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10471 if (logging != MagickFalse && y == 0)
10472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10473 " Writing row of pixels (2)");
10475 png_write_row(ping,ping_pixels);
10478 if (image->previous == (Image *) NULL)
10480 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10481 if (status == MagickFalse)
10489 register const Quantum
10492 for (pass=0; pass < num_passes; pass++)
10494 if ((image_depth > 8) || (mng_info->write_png24 ||
10495 mng_info->write_png32 ||
10496 (!mng_info->write_png8 && !mng_info->IsPalette)))
10498 for (y=0; y < (ssize_t) image->rows; y++)
10500 p=GetVirtualPixels(image,0,y,image->columns,1,
10503 if (p == (const Quantum *) NULL)
10506 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10508 if (image->storage_class == DirectClass)
10509 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10510 quantum_info,RedQuantum,ping_pixels,exception);
10513 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10514 quantum_info,GrayQuantum,ping_pixels,exception);
10517 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10519 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10520 quantum_info,GrayAlphaQuantum,ping_pixels,
10523 if (logging != MagickFalse && y == 0)
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " Writing GRAY_ALPHA PNG pixels (3)");
10528 else if (image_matte != MagickFalse)
10529 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10530 quantum_info,RGBAQuantum,ping_pixels,exception);
10533 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10534 quantum_info,RGBQuantum,ping_pixels,exception);
10536 if (logging != MagickFalse && y == 0)
10537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10538 " Writing row of pixels (3)");
10540 png_write_row(ping,ping_pixels);
10545 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10546 mng_info->write_png32 ||
10547 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10549 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10550 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10552 if (logging != MagickFalse)
10553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10554 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10556 quantum_info->depth=8;
10560 for (y=0; y < (ssize_t) image->rows; y++)
10562 if (logging != MagickFalse && y == 0)
10563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10566 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10568 if (p == (const Quantum *) NULL)
10571 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10573 quantum_info->depth=image->depth;
10575 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10576 quantum_info,GrayQuantum,ping_pixels,exception);
10579 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10581 if (logging != MagickFalse && y == 0)
10582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583 " Writing GRAY_ALPHA PNG pixels (4)");
10585 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10586 quantum_info,GrayAlphaQuantum,ping_pixels,
10592 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10593 quantum_info,IndexQuantum,ping_pixels,exception);
10595 if (logging != MagickFalse && y <= 2)
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598 " Writing row of non-gray pixels (4)");
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10602 (int)ping_pixels[0],(int)ping_pixels[1]);
10605 png_write_row(ping,ping_pixels);
10609 if (image->previous == (Image *) NULL)
10611 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10612 if (status == MagickFalse)
10619 if (quantum_info != (QuantumInfo *) NULL)
10620 quantum_info=DestroyQuantumInfo(quantum_info);
10622 if (logging != MagickFalse)
10624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625 " Wrote PNG image data");
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 " Width: %.20g",(double) ping_width);
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " Height: %.20g",(double) ping_height);
10633 if (mng_info->write_png_depth)
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " PNG bit-depth written: %d",ping_bit_depth);
10642 if (mng_info->write_png_colortype)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " PNG color-type written: %d",ping_color_type);
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " PNG Interlace method: %d",ping_interlace_method);
10655 Generate text chunks after IDAT.
10657 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10659 ResetImagePropertyIterator(image);
10660 property=GetNextImageProperty(image);
10661 while (property != (const char *) NULL)
10666 value=GetImageProperty(image,property,exception);
10668 /* Don't write any "png:" properties; those are just for "identify" */
10669 if (LocaleNCompare(property,"png:",4) != 0 &&
10671 /* Suppress density and units if we wrote a pHYs chunk */
10672 (ping_exclude_pHYs != MagickFalse ||
10673 LocaleCompare(property,"density") != 0 ||
10674 LocaleCompare(property,"units") != 0) &&
10676 /* Suppress the IM-generated Date:create and Date:modify */
10677 (ping_exclude_date == MagickFalse ||
10678 LocaleNCompare(property, "Date:",5) != 0))
10680 if (value != (const char *) NULL)
10682 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10683 text[0].key=(char *) property;
10684 text[0].text=(char *) value;
10685 text[0].text_length=strlen(value);
10687 if (ping_exclude_tEXt != MagickFalse)
10688 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10690 else if (ping_exclude_zTXt != MagickFalse)
10691 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10695 text[0].compression=image_info->compression == NoCompression ||
10696 (image_info->compression == UndefinedCompression &&
10697 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10698 PNG_TEXT_COMPRESSION_zTXt ;
10701 if (logging != MagickFalse)
10703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10704 " Setting up text chunk");
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " keyword: %s",text[0].key);
10710 png_set_text(ping,ping_info,text,1);
10711 png_free(ping,text);
10714 property=GetNextImageProperty(image);
10718 /* write any PNG-chunk-e profiles */
10719 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10721 if (logging != MagickFalse)
10722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10723 " Writing PNG end info");
10725 png_write_end(ping,ping_info);
10727 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10729 if (mng_info->page.x || mng_info->page.y ||
10730 (ping_width != mng_info->page.width) ||
10731 (ping_height != mng_info->page.height))
10737 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10739 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10740 PNGType(chunk,mng_FRAM);
10741 LogPNGChunk(logging,mng_FRAM,27L);
10743 chunk[5]=0; /* frame name separator (no name) */
10744 chunk[6]=1; /* flag for changing delay, for next frame only */
10745 chunk[7]=0; /* flag for changing frame timeout */
10746 chunk[8]=1; /* flag for changing frame clipping for next frame */
10747 chunk[9]=0; /* flag for changing frame sync_id */
10748 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10749 chunk[14]=0; /* clipping boundaries delta type */
10750 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10752 (png_uint_32) (mng_info->page.x + ping_width));
10753 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10755 (png_uint_32) (mng_info->page.y + ping_height));
10756 (void) WriteBlob(image,31,chunk);
10757 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10758 mng_info->old_framing_mode=4;
10759 mng_info->framing_mode=1;
10763 mng_info->framing_mode=3;
10765 if (mng_info->write_mng && !mng_info->need_fram &&
10766 ((int) image->dispose == 3))
10767 (void) ThrowMagickException(exception,GetMagickModule(),
10768 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10769 "`%s'",image->filename);
10772 Free PNG resources.
10775 png_destroy_write_struct(&ping,&ping_info);
10777 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10779 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10780 UnlockSemaphoreInfo(ping_semaphore);
10783 if (ping_have_blob != MagickFalse)
10784 (void) CloseBlob(image);
10786 image_info=DestroyImageInfo(image_info);
10787 image=DestroyImage(image);
10789 /* Store bit depth actually written */
10790 s[0]=(char) ping_bit_depth;
10793 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10795 if (logging != MagickFalse)
10796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10797 " exit WriteOnePNGImage()");
10799 return(MagickTrue);
10800 /* End write one PNG image */
10804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10808 % W r i t e P N G I m a g e %
10812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10814 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10815 % Multiple-image Network Graphics (MNG) image file.
10817 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10819 % The format of the WritePNGImage method is:
10821 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10822 % Image *image,ExceptionInfo *exception)
10824 % A description of each parameter follows:
10826 % o image_info: the image info.
10828 % o image: The image.
10830 % o exception: return any errors or warnings in this structure.
10832 % Returns MagickTrue on success, MagickFalse on failure.
10834 % Communicating with the PNG encoder:
10836 % While the datastream written is always in PNG format and normally would
10837 % be given the "png" file extension, this method also writes the following
10838 % pseudo-formats which are subsets of png:
10840 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10841 % a depth greater than 8, the depth is reduced. If transparency
10842 % is present, the tRNS chunk must only have values 0 and 255
10843 % (i.e., transparency is binary: fully opaque or fully
10844 % transparent). If other values are present they will be
10845 % 50%-thresholded to binary transparency. If more than 256
10846 % colors are present, they will be quantized to the 4-4-4-1,
10847 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10848 % of any resulting fully-transparent pixels is changed to
10849 % the image's background color.
10851 % If you want better quantization or dithering of the colors
10852 % or alpha than that, you need to do it before calling the
10853 % PNG encoder. The pixels contain 8-bit indices even if
10854 % they could be represented with 1, 2, or 4 bits. Grayscale
10855 % images will be written as indexed PNG files even though the
10856 % PNG grayscale type might be slightly more efficient. Please
10857 % note that writing to the PNG8 format may result in loss
10858 % of color and alpha data.
10860 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10861 % chunk can be present to convey binary transparency by naming
10862 % one of the colors as transparent. The only loss incurred
10863 % is reduction of sample depth to 8. If the image has more
10864 % than one transparent color, has semitransparent pixels, or
10865 % has an opaque pixel with the same RGB components as the
10866 % transparent color, an image is not written.
10868 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10869 % transparency is permitted, i.e., the alpha sample for
10870 % each pixel can have any value from 0 to 255. The alpha
10871 % channel is present even if the image is fully opaque.
10872 % The only loss in data is the reduction of the sample depth
10875 % o -define: For more precise control of the PNG output, you can use the
10876 % Image options "png:bit-depth" and "png:color-type". These
10877 % can be set from the commandline with "-define" and also
10878 % from the application programming interfaces. The options
10879 % are case-independent and are converted to lowercase before
10880 % being passed to this encoder.
10882 % png:color-type can be 0, 2, 3, 4, or 6.
10884 % When png:color-type is 0 (Grayscale), png:bit-depth can
10885 % be 1, 2, 4, 8, or 16.
10887 % When png:color-type is 2 (RGB), png:bit-depth can
10890 % When png:color-type is 3 (Indexed), png:bit-depth can
10891 % be 1, 2, 4, or 8. This refers to the number of bits
10892 % used to store the index. The color samples always have
10893 % bit-depth 8 in indexed PNG files.
10895 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10896 % png:bit-depth can be 8 or 16.
10898 % If the image cannot be written without loss with the requested bit-depth
10899 % and color-type, a PNG file will not be written, and the encoder will
10900 % return MagickFalse.
10902 % Since image encoders should not be responsible for the "heavy lifting",
10903 % the user should make sure that ImageMagick has already reduced the
10904 % image depth and number of colors and limit transparency to binary
10905 % transparency prior to attempting to write the image with depth, color,
10906 % or transparency limitations.
10908 % Note that another definition, "png:bit-depth-written" exists, but it
10909 % is not intended for external use. It is only used internally by the
10910 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10912 % It is possible to request that the PNG encoder write previously-formatted
10913 % ancillary chunks in the output PNG file, using the "-profile" commandline
10914 % option as shown below or by setting the profile via a programming
10917 % -profile PNG-chunk-x:<file>
10919 % where x is a location flag and <file> is a file containing the chunk
10920 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10921 % This encoder will compute the chunk length and CRC, so those must not
10922 % be included in the file.
10924 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10925 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10926 % of the same type, then add a short unique string after the "x" to prevent
10927 % subsequent profiles from overwriting the preceding ones, e.g.,
10929 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10931 % As of version 6.6.6 the following optimizations are always done:
10933 % o 32-bit depth is reduced to 16.
10934 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10935 % high byte and low byte are identical.
10936 % o Palette is sorted to remove unused entries and to put a
10937 % transparent color first, if BUILD_PNG_PALETTE is defined.
10938 % o Opaque matte channel is removed when writing an indexed PNG.
10939 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10940 % this can be done without loss and a larger bit depth N was not
10941 % requested via the "-define png:bit-depth=N" option.
10942 % o If matte channel is present but only one transparent color is
10943 % present, RGB+tRNS is written instead of RGBA
10944 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10945 % was requested when converting an opaque image).
10947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10949 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10950 Image *image,ExceptionInfo *exception)
10955 have_mng_structure,
10971 assert(image_info != (const ImageInfo *) NULL);
10972 assert(image_info->signature == MagickSignature);
10973 assert(image != (Image *) NULL);
10974 assert(image->signature == MagickSignature);
10975 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10976 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10978 Allocate a MngInfo structure.
10980 have_mng_structure=MagickFalse;
10981 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10983 if (mng_info == (MngInfo *) NULL)
10984 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10987 Initialize members of the MngInfo structure.
10989 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10990 mng_info->image=image;
10991 mng_info->equal_backgrounds=MagickTrue;
10992 have_mng_structure=MagickTrue;
10994 /* See if user has requested a specific PNG subformat */
10996 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10997 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10998 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11000 if (mng_info->write_png8)
11002 mng_info->write_png_colortype = /* 3 */ 4;
11003 mng_info->write_png_depth = 8;
11007 if (mng_info->write_png24)
11009 mng_info->write_png_colortype = /* 2 */ 3;
11010 mng_info->write_png_depth = 8;
11013 if (image->matte == MagickTrue)
11014 (void) SetImageType(image,TrueColorMatteType,exception);
11017 (void) SetImageType(image,TrueColorType,exception);
11019 (void) SyncImage(image,exception);
11022 if (mng_info->write_png32)
11024 mng_info->write_png_colortype = /* 6 */ 7;
11025 mng_info->write_png_depth = 8;
11028 if (image->matte == MagickTrue)
11029 (void) SetImageType(image,TrueColorMatteType,exception);
11032 (void) SetImageType(image,TrueColorType,exception);
11034 (void) SyncImage(image,exception);
11037 value=GetImageOption(image_info,"png:bit-depth");
11039 if (value != (char *) NULL)
11041 if (LocaleCompare(value,"1") == 0)
11042 mng_info->write_png_depth = 1;
11044 else if (LocaleCompare(value,"2") == 0)
11045 mng_info->write_png_depth = 2;
11047 else if (LocaleCompare(value,"4") == 0)
11048 mng_info->write_png_depth = 4;
11050 else if (LocaleCompare(value,"8") == 0)
11051 mng_info->write_png_depth = 8;
11053 else if (LocaleCompare(value,"16") == 0)
11054 mng_info->write_png_depth = 16;
11057 (void) ThrowMagickException(exception,
11058 GetMagickModule(),CoderWarning,
11059 "ignoring invalid defined png:bit-depth",
11062 if (logging != MagickFalse)
11063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11064 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11067 value=GetImageOption(image_info,"png:color-type");
11069 if (value != (char *) NULL)
11071 /* We must store colortype+1 because 0 is a valid colortype */
11072 if (LocaleCompare(value,"0") == 0)
11073 mng_info->write_png_colortype = 1;
11075 else if (LocaleCompare(value,"1") == 0)
11076 mng_info->write_png_colortype = 2;
11078 else if (LocaleCompare(value,"2") == 0)
11079 mng_info->write_png_colortype = 3;
11081 else if (LocaleCompare(value,"3") == 0)
11082 mng_info->write_png_colortype = 4;
11084 else if (LocaleCompare(value,"4") == 0)
11085 mng_info->write_png_colortype = 5;
11087 else if (LocaleCompare(value,"6") == 0)
11088 mng_info->write_png_colortype = 7;
11091 (void) ThrowMagickException(exception,
11092 GetMagickModule(),CoderWarning,
11093 "ignoring invalid defined png:color-type",
11096 if (logging != MagickFalse)
11097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11101 /* Check for chunks to be excluded:
11103 * The default is to not exclude any known chunks except for any
11104 * listed in the "unused_chunks" array, above.
11106 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11107 * define (in the image properties or in the image artifacts)
11108 * or via a mng_info member. For convenience, in addition
11109 * to or instead of a comma-separated list of chunks, the
11110 * "exclude-chunk" string can be simply "all" or "none".
11112 * The exclude-chunk define takes priority over the mng_info.
11114 * A "png:include-chunk" define takes priority over both the
11115 * mng_info and the "png:exclude-chunk" define. Like the
11116 * "exclude-chunk" string, it can define "all" or "none" as
11117 * well as a comma-separated list. Chunks that are unknown to
11118 * ImageMagick are always excluded, regardless of their "copy-safe"
11119 * status according to the PNG specification, and even if they
11120 * appear in the "include-chunk" list. Such defines appearing among
11121 * the image options take priority over those found among the image
11124 * Finally, all chunks listed in the "unused_chunks" array are
11125 * automatically excluded, regardless of the other instructions
11128 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11129 * will not be written and the gAMA chunk will only be written if it
11130 * is not between .45 and .46, or approximately (1.0/2.2).
11132 * If you exclude tRNS and the image has transparency, the colortype
11133 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11135 * The -strip option causes StripImage() to set the png:include-chunk
11136 * artifact to "none,trns,gama".
11139 mng_info->ping_exclude_bKGD=MagickFalse;
11140 mng_info->ping_exclude_cHRM=MagickFalse;
11141 mng_info->ping_exclude_date=MagickFalse;
11142 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11143 mng_info->ping_exclude_gAMA=MagickFalse;
11144 mng_info->ping_exclude_iCCP=MagickFalse;
11145 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11146 mng_info->ping_exclude_oFFs=MagickFalse;
11147 mng_info->ping_exclude_pHYs=MagickFalse;
11148 mng_info->ping_exclude_sRGB=MagickFalse;
11149 mng_info->ping_exclude_tEXt=MagickFalse;
11150 mng_info->ping_exclude_tRNS=MagickFalse;
11151 mng_info->ping_exclude_vpAg=MagickFalse;
11152 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11153 mng_info->ping_exclude_zTXt=MagickFalse;
11155 mng_info->ping_preserve_colormap=MagickFalse;
11157 value=GetImageArtifact(image,"png:preserve-colormap");
11159 value=GetImageOption(image_info,"png:preserve-colormap");
11161 mng_info->ping_preserve_colormap=MagickTrue;
11163 /* Thes compression-level, compression-strategy, and compression-filter
11164 * defines take precedence over values from the -quality option.
11166 value=GetImageArtifact(image,"png:compression-level");
11168 value=GetImageOption(image_info,"png:compression-level");
11171 /* We have to add 1 to everything because 0 is a valid input,
11172 * and we want to use 0 (the default) to mean undefined.
11174 if (LocaleCompare(value,"0") == 0)
11175 mng_info->write_png_compression_level = 1;
11177 else if (LocaleCompare(value,"1") == 0)
11178 mng_info->write_png_compression_level = 2;
11180 else if (LocaleCompare(value,"2") == 0)
11181 mng_info->write_png_compression_level = 3;
11183 else if (LocaleCompare(value,"3") == 0)
11184 mng_info->write_png_compression_level = 4;
11186 else if (LocaleCompare(value,"4") == 0)
11187 mng_info->write_png_compression_level = 5;
11189 else if (LocaleCompare(value,"5") == 0)
11190 mng_info->write_png_compression_level = 6;
11192 else if (LocaleCompare(value,"6") == 0)
11193 mng_info->write_png_compression_level = 7;
11195 else if (LocaleCompare(value,"7") == 0)
11196 mng_info->write_png_compression_level = 8;
11198 else if (LocaleCompare(value,"8") == 0)
11199 mng_info->write_png_compression_level = 9;
11201 else if (LocaleCompare(value,"9") == 0)
11202 mng_info->write_png_compression_level = 10;
11205 (void) ThrowMagickException(exception,
11206 GetMagickModule(),CoderWarning,
11207 "ignoring invalid defined png:compression-level",
11211 value=GetImageArtifact(image,"png:compression-strategy");
11213 value=GetImageOption(image_info,"png:compression-strategy");
11217 if (LocaleCompare(value,"0") == 0)
11218 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11220 else if (LocaleCompare(value,"1") == 0)
11221 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11223 else if (LocaleCompare(value,"2") == 0)
11224 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11226 else if (LocaleCompare(value,"3") == 0)
11227 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11228 mng_info->write_png_compression_strategy = Z_RLE+1;
11230 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11233 else if (LocaleCompare(value,"4") == 0)
11234 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11235 mng_info->write_png_compression_strategy = Z_FIXED+1;
11237 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11241 (void) ThrowMagickException(exception,
11242 GetMagickModule(),CoderWarning,
11243 "ignoring invalid defined png:compression-strategy",
11247 value=GetImageArtifact(image,"png:compression-filter");
11249 value=GetImageOption(image_info,"png:compression-filter");
11253 /* To do: combinations of filters allowed by libpng
11254 * masks 0x08 through 0xf8
11256 * Implement this as a comma-separated list of 0,1,2,3,4,5
11257 * where 5 is a special case meaning PNG_ALL_FILTERS.
11260 if (LocaleCompare(value,"0") == 0)
11261 mng_info->write_png_compression_filter = 1;
11263 if (LocaleCompare(value,"1") == 0)
11264 mng_info->write_png_compression_filter = 2;
11266 else if (LocaleCompare(value,"2") == 0)
11267 mng_info->write_png_compression_filter = 3;
11269 else if (LocaleCompare(value,"3") == 0)
11270 mng_info->write_png_compression_filter = 4;
11272 else if (LocaleCompare(value,"4") == 0)
11273 mng_info->write_png_compression_filter = 5;
11275 else if (LocaleCompare(value,"5") == 0)
11276 mng_info->write_png_compression_filter = 6;
11279 (void) ThrowMagickException(exception,
11280 GetMagickModule(),CoderWarning,
11281 "ignoring invalid defined png:compression-filter",
11285 excluding=MagickFalse;
11287 for (source=0; source<1; source++)
11291 value=GetImageArtifact(image,"png:exclude-chunk");
11294 value=GetImageArtifact(image,"png:exclude-chunks");
11298 value=GetImageOption(image_info,"png:exclude-chunk");
11301 value=GetImageOption(image_info,"png:exclude-chunks");
11310 excluding=MagickTrue;
11312 if (logging != MagickFalse)
11315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11316 " png:exclude-chunk=%s found in image artifacts.\n", value);
11318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319 " png:exclude-chunk=%s found in image properties.\n", value);
11322 last=strlen(value);
11324 for (i=0; i<(int) last; i+=5)
11327 if (LocaleNCompare(value+i,"all",3) == 0)
11329 mng_info->ping_exclude_bKGD=MagickTrue;
11330 mng_info->ping_exclude_cHRM=MagickTrue;
11331 mng_info->ping_exclude_date=MagickTrue;
11332 mng_info->ping_exclude_EXIF=MagickTrue;
11333 mng_info->ping_exclude_gAMA=MagickTrue;
11334 mng_info->ping_exclude_iCCP=MagickTrue;
11335 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11336 mng_info->ping_exclude_oFFs=MagickTrue;
11337 mng_info->ping_exclude_pHYs=MagickTrue;
11338 mng_info->ping_exclude_sRGB=MagickTrue;
11339 mng_info->ping_exclude_tEXt=MagickTrue;
11340 mng_info->ping_exclude_tRNS=MagickTrue;
11341 mng_info->ping_exclude_vpAg=MagickTrue;
11342 mng_info->ping_exclude_zCCP=MagickTrue;
11343 mng_info->ping_exclude_zTXt=MagickTrue;
11347 if (LocaleNCompare(value+i,"none",4) == 0)
11349 mng_info->ping_exclude_bKGD=MagickFalse;
11350 mng_info->ping_exclude_cHRM=MagickFalse;
11351 mng_info->ping_exclude_date=MagickFalse;
11352 mng_info->ping_exclude_EXIF=MagickFalse;
11353 mng_info->ping_exclude_gAMA=MagickFalse;
11354 mng_info->ping_exclude_iCCP=MagickFalse;
11355 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11356 mng_info->ping_exclude_oFFs=MagickFalse;
11357 mng_info->ping_exclude_pHYs=MagickFalse;
11358 mng_info->ping_exclude_sRGB=MagickFalse;
11359 mng_info->ping_exclude_tEXt=MagickFalse;
11360 mng_info->ping_exclude_tRNS=MagickFalse;
11361 mng_info->ping_exclude_vpAg=MagickFalse;
11362 mng_info->ping_exclude_zCCP=MagickFalse;
11363 mng_info->ping_exclude_zTXt=MagickFalse;
11366 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11367 mng_info->ping_exclude_bKGD=MagickTrue;
11369 if (LocaleNCompare(value+i,"chrm",4) == 0)
11370 mng_info->ping_exclude_cHRM=MagickTrue;
11372 if (LocaleNCompare(value+i,"date",4) == 0)
11373 mng_info->ping_exclude_date=MagickTrue;
11375 if (LocaleNCompare(value+i,"exif",4) == 0)
11376 mng_info->ping_exclude_EXIF=MagickTrue;
11378 if (LocaleNCompare(value+i,"gama",4) == 0)
11379 mng_info->ping_exclude_gAMA=MagickTrue;
11381 if (LocaleNCompare(value+i,"iccp",4) == 0)
11382 mng_info->ping_exclude_iCCP=MagickTrue;
11385 if (LocaleNCompare(value+i,"itxt",4) == 0)
11386 mng_info->ping_exclude_iTXt=MagickTrue;
11389 if (LocaleNCompare(value+i,"gama",4) == 0)
11390 mng_info->ping_exclude_gAMA=MagickTrue;
11392 if (LocaleNCompare(value+i,"offs",4) == 0)
11393 mng_info->ping_exclude_oFFs=MagickTrue;
11395 if (LocaleNCompare(value+i,"phys",4) == 0)
11396 mng_info->ping_exclude_pHYs=MagickTrue;
11398 if (LocaleNCompare(value+i,"srgb",4) == 0)
11399 mng_info->ping_exclude_sRGB=MagickTrue;
11401 if (LocaleNCompare(value+i,"text",4) == 0)
11402 mng_info->ping_exclude_tEXt=MagickTrue;
11404 if (LocaleNCompare(value+i,"trns",4) == 0)
11405 mng_info->ping_exclude_tRNS=MagickTrue;
11407 if (LocaleNCompare(value+i,"vpag",4) == 0)
11408 mng_info->ping_exclude_vpAg=MagickTrue;
11410 if (LocaleNCompare(value+i,"zccp",4) == 0)
11411 mng_info->ping_exclude_zCCP=MagickTrue;
11413 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11414 mng_info->ping_exclude_zTXt=MagickTrue;
11420 for (source=0; source<1; source++)
11424 value=GetImageArtifact(image,"png:include-chunk");
11427 value=GetImageArtifact(image,"png:include-chunks");
11431 value=GetImageOption(image_info,"png:include-chunk");
11434 value=GetImageOption(image_info,"png:include-chunks");
11442 excluding=MagickTrue;
11444 if (logging != MagickFalse)
11447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11448 " png:include-chunk=%s found in image artifacts.\n", value);
11450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11451 " png:include-chunk=%s found in image properties.\n", value);
11454 last=strlen(value);
11456 for (i=0; i<(int) last; i+=5)
11458 if (LocaleNCompare(value+i,"all",3) == 0)
11460 mng_info->ping_exclude_bKGD=MagickFalse;
11461 mng_info->ping_exclude_cHRM=MagickFalse;
11462 mng_info->ping_exclude_date=MagickFalse;
11463 mng_info->ping_exclude_EXIF=MagickFalse;
11464 mng_info->ping_exclude_gAMA=MagickFalse;
11465 mng_info->ping_exclude_iCCP=MagickFalse;
11466 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11467 mng_info->ping_exclude_oFFs=MagickFalse;
11468 mng_info->ping_exclude_pHYs=MagickFalse;
11469 mng_info->ping_exclude_sRGB=MagickFalse;
11470 mng_info->ping_exclude_tEXt=MagickFalse;
11471 mng_info->ping_exclude_tRNS=MagickFalse;
11472 mng_info->ping_exclude_vpAg=MagickFalse;
11473 mng_info->ping_exclude_zCCP=MagickFalse;
11474 mng_info->ping_exclude_zTXt=MagickFalse;
11478 if (LocaleNCompare(value+i,"none",4) == 0)
11480 mng_info->ping_exclude_bKGD=MagickTrue;
11481 mng_info->ping_exclude_cHRM=MagickTrue;
11482 mng_info->ping_exclude_date=MagickTrue;
11483 mng_info->ping_exclude_EXIF=MagickTrue;
11484 mng_info->ping_exclude_gAMA=MagickTrue;
11485 mng_info->ping_exclude_iCCP=MagickTrue;
11486 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11487 mng_info->ping_exclude_oFFs=MagickTrue;
11488 mng_info->ping_exclude_pHYs=MagickTrue;
11489 mng_info->ping_exclude_sRGB=MagickTrue;
11490 mng_info->ping_exclude_tEXt=MagickTrue;
11491 mng_info->ping_exclude_tRNS=MagickTrue;
11492 mng_info->ping_exclude_vpAg=MagickTrue;
11493 mng_info->ping_exclude_zCCP=MagickTrue;
11494 mng_info->ping_exclude_zTXt=MagickTrue;
11497 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11498 mng_info->ping_exclude_bKGD=MagickFalse;
11500 if (LocaleNCompare(value+i,"chrm",4) == 0)
11501 mng_info->ping_exclude_cHRM=MagickFalse;
11503 if (LocaleNCompare(value+i,"date",4) == 0)
11504 mng_info->ping_exclude_date=MagickFalse;
11506 if (LocaleNCompare(value+i,"exif",4) == 0)
11507 mng_info->ping_exclude_EXIF=MagickFalse;
11509 if (LocaleNCompare(value+i,"gama",4) == 0)
11510 mng_info->ping_exclude_gAMA=MagickFalse;
11512 if (LocaleNCompare(value+i,"iccp",4) == 0)
11513 mng_info->ping_exclude_iCCP=MagickFalse;
11516 if (LocaleNCompare(value+i,"itxt",4) == 0)
11517 mng_info->ping_exclude_iTXt=MagickFalse;
11520 if (LocaleNCompare(value+i,"gama",4) == 0)
11521 mng_info->ping_exclude_gAMA=MagickFalse;
11523 if (LocaleNCompare(value+i,"offs",4) == 0)
11524 mng_info->ping_exclude_oFFs=MagickFalse;
11526 if (LocaleNCompare(value+i,"phys",4) == 0)
11527 mng_info->ping_exclude_pHYs=MagickFalse;
11529 if (LocaleNCompare(value+i,"srgb",4) == 0)
11530 mng_info->ping_exclude_sRGB=MagickFalse;
11532 if (LocaleNCompare(value+i,"text",4) == 0)
11533 mng_info->ping_exclude_tEXt=MagickFalse;
11535 if (LocaleNCompare(value+i,"trns",4) == 0)
11536 mng_info->ping_exclude_tRNS=MagickFalse;
11538 if (LocaleNCompare(value+i,"vpag",4) == 0)
11539 mng_info->ping_exclude_vpAg=MagickFalse;
11541 if (LocaleNCompare(value+i,"zccp",4) == 0)
11542 mng_info->ping_exclude_zCCP=MagickFalse;
11544 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11545 mng_info->ping_exclude_zTXt=MagickFalse;
11551 if (excluding != MagickFalse && logging != MagickFalse)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 " Chunks to be excluded from the output png:");
11555 if (mng_info->ping_exclude_bKGD != MagickFalse)
11556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 if (mng_info->ping_exclude_cHRM != MagickFalse)
11559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11561 if (mng_info->ping_exclude_date != MagickFalse)
11562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 if (mng_info->ping_exclude_EXIF != MagickFalse)
11565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 if (mng_info->ping_exclude_gAMA != MagickFalse)
11568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 if (mng_info->ping_exclude_iCCP != MagickFalse)
11571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 if (mng_info->ping_exclude_iTXt != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 if (mng_info->ping_exclude_oFFs != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 if (mng_info->ping_exclude_pHYs != MagickFalse)
11582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11584 if (mng_info->ping_exclude_sRGB != MagickFalse)
11585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587 if (mng_info->ping_exclude_tEXt != MagickFalse)
11588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11590 if (mng_info->ping_exclude_tRNS != MagickFalse)
11591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11593 if (mng_info->ping_exclude_vpAg != MagickFalse)
11594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 if (mng_info->ping_exclude_zCCP != MagickFalse)
11597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11599 if (mng_info->ping_exclude_zTXt != MagickFalse)
11600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11604 mng_info->need_blob = MagickTrue;
11606 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11608 MngInfoFreeStruct(mng_info,&have_mng_structure);
11610 if (logging != MagickFalse)
11611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11616 #if defined(JNG_SUPPORTED)
11618 /* Write one JNG image */
11619 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11620 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11641 jng_alpha_compression_method,
11642 jng_alpha_sample_depth,
11650 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11651 " Enter WriteOneJNGImage()");
11653 blob=(unsigned char *) NULL;
11654 jpeg_image=(Image *) NULL;
11655 jpeg_image_info=(ImageInfo *) NULL;
11658 transparent=image_info->type==GrayscaleMatteType ||
11659 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
11661 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11663 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11665 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11666 image_info->quality;
11668 if (jng_alpha_quality >= 1000)
11669 jng_alpha_quality /= 1000;
11675 /* Create JPEG blob, image, and image_info */
11676 if (logging != MagickFalse)
11677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11678 " Creating jpeg_image_info for alpha.");
11680 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11682 if (jpeg_image_info == (ImageInfo *) NULL)
11683 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11685 if (logging != MagickFalse)
11686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11687 " Creating jpeg_image.");
11689 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11690 if (jpeg_image == (Image *) NULL)
11691 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11692 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11693 jpeg_image->matte=MagickFalse;
11694 jpeg_image->quality=jng_alpha_quality;
11695 jpeg_image_info->type=GrayscaleType;
11696 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11697 (void) AcquireUniqueFilename(jpeg_image->filename);
11698 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11699 "%s",jpeg_image->filename);
11703 jng_alpha_compression_method=0;
11705 jng_alpha_sample_depth=0;
11708 /* To do: check bit depth of PNG alpha channel */
11710 /* Check if image is grayscale. */
11711 if (image_info->type != TrueColorMatteType && image_info->type !=
11712 TrueColorType && ImageIsGray(image,exception))
11715 if (logging != MagickFalse)
11717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11718 " JNG Quality = %d",(int) jng_quality);
11719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11720 " JNG Color Type = %d",jng_color_type);
11723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11726 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11728 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11734 if (jng_alpha_compression_method==0)
11739 /* Encode alpha as a grayscale PNG blob */
11740 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11742 if (logging != MagickFalse)
11743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11744 " Creating PNG blob.");
11747 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11748 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11749 jpeg_image_info->interlace=NoInterlace;
11751 /* Exclude all ancillary chunks */
11752 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11754 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11757 /* Retrieve sample depth used */
11758 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11759 if (value != (char *) NULL)
11760 jng_alpha_sample_depth= (unsigned int) value[0];
11764 /* Encode alpha as a grayscale JPEG blob */
11766 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11769 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11770 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11771 jpeg_image_info->interlace=NoInterlace;
11772 if (logging != MagickFalse)
11773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11774 " Creating blob.");
11775 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11777 jng_alpha_sample_depth=8;
11779 if (logging != MagickFalse)
11780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11781 " Successfully read jpeg_image into a blob, length=%.20g.",
11785 /* Destroy JPEG image and image_info */
11786 jpeg_image=DestroyImage(jpeg_image);
11787 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11788 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11791 /* Write JHDR chunk */
11792 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11793 PNGType(chunk,mng_JHDR);
11794 LogPNGChunk(logging,mng_JHDR,16L);
11795 PNGLong(chunk+4,(png_uint_32) image->columns);
11796 PNGLong(chunk+8,(png_uint_32) image->rows);
11797 chunk[12]=jng_color_type;
11798 chunk[13]=8; /* sample depth */
11799 chunk[14]=8; /*jng_image_compression_method */
11800 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11801 chunk[16]=jng_alpha_sample_depth;
11802 chunk[17]=jng_alpha_compression_method;
11803 chunk[18]=0; /*jng_alpha_filter_method */
11804 chunk[19]=0; /*jng_alpha_interlace_method */
11805 (void) WriteBlob(image,20,chunk);
11806 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11807 if (logging != MagickFalse)
11809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG width:%15lu",(unsigned long) image->columns);
11812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG height:%14lu",(unsigned long) image->rows);
11815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " JNG color type:%10d",jng_color_type);
11818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG sample depth:%8d",8);
11821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11822 " JNG compression:%9d",8);
11824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11825 " JNG interlace:%11d",0);
11827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11828 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11831 " JNG alpha compression:%3d",jng_alpha_compression_method);
11833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11834 " JNG alpha filter:%8d",0);
11836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11837 " JNG alpha interlace:%5d",0);
11840 /* Write any JNG-chunk-b profiles */
11841 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11844 Write leading ancillary chunks
11850 Write JNG bKGD chunk
11861 if (jng_color_type == 8 || jng_color_type == 12)
11865 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11866 PNGType(chunk,mng_bKGD);
11867 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11868 red=ScaleQuantumToChar(image->background_color.red);
11869 green=ScaleQuantumToChar(image->background_color.green);
11870 blue=ScaleQuantumToChar(image->background_color.blue);
11877 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11878 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11881 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11884 Write JNG sRGB chunk
11886 (void) WriteBlobMSBULong(image,1L);
11887 PNGType(chunk,mng_sRGB);
11888 LogPNGChunk(logging,mng_sRGB,1L);
11890 if (image->rendering_intent != UndefinedIntent)
11891 chunk[4]=(unsigned char)
11892 Magick_RenderingIntent_to_PNG_RenderingIntent(
11893 (image->rendering_intent));
11896 chunk[4]=(unsigned char)
11897 Magick_RenderingIntent_to_PNG_RenderingIntent(
11898 (PerceptualIntent));
11900 (void) WriteBlob(image,5,chunk);
11901 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11905 if (image->gamma != 0.0)
11908 Write JNG gAMA chunk
11910 (void) WriteBlobMSBULong(image,4L);
11911 PNGType(chunk,mng_gAMA);
11912 LogPNGChunk(logging,mng_gAMA,4L);
11913 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11914 (void) WriteBlob(image,8,chunk);
11915 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11918 if ((mng_info->equal_chrms == MagickFalse) &&
11919 (image->chromaticity.red_primary.x != 0.0))
11925 Write JNG cHRM chunk
11927 (void) WriteBlobMSBULong(image,32L);
11928 PNGType(chunk,mng_cHRM);
11929 LogPNGChunk(logging,mng_cHRM,32L);
11930 primary=image->chromaticity.white_point;
11931 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11932 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11933 primary=image->chromaticity.red_primary;
11934 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11935 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11936 primary=image->chromaticity.green_primary;
11937 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11938 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11939 primary=image->chromaticity.blue_primary;
11940 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11941 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11942 (void) WriteBlob(image,36,chunk);
11943 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11947 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
11950 Write JNG pHYs chunk
11952 (void) WriteBlobMSBULong(image,9L);
11953 PNGType(chunk,mng_pHYs);
11954 LogPNGChunk(logging,mng_pHYs,9L);
11955 if (image->units == PixelsPerInchResolution)
11957 PNGLong(chunk+4,(png_uint_32)
11958 (image->resolution.x*100.0/2.54+0.5));
11960 PNGLong(chunk+8,(png_uint_32)
11961 (image->resolution.y*100.0/2.54+0.5));
11968 if (image->units == PixelsPerCentimeterResolution)
11970 PNGLong(chunk+4,(png_uint_32)
11971 (image->resolution.x*100.0+0.5));
11973 PNGLong(chunk+8,(png_uint_32)
11974 (image->resolution.y*100.0+0.5));
11981 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11982 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
11986 (void) WriteBlob(image,13,chunk);
11987 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11990 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11993 Write JNG oFFs chunk
11995 (void) WriteBlobMSBULong(image,9L);
11996 PNGType(chunk,mng_oFFs);
11997 LogPNGChunk(logging,mng_oFFs,9L);
11998 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11999 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12001 (void) WriteBlob(image,13,chunk);
12002 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12004 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12006 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12007 PNGType(chunk,mng_vpAg);
12008 LogPNGChunk(logging,mng_vpAg,9L);
12009 PNGLong(chunk+4,(png_uint_32) image->page.width);
12010 PNGLong(chunk+8,(png_uint_32) image->page.height);
12011 chunk[12]=0; /* unit = pixels */
12012 (void) WriteBlob(image,13,chunk);
12013 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12019 if (jng_alpha_compression_method==0)
12027 /* Write IDAT chunk header */
12028 if (logging != MagickFalse)
12029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12030 " Write IDAT chunks from blob, length=%.20g.",(double)
12033 /* Copy IDAT chunks */
12036 for (i=8; i<(ssize_t) length; i+=len+12)
12038 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12041 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12043 /* Found an IDAT chunk. */
12044 (void) WriteBlobMSBULong(image,(size_t) len);
12045 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12046 (void) WriteBlob(image,(size_t) len+4,p);
12047 (void) WriteBlobMSBULong(image,
12048 crc32(0,p,(uInt) len+4));
12053 if (logging != MagickFalse)
12054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12055 " Skipping %c%c%c%c chunk, length=%.20g.",
12056 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12063 /* Write JDAA chunk header */
12064 if (logging != MagickFalse)
12065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12066 " Write JDAA chunk, length=%.20g.",(double) length);
12067 (void) WriteBlobMSBULong(image,(size_t) length);
12068 PNGType(chunk,mng_JDAA);
12069 LogPNGChunk(logging,mng_JDAA,length);
12070 /* Write JDAT chunk(s) data */
12071 (void) WriteBlob(image,4,chunk);
12072 (void) WriteBlob(image,length,blob);
12073 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12076 blob=(unsigned char *) RelinquishMagickMemory(blob);
12079 /* Encode image as a JPEG blob */
12080 if (logging != MagickFalse)
12081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12082 " Creating jpeg_image_info.");
12083 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12084 if (jpeg_image_info == (ImageInfo *) NULL)
12085 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12087 if (logging != MagickFalse)
12088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12089 " Creating jpeg_image.");
12091 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12092 if (jpeg_image == (Image *) NULL)
12093 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12094 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12096 (void) AcquireUniqueFilename(jpeg_image->filename);
12097 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12098 jpeg_image->filename);
12100 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12103 if (logging != MagickFalse)
12104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12105 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12106 (double) jpeg_image->rows);
12108 if (jng_color_type == 8 || jng_color_type == 12)
12109 jpeg_image_info->type=GrayscaleType;
12111 jpeg_image_info->quality=jng_quality;
12112 jpeg_image->quality=jng_quality;
12113 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12114 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12116 if (logging != MagickFalse)
12117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12118 " Creating blob.");
12120 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12122 if (logging != MagickFalse)
12124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12125 " Successfully read jpeg_image into a blob, length=%.20g.",
12128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12129 " Write JDAT chunk, length=%.20g.",(double) length);
12132 /* Write JDAT chunk(s) */
12133 (void) WriteBlobMSBULong(image,(size_t) length);
12134 PNGType(chunk,mng_JDAT);
12135 LogPNGChunk(logging,mng_JDAT,length);
12136 (void) WriteBlob(image,4,chunk);
12137 (void) WriteBlob(image,length,blob);
12138 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12140 jpeg_image=DestroyImage(jpeg_image);
12141 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12142 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12143 blob=(unsigned char *) RelinquishMagickMemory(blob);
12145 /* Write any JNG-chunk-e profiles */
12146 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12148 /* Write IEND chunk */
12149 (void) WriteBlobMSBULong(image,0L);
12150 PNGType(chunk,mng_IEND);
12151 LogPNGChunk(logging,mng_IEND,0);
12152 (void) WriteBlob(image,4,chunk);
12153 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12155 if (logging != MagickFalse)
12156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12157 " exit WriteOneJNGImage()");
12164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12168 % W r i t e J N G I m a g e %
12172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12174 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12176 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12178 % The format of the WriteJNGImage method is:
12180 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12181 % Image *image,ExceptionInfo *exception)
12183 % A description of each parameter follows:
12185 % o image_info: the image info.
12187 % o image: The image.
12189 % o exception: return any errors or warnings in this structure.
12191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12193 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12194 ExceptionInfo *exception)
12197 have_mng_structure,
12207 assert(image_info != (const ImageInfo *) NULL);
12208 assert(image_info->signature == MagickSignature);
12209 assert(image != (Image *) NULL);
12210 assert(image->signature == MagickSignature);
12211 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12212 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12213 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12214 if (status == MagickFalse)
12218 Allocate a MngInfo structure.
12220 have_mng_structure=MagickFalse;
12221 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12222 if (mng_info == (MngInfo *) NULL)
12223 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12225 Initialize members of the MngInfo structure.
12227 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12228 mng_info->image=image;
12229 have_mng_structure=MagickTrue;
12231 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12233 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12234 (void) CloseBlob(image);
12236 (void) CatchImageException(image);
12237 MngInfoFreeStruct(mng_info,&have_mng_structure);
12238 if (logging != MagickFalse)
12239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12244 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12245 ExceptionInfo *exception)
12254 have_mng_structure,
12257 volatile MagickBooleanType
12269 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12270 defined(PNG_MNG_FEATURES_SUPPORTED)
12273 all_images_are_gray,
12283 volatile unsigned int
12294 #if (PNG_LIBPNG_VER < 10200)
12295 if (image_info->verbose)
12296 printf("Your PNG library (libpng-%s) is rather old.\n",
12297 PNG_LIBPNG_VER_STRING);
12303 assert(image_info != (const ImageInfo *) NULL);
12304 assert(image_info->signature == MagickSignature);
12305 assert(image != (Image *) NULL);
12306 assert(image->signature == MagickSignature);
12307 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12308 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12309 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12310 if (status == MagickFalse)
12314 Allocate a MngInfo structure.
12316 have_mng_structure=MagickFalse;
12317 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12318 if (mng_info == (MngInfo *) NULL)
12319 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12321 Initialize members of the MngInfo structure.
12323 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12324 mng_info->image=image;
12325 have_mng_structure=MagickTrue;
12326 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12329 * See if user has requested a specific PNG subformat to be used
12330 * for all of the PNGs in the MNG being written, e.g.,
12332 * convert *.png png8:animation.mng
12334 * To do: check -define png:bit_depth and png:color_type as well,
12335 * or perhaps use mng:bit_depth and mng:color_type instead for
12339 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12340 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12341 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12343 write_jng=MagickFalse;
12344 if (image_info->compression == JPEGCompression)
12345 write_jng=MagickTrue;
12347 mng_info->adjoin=image_info->adjoin &&
12348 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12350 if (logging != MagickFalse)
12352 /* Log some info about the input */
12356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357 " Checking input image(s)");
12359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12360 " Image_info depth: %.20g",(double) image_info->depth);
12362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12363 " Type: %d",image_info->type);
12366 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12369 " Scene: %.20g",(double) scene++);
12371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12372 " Image depth: %.20g",(double) p->depth);
12375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12382 if (p->storage_class == PseudoClass)
12383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12384 " Storage class: PseudoClass");
12387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12388 " Storage class: DirectClass");
12391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12392 " Number of colors: %.20g",(double) p->colors);
12395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12396 " Number of colors: unspecified");
12398 if (mng_info->adjoin == MagickFalse)
12403 use_global_plte=MagickFalse;
12404 all_images_are_gray=MagickFalse;
12405 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12406 need_local_plte=MagickTrue;
12408 need_defi=MagickFalse;
12409 need_matte=MagickFalse;
12410 mng_info->framing_mode=1;
12411 mng_info->old_framing_mode=1;
12414 if (image_info->page != (char *) NULL)
12417 Determine image bounding box.
12419 SetGeometry(image,&mng_info->page);
12420 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12421 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12433 mng_info->page=image->page;
12434 need_geom=MagickTrue;
12435 if (mng_info->page.width || mng_info->page.height)
12436 need_geom=MagickFalse;
12438 Check all the scenes.
12440 initial_delay=image->delay;
12441 need_iterations=MagickFalse;
12442 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12443 mng_info->equal_physs=MagickTrue,
12444 mng_info->equal_gammas=MagickTrue;
12445 mng_info->equal_srgbs=MagickTrue;
12446 mng_info->equal_backgrounds=MagickTrue;
12448 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12449 defined(PNG_MNG_FEATURES_SUPPORTED)
12450 all_images_are_gray=MagickTrue;
12451 mng_info->equal_palettes=MagickFalse;
12452 need_local_plte=MagickFalse;
12454 for (next_image=image; next_image != (Image *) NULL; )
12458 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12459 mng_info->page.width=next_image->columns+next_image->page.x;
12461 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12462 mng_info->page.height=next_image->rows+next_image->page.y;
12465 if (next_image->page.x || next_image->page.y)
12466 need_defi=MagickTrue;
12468 if (next_image->matte)
12469 need_matte=MagickTrue;
12471 if ((int) next_image->dispose >= BackgroundDispose)
12472 if (next_image->matte || next_image->page.x || next_image->page.y ||
12473 ((next_image->columns < mng_info->page.width) &&
12474 (next_image->rows < mng_info->page.height)))
12475 mng_info->need_fram=MagickTrue;
12477 if (next_image->iterations)
12478 need_iterations=MagickTrue;
12480 final_delay=next_image->delay;
12482 if (final_delay != initial_delay || final_delay > 1UL*
12483 next_image->ticks_per_second)
12484 mng_info->need_fram=1;
12486 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12487 defined(PNG_MNG_FEATURES_SUPPORTED)
12489 check for global palette possibility.
12491 if (image->matte != MagickFalse)
12492 need_local_plte=MagickTrue;
12494 if (need_local_plte == 0)
12496 if (ImageIsGray(image,exception) == MagickFalse)
12497 all_images_are_gray=MagickFalse;
12498 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12499 if (use_global_plte == 0)
12500 use_global_plte=mng_info->equal_palettes;
12501 need_local_plte=!mng_info->equal_palettes;
12504 if (GetNextImageInList(next_image) != (Image *) NULL)
12506 if (next_image->background_color.red !=
12507 next_image->next->background_color.red ||
12508 next_image->background_color.green !=
12509 next_image->next->background_color.green ||
12510 next_image->background_color.blue !=
12511 next_image->next->background_color.blue)
12512 mng_info->equal_backgrounds=MagickFalse;
12514 if (next_image->gamma != next_image->next->gamma)
12515 mng_info->equal_gammas=MagickFalse;
12517 if (next_image->rendering_intent !=
12518 next_image->next->rendering_intent)
12519 mng_info->equal_srgbs=MagickFalse;
12521 if ((next_image->units != next_image->next->units) ||
12522 (next_image->resolution.x != next_image->next->resolution.x) ||
12523 (next_image->resolution.y != next_image->next->resolution.y))
12524 mng_info->equal_physs=MagickFalse;
12526 if (mng_info->equal_chrms)
12528 if (next_image->chromaticity.red_primary.x !=
12529 next_image->next->chromaticity.red_primary.x ||
12530 next_image->chromaticity.red_primary.y !=
12531 next_image->next->chromaticity.red_primary.y ||
12532 next_image->chromaticity.green_primary.x !=
12533 next_image->next->chromaticity.green_primary.x ||
12534 next_image->chromaticity.green_primary.y !=
12535 next_image->next->chromaticity.green_primary.y ||
12536 next_image->chromaticity.blue_primary.x !=
12537 next_image->next->chromaticity.blue_primary.x ||
12538 next_image->chromaticity.blue_primary.y !=
12539 next_image->next->chromaticity.blue_primary.y ||
12540 next_image->chromaticity.white_point.x !=
12541 next_image->next->chromaticity.white_point.x ||
12542 next_image->chromaticity.white_point.y !=
12543 next_image->next->chromaticity.white_point.y)
12544 mng_info->equal_chrms=MagickFalse;
12548 next_image=GetNextImageInList(next_image);
12550 if (image_count < 2)
12552 mng_info->equal_backgrounds=MagickFalse;
12553 mng_info->equal_chrms=MagickFalse;
12554 mng_info->equal_gammas=MagickFalse;
12555 mng_info->equal_srgbs=MagickFalse;
12556 mng_info->equal_physs=MagickFalse;
12557 use_global_plte=MagickFalse;
12558 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12559 need_local_plte=MagickTrue;
12561 need_iterations=MagickFalse;
12564 if (mng_info->need_fram == MagickFalse)
12567 Only certain framing rates 100/n are exactly representable without
12568 the FRAM chunk but we'll allow some slop in VLC files
12570 if (final_delay == 0)
12572 if (need_iterations != MagickFalse)
12575 It's probably a GIF with loop; don't run it *too* fast.
12577 if (mng_info->adjoin)
12580 (void) ThrowMagickException(exception,GetMagickModule(),
12582 "input has zero delay between all frames; assuming",
12587 mng_info->ticks_per_second=0;
12589 if (final_delay != 0)
12590 mng_info->ticks_per_second=(png_uint_32)
12591 (image->ticks_per_second/final_delay);
12592 if (final_delay > 50)
12593 mng_info->ticks_per_second=2;
12595 if (final_delay > 75)
12596 mng_info->ticks_per_second=1;
12598 if (final_delay > 125)
12599 mng_info->need_fram=MagickTrue;
12601 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12602 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12603 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12604 1UL*image->ticks_per_second))
12605 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12608 if (mng_info->need_fram != MagickFalse)
12609 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12611 If pseudocolor, we should also check to see if all the
12612 palettes are identical and write a global PLTE if they are.
12616 Write the MNG version 1.0 signature and MHDR chunk.
12618 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12619 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12620 PNGType(chunk,mng_MHDR);
12621 LogPNGChunk(logging,mng_MHDR,28L);
12622 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12623 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12624 PNGLong(chunk+12,mng_info->ticks_per_second);
12625 PNGLong(chunk+16,0L); /* layer count=unknown */
12626 PNGLong(chunk+20,0L); /* frame count=unknown */
12627 PNGLong(chunk+24,0L); /* play time=unknown */
12632 if (need_defi || mng_info->need_fram || use_global_plte)
12633 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12636 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12641 if (need_defi || mng_info->need_fram || use_global_plte)
12642 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12645 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12653 if (need_defi || mng_info->need_fram || use_global_plte)
12654 PNGLong(chunk+28,11L); /* simplicity=LC */
12657 PNGLong(chunk+28,9L); /* simplicity=VLC */
12662 if (need_defi || mng_info->need_fram || use_global_plte)
12663 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12666 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12669 (void) WriteBlob(image,32,chunk);
12670 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12671 option=GetImageOption(image_info,"mng:need-cacheoff");
12672 if (option != (const char *) NULL)
12678 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12680 PNGType(chunk,mng_nEED);
12681 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12682 (void) WriteBlobMSBULong(image,(size_t) length);
12683 LogPNGChunk(logging,mng_nEED,(size_t) length);
12685 (void) WriteBlob(image,length,chunk);
12686 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12688 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12689 (GetNextImageInList(image) != (Image *) NULL) &&
12690 (image->iterations != 1))
12693 Write MNG TERM chunk
12695 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12696 PNGType(chunk,mng_TERM);
12697 LogPNGChunk(logging,mng_TERM,10L);
12698 chunk[4]=3; /* repeat animation */
12699 chunk[5]=0; /* show last frame when done */
12700 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12701 final_delay/MagickMax(image->ticks_per_second,1)));
12703 if (image->iterations == 0)
12704 PNGLong(chunk+10,PNG_UINT_31_MAX);
12707 PNGLong(chunk+10,(png_uint_32) image->iterations);
12709 if (logging != MagickFalse)
12711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12712 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12713 final_delay/MagickMax(image->ticks_per_second,1)));
12715 if (image->iterations == 0)
12716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12717 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12721 " Image iterations: %.20g",(double) image->iterations);
12723 (void) WriteBlob(image,14,chunk);
12724 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12727 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12729 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12730 mng_info->equal_srgbs)
12733 Write MNG sRGB chunk
12735 (void) WriteBlobMSBULong(image,1L);
12736 PNGType(chunk,mng_sRGB);
12737 LogPNGChunk(logging,mng_sRGB,1L);
12739 if (image->rendering_intent != UndefinedIntent)
12740 chunk[4]=(unsigned char)
12741 Magick_RenderingIntent_to_PNG_RenderingIntent(
12742 (image->rendering_intent));
12745 chunk[4]=(unsigned char)
12746 Magick_RenderingIntent_to_PNG_RenderingIntent(
12747 (PerceptualIntent));
12749 (void) WriteBlob(image,5,chunk);
12750 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12751 mng_info->have_write_global_srgb=MagickTrue;
12756 if (image->gamma && mng_info->equal_gammas)
12759 Write MNG gAMA chunk
12761 (void) WriteBlobMSBULong(image,4L);
12762 PNGType(chunk,mng_gAMA);
12763 LogPNGChunk(logging,mng_gAMA,4L);
12764 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12765 (void) WriteBlob(image,8,chunk);
12766 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12767 mng_info->have_write_global_gama=MagickTrue;
12769 if (mng_info->equal_chrms)
12775 Write MNG cHRM chunk
12777 (void) WriteBlobMSBULong(image,32L);
12778 PNGType(chunk,mng_cHRM);
12779 LogPNGChunk(logging,mng_cHRM,32L);
12780 primary=image->chromaticity.white_point;
12781 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12782 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12783 primary=image->chromaticity.red_primary;
12784 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12785 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12786 primary=image->chromaticity.green_primary;
12787 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12788 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12789 primary=image->chromaticity.blue_primary;
12790 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12791 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12792 (void) WriteBlob(image,36,chunk);
12793 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12794 mng_info->have_write_global_chrm=MagickTrue;
12797 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12800 Write MNG pHYs chunk
12802 (void) WriteBlobMSBULong(image,9L);
12803 PNGType(chunk,mng_pHYs);
12804 LogPNGChunk(logging,mng_pHYs,9L);
12806 if (image->units == PixelsPerInchResolution)
12808 PNGLong(chunk+4,(png_uint_32)
12809 (image->resolution.x*100.0/2.54+0.5));
12811 PNGLong(chunk+8,(png_uint_32)
12812 (image->resolution.y*100.0/2.54+0.5));
12819 if (image->units == PixelsPerCentimeterResolution)
12821 PNGLong(chunk+4,(png_uint_32)
12822 (image->resolution.x*100.0+0.5));
12824 PNGLong(chunk+8,(png_uint_32)
12825 (image->resolution.y*100.0+0.5));
12832 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12833 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12837 (void) WriteBlob(image,13,chunk);
12838 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12841 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12842 or does not cover the entire frame.
12844 if (write_mng && (image->matte || image->page.x > 0 ||
12845 image->page.y > 0 || (image->page.width &&
12846 (image->page.width+image->page.x < mng_info->page.width))
12847 || (image->page.height && (image->page.height+image->page.y
12848 < mng_info->page.height))))
12850 (void) WriteBlobMSBULong(image,6L);
12851 PNGType(chunk,mng_BACK);
12852 LogPNGChunk(logging,mng_BACK,6L);
12853 red=ScaleQuantumToShort(image->background_color.red);
12854 green=ScaleQuantumToShort(image->background_color.green);
12855 blue=ScaleQuantumToShort(image->background_color.blue);
12856 PNGShort(chunk+4,red);
12857 PNGShort(chunk+6,green);
12858 PNGShort(chunk+8,blue);
12859 (void) WriteBlob(image,10,chunk);
12860 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12861 if (mng_info->equal_backgrounds)
12863 (void) WriteBlobMSBULong(image,6L);
12864 PNGType(chunk,mng_bKGD);
12865 LogPNGChunk(logging,mng_bKGD,6L);
12866 (void) WriteBlob(image,10,chunk);
12867 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12871 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12872 if ((need_local_plte == MagickFalse) &&
12873 (image->storage_class == PseudoClass) &&
12874 (all_images_are_gray == MagickFalse))
12880 Write MNG PLTE chunk
12882 data_length=3*image->colors;
12883 (void) WriteBlobMSBULong(image,data_length);
12884 PNGType(chunk,mng_PLTE);
12885 LogPNGChunk(logging,mng_PLTE,data_length);
12887 for (i=0; i < (ssize_t) image->colors; i++)
12889 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12890 image->colormap[i].red) & 0xff);
12891 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12892 image->colormap[i].green) & 0xff);
12893 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12894 image->colormap[i].blue) & 0xff);
12897 (void) WriteBlob(image,data_length+4,chunk);
12898 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12899 mng_info->have_write_global_plte=MagickTrue;
12905 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12906 defined(PNG_MNG_FEATURES_SUPPORTED)
12907 mng_info->equal_palettes=MagickFalse;
12911 if (mng_info->adjoin)
12913 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12914 defined(PNG_MNG_FEATURES_SUPPORTED)
12916 If we aren't using a global palette for the entire MNG, check to
12917 see if we can use one for two or more consecutive images.
12919 if (need_local_plte && use_global_plte && !all_images_are_gray)
12921 if (mng_info->IsPalette)
12924 When equal_palettes is true, this image has the same palette
12925 as the previous PseudoClass image
12927 mng_info->have_write_global_plte=mng_info->equal_palettes;
12928 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12929 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12932 Write MNG PLTE chunk
12937 data_length=3*image->colors;
12938 (void) WriteBlobMSBULong(image,data_length);
12939 PNGType(chunk,mng_PLTE);
12940 LogPNGChunk(logging,mng_PLTE,data_length);
12942 for (i=0; i < (ssize_t) image->colors; i++)
12944 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12945 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12946 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12949 (void) WriteBlob(image,data_length+4,chunk);
12950 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12951 (uInt) (data_length+4)));
12952 mng_info->have_write_global_plte=MagickTrue;
12956 mng_info->have_write_global_plte=MagickFalse;
12967 previous_x=mng_info->page.x;
12968 previous_y=mng_info->page.y;
12975 mng_info->page=image->page;
12976 if ((mng_info->page.x != previous_x) ||
12977 (mng_info->page.y != previous_y))
12979 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12980 PNGType(chunk,mng_DEFI);
12981 LogPNGChunk(logging,mng_DEFI,12L);
12982 chunk[4]=0; /* object 0 MSB */
12983 chunk[5]=0; /* object 0 LSB */
12984 chunk[6]=0; /* visible */
12985 chunk[7]=0; /* abstract */
12986 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12987 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12988 (void) WriteBlob(image,16,chunk);
12989 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12994 mng_info->write_mng=write_mng;
12996 if ((int) image->dispose >= 3)
12997 mng_info->framing_mode=3;
12999 if (mng_info->need_fram && mng_info->adjoin &&
13000 ((image->delay != mng_info->delay) ||
13001 (mng_info->framing_mode != mng_info->old_framing_mode)))
13003 if (image->delay == mng_info->delay)
13006 Write a MNG FRAM chunk with the new framing mode.
13008 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13009 PNGType(chunk,mng_FRAM);
13010 LogPNGChunk(logging,mng_FRAM,1L);
13011 chunk[4]=(unsigned char) mng_info->framing_mode;
13012 (void) WriteBlob(image,5,chunk);
13013 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13018 Write a MNG FRAM chunk with the delay.
13020 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13021 PNGType(chunk,mng_FRAM);
13022 LogPNGChunk(logging,mng_FRAM,10L);
13023 chunk[4]=(unsigned char) mng_info->framing_mode;
13024 chunk[5]=0; /* frame name separator (no name) */
13025 chunk[6]=2; /* flag for changing default delay */
13026 chunk[7]=0; /* flag for changing frame timeout */
13027 chunk[8]=0; /* flag for changing frame clipping */
13028 chunk[9]=0; /* flag for changing frame sync_id */
13029 PNGLong(chunk+10,(png_uint_32)
13030 ((mng_info->ticks_per_second*
13031 image->delay)/MagickMax(image->ticks_per_second,1)));
13032 (void) WriteBlob(image,14,chunk);
13033 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13034 mng_info->delay=(png_uint_32) image->delay;
13036 mng_info->old_framing_mode=mng_info->framing_mode;
13039 #if defined(JNG_SUPPORTED)
13040 if (image_info->compression == JPEGCompression)
13045 if (logging != MagickFalse)
13046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13047 " Writing JNG object.");
13048 /* To do: specify the desired alpha compression method. */
13049 write_info=CloneImageInfo(image_info);
13050 write_info->compression=UndefinedCompression;
13051 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13052 write_info=DestroyImageInfo(write_info);
13057 if (logging != MagickFalse)
13058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13059 " Writing PNG object.");
13061 mng_info->need_blob = MagickFalse;
13062 mng_info->ping_preserve_colormap = MagickFalse;
13064 /* We don't want any ancillary chunks written */
13065 mng_info->ping_exclude_bKGD=MagickTrue;
13066 mng_info->ping_exclude_cHRM=MagickTrue;
13067 mng_info->ping_exclude_date=MagickTrue;
13068 mng_info->ping_exclude_EXIF=MagickTrue;
13069 mng_info->ping_exclude_gAMA=MagickTrue;
13070 mng_info->ping_exclude_iCCP=MagickTrue;
13071 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13072 mng_info->ping_exclude_oFFs=MagickTrue;
13073 mng_info->ping_exclude_pHYs=MagickTrue;
13074 mng_info->ping_exclude_sRGB=MagickTrue;
13075 mng_info->ping_exclude_tEXt=MagickTrue;
13076 mng_info->ping_exclude_tRNS=MagickTrue;
13077 mng_info->ping_exclude_vpAg=MagickTrue;
13078 mng_info->ping_exclude_zCCP=MagickTrue;
13079 mng_info->ping_exclude_zTXt=MagickTrue;
13081 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13084 if (status == MagickFalse)
13086 MngInfoFreeStruct(mng_info,&have_mng_structure);
13087 (void) CloseBlob(image);
13088 return(MagickFalse);
13090 (void) CatchImageException(image);
13091 if (GetNextImageInList(image) == (Image *) NULL)
13093 image=SyncNextImageInList(image);
13094 status=SetImageProgress(image,SaveImagesTag,scene++,
13095 GetImageListLength(image));
13097 if (status == MagickFalse)
13100 } while (mng_info->adjoin);
13104 while (GetPreviousImageInList(image) != (Image *) NULL)
13105 image=GetPreviousImageInList(image);
13107 Write the MEND chunk.
13109 (void) WriteBlobMSBULong(image,0x00000000L);
13110 PNGType(chunk,mng_MEND);
13111 LogPNGChunk(logging,mng_MEND,0L);
13112 (void) WriteBlob(image,4,chunk);
13113 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13116 Relinquish resources.
13118 (void) CloseBlob(image);
13119 MngInfoFreeStruct(mng_info,&have_mng_structure);
13121 if (logging != MagickFalse)
13122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13124 return(MagickTrue);
13126 #else /* PNG_LIBPNG_VER > 10011 */
13128 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13131 printf("Your PNG library is too old: You have libpng-%s\n",
13132 PNG_LIBPNG_VER_STRING);
13134 ThrowBinaryException(CoderError,"PNG library is too old",
13135 image_info->filename);
13138 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13140 return(WritePNGImage(image_info,image));
13142 #endif /* PNG_LIBPNG_VER > 10011 */