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)
2344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
2347 if (png_get_sRGB(ping,ping_info,&intent))
2349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2373 if (mng_info->have_global_chrm != MagickFalse)
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
2399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2404 if (image->rendering_intent != UndefinedIntent)
2406 png_set_sRGB(ping,ping_info,
2407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
2409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2414 #if defined(PNG_oFFs_SUPPORTED)
2415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
2427 #if defined(PNG_pHYs_SUPPORTED)
2428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2430 if (mng_info->have_global_phys)
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2442 Set image resolution.
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
2449 if (unit_type == PNG_RESOLUTION_METER)
2451 image->units=PixelsPerCentimeterResolution;
2452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
2456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
2463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2473 if ((number_colors == 0) &&
2474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2476 if (mng_info->global_plte_length)
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
2481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2482 if (mng_info->global_trns_length)
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
2486 (void) ThrowMagickException(exception,
2487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2493 #ifdef PNG_READ_bKGD_SUPPORTED
2495 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2503 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
2507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
2510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
2513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
2516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
2519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2522 png_set_bKGD(ping,ping_info,&background);
2527 (void) ThrowMagickException(exception,GetMagickModule(),
2528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2533 #ifdef PNG_READ_bKGD_SUPPORTED
2534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2536 image->background_color=mng_info->mng_global_bkgd;
2538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2544 Set image background color.
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
2550 /* Scale background components to 16-bit, then scale
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
2560 if (ping_bit_depth == 1)
2563 else if (ping_bit_depth == 2)
2566 else if (ping_bit_depth == 4)
2569 if (ping_bit_depth <= 8)
2572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
2576 if (logging != MagickFalse)
2578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
2581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
2586 image->background_color.red=
2587 ScaleShortToQuantum(ping_background->red);
2589 image->background_color.green=
2590 ScaleShortToQuantum(ping_background->green);
2592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
2595 image->background_color.alpha=OpaqueAlpha;
2597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
2604 #endif /* PNG_READ_bKGD_SUPPORTED */
2606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2609 Image has a tRNS chunk.
2617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2621 max_sample = (int) ((one << ping_bit_depth) - 1);
2623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
2633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2635 image->matte=MagickFalse;
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2652 if (logging != MagickFalse)
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2658 " scaled graylevel is %.20g.",transparent_color.alpha);
2660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
2666 #if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2673 num_passes=png_set_interlace_handling(ping);
2675 png_read_update_info(ping,ping_info);
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2680 Initialize image structure.
2682 mng_info->image_box.left=0;
2683 mng_info->image_box.right=(ssize_t) ping_width;
2684 mng_info->image_box.top=0;
2685 mng_info->image_box.bottom=(ssize_t) ping_height;
2686 if (mng_info->mng_type == 0)
2688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
2690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2699 image->compression=ZipCompression;
2700 image->columns=ping_width;
2701 image->rows=ping_height;
2702 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2704 image->colorspace=GRAYColorspace;
2705 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2706 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2711 image->storage_class=PseudoClass;
2713 image->colors=one << ping_bit_depth;
2714 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2715 if (image->colors > 256)
2718 if (image->colors > 65536L)
2719 image->colors=65536L;
2721 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2729 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2730 image->colors=(size_t) number_colors;
2732 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2738 if (image->storage_class == PseudoClass)
2741 Initialize image colormap.
2743 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2744 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2746 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2754 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2756 for (i=0; i < (ssize_t) number_colors; i++)
2758 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2759 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2760 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2763 for ( ; i < (ssize_t) image->colors; i++)
2765 image->colormap[i].red=0;
2766 image->colormap[i].green=0;
2767 image->colormap[i].blue=0;
2776 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2781 for (i=0; i < (ssize_t) image->colors; i++)
2783 image->colormap[i].red=(Quantum) (i*scale);
2784 image->colormap[i].green=(Quantum) (i*scale);
2785 image->colormap[i].blue=(Quantum) (i*scale);
2790 /* Set some properties for reporting by "identify" */
2795 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2796 ping_interlace_method in value */
2798 (void) FormatLocaleString(msg,MaxTextExtent,
2799 "%d, %d",(int) ping_width, (int) ping_height);
2800 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
2802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2803 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
2805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2806 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
2808 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2809 (int) ping_interlace_method);
2810 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2814 Read image scanlines.
2816 if (image->delay != 0)
2817 mng_info->scenes_found++;
2819 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2820 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2821 (image_info->first_scene+image_info->number_scenes))))
2823 if (logging != MagickFalse)
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2825 " Skipping PNG image data for scene %.20g",(double)
2826 mng_info->scenes_found-1);
2827 png_destroy_read_struct(&ping,&ping_info,&end_info);
2828 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2829 UnlockSemaphoreInfo(ping_semaphore);
2831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " exit ReadOnePNGImage().");
2838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " Reading PNG IDAT chunk(s)");
2843 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2844 ping_rowbytes*sizeof(*ping_pixels));
2847 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2848 sizeof(*ping_pixels));
2850 if (ping_pixels == (unsigned char *) NULL)
2851 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2853 if (logging != MagickFalse)
2854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2855 " Converting PNG pixels to pixel packets");
2857 Convert PNG pixels to pixel packets.
2859 if (setjmp(png_jmpbuf(ping)))
2862 PNG image is corrupt.
2864 png_destroy_read_struct(&ping,&ping_info,&end_info);
2865 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2866 UnlockSemaphoreInfo(ping_semaphore);
2868 if (quantum_info != (QuantumInfo *) NULL)
2869 quantum_info = DestroyQuantumInfo(quantum_info);
2871 if (ping_pixels != (unsigned char *) NULL)
2872 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2874 if (logging != MagickFalse)
2875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2876 " exit ReadOnePNGImage() with error.");
2878 if (image != (Image *) NULL)
2880 InheritException(exception,exception);
2884 return(GetFirstImageInList(image));
2887 quantum_info=AcquireQuantumInfo(image_info,image);
2889 if (quantum_info == (QuantumInfo *) NULL)
2890 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2895 found_transparent_pixel;
2897 found_transparent_pixel=MagickFalse;
2899 if (image->storage_class == DirectClass)
2901 for (pass=0; pass < num_passes; pass++)
2904 Convert image to DirectClass pixel packets.
2906 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2910 depth=(ssize_t) ping_bit_depth;
2912 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2913 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2914 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2915 MagickTrue : MagickFalse;
2917 for (y=0; y < (ssize_t) image->rows; y++)
2920 row_offset=ping_rowbytes*y;
2925 png_read_row(ping,ping_pixels+row_offset,NULL);
2926 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2928 if (q == (Quantum *) NULL)
2931 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2932 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2933 GrayQuantum,ping_pixels+row_offset,exception);
2935 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2936 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2937 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2939 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2940 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2941 RGBAQuantum,ping_pixels+row_offset,exception);
2943 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2944 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2945 IndexQuantum,ping_pixels+row_offset,exception);
2947 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2948 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2949 RGBQuantum,ping_pixels+row_offset,exception);
2951 if (found_transparent_pixel == MagickFalse)
2953 /* Is there a transparent pixel in the row? */
2954 if (y== 0 && logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " Looking for cheap transparent pixel");
2958 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2960 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2961 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2962 (GetPixelAlpha(image,q) != OpaqueAlpha))
2964 if (logging != MagickFalse)
2965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2968 found_transparent_pixel = MagickTrue;
2971 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2972 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2973 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2974 transparent_color.red &&
2975 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2976 transparent_color.green &&
2977 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2978 transparent_color.blue))
2980 if (logging != MagickFalse)
2981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2983 found_transparent_pixel = MagickTrue;
2986 q+=GetPixelChannels(image);
2990 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2992 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2995 if (status == MagickFalse)
2998 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3002 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3004 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3005 if (status == MagickFalse)
3011 else /* image->storage_class != DirectClass */
3013 for (pass=0; pass < num_passes; pass++)
3022 Convert grayscale image to PseudoClass pixel packets.
3024 if (logging != MagickFalse)
3025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3026 " Converting grayscale pixels to pixel packets");
3028 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3029 MagickTrue : MagickFalse;
3031 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3032 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
3034 if (quantum_scanline == (Quantum *) NULL)
3035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3037 for (y=0; y < (ssize_t) image->rows; y++)
3040 row_offset=ping_rowbytes*y;
3045 png_read_row(ping,ping_pixels+row_offset,NULL);
3046 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3048 if (q == (Quantum *) NULL)
3051 p=ping_pixels+row_offset;
3054 switch (ping_bit_depth)
3061 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3063 for (bit=7; bit >= 0; bit--)
3064 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3068 if ((image->columns % 8) != 0)
3070 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3071 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3079 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3081 *r++=(*p >> 6) & 0x03;
3082 *r++=(*p >> 4) & 0x03;
3083 *r++=(*p >> 2) & 0x03;
3087 if ((image->columns % 4) != 0)
3089 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3090 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3098 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3100 *r++=(*p >> 4) & 0x0f;
3104 if ((image->columns % 2) != 0)
3105 *r++=(*p++ >> 4) & 0x0f;
3112 if (ping_color_type == 4)
3113 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3116 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3117 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3118 found_transparent_pixel = MagickTrue;
3119 q+=GetPixelChannels(image);
3123 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3131 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3133 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3137 if (image->colors > 256)
3138 quantum=((*p++) << 8);
3144 *r=ScaleShortToQuantum(quantum);
3147 if (ping_color_type == 4)
3149 if (image->colors > 256)
3150 quantum=((*p++) << 8);
3155 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3156 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3157 found_transparent_pixel = MagickTrue;
3158 q+=GetPixelChannels(image);
3161 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3163 p++; /* strip low byte */
3165 if (ping_color_type == 4)
3167 SetPixelAlpha(image,*p++,q);
3168 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3169 found_transparent_pixel = MagickTrue;
3171 q+=GetPixelChannels(image);
3184 Transfer image scanline.
3188 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3190 if (q == (Quantum *) NULL)
3192 for (x=0; x < (ssize_t) image->columns; x++)
3194 SetPixelIndex(image,*r++,q);
3195 q+=GetPixelChannels(image);
3198 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3201 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3203 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3206 if (status == MagickFalse)
3211 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3213 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3215 if (status == MagickFalse)
3219 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3222 image->matte=found_transparent_pixel;
3224 if (logging != MagickFalse)
3226 if (found_transparent_pixel != MagickFalse)
3227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3228 " Found transparent pixel");
3231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3232 " No transparent pixel was found");
3234 ping_color_type&=0x03;
3239 if (quantum_info != (QuantumInfo *) NULL)
3240 quantum_info=DestroyQuantumInfo(quantum_info);
3242 if (image->storage_class == PseudoClass)
3248 image->matte=MagickFalse;
3249 (void) SyncImage(image,exception);
3253 png_read_end(ping,end_info);
3255 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3256 (ssize_t) image_info->first_scene && image->delay != 0)
3258 png_destroy_read_struct(&ping,&ping_info,&end_info);
3259 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3261 (void) SetImageBackgroundColor(image,exception);
3262 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3263 UnlockSemaphoreInfo(ping_semaphore);
3265 if (logging != MagickFalse)
3266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3267 " exit ReadOnePNGImage() early.");
3271 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3277 Image has a transparent background.
3279 storage_class=image->storage_class;
3280 image->matte=MagickTrue;
3282 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3284 if (storage_class == PseudoClass)
3286 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3288 for (x=0; x < ping_num_trans; x++)
3290 image->colormap[x].matte=MagickTrue;
3291 image->colormap[x].alpha =
3292 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3296 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3298 for (x=0; x < (int) image->colors; x++)
3300 if (ScaleQuantumToShort(image->colormap[x].red) ==
3301 transparent_color.alpha)
3303 image->colormap[x].matte=MagickTrue;
3304 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3308 (void) SyncImage(image,exception);
3311 #if 1 /* Should have already been done above, but glennrp problem P10
3316 for (y=0; y < (ssize_t) image->rows; y++)
3318 image->storage_class=storage_class;
3319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3321 if (q == (Quantum *) NULL)
3325 /* Caution: on a Q8 build, this does not distinguish between
3326 * 16-bit colors that differ only in the low byte
3328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3330 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3331 transparent_color.red &&
3332 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3333 transparent_color.green &&
3334 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3335 transparent_color.blue)
3337 SetPixelAlpha(image,TransparentAlpha,q);
3340 #if 0 /* I have not found a case where this is needed. */
3343 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3347 q+=GetPixelChannels(image);
3350 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3356 image->storage_class=DirectClass;
3359 for (j = 0; j < 2; j++)
3362 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3363 MagickTrue : MagickFalse;
3365 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3366 MagickTrue : MagickFalse;
3368 if (status != MagickFalse)
3369 for (i=0; i < (ssize_t) num_text; i++)
3371 /* Check for a profile */
3373 if (logging != MagickFalse)
3374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375 " Reading PNG text chunk");
3377 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3379 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3389 length=text[i].text_length;
3390 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3392 if (value == (char *) NULL)
3394 (void) ThrowMagickException(exception,GetMagickModule(),
3395 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3400 (void) ConcatenateMagickString(value,text[i].text,length+2);
3402 /* Don't save "density" or "units" property if we have a pHYs
3405 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3406 (LocaleCompare(text[i].key,"density") != 0 &&
3407 LocaleCompare(text[i].key,"units") != 0))
3408 (void) SetImageProperty(image,text[i].key,value,exception);
3410 if (logging != MagickFalse)
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3413 " length: %lu",(unsigned long) length);
3414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415 " Keyword: %s",text[i].key);
3418 value=DestroyString(value);
3421 num_text_total += num_text;
3424 #ifdef MNG_OBJECT_BUFFERS
3426 Store the object if necessary.
3428 if (object_id && !mng_info->frozen[object_id])
3430 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3433 create a new object buffer.
3435 mng_info->ob[object_id]=(MngBuffer *)
3436 AcquireMagickMemory(sizeof(MngBuffer));
3438 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3440 mng_info->ob[object_id]->image=(Image *) NULL;
3441 mng_info->ob[object_id]->reference_count=1;
3445 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3446 mng_info->ob[object_id]->frozen)
3448 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3449 (void) ThrowMagickException(exception,GetMagickModule(),
3450 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3453 if (mng_info->ob[object_id]->frozen)
3454 (void) ThrowMagickException(exception,GetMagickModule(),
3455 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3456 "`%s'",image->filename);
3462 if (mng_info->ob[object_id]->image != (Image *) NULL)
3463 mng_info->ob[object_id]->image=DestroyImage
3464 (mng_info->ob[object_id]->image);
3466 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3469 if (mng_info->ob[object_id]->image != (Image *) NULL)
3470 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3473 (void) ThrowMagickException(exception,GetMagickModule(),
3474 ResourceLimitError,"Cloning image for object buffer failed",
3475 "`%s'",image->filename);
3477 if (ping_width > 250000L || ping_height > 250000L)
3478 png_error(ping,"PNG Image dimensions are too large.");
3480 mng_info->ob[object_id]->width=ping_width;
3481 mng_info->ob[object_id]->height=ping_height;
3482 mng_info->ob[object_id]->color_type=ping_color_type;
3483 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3484 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3485 mng_info->ob[object_id]->compression_method=
3486 ping_compression_method;
3487 mng_info->ob[object_id]->filter_method=ping_filter_method;
3489 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3498 Copy the PLTE to the object buffer.
3500 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3501 mng_info->ob[object_id]->plte_length=number_colors;
3503 for (i=0; i < number_colors; i++)
3505 mng_info->ob[object_id]->plte[i]=plte[i];
3510 mng_info->ob[object_id]->plte_length=0;
3515 /* Set image->matte to MagickTrue if the input colortype supports
3516 * alpha or if a valid tRNS chunk is present, no matter whether there
3517 * is actual transparency present.
3519 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3520 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3521 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3522 MagickTrue : MagickFalse;
3524 /* Set more properties for identify to retrieve */
3529 if (num_text_total != 0)
3531 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3532 (void) FormatLocaleString(msg,MaxTextExtent,
3533 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3534 (void) SetImageProperty(image,"png:text ",msg,
3538 if (num_raw_profiles != 0)
3540 (void) FormatLocaleString(msg,MaxTextExtent,
3541 "%d were found", num_raw_profiles);
3542 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3546 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3548 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3549 "chunk was found (see Chromaticity, above)");
3550 (void) SetImageProperty(image,"png:cHRM ",msg,
3554 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3556 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3557 "chunk was found (see Background color, above)");
3558 (void) SetImageProperty(image,"png:bKGD ",msg,
3562 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3565 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3566 (void) SetImageProperty(image,"png:iCCP ",msg,
3569 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3570 (void) SetImageProperty(image,"png:tRNS ",msg,
3573 #if defined(PNG_sRGB_SUPPORTED)
3574 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3576 (void) FormatLocaleString(msg,MaxTextExtent,
3577 "intent=%d (See Rendering intent)",
3579 (void) SetImageProperty(image,"png:sRGB ",msg,
3584 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3586 (void) FormatLocaleString(msg,MaxTextExtent,
3587 "gamma=%.8g (See Gamma, above)",
3589 (void) SetImageProperty(image,"png:gAMA ",msg,
3593 #if defined(PNG_pHYs_SUPPORTED)
3594 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3596 (void) FormatLocaleString(msg,MaxTextExtent,
3597 "x_res=%.10g, y_res=%.10g, units=%d",
3598 (double) x_resolution,(double) y_resolution, unit_type);
3599 (void) SetImageProperty(image,"png:pHYs ",msg,
3604 #if defined(PNG_oFFs_SUPPORTED)
3605 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3607 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3608 (double) image->page.x,(double) image->page.y);
3609 (void) SetImageProperty(image,"png:oFFs ",msg,
3614 if ((image->page.width != 0 && image->page.width != image->columns) ||
3615 (image->page.height != 0 && image->page.height != image->rows))
3617 (void) FormatLocaleString(msg,MaxTextExtent,
3618 "width=%.20g, height=%.20g",
3619 (double) image->page.width,(double) image->page.height);
3620 (void) SetImageProperty(image,"png:vpAg ",msg,
3626 Relinquish resources.
3628 png_destroy_read_struct(&ping,&ping_info,&end_info);
3630 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3631 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3632 UnlockSemaphoreInfo(ping_semaphore);
3635 if (logging != MagickFalse)
3636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3637 " exit ReadOnePNGImage()");
3641 /* end of reading one PNG image */
3644 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3659 magic_number[MaxTextExtent];
3667 assert(image_info != (const ImageInfo *) NULL);
3668 assert(image_info->signature == MagickSignature);
3670 if (image_info->debug != MagickFalse)
3671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3672 image_info->filename);
3674 assert(exception != (ExceptionInfo *) NULL);
3675 assert(exception->signature == MagickSignature);
3676 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3677 image=AcquireImage(image_info,exception);
3678 mng_info=(MngInfo *) NULL;
3679 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3681 if (status == MagickFalse)
3682 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3685 Verify PNG signature.
3687 count=ReadBlob(image,8,(unsigned char *) magic_number);
3689 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3690 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3693 Allocate a MngInfo structure.
3695 have_mng_structure=MagickFalse;
3696 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3698 if (mng_info == (MngInfo *) NULL)
3699 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3702 Initialize members of the MngInfo structure.
3704 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3705 mng_info->image=image;
3706 have_mng_structure=MagickTrue;
3709 image=ReadOnePNGImage(mng_info,image_info,exception);
3710 MngInfoFreeStruct(mng_info,&have_mng_structure);
3712 if (image == (Image *) NULL)
3714 if (previous != (Image *) NULL)
3716 if (previous->signature != MagickSignature)
3717 ThrowReaderException(CorruptImageError,"CorruptImage");
3719 (void) CloseBlob(previous);
3720 (void) DestroyImageList(previous);
3723 if (logging != MagickFalse)
3724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725 "exit ReadPNGImage() with error");
3727 return((Image *) NULL);
3730 (void) CloseBlob(image);
3732 if ((image->columns == 0) || (image->rows == 0))
3734 if (logging != MagickFalse)
3735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3736 "exit ReadPNGImage() with error.");
3738 ThrowReaderException(CorruptImageError,"CorruptImage");
3741 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3743 (void) SetImageType(image,TrueColorType,exception);
3744 image->matte=MagickFalse;
3747 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3748 (void) SetImageType(image,TrueColorMatteType,exception);
3750 if (logging != MagickFalse)
3751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3752 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3753 (double) image->page.width,(double) image->page.height,
3754 (double) image->page.x,(double) image->page.y);
3756 if (logging != MagickFalse)
3757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3764 #if defined(JNG_SUPPORTED)
3766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3770 % R e a d O n e J N G I m a g e %
3774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3776 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3777 % (minus the 8-byte signature) and returns it. It allocates the memory
3778 % necessary for the new Image structure and returns a pointer to the new
3781 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3783 % The format of the ReadOneJNGImage method is:
3785 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3786 % ExceptionInfo *exception)
3788 % A description of each parameter follows:
3790 % o mng_info: Specifies a pointer to a MngInfo structure.
3792 % o image_info: the image info.
3794 % o exception: return any errors or warnings in this structure.
3797 static Image *ReadOneJNGImage(MngInfo *mng_info,
3798 const ImageInfo *image_info, ExceptionInfo *exception)
3825 jng_image_sample_depth,
3826 jng_image_compression_method,
3827 jng_image_interlace_method,
3828 jng_alpha_sample_depth,
3829 jng_alpha_compression_method,
3830 jng_alpha_filter_method,
3831 jng_alpha_interlace_method;
3833 register const Quantum
3843 register unsigned char
3854 jng_alpha_compression_method=0;
3855 jng_alpha_sample_depth=8;
3859 alpha_image=(Image *) NULL;
3860 color_image=(Image *) NULL;
3861 alpha_image_info=(ImageInfo *) NULL;
3862 color_image_info=(ImageInfo *) NULL;
3864 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3865 " Enter ReadOneJNGImage()");
3867 image=mng_info->image;
3869 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3872 Allocate next image structure.
3874 if (logging != MagickFalse)
3875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3876 " AcquireNextImage()");
3878 AcquireNextImage(image_info,image,exception);
3880 if (GetNextImageInList(image) == (Image *) NULL)
3881 return((Image *) NULL);
3883 image=SyncNextImageInList(image);
3885 mng_info->image=image;
3888 Signature bytes have already been read.
3891 read_JSEP=MagickFalse;
3892 reading_idat=MagickFalse;
3893 skip_to_iend=MagickFalse;
3897 type[MaxTextExtent];
3906 Read a new JNG chunk.
3908 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3909 2*GetBlobSize(image));
3911 if (status == MagickFalse)
3915 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3916 length=ReadBlobMSBLong(image);
3917 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3919 if (logging != MagickFalse)
3920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3921 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3922 type[0],type[1],type[2],type[3],(double) length);
3924 if (length > PNG_UINT_31_MAX || count == 0)
3925 ThrowReaderException(CorruptImageError,"CorruptImage");
3928 chunk=(unsigned char *) NULL;
3932 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3934 if (chunk == (unsigned char *) NULL)
3935 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3937 for (i=0; i < (ssize_t) length; i++)
3938 chunk[i]=(unsigned char) ReadBlobByte(image);
3943 (void) ReadBlobMSBLong(image); /* read crc word */
3948 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3953 if (memcmp(type,mng_JHDR,4) == 0)
3957 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3958 (p[2] << 8) | p[3]);
3959 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3960 (p[6] << 8) | p[7]);
3961 jng_color_type=p[8];
3962 jng_image_sample_depth=p[9];
3963 jng_image_compression_method=p[10];
3964 jng_image_interlace_method=p[11];
3966 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3969 jng_alpha_sample_depth=p[12];
3970 jng_alpha_compression_method=p[13];
3971 jng_alpha_filter_method=p[14];
3972 jng_alpha_interlace_method=p[15];
3974 if (logging != MagickFalse)
3976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977 " jng_width: %16lu",(unsigned long) jng_width);
3979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3980 " jng_width: %16lu",(unsigned long) jng_height);
3982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3983 " jng_color_type: %16d",jng_color_type);
3985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3986 " jng_image_sample_depth: %3d",
3987 jng_image_sample_depth);
3989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3990 " jng_image_compression_method:%3d",
3991 jng_image_compression_method);
3993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " jng_image_interlace_method: %3d",
3995 jng_image_interlace_method);
3997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " jng_alpha_sample_depth: %3d",
3999 jng_alpha_sample_depth);
4001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " jng_alpha_compression_method:%3d",
4003 jng_alpha_compression_method);
4005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 " jng_alpha_filter_method: %3d",
4007 jng_alpha_filter_method);
4009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4010 " jng_alpha_interlace_method: %3d",
4011 jng_alpha_interlace_method);
4016 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4022 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4023 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4024 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4027 o create color_image
4028 o open color_blob, attached to color_image
4029 o if (color type has alpha)
4030 open alpha_blob, attached to alpha_image
4033 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4035 if (color_image_info == (ImageInfo *) NULL)
4036 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4038 GetImageInfo(color_image_info);
4039 color_image=AcquireImage(color_image_info,exception);
4041 if (color_image == (Image *) NULL)
4042 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4044 if (logging != MagickFalse)
4045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4046 " Creating color_blob.");
4048 (void) AcquireUniqueFilename(color_image->filename);
4049 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4052 if (status == MagickFalse)
4053 return((Image *) NULL);
4055 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4057 alpha_image_info=(ImageInfo *)
4058 AcquireMagickMemory(sizeof(ImageInfo));
4060 if (alpha_image_info == (ImageInfo *) NULL)
4061 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4063 GetImageInfo(alpha_image_info);
4064 alpha_image=AcquireImage(alpha_image_info,exception);
4066 if (alpha_image == (Image *) NULL)
4068 alpha_image=DestroyImage(alpha_image);
4069 ThrowReaderException(ResourceLimitError,
4070 "MemoryAllocationFailed");
4073 if (logging != MagickFalse)
4074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4075 " Creating alpha_blob.");
4077 (void) AcquireUniqueFilename(alpha_image->filename);
4078 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4081 if (status == MagickFalse)
4082 return((Image *) NULL);
4084 if (jng_alpha_compression_method == 0)
4089 if (logging != MagickFalse)
4090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4091 " Writing IHDR chunk to alpha_blob.");
4093 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4094 "\211PNG\r\n\032\n");
4096 (void) WriteBlobMSBULong(alpha_image,13L);
4097 PNGType(data,mng_IHDR);
4098 LogPNGChunk(logging,mng_IHDR,13L);
4099 PNGLong(data+4,jng_width);
4100 PNGLong(data+8,jng_height);
4101 data[12]=jng_alpha_sample_depth;
4102 data[13]=0; /* color_type gray */
4103 data[14]=0; /* compression method 0 */
4104 data[15]=0; /* filter_method 0 */
4105 data[16]=0; /* interlace_method 0 */
4106 (void) WriteBlob(alpha_image,17,data);
4107 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4110 reading_idat=MagickTrue;
4113 if (memcmp(type,mng_JDAT,4) == 0)
4115 /* Copy chunk to color_image->blob */
4117 if (logging != MagickFalse)
4118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4119 " Copying JDAT chunk data to color_blob.");
4121 (void) WriteBlob(color_image,length,chunk);
4124 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4129 if (memcmp(type,mng_IDAT,4) == 0)
4134 /* Copy IDAT header and chunk data to alpha_image->blob */
4136 if (image_info->ping == MagickFalse)
4138 if (logging != MagickFalse)
4139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4140 " Copying IDAT chunk data to alpha_blob.");
4142 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4143 PNGType(data,mng_IDAT);
4144 LogPNGChunk(logging,mng_IDAT,length);
4145 (void) WriteBlob(alpha_image,4,data);
4146 (void) WriteBlob(alpha_image,length,chunk);
4147 (void) WriteBlobMSBULong(alpha_image,
4148 crc32(crc32(0,data,4),chunk,(uInt) length));
4152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4157 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4159 /* Copy chunk data to alpha_image->blob */
4161 if (image_info->ping == MagickFalse)
4163 if (logging != MagickFalse)
4164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4165 " Copying JDAA chunk data to alpha_blob.");
4167 (void) WriteBlob(alpha_image,length,chunk);
4171 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4176 if (memcmp(type,mng_JSEP,4) == 0)
4178 read_JSEP=MagickTrue;
4181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4186 if (memcmp(type,mng_bKGD,4) == 0)
4190 image->background_color.red=ScaleCharToQuantum(p[1]);
4191 image->background_color.green=image->background_color.red;
4192 image->background_color.blue=image->background_color.red;
4197 image->background_color.red=ScaleCharToQuantum(p[1]);
4198 image->background_color.green=ScaleCharToQuantum(p[3]);
4199 image->background_color.blue=ScaleCharToQuantum(p[5]);
4202 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4206 if (memcmp(type,mng_gAMA,4) == 0)
4209 image->gamma=((float) mng_get_long(p))*0.00001;
4211 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4215 if (memcmp(type,mng_cHRM,4) == 0)
4219 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4220 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4221 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4222 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4223 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4224 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4225 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4226 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4229 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4233 if (memcmp(type,mng_sRGB,4) == 0)
4237 image->rendering_intent=
4238 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4239 image->gamma=0.45455f;
4240 image->chromaticity.red_primary.x=0.6400f;
4241 image->chromaticity.red_primary.y=0.3300f;
4242 image->chromaticity.green_primary.x=0.3000f;
4243 image->chromaticity.green_primary.y=0.6000f;
4244 image->chromaticity.blue_primary.x=0.1500f;
4245 image->chromaticity.blue_primary.y=0.0600f;
4246 image->chromaticity.white_point.x=0.3127f;
4247 image->chromaticity.white_point.y=0.3290f;
4250 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4254 if (memcmp(type,mng_oFFs,4) == 0)
4258 image->page.x=(ssize_t) mng_get_long(p);
4259 image->page.y=(ssize_t) mng_get_long(&p[4]);
4261 if ((int) p[8] != 0)
4263 image->page.x/=10000;
4264 image->page.y/=10000;
4269 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4274 if (memcmp(type,mng_pHYs,4) == 0)
4278 image->resolution.x=(double) mng_get_long(p);
4279 image->resolution.y=(double) mng_get_long(&p[4]);
4280 if ((int) p[8] == PNG_RESOLUTION_METER)
4282 image->units=PixelsPerCentimeterResolution;
4283 image->resolution.x=image->resolution.x/100.0f;
4284 image->resolution.y=image->resolution.y/100.0f;
4288 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4293 if (memcmp(type,mng_iCCP,4) == 0)
4297 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4306 if (memcmp(type,mng_IEND,4))
4316 Finish up reading image data:
4318 o read main image from color_blob.
4322 o if (color_type has alpha)
4323 if alpha_encoding is PNG
4324 read secondary image from alpha_blob via ReadPNG
4325 if alpha_encoding is JPEG
4326 read secondary image from alpha_blob via ReadJPEG
4330 o copy intensity of secondary image into
4331 alpha samples of main image.
4333 o destroy the secondary image.
4336 (void) CloseBlob(color_image);
4338 if (logging != MagickFalse)
4339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4340 " Reading jng_image from color_blob.");
4342 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4343 color_image->filename);
4345 color_image_info->ping=MagickFalse; /* To do: avoid this */
4346 jng_image=ReadImage(color_image_info,exception);
4348 if (jng_image == (Image *) NULL)
4349 return((Image *) NULL);
4351 (void) RelinquishUniqueFileResource(color_image->filename);
4352 color_image=DestroyImage(color_image);
4353 color_image_info=DestroyImageInfo(color_image_info);
4355 if (jng_image == (Image *) NULL)
4356 return((Image *) NULL);
4358 if (logging != MagickFalse)
4359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360 " Copying jng_image pixels to main image.");
4362 image->rows=jng_height;
4363 image->columns=jng_width;
4365 for (y=0; y < (ssize_t) image->rows; y++)
4367 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4368 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4369 for (x=(ssize_t) image->columns; x != 0; x--)
4371 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4372 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4373 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4374 q+=GetPixelChannels(image);
4375 s+=GetPixelChannels(jng_image);
4378 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4382 jng_image=DestroyImage(jng_image);
4384 if (image_info->ping == MagickFalse)
4386 if (jng_color_type >= 12)
4388 if (jng_alpha_compression_method == 0)
4392 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4393 PNGType(data,mng_IEND);
4394 LogPNGChunk(logging,mng_IEND,0L);
4395 (void) WriteBlob(alpha_image,4,data);
4396 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4399 (void) CloseBlob(alpha_image);
4401 if (logging != MagickFalse)
4402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4403 " Reading alpha from alpha_blob.");
4405 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4406 "%s",alpha_image->filename);
4408 jng_image=ReadImage(alpha_image_info,exception);
4410 if (jng_image != (Image *) NULL)
4411 for (y=0; y < (ssize_t) image->rows; y++)
4413 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4415 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4417 if (image->matte != MagickFalse)
4418 for (x=(ssize_t) image->columns; x != 0; x--)
4420 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4421 q+=GetPixelChannels(image);
4422 s+=GetPixelChannels(jng_image);
4426 for (x=(ssize_t) image->columns; x != 0; x--)
4428 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4429 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4430 image->matte=MagickTrue;
4431 q+=GetPixelChannels(image);
4432 s+=GetPixelChannels(jng_image);
4435 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4438 (void) RelinquishUniqueFileResource(alpha_image->filename);
4439 alpha_image=DestroyImage(alpha_image);
4440 alpha_image_info=DestroyImageInfo(alpha_image_info);
4441 if (jng_image != (Image *) NULL)
4442 jng_image=DestroyImage(jng_image);
4446 /* Read the JNG image. */
4448 if (mng_info->mng_type == 0)
4450 mng_info->mng_width=jng_width;
4451 mng_info->mng_height=jng_height;
4454 if (image->page.width == 0 && image->page.height == 0)
4456 image->page.width=jng_width;
4457 image->page.height=jng_height;
4460 if (image->page.x == 0 && image->page.y == 0)
4462 image->page.x=mng_info->x_off[mng_info->object_id];
4463 image->page.y=mng_info->y_off[mng_info->object_id];
4468 image->page.y=mng_info->y_off[mng_info->object_id];
4471 mng_info->image_found++;
4472 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4473 2*GetBlobSize(image));
4475 if (logging != MagickFalse)
4476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4477 " exit ReadOneJNGImage()");
4483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4487 % R e a d J N G I m a g e %
4491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4493 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4494 % (including the 8-byte signature) and returns it. It allocates the memory
4495 % necessary for the new Image structure and returns a pointer to the new
4498 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4500 % The format of the ReadJNGImage method is:
4502 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4505 % A description of each parameter follows:
4507 % o image_info: the image info.
4509 % o exception: return any errors or warnings in this structure.
4513 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4528 magic_number[MaxTextExtent];
4536 assert(image_info != (const ImageInfo *) NULL);
4537 assert(image_info->signature == MagickSignature);
4538 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4539 assert(exception != (ExceptionInfo *) NULL);
4540 assert(exception->signature == MagickSignature);
4541 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4542 image=AcquireImage(image_info,exception);
4543 mng_info=(MngInfo *) NULL;
4544 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4546 if (status == MagickFalse)
4547 return((Image *) NULL);
4549 if (LocaleCompare(image_info->magick,"JNG") != 0)
4550 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4552 /* Verify JNG signature. */
4554 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4556 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4557 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4559 /* Allocate a MngInfo structure. */
4561 have_mng_structure=MagickFalse;
4562 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4564 if (mng_info == (MngInfo *) NULL)
4565 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4567 /* Initialize members of the MngInfo structure. */
4569 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4570 have_mng_structure=MagickTrue;
4572 mng_info->image=image;
4574 image=ReadOneJNGImage(mng_info,image_info,exception);
4575 MngInfoFreeStruct(mng_info,&have_mng_structure);
4577 if (image == (Image *) NULL)
4579 if (IsImageObject(previous) != MagickFalse)
4581 (void) CloseBlob(previous);
4582 (void) DestroyImageList(previous);
4585 if (logging != MagickFalse)
4586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4587 "exit ReadJNGImage() with error");
4589 return((Image *) NULL);
4591 (void) CloseBlob(image);
4593 if (image->columns == 0 || image->rows == 0)
4595 if (logging != MagickFalse)
4596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4597 "exit ReadJNGImage() with error");
4599 ThrowReaderException(CorruptImageError,"CorruptImage");
4602 if (logging != MagickFalse)
4603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4609 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4612 page_geometry[MaxTextExtent];
4645 #if defined(MNG_INSERT_LAYERS)
4647 mng_background_color;
4650 register unsigned char
4665 #if defined(MNG_INSERT_LAYERS)
4670 volatile unsigned int
4671 #ifdef MNG_OBJECT_BUFFERS
4672 mng_background_object=0,
4674 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4677 default_frame_timeout,
4679 #if defined(MNG_INSERT_LAYERS)
4685 /* These delays are all measured in image ticks_per_second,
4686 * not in MNG ticks_per_second
4689 default_frame_delay,
4693 #if defined(MNG_INSERT_LAYERS)
4702 previous_fb.bottom=0;
4704 previous_fb.right=0;
4706 default_fb.bottom=0;
4710 /* Open image file. */
4712 assert(image_info != (const ImageInfo *) NULL);
4713 assert(image_info->signature == MagickSignature);
4714 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4715 assert(exception != (ExceptionInfo *) NULL);
4716 assert(exception->signature == MagickSignature);
4717 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4718 image=AcquireImage(image_info,exception);
4719 mng_info=(MngInfo *) NULL;
4720 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4722 if (status == MagickFalse)
4723 return((Image *) NULL);
4725 first_mng_object=MagickFalse;
4727 have_mng_structure=MagickFalse;
4729 /* Allocate a MngInfo structure. */
4731 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4733 if (mng_info == (MngInfo *) NULL)
4734 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4736 /* Initialize members of the MngInfo structure. */
4738 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4739 mng_info->image=image;
4740 have_mng_structure=MagickTrue;
4742 if (LocaleCompare(image_info->magick,"MNG") == 0)
4745 magic_number[MaxTextExtent];
4747 /* Verify MNG signature. */
4748 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4749 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4750 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4752 /* Initialize some nonzero members of the MngInfo structure. */
4753 for (i=0; i < MNG_MAX_OBJECTS; i++)
4755 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4756 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4758 mng_info->exists[0]=MagickTrue;
4761 first_mng_object=MagickTrue;
4763 #if defined(MNG_INSERT_LAYERS)
4764 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4766 default_frame_delay=0;
4767 default_frame_timeout=0;
4770 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4772 skip_to_iend=MagickFalse;
4773 term_chunk_found=MagickFalse;
4774 mng_info->framing_mode=1;
4775 #if defined(MNG_INSERT_LAYERS)
4776 mandatory_back=MagickFalse;
4778 #if defined(MNG_INSERT_LAYERS)
4779 mng_background_color=image->background_color;
4781 default_fb=mng_info->frame;
4782 previous_fb=mng_info->frame;
4786 type[MaxTextExtent];
4788 if (LocaleCompare(image_info->magick,"MNG") == 0)
4797 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4798 length=ReadBlobMSBLong(image);
4799 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4801 if (logging != MagickFalse)
4802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4803 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4804 type[0],type[1],type[2],type[3],(double) length);
4806 if (length > PNG_UINT_31_MAX)
4810 ThrowReaderException(CorruptImageError,"CorruptImage");
4813 chunk=(unsigned char *) NULL;
4817 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4819 if (chunk == (unsigned char *) NULL)
4820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4822 for (i=0; i < (ssize_t) length; i++)
4823 chunk[i]=(unsigned char) ReadBlobByte(image);
4828 (void) ReadBlobMSBLong(image); /* read crc word */
4830 #if !defined(JNG_SUPPORTED)
4831 if (memcmp(type,mng_JHDR,4) == 0)
4833 skip_to_iend=MagickTrue;
4835 if (mng_info->jhdr_warning == 0)
4836 (void) ThrowMagickException(exception,GetMagickModule(),
4837 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4839 mng_info->jhdr_warning++;
4842 if (memcmp(type,mng_DHDR,4) == 0)
4844 skip_to_iend=MagickTrue;
4846 if (mng_info->dhdr_warning == 0)
4847 (void) ThrowMagickException(exception,GetMagickModule(),
4848 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4850 mng_info->dhdr_warning++;
4852 if (memcmp(type,mng_MEND,4) == 0)
4857 if (memcmp(type,mng_IEND,4) == 0)
4858 skip_to_iend=MagickFalse;
4861 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4863 if (logging != MagickFalse)
4864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4870 if (memcmp(type,mng_MHDR,4) == 0)
4872 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4873 (p[2] << 8) | p[3]);
4875 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4876 (p[6] << 8) | p[7]);
4878 if (logging != MagickFalse)
4880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4881 " MNG width: %.20g",(double) mng_info->mng_width);
4882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4883 " MNG height: %.20g",(double) mng_info->mng_height);
4887 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4889 if (mng_info->ticks_per_second == 0)
4890 default_frame_delay=0;
4893 default_frame_delay=1UL*image->ticks_per_second/
4894 mng_info->ticks_per_second;
4896 frame_delay=default_frame_delay;
4902 simplicity=(size_t) mng_get_long(p);
4905 mng_type=1; /* Full MNG */
4907 if ((simplicity != 0) && ((simplicity | 11) == 11))
4908 mng_type=2; /* LC */
4910 if ((simplicity != 0) && ((simplicity | 9) == 9))
4911 mng_type=3; /* VLC */
4913 #if defined(MNG_INSERT_LAYERS)
4915 insert_layers=MagickTrue;
4917 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4919 /* Allocate next image structure. */
4920 AcquireNextImage(image_info,image,exception);
4922 if (GetNextImageInList(image) == (Image *) NULL)
4923 return((Image *) NULL);
4925 image=SyncNextImageInList(image);
4926 mng_info->image=image;
4929 if ((mng_info->mng_width > 65535L) ||
4930 (mng_info->mng_height > 65535L))
4931 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4933 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4934 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4935 mng_info->mng_height);
4937 mng_info->frame.left=0;
4938 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4939 mng_info->frame.top=0;
4940 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4941 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4943 for (i=0; i < MNG_MAX_OBJECTS; i++)
4944 mng_info->object_clip[i]=mng_info->frame;
4946 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4950 if (memcmp(type,mng_TERM,4) == 0)
4961 final_delay=(png_uint_32) mng_get_long(&p[2]);
4962 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4964 if (mng_iterations == PNG_UINT_31_MAX)
4967 image->iterations=mng_iterations;
4968 term_chunk_found=MagickTrue;
4971 if (logging != MagickFalse)
4973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4974 " repeat=%d",repeat);
4976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4977 " final_delay=%.20g",(double) final_delay);
4979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4980 " image->iterations=%.20g",(double) image->iterations);
4983 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4986 if (memcmp(type,mng_DEFI,4) == 0)
4989 (void) ThrowMagickException(exception,GetMagickModule(),
4990 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4993 object_id=(p[0] << 8) | p[1];
4995 if (mng_type == 2 && object_id != 0)
4996 (void) ThrowMagickException(exception,GetMagickModule(),
4997 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5000 if (object_id > MNG_MAX_OBJECTS)
5003 Instead ofsuing a warning we should allocate a larger
5004 MngInfo structure and continue.
5006 (void) ThrowMagickException(exception,GetMagickModule(),
5007 CoderError,"object id too large","`%s'",image->filename);
5008 object_id=MNG_MAX_OBJECTS;
5011 if (mng_info->exists[object_id])
5012 if (mng_info->frozen[object_id])
5014 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5015 (void) ThrowMagickException(exception,
5016 GetMagickModule(),CoderError,
5017 "DEFI cannot redefine a frozen MNG object","`%s'",
5022 mng_info->exists[object_id]=MagickTrue;
5025 mng_info->invisible[object_id]=p[2];
5028 Extract object offset info.
5032 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5033 (p[5] << 16) | (p[6] << 8) | p[7]);
5035 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5036 (p[9] << 16) | (p[10] << 8) | p[11]);
5038 if (logging != MagickFalse)
5040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5041 " x_off[%d]: %.20g",object_id,(double)
5042 mng_info->x_off[object_id]);
5044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5045 " y_off[%d]: %.20g",object_id,(double)
5046 mng_info->y_off[object_id]);
5051 Extract object clipping info.
5054 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5057 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5060 if (memcmp(type,mng_bKGD,4) == 0)
5062 mng_info->have_global_bkgd=MagickFalse;
5066 mng_info->mng_global_bkgd.red=
5067 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5069 mng_info->mng_global_bkgd.green=
5070 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5072 mng_info->mng_global_bkgd.blue=
5073 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5075 mng_info->have_global_bkgd=MagickTrue;
5078 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5081 if (memcmp(type,mng_BACK,4) == 0)
5083 #if defined(MNG_INSERT_LAYERS)
5085 mandatory_back=p[6];
5090 if (mandatory_back && length > 5)
5092 mng_background_color.red=
5093 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5095 mng_background_color.green=
5096 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5098 mng_background_color.blue=
5099 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5101 mng_background_color.alpha=OpaqueAlpha;
5104 #ifdef MNG_OBJECT_BUFFERS
5106 mng_background_object=(p[7] << 8) | p[8];
5109 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5113 if (memcmp(type,mng_PLTE,4) == 0)
5115 /* Read global PLTE. */
5117 if (length && (length < 769))
5119 if (mng_info->global_plte == (png_colorp) NULL)
5120 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5121 sizeof(*mng_info->global_plte));
5123 for (i=0; i < (ssize_t) (length/3); i++)
5125 mng_info->global_plte[i].red=p[3*i];
5126 mng_info->global_plte[i].green=p[3*i+1];
5127 mng_info->global_plte[i].blue=p[3*i+2];
5130 mng_info->global_plte_length=(unsigned int) (length/3);
5133 for ( ; i < 256; i++)
5135 mng_info->global_plte[i].red=i;
5136 mng_info->global_plte[i].green=i;
5137 mng_info->global_plte[i].blue=i;
5141 mng_info->global_plte_length=256;
5144 mng_info->global_plte_length=0;
5146 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5150 if (memcmp(type,mng_tRNS,4) == 0)
5152 /* read global tRNS */
5155 for (i=0; i < (ssize_t) length; i++)
5156 mng_info->global_trns[i]=p[i];
5159 for ( ; i < 256; i++)
5160 mng_info->global_trns[i]=255;
5162 mng_info->global_trns_length=(unsigned int) length;
5163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5166 if (memcmp(type,mng_gAMA,4) == 0)
5173 igamma=mng_get_long(p);
5174 mng_info->global_gamma=((float) igamma)*0.00001;
5175 mng_info->have_global_gama=MagickTrue;
5179 mng_info->have_global_gama=MagickFalse;
5181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5185 if (memcmp(type,mng_cHRM,4) == 0)
5187 /* Read global cHRM */
5191 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5192 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5193 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5194 mng_info->global_chrm.red_primary.y=0.00001*
5195 mng_get_long(&p[12]);
5196 mng_info->global_chrm.green_primary.x=0.00001*
5197 mng_get_long(&p[16]);
5198 mng_info->global_chrm.green_primary.y=0.00001*
5199 mng_get_long(&p[20]);
5200 mng_info->global_chrm.blue_primary.x=0.00001*
5201 mng_get_long(&p[24]);
5202 mng_info->global_chrm.blue_primary.y=0.00001*
5203 mng_get_long(&p[28]);
5204 mng_info->have_global_chrm=MagickTrue;
5207 mng_info->have_global_chrm=MagickFalse;
5209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5213 if (memcmp(type,mng_sRGB,4) == 0)
5220 mng_info->global_srgb_intent=
5221 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5222 mng_info->have_global_srgb=MagickTrue;
5225 mng_info->have_global_srgb=MagickFalse;
5227 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5231 if (memcmp(type,mng_iCCP,4) == 0)
5239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5244 if (memcmp(type,mng_FRAM,4) == 0)
5247 (void) ThrowMagickException(exception,GetMagickModule(),
5248 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5251 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5252 image->delay=frame_delay;
5254 frame_delay=default_frame_delay;
5255 frame_timeout=default_frame_timeout;
5260 mng_info->framing_mode=p[0];
5262 if (logging != MagickFalse)
5263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5264 " Framing_mode=%d",mng_info->framing_mode);
5268 /* Note the delay and frame clipping boundaries. */
5270 p++; /* framing mode */
5272 while (*p && ((p-chunk) < (ssize_t) length))
5273 p++; /* frame name */
5275 p++; /* frame name terminator */
5277 if ((p-chunk) < (ssize_t) (length-4))
5284 change_delay=(*p++);
5285 change_timeout=(*p++);
5286 change_clipping=(*p++);
5287 p++; /* change_sync */
5291 frame_delay=1UL*image->ticks_per_second*
5294 if (mng_info->ticks_per_second != 0)
5295 frame_delay/=mng_info->ticks_per_second;
5298 frame_delay=PNG_UINT_31_MAX;
5300 if (change_delay == 2)
5301 default_frame_delay=frame_delay;
5305 if (logging != MagickFalse)
5306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5307 " Framing_delay=%.20g",(double) frame_delay);
5312 frame_timeout=1UL*image->ticks_per_second*
5315 if (mng_info->ticks_per_second != 0)
5316 frame_timeout/=mng_info->ticks_per_second;
5319 frame_timeout=PNG_UINT_31_MAX;
5321 if (change_delay == 2)
5322 default_frame_timeout=frame_timeout;
5326 if (logging != MagickFalse)
5327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5328 " Framing_timeout=%.20g",(double) frame_timeout);
5331 if (change_clipping)
5333 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5337 if (logging != MagickFalse)
5338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5339 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5340 (double) fb.left,(double) fb.right,(double) fb.top,
5341 (double) fb.bottom);
5343 if (change_clipping == 2)
5349 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5351 subframe_width=(size_t) (mng_info->clip.right
5352 -mng_info->clip.left);
5354 subframe_height=(size_t) (mng_info->clip.bottom
5355 -mng_info->clip.top);
5357 Insert a background layer behind the frame if framing_mode is 4.
5359 #if defined(MNG_INSERT_LAYERS)
5360 if (logging != MagickFalse)
5361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5362 " subframe_width=%.20g, subframe_height=%.20g",(double)
5363 subframe_width,(double) subframe_height);
5365 if (insert_layers && (mng_info->framing_mode == 4) &&
5366 (subframe_width) && (subframe_height))
5368 /* Allocate next image structure. */
5369 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5371 AcquireNextImage(image_info,image,exception);
5373 if (GetNextImageInList(image) == (Image *) NULL)
5375 image=DestroyImageList(image);
5376 MngInfoFreeStruct(mng_info,&have_mng_structure);
5377 return((Image *) NULL);
5380 image=SyncNextImageInList(image);
5383 mng_info->image=image;
5385 if (term_chunk_found)
5387 image->start_loop=MagickTrue;
5388 image->iterations=mng_iterations;
5389 term_chunk_found=MagickFalse;
5393 image->start_loop=MagickFalse;
5395 image->columns=subframe_width;
5396 image->rows=subframe_height;
5397 image->page.width=subframe_width;
5398 image->page.height=subframe_height;
5399 image->page.x=mng_info->clip.left;
5400 image->page.y=mng_info->clip.top;
5401 image->background_color=mng_background_color;
5402 image->matte=MagickFalse;
5404 (void) SetImageBackgroundColor(image,exception);
5406 if (logging != MagickFalse)
5407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5408 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5409 (double) mng_info->clip.left,(double) mng_info->clip.right,
5410 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5416 if (memcmp(type,mng_CLIP,4) == 0)
5425 first_object=(p[0] << 8) | p[1];
5426 last_object=(p[2] << 8) | p[3];
5428 for (i=(int) first_object; i <= (int) last_object; i++)
5430 if (mng_info->exists[i] && !mng_info->frozen[i])
5435 box=mng_info->object_clip[i];
5436 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5440 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5443 if (memcmp(type,mng_SAVE,4) == 0)
5445 for (i=1; i < MNG_MAX_OBJECTS; i++)
5446 if (mng_info->exists[i])
5448 mng_info->frozen[i]=MagickTrue;
5449 #ifdef MNG_OBJECT_BUFFERS
5450 if (mng_info->ob[i] != (MngBuffer *) NULL)
5451 mng_info->ob[i]->frozen=MagickTrue;
5456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5461 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5463 /* Read DISC or SEEK. */
5465 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5467 for (i=1; i < MNG_MAX_OBJECTS; i++)
5468 MngInfoDiscardObject(mng_info,i);
5476 for (j=0; j < (ssize_t) length; j+=2)
5478 i=p[j] << 8 | p[j+1];
5479 MngInfoDiscardObject(mng_info,i);
5484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5489 if (memcmp(type,mng_MOVE,4) == 0)
5497 first_object=(p[0] << 8) | p[1];
5498 last_object=(p[2] << 8) | p[3];
5499 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5501 if (mng_info->exists[i] && !mng_info->frozen[i])
5509 old_pair.a=mng_info->x_off[i];
5510 old_pair.b=mng_info->y_off[i];
5511 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5512 mng_info->x_off[i]=new_pair.a;
5513 mng_info->y_off[i]=new_pair.b;
5517 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5521 if (memcmp(type,mng_LOOP,4) == 0)
5523 ssize_t loop_iters=1;
5524 loop_level=chunk[0];
5525 mng_info->loop_active[loop_level]=1; /* mark loop active */
5527 /* Record starting point. */
5528 loop_iters=mng_get_long(&chunk[1]);
5530 if (logging != MagickFalse)
5531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5532 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5533 (double) loop_iters);
5535 if (loop_iters == 0)
5536 skipping_loop=loop_level;
5540 mng_info->loop_jump[loop_level]=TellBlob(image);
5541 mng_info->loop_count[loop_level]=loop_iters;
5544 mng_info->loop_iteration[loop_level]=0;
5545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5549 if (memcmp(type,mng_ENDL,4) == 0)
5551 loop_level=chunk[0];
5553 if (skipping_loop > 0)
5555 if (skipping_loop == loop_level)
5558 Found end of zero-iteration loop.
5561 mng_info->loop_active[loop_level]=0;
5567 if (mng_info->loop_active[loop_level] == 1)
5569 mng_info->loop_count[loop_level]--;
5570 mng_info->loop_iteration[loop_level]++;
5572 if (logging != MagickFalse)
5573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5574 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5575 (double) loop_level,(double)
5576 mng_info->loop_count[loop_level]);
5578 if (mng_info->loop_count[loop_level] != 0)
5580 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5584 ThrowReaderException(CorruptImageError,
5585 "ImproperImageHeader");
5596 mng_info->loop_active[loop_level]=0;
5598 for (i=0; i < loop_level; i++)
5599 if (mng_info->loop_active[i] == 1)
5600 last_level=(short) i;
5601 loop_level=last_level;
5606 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5610 if (memcmp(type,mng_CLON,4) == 0)
5612 if (mng_info->clon_warning == 0)
5613 (void) ThrowMagickException(exception,GetMagickModule(),
5614 CoderError,"CLON is not implemented yet","`%s'",
5617 mng_info->clon_warning++;
5620 if (memcmp(type,mng_MAGN,4) == 0)
5635 magn_first=(p[0] << 8) | p[1];
5641 magn_last=(p[2] << 8) | p[3];
5644 magn_last=magn_first;
5645 #ifndef MNG_OBJECT_BUFFERS
5646 if (magn_first || magn_last)
5647 if (mng_info->magn_warning == 0)
5649 (void) ThrowMagickException(exception,
5650 GetMagickModule(),CoderError,
5651 "MAGN is not implemented yet for nonzero objects",
5652 "`%s'",image->filename);
5654 mng_info->magn_warning++;
5664 magn_mx=(p[5] << 8) | p[6];
5673 magn_my=(p[7] << 8) | p[8];
5682 magn_ml=(p[9] << 8) | p[10];
5691 magn_mr=(p[11] << 8) | p[12];
5700 magn_mt=(p[13] << 8) | p[14];
5709 magn_mb=(p[15] << 8) | p[16];
5721 magn_methy=magn_methx;
5724 if (magn_methx > 5 || magn_methy > 5)
5725 if (mng_info->magn_warning == 0)
5727 (void) ThrowMagickException(exception,
5728 GetMagickModule(),CoderError,
5729 "Unknown MAGN method in MNG datastream","`%s'",
5732 mng_info->magn_warning++;
5734 #ifdef MNG_OBJECT_BUFFERS
5735 /* Magnify existing objects in the range magn_first to magn_last */
5737 if (magn_first == 0 || magn_last == 0)
5739 /* Save the magnification factors for object 0 */
5740 mng_info->magn_mb=magn_mb;
5741 mng_info->magn_ml=magn_ml;
5742 mng_info->magn_mr=magn_mr;
5743 mng_info->magn_mt=magn_mt;
5744 mng_info->magn_mx=magn_mx;
5745 mng_info->magn_my=magn_my;
5746 mng_info->magn_methx=magn_methx;
5747 mng_info->magn_methy=magn_methy;
5751 if (memcmp(type,mng_PAST,4) == 0)
5753 if (mng_info->past_warning == 0)
5754 (void) ThrowMagickException(exception,GetMagickModule(),
5755 CoderError,"PAST is not implemented yet","`%s'",
5758 mng_info->past_warning++;
5761 if (memcmp(type,mng_SHOW,4) == 0)
5763 if (mng_info->show_warning == 0)
5764 (void) ThrowMagickException(exception,GetMagickModule(),
5765 CoderError,"SHOW is not implemented yet","`%s'",
5768 mng_info->show_warning++;
5771 if (memcmp(type,mng_sBIT,4) == 0)
5774 mng_info->have_global_sbit=MagickFalse;
5778 mng_info->global_sbit.gray=p[0];
5779 mng_info->global_sbit.red=p[0];
5780 mng_info->global_sbit.green=p[1];
5781 mng_info->global_sbit.blue=p[2];
5782 mng_info->global_sbit.alpha=p[3];
5783 mng_info->have_global_sbit=MagickTrue;
5786 if (memcmp(type,mng_pHYs,4) == 0)
5790 mng_info->global_x_pixels_per_unit=
5791 (size_t) mng_get_long(p);
5792 mng_info->global_y_pixels_per_unit=
5793 (size_t) mng_get_long(&p[4]);
5794 mng_info->global_phys_unit_type=p[8];
5795 mng_info->have_global_phys=MagickTrue;
5799 mng_info->have_global_phys=MagickFalse;
5801 if (memcmp(type,mng_pHYg,4) == 0)
5803 if (mng_info->phyg_warning == 0)
5804 (void) ThrowMagickException(exception,GetMagickModule(),
5805 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5807 mng_info->phyg_warning++;
5809 if (memcmp(type,mng_BASI,4) == 0)
5811 skip_to_iend=MagickTrue;
5813 if (mng_info->basi_warning == 0)
5814 (void) ThrowMagickException(exception,GetMagickModule(),
5815 CoderError,"BASI is not implemented yet","`%s'",
5818 mng_info->basi_warning++;
5819 #ifdef MNG_BASI_SUPPORTED
5820 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5821 (p[2] << 8) | p[3]);
5822 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5823 (p[6] << 8) | p[7]);
5824 basi_color_type=p[8];
5825 basi_compression_method=p[9];
5826 basi_filter_type=p[10];
5827 basi_interlace_method=p[11];
5829 basi_red=(p[12] << 8) & p[13];
5835 basi_green=(p[14] << 8) & p[15];
5841 basi_blue=(p[16] << 8) & p[17];
5847 basi_alpha=(p[18] << 8) & p[19];
5851 if (basi_sample_depth == 16)
5858 basi_viewable=p[20];
5864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5868 if (memcmp(type,mng_IHDR,4)
5869 #if defined(JNG_SUPPORTED)
5870 && memcmp(type,mng_JHDR,4)
5874 /* Not an IHDR or JHDR chunk */
5876 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5881 if (logging != MagickFalse)
5882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5883 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5885 mng_info->exists[object_id]=MagickTrue;
5886 mng_info->viewable[object_id]=MagickTrue;
5888 if (mng_info->invisible[object_id])
5890 if (logging != MagickFalse)
5891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5892 " Skipping invisible object");
5894 skip_to_iend=MagickTrue;
5895 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5898 #if defined(MNG_INSERT_LAYERS)
5900 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5902 image_width=(size_t) mng_get_long(p);
5903 image_height=(size_t) mng_get_long(&p[4]);
5905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5908 Insert a transparent background layer behind the entire animation
5909 if it is not full screen.
5911 #if defined(MNG_INSERT_LAYERS)
5912 if (insert_layers && mng_type && first_mng_object)
5914 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5915 (image_width < mng_info->mng_width) ||
5916 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5917 (image_height < mng_info->mng_height) ||
5918 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5920 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5923 Allocate next image structure.
5925 AcquireNextImage(image_info,image,exception);
5927 if (GetNextImageInList(image) == (Image *) NULL)
5929 image=DestroyImageList(image);
5930 MngInfoFreeStruct(mng_info,&have_mng_structure);
5931 return((Image *) NULL);
5934 image=SyncNextImageInList(image);
5936 mng_info->image=image;
5938 if (term_chunk_found)
5940 image->start_loop=MagickTrue;
5941 image->iterations=mng_iterations;
5942 term_chunk_found=MagickFalse;
5946 image->start_loop=MagickFalse;
5948 /* Make a background rectangle. */
5951 image->columns=mng_info->mng_width;
5952 image->rows=mng_info->mng_height;
5953 image->page.width=mng_info->mng_width;
5954 image->page.height=mng_info->mng_height;
5957 image->background_color=mng_background_color;
5958 (void) SetImageBackgroundColor(image,exception);
5959 if (logging != MagickFalse)
5960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5961 " Inserted transparent background layer, W=%.20g, H=%.20g",
5962 (double) mng_info->mng_width,(double) mng_info->mng_height);
5966 Insert a background layer behind the upcoming image if
5967 framing_mode is 3, and we haven't already inserted one.
5969 if (insert_layers && (mng_info->framing_mode == 3) &&
5970 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5971 (simplicity & 0x08)))
5973 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5976 Allocate next image structure.
5978 AcquireNextImage(image_info,image,exception);
5980 if (GetNextImageInList(image) == (Image *) NULL)
5982 image=DestroyImageList(image);
5983 MngInfoFreeStruct(mng_info,&have_mng_structure);
5984 return((Image *) NULL);
5987 image=SyncNextImageInList(image);
5990 mng_info->image=image;
5992 if (term_chunk_found)
5994 image->start_loop=MagickTrue;
5995 image->iterations=mng_iterations;
5996 term_chunk_found=MagickFalse;
6000 image->start_loop=MagickFalse;
6003 image->columns=subframe_width;
6004 image->rows=subframe_height;
6005 image->page.width=subframe_width;
6006 image->page.height=subframe_height;
6007 image->page.x=mng_info->clip.left;
6008 image->page.y=mng_info->clip.top;
6009 image->background_color=mng_background_color;
6010 image->matte=MagickFalse;
6011 (void) SetImageBackgroundColor(image,exception);
6013 if (logging != MagickFalse)
6014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6015 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6016 (double) mng_info->clip.left,(double) mng_info->clip.right,
6017 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6019 #endif /* MNG_INSERT_LAYERS */
6020 first_mng_object=MagickFalse;
6022 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6025 Allocate next image structure.
6027 AcquireNextImage(image_info,image,exception);
6029 if (GetNextImageInList(image) == (Image *) NULL)
6031 image=DestroyImageList(image);
6032 MngInfoFreeStruct(mng_info,&have_mng_structure);
6033 return((Image *) NULL);
6036 image=SyncNextImageInList(image);
6038 mng_info->image=image;
6039 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6040 GetBlobSize(image));
6042 if (status == MagickFalse)
6045 if (term_chunk_found)
6047 image->start_loop=MagickTrue;
6048 term_chunk_found=MagickFalse;
6052 image->start_loop=MagickFalse;
6054 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6056 image->delay=frame_delay;
6057 frame_delay=default_frame_delay;
6063 image->page.width=mng_info->mng_width;
6064 image->page.height=mng_info->mng_height;
6065 image->page.x=mng_info->x_off[object_id];
6066 image->page.y=mng_info->y_off[object_id];
6067 image->iterations=mng_iterations;
6070 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6073 if (logging != MagickFalse)
6074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6075 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6078 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6081 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6085 mng_info->image=image;
6086 mng_info->mng_type=mng_type;
6087 mng_info->object_id=object_id;
6089 if (memcmp(type,mng_IHDR,4) == 0)
6090 image=ReadOnePNGImage(mng_info,image_info,exception);
6092 #if defined(JNG_SUPPORTED)
6094 image=ReadOneJNGImage(mng_info,image_info,exception);
6097 if (image == (Image *) NULL)
6099 if (IsImageObject(previous) != MagickFalse)
6101 (void) DestroyImageList(previous);
6102 (void) CloseBlob(previous);
6105 MngInfoFreeStruct(mng_info,&have_mng_structure);
6106 return((Image *) NULL);
6109 if (image->columns == 0 || image->rows == 0)
6111 (void) CloseBlob(image);
6112 image=DestroyImageList(image);
6113 MngInfoFreeStruct(mng_info,&have_mng_structure);
6114 return((Image *) NULL);
6117 mng_info->image=image;
6124 if (mng_info->magn_methx || mng_info->magn_methy)
6130 if (logging != MagickFalse)
6131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6132 " Processing MNG MAGN chunk");
6134 if (mng_info->magn_methx == 1)
6136 magnified_width=mng_info->magn_ml;
6138 if (image->columns > 1)
6139 magnified_width += mng_info->magn_mr;
6141 if (image->columns > 2)
6142 magnified_width += (png_uint_32)
6143 ((image->columns-2)*(mng_info->magn_mx));
6148 magnified_width=(png_uint_32) image->columns;
6150 if (image->columns > 1)
6151 magnified_width += mng_info->magn_ml-1;
6153 if (image->columns > 2)
6154 magnified_width += mng_info->magn_mr-1;
6156 if (image->columns > 3)
6157 magnified_width += (png_uint_32)
6158 ((image->columns-3)*(mng_info->magn_mx-1));
6161 if (mng_info->magn_methy == 1)
6163 magnified_height=mng_info->magn_mt;
6165 if (image->rows > 1)
6166 magnified_height += mng_info->magn_mb;
6168 if (image->rows > 2)
6169 magnified_height += (png_uint_32)
6170 ((image->rows-2)*(mng_info->magn_my));
6175 magnified_height=(png_uint_32) image->rows;
6177 if (image->rows > 1)
6178 magnified_height += mng_info->magn_mt-1;
6180 if (image->rows > 2)
6181 magnified_height += mng_info->magn_mb-1;
6183 if (image->rows > 3)
6184 magnified_height += (png_uint_32)
6185 ((image->rows-3)*(mng_info->magn_my-1));
6188 if (magnified_height > image->rows ||
6189 magnified_width > image->columns)
6216 /* Allocate next image structure. */
6218 if (logging != MagickFalse)
6219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220 " Allocate magnified image");
6222 AcquireNextImage(image_info,image,exception);
6224 if (GetNextImageInList(image) == (Image *) NULL)
6226 image=DestroyImageList(image);
6227 MngInfoFreeStruct(mng_info,&have_mng_structure);
6228 return((Image *) NULL);
6231 large_image=SyncNextImageInList(image);
6233 large_image->columns=magnified_width;
6234 large_image->rows=magnified_height;
6236 magn_methx=mng_info->magn_methx;
6237 magn_methy=mng_info->magn_methy;
6239 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6240 #define QM unsigned short
6241 if (magn_methx != 1 || magn_methy != 1)
6244 Scale pixels to unsigned shorts to prevent
6245 overflow of intermediate values of interpolations
6247 for (y=0; y < (ssize_t) image->rows; y++)
6249 q=GetAuthenticPixels(image,0,y,image->columns,1,
6252 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6254 SetPixelRed(image,ScaleQuantumToShort(
6255 GetPixelRed(image,q)),q);
6256 SetPixelGreen(image,ScaleQuantumToShort(
6257 GetPixelGreen(image,q)),q);
6258 SetPixelBlue(image,ScaleQuantumToShort(
6259 GetPixelBlue(image,q)),q);
6260 SetPixelAlpha(image,ScaleQuantumToShort(
6261 GetPixelAlpha(image,q)),q);
6262 q+=GetPixelChannels(image);
6265 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6273 if (image->matte != MagickFalse)
6274 (void) SetImageBackgroundColor(large_image,exception);
6278 large_image->background_color.alpha=OpaqueAlpha;
6279 (void) SetImageBackgroundColor(large_image,exception);
6281 if (magn_methx == 4)
6284 if (magn_methx == 5)
6287 if (magn_methy == 4)
6290 if (magn_methy == 5)
6294 /* magnify the rows into the right side of the large image */
6296 if (logging != MagickFalse)
6297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6298 " Magnify the rows to %.20g",(double) large_image->rows);
6299 m=(ssize_t) mng_info->magn_mt;
6301 length=(size_t) image->columns*GetPixelChannels(image);
6302 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6303 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6305 if ((prev == (Quantum *) NULL) ||
6306 (next == (Quantum *) NULL))
6308 image=DestroyImageList(image);
6309 MngInfoFreeStruct(mng_info,&have_mng_structure);
6310 ThrowReaderException(ResourceLimitError,
6311 "MemoryAllocationFailed");
6314 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6315 (void) CopyMagickMemory(next,n,length);
6317 for (y=0; y < (ssize_t) image->rows; y++)
6320 m=(ssize_t) mng_info->magn_mt;
6322 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6323 m=(ssize_t) mng_info->magn_mb;
6325 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6326 m=(ssize_t) mng_info->magn_mb;
6328 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6332 m=(ssize_t) mng_info->magn_my;
6338 if (y < (ssize_t) image->rows-1)
6340 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6342 (void) CopyMagickMemory(next,n,length);
6345 for (i=0; i < m; i++, yy++)
6350 assert(yy < (ssize_t) large_image->rows);
6353 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6355 q+=(large_image->columns-image->columns)*
6356 GetPixelChannels(large_image);
6358 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6360 /* To do: get color as function of indexes[x] */
6362 if (image->storage_class == PseudoClass)
6367 if (magn_methy <= 1)
6369 /* replicate previous */
6370 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6371 SetPixelGreen(large_image,GetPixelGreen(image,
6373 SetPixelBlue(large_image,GetPixelBlue(image,
6375 SetPixelAlpha(large_image,GetPixelAlpha(image,
6379 else if (magn_methy == 2 || magn_methy == 4)
6383 SetPixelRed(large_image,GetPixelRed(image,
6385 SetPixelGreen(large_image,GetPixelGreen(image,
6387 SetPixelBlue(large_image,GetPixelBlue(image,
6389 SetPixelAlpha(large_image,GetPixelAlpha(image,
6396 SetPixelRed(large_image,((QM) (((ssize_t)
6397 (2*i*(GetPixelRed(image,n)
6398 -GetPixelRed(image,pixels)+m))/
6400 +GetPixelRed(image,pixels)))),q);
6401 SetPixelGreen(large_image,((QM) (((ssize_t)
6402 (2*i*(GetPixelGreen(image,n)
6403 -GetPixelGreen(image,pixels)+m))/
6405 +GetPixelGreen(image,pixels)))),q);
6406 SetPixelBlue(large_image,((QM) (((ssize_t)
6407 (2*i*(GetPixelBlue(image,n)
6408 -GetPixelBlue(image,pixels)+m))/
6410 +GetPixelBlue(image,pixels)))),q);
6412 if (image->matte != MagickFalse)
6413 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6414 (2*i*(GetPixelAlpha(image,n)
6415 -GetPixelAlpha(image,pixels)+m))
6417 GetPixelAlpha(image,pixels)))),q);
6420 if (magn_methy == 4)
6422 /* Replicate nearest */
6423 if (i <= ((m+1) << 1))
6424 SetPixelAlpha(large_image,GetPixelAlpha(image,
6427 SetPixelAlpha(large_image,GetPixelAlpha(image,
6432 else /* if (magn_methy == 3 || magn_methy == 5) */
6434 /* Replicate nearest */
6435 if (i <= ((m+1) << 1))
6437 SetPixelRed(large_image,GetPixelRed(image,
6439 SetPixelGreen(large_image,GetPixelGreen(image,
6441 SetPixelBlue(large_image,GetPixelBlue(image,
6443 SetPixelAlpha(large_image,GetPixelAlpha(image,
6449 SetPixelRed(large_image,GetPixelRed(image,n),q);
6450 SetPixelGreen(large_image,GetPixelGreen(image,n),
6452 SetPixelBlue(large_image,GetPixelBlue(image,n),
6454 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6458 if (magn_methy == 5)
6460 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6461 (GetPixelAlpha(image,n)
6462 -GetPixelAlpha(image,pixels))
6463 +m))/((ssize_t) (m*2))
6464 +GetPixelAlpha(image,pixels)),q);
6467 n+=GetPixelChannels(image);
6468 q+=GetPixelChannels(large_image);
6469 pixels+=GetPixelChannels(image);
6472 if (SyncAuthenticPixels(large_image,exception) == 0)
6478 prev=(Quantum *) RelinquishMagickMemory(prev);
6479 next=(Quantum *) RelinquishMagickMemory(next);
6481 length=image->columns;
6483 if (logging != MagickFalse)
6484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6485 " Delete original image");
6487 DeleteImageFromList(&image);
6491 mng_info->image=image;
6493 /* magnify the columns */
6494 if (logging != MagickFalse)
6495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6496 " Magnify the columns to %.20g",(double) image->columns);
6498 for (y=0; y < (ssize_t) image->rows; y++)
6503 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6504 pixels=q+(image->columns-length)*GetPixelChannels(image);
6505 n=pixels+GetPixelChannels(image);
6507 for (x=(ssize_t) (image->columns-length);
6508 x < (ssize_t) image->columns; x++)
6510 /* To do: Rewrite using Get/Set***PixelChannel() */
6512 if (x == (ssize_t) (image->columns-length))
6513 m=(ssize_t) mng_info->magn_ml;
6515 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6516 m=(ssize_t) mng_info->magn_mr;
6518 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6519 m=(ssize_t) mng_info->magn_mr;
6521 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6525 m=(ssize_t) mng_info->magn_mx;
6527 for (i=0; i < m; i++)
6529 if (magn_methx <= 1)
6531 /* replicate previous */
6532 SetPixelRed(image,GetPixelRed(image,pixels),q);
6533 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6534 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6535 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6538 else if (magn_methx == 2 || magn_methx == 4)
6542 SetPixelRed(image,GetPixelRed(image,pixels),q);
6543 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6544 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6545 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6548 /* To do: Rewrite using Get/Set***PixelChannel() */
6552 SetPixelRed(image,(QM) ((2*i*(
6553 GetPixelRed(image,n)
6554 -GetPixelRed(image,pixels))+m)
6556 GetPixelRed(image,pixels)),q);
6558 SetPixelGreen(image,(QM) ((2*i*(
6559 GetPixelGreen(image,n)
6560 -GetPixelGreen(image,pixels))+m)
6562 GetPixelGreen(image,pixels)),q);
6564 SetPixelBlue(image,(QM) ((2*i*(
6565 GetPixelBlue(image,n)
6566 -GetPixelBlue(image,pixels))+m)
6568 GetPixelBlue(image,pixels)),q);
6569 if (image->matte != MagickFalse)
6570 SetPixelAlpha(image,(QM) ((2*i*(
6571 GetPixelAlpha(image,n)
6572 -GetPixelAlpha(image,pixels))+m)
6574 GetPixelAlpha(image,pixels)),q);
6577 if (magn_methx == 4)
6579 /* Replicate nearest */
6580 if (i <= ((m+1) << 1))
6582 SetPixelAlpha(image,
6583 GetPixelAlpha(image,pixels)+0,q);
6587 SetPixelAlpha(image,
6588 GetPixelAlpha(image,n)+0,q);
6593 else /* if (magn_methx == 3 || magn_methx == 5) */
6595 /* Replicate nearest */
6596 if (i <= ((m+1) << 1))
6598 SetPixelRed(image,GetPixelRed(image,pixels),q);
6599 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6600 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6601 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6606 SetPixelRed(image,GetPixelRed(image,n),q);
6607 SetPixelGreen(image,GetPixelGreen(image,n),q);
6608 SetPixelBlue(image,GetPixelBlue(image,n),q);
6609 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6612 if (magn_methx == 5)
6615 SetPixelAlpha(image,
6616 (QM) ((2*i*( GetPixelAlpha(image,n)
6617 -GetPixelAlpha(image,pixels))+m)/
6619 +GetPixelAlpha(image,pixels)),q);
6622 q+=GetPixelChannels(image);
6624 n+=GetPixelChannels(image);
6625 p+=GetPixelChannels(image);
6628 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6631 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6632 if (magn_methx != 1 || magn_methy != 1)
6635 Rescale pixels to Quantum
6637 for (y=0; y < (ssize_t) image->rows; y++)
6639 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6641 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6643 SetPixelRed(image,ScaleShortToQuantum(
6644 GetPixelRed(image,q)),q);
6645 SetPixelGreen(image,ScaleShortToQuantum(
6646 GetPixelGreen(image,q)),q);
6647 SetPixelBlue(image,ScaleShortToQuantum(
6648 GetPixelBlue(image,q)),q);
6649 SetPixelAlpha(image,ScaleShortToQuantum(
6650 GetPixelAlpha(image,q)),q);
6651 q+=GetPixelChannels(image);
6654 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6659 if (logging != MagickFalse)
6660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6661 " Finished MAGN processing");
6666 Crop_box is with respect to the upper left corner of the MNG.
6668 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6669 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6670 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6671 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6672 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6673 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6674 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6675 if ((crop_box.left != (mng_info->image_box.left
6676 +mng_info->x_off[object_id])) ||
6677 (crop_box.right != (mng_info->image_box.right
6678 +mng_info->x_off[object_id])) ||
6679 (crop_box.top != (mng_info->image_box.top
6680 +mng_info->y_off[object_id])) ||
6681 (crop_box.bottom != (mng_info->image_box.bottom
6682 +mng_info->y_off[object_id])))
6684 if (logging != MagickFalse)
6685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686 " Crop the PNG image");
6688 if ((crop_box.left < crop_box.right) &&
6689 (crop_box.top < crop_box.bottom))
6698 Crop_info is with respect to the upper left corner of
6701 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6702 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6703 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6704 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6705 image->page.width=image->columns;
6706 image->page.height=image->rows;
6709 im=CropImage(image,&crop_info,exception);
6711 if (im != (Image *) NULL)
6713 image->columns=im->columns;
6714 image->rows=im->rows;
6715 im=DestroyImage(im);
6716 image->page.width=image->columns;
6717 image->page.height=image->rows;
6718 image->page.x=crop_box.left;
6719 image->page.y=crop_box.top;
6726 No pixels in crop area. The MNG spec still requires
6727 a layer, though, so make a single transparent pixel in
6728 the top left corner.
6733 (void) SetImageBackgroundColor(image,exception);
6734 image->page.width=1;
6735 image->page.height=1;
6740 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6741 image=mng_info->image;
6745 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6746 /* PNG does not handle depths greater than 16 so reduce it even
6749 if (image->depth > 16)
6753 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6754 if (image->depth > 8)
6756 /* To do: fill low byte properly */
6760 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6764 if (image_info->number_scenes != 0)
6766 if (mng_info->scenes_found >
6767 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6771 if (logging != MagickFalse)
6772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6773 " Finished reading image datastream.");
6775 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6777 (void) CloseBlob(image);
6779 if (logging != MagickFalse)
6780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6781 " Finished reading all image datastreams.");
6783 #if defined(MNG_INSERT_LAYERS)
6784 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6785 (mng_info->mng_height))
6788 Insert a background layer if nothing else was found.
6790 if (logging != MagickFalse)
6791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6792 " No images found. Inserting a background layer.");
6794 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6797 Allocate next image structure.
6799 AcquireNextImage(image_info,image,exception);
6800 if (GetNextImageInList(image) == (Image *) NULL)
6802 image=DestroyImageList(image);
6803 MngInfoFreeStruct(mng_info,&have_mng_structure);
6805 if (logging != MagickFalse)
6806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6807 " Allocation failed, returning NULL.");
6809 return((Image *) NULL);
6811 image=SyncNextImageInList(image);
6813 image->columns=mng_info->mng_width;
6814 image->rows=mng_info->mng_height;
6815 image->page.width=mng_info->mng_width;
6816 image->page.height=mng_info->mng_height;
6819 image->background_color=mng_background_color;
6820 image->matte=MagickFalse;
6822 if (image_info->ping == MagickFalse)
6823 (void) SetImageBackgroundColor(image,exception);
6825 mng_info->image_found++;
6828 image->iterations=mng_iterations;
6830 if (mng_iterations == 1)
6831 image->start_loop=MagickTrue;
6833 while (GetPreviousImageInList(image) != (Image *) NULL)
6836 if (image_count > 10*mng_info->image_found)
6838 if (logging != MagickFalse)
6839 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6841 (void) ThrowMagickException(exception,GetMagickModule(),
6842 CoderError,"Linked list is corrupted, beginning of list not found",
6843 "`%s'",image_info->filename);
6845 return((Image *) NULL);
6848 image=GetPreviousImageInList(image);
6850 if (GetNextImageInList(image) == (Image *) NULL)
6852 if (logging != MagickFalse)
6853 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6855 (void) ThrowMagickException(exception,GetMagickModule(),
6856 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6857 image_info->filename);
6861 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6862 GetNextImageInList(image) ==
6865 if (logging != MagickFalse)
6866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6867 " First image null");
6869 (void) ThrowMagickException(exception,GetMagickModule(),
6870 CoderError,"image->next for first image is NULL but shouldn't be.",
6871 "`%s'",image_info->filename);
6874 if (mng_info->image_found == 0)
6876 if (logging != MagickFalse)
6877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6878 " No visible images found.");
6880 (void) ThrowMagickException(exception,GetMagickModule(),
6881 CoderError,"No visible images in file","`%s'",image_info->filename);
6883 if (image != (Image *) NULL)
6884 image=DestroyImageList(image);
6886 MngInfoFreeStruct(mng_info,&have_mng_structure);
6887 return((Image *) NULL);
6890 if (mng_info->ticks_per_second)
6891 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6892 final_delay/mng_info->ticks_per_second;
6895 image->start_loop=MagickTrue;
6897 /* Find final nonzero image delay */
6898 final_image_delay=0;
6900 while (GetNextImageInList(image) != (Image *) NULL)
6903 final_image_delay=image->delay;
6905 image=GetNextImageInList(image);
6908 if (final_delay < final_image_delay)
6909 final_delay=final_image_delay;
6911 image->delay=final_delay;
6913 if (logging != MagickFalse)
6914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6915 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6916 (double) final_delay);
6918 if (logging != MagickFalse)
6924 image=GetFirstImageInList(image);
6926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6927 " Before coalesce:");
6929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6930 " scene 0 delay=%.20g",(double) image->delay);
6932 while (GetNextImageInList(image) != (Image *) NULL)
6934 image=GetNextImageInList(image);
6935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6936 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6940 image=GetFirstImageInList(image);
6941 #ifdef MNG_COALESCE_LAYERS
6951 if (logging != MagickFalse)
6952 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6955 next_image=CoalesceImages(image,exception);
6957 if (next_image == (Image *) NULL)
6958 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6960 image=DestroyImageList(image);
6963 for (next=image; next != (Image *) NULL; next=next_image)
6965 next->page.width=mng_info->mng_width;
6966 next->page.height=mng_info->mng_height;
6969 next->scene=scene++;
6970 next_image=GetNextImageInList(next);
6972 if (next_image == (Image *) NULL)
6975 if (next->delay == 0)
6978 next_image->previous=GetPreviousImageInList(next);
6979 if (GetPreviousImageInList(next) == (Image *) NULL)
6982 next->previous->next=next_image;
6983 next=DestroyImage(next);
6989 while (GetNextImageInList(image) != (Image *) NULL)
6990 image=GetNextImageInList(image);
6992 image->dispose=BackgroundDispose;
6994 if (logging != MagickFalse)
7000 image=GetFirstImageInList(image);
7002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7003 " After coalesce:");
7005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7006 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7007 (double) image->dispose);
7009 while (GetNextImageInList(image) != (Image *) NULL)
7011 image=GetNextImageInList(image);
7013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7014 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7015 (double) image->delay,(double) image->dispose);
7019 image=GetFirstImageInList(image);
7020 MngInfoFreeStruct(mng_info,&have_mng_structure);
7021 have_mng_structure=MagickFalse;
7023 if (logging != MagickFalse)
7024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7026 return(GetFirstImageInList(image));
7028 #else /* PNG_LIBPNG_VER > 10011 */
7029 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7031 printf("Your PNG library is too old: You have libpng-%s\n",
7032 PNG_LIBPNG_VER_STRING);
7034 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7035 "PNG library is too old","`%s'",image_info->filename);
7037 return(Image *) NULL;
7040 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7042 return(ReadPNGImage(image_info,exception));
7044 #endif /* PNG_LIBPNG_VER > 10011 */
7048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7052 % R e g i s t e r P N G I m a g e %
7056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7058 % RegisterPNGImage() adds properties for the PNG image format to
7059 % the list of supported formats. The properties include the image format
7060 % tag, a method to read and/or write the format, whether the format
7061 % supports the saving of more than one frame to the same file or blob,
7062 % whether the format supports native in-memory I/O, and a brief
7063 % description of the format.
7065 % The format of the RegisterPNGImage method is:
7067 % size_t RegisterPNGImage(void)
7070 ModuleExport size_t RegisterPNGImage(void)
7073 version[MaxTextExtent];
7081 "See http://www.libpng.org/ for details about the PNG format."
7086 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7092 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7098 #if defined(PNG_LIBPNG_VER_STRING)
7099 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7100 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7102 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7104 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7105 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7110 entry=SetMagickInfo("MNG");
7111 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7113 #if defined(MAGICKCORE_PNG_DELEGATE)
7114 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7115 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7118 entry->magick=(IsImageFormatHandler *) IsMNG;
7119 entry->description=ConstantString("Multiple-image Network Graphics");
7121 if (*version != '\0')
7122 entry->version=ConstantString(version);
7124 entry->module=ConstantString("PNG");
7125 entry->note=ConstantString(MNGNote);
7126 (void) RegisterMagickInfo(entry);
7128 entry=SetMagickInfo("PNG");
7130 #if defined(MAGICKCORE_PNG_DELEGATE)
7131 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7132 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7135 entry->magick=(IsImageFormatHandler *) IsPNG;
7136 entry->adjoin=MagickFalse;
7137 entry->description=ConstantString("Portable Network Graphics");
7138 entry->module=ConstantString("PNG");
7140 if (*version != '\0')
7141 entry->version=ConstantString(version);
7143 entry->note=ConstantString(PNGNote);
7144 (void) RegisterMagickInfo(entry);
7146 entry=SetMagickInfo("PNG8");
7148 #if defined(MAGICKCORE_PNG_DELEGATE)
7149 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7150 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7153 entry->magick=(IsImageFormatHandler *) IsPNG;
7154 entry->adjoin=MagickFalse;
7155 entry->description=ConstantString(
7156 "8-bit indexed with optional binary transparency");
7157 entry->module=ConstantString("PNG");
7158 (void) RegisterMagickInfo(entry);
7160 entry=SetMagickInfo("PNG24");
7163 #if defined(ZLIB_VERSION)
7164 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7165 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7167 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7169 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7170 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7174 if (*version != '\0')
7175 entry->version=ConstantString(version);
7177 #if defined(MAGICKCORE_PNG_DELEGATE)
7178 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7179 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7182 entry->magick=(IsImageFormatHandler *) IsPNG;
7183 entry->adjoin=MagickFalse;
7184 entry->description=ConstantString("opaque 24-bit RGB");
7185 entry->module=ConstantString("PNG");
7186 (void) RegisterMagickInfo(entry);
7188 entry=SetMagickInfo("PNG32");
7190 #if defined(MAGICKCORE_PNG_DELEGATE)
7191 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7192 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7195 entry->magick=(IsImageFormatHandler *) IsPNG;
7196 entry->adjoin=MagickFalse;
7197 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7198 entry->module=ConstantString("PNG");
7199 (void) RegisterMagickInfo(entry);
7201 entry=SetMagickInfo("JNG");
7203 #if defined(JNG_SUPPORTED)
7204 #if defined(MAGICKCORE_PNG_DELEGATE)
7205 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7206 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7210 entry->magick=(IsImageFormatHandler *) IsJNG;
7211 entry->adjoin=MagickFalse;
7212 entry->description=ConstantString("JPEG Network Graphics");
7213 entry->module=ConstantString("PNG");
7214 entry->note=ConstantString(JNGNote);
7215 (void) RegisterMagickInfo(entry);
7217 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7218 ping_semaphore=AllocateSemaphoreInfo();
7221 return(MagickImageCoderSignature);
7225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7229 % U n r e g i s t e r P N G I m a g e %
7233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7235 % UnregisterPNGImage() removes format registrations made by the
7236 % PNG module from the list of supported formats.
7238 % The format of the UnregisterPNGImage method is:
7240 % UnregisterPNGImage(void)
7243 ModuleExport void UnregisterPNGImage(void)
7245 (void) UnregisterMagickInfo("MNG");
7246 (void) UnregisterMagickInfo("PNG");
7247 (void) UnregisterMagickInfo("PNG8");
7248 (void) UnregisterMagickInfo("PNG24");
7249 (void) UnregisterMagickInfo("PNG32");
7250 (void) UnregisterMagickInfo("JNG");
7252 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7253 if (ping_semaphore != (SemaphoreInfo *) NULL)
7254 DestroySemaphoreInfo(&ping_semaphore);
7258 #if defined(MAGICKCORE_PNG_DELEGATE)
7259 #if PNG_LIBPNG_VER > 10011
7261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7265 % W r i t e M N G I m a g e %
7269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7271 % WriteMNGImage() writes an image in the Portable Network Graphics
7272 % Group's "Multiple-image Network Graphics" encoded image format.
7274 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7276 % The format of the WriteMNGImage method is:
7278 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7279 % Image *image,ExceptionInfo *exception)
7281 % A description of each parameter follows.
7283 % o image_info: the image info.
7285 % o image: The image.
7287 % o exception: return any errors or warnings in this structure.
7289 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7290 % "To do" under ReadPNGImage):
7292 % Preserve all unknown and not-yet-handled known chunks found in input
7293 % PNG file and copy them into output PNG files according to the PNG
7296 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7298 % Improve selection of color type (use indexed-colour or indexed-colour
7299 % with tRNS when 256 or fewer unique RGBA values are present).
7301 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7302 % This will be complicated if we limit ourselves to generating MNG-LC
7303 % files. For now we ignore disposal method 3 and simply overlay the next
7306 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7307 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7308 % [mostly done 15 June 1999 but still need to take care of tRNS]
7310 % Check for identical sRGB and replace with a global sRGB (and remove
7311 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7312 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7313 % local gAMA/cHRM with local sRGB if appropriate).
7315 % Check for identical sBIT chunks and write global ones.
7317 % Provide option to skip writing the signature tEXt chunks.
7319 % Use signatures to detect identical objects and reuse the first
7320 % instance of such objects instead of writing duplicate objects.
7322 % Use a smaller-than-32k value of compression window size when
7325 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7326 % ancillary text chunks and save profiles.
7328 % Provide an option to force LC files (to ensure exact framing rate)
7331 % Provide an option to force VLC files instead of LC, even when offsets
7332 % are present. This will involve expanding the embedded images with a
7333 % transparent region at the top and/or left.
7337 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7338 png_info *ping_info, unsigned char *profile_type, unsigned char
7339 *profile_description, unsigned char *profile_data, png_uint_32 length)
7358 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7360 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7363 if (image_info->verbose)
7365 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7366 (char *) profile_type, (double) length);
7369 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7370 description_length=(png_uint_32) strlen((const char *) profile_description);
7371 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7372 + description_length);
7373 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7374 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7375 text[0].key[0]='\0';
7376 (void) ConcatenateMagickString(text[0].key,
7377 "Raw profile type ",MaxTextExtent);
7378 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7382 (void) CopyMagickString(dp,(const char *) profile_description,
7384 dp+=description_length;
7386 (void) FormatLocaleString(dp,allocated_length-
7387 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7390 for (i=0; i < (ssize_t) length; i++)
7394 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7395 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7400 text[0].text_length=(png_size_t) (dp-text[0].text);
7401 text[0].compression=image_info->compression == NoCompression ||
7402 (image_info->compression == UndefinedCompression &&
7403 text[0].text_length < 128) ? -1 : 0;
7405 if (text[0].text_length <= allocated_length)
7406 png_set_text(ping,ping_info,text,1);
7408 png_free(ping,text[0].text);
7409 png_free(ping,text[0].key);
7410 png_free(ping,text);
7413 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7414 const char *string, MagickBooleanType logging)
7427 ResetImageProfileIterator(image);
7429 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7431 profile=GetImageProfile(image,name);
7433 if (profile != (const StringInfo *) NULL)
7438 if (LocaleNCompare(name,string,11) == 0)
7440 if (logging != MagickFalse)
7441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442 " Found %s profile",name);
7444 ping_profile=CloneStringInfo(profile);
7445 data=GetStringInfoDatum(ping_profile),
7446 length=(png_uint_32) GetStringInfoLength(ping_profile);
7451 (void) WriteBlobMSBULong(image,length-5); /* data length */
7452 (void) WriteBlob(image,length-1,data+1);
7453 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7454 ping_profile=DestroyStringInfo(ping_profile);
7458 name=GetNextImageProfile(image);
7465 /* Write one PNG image */
7466 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7467 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7491 ping_trans_alpha[256];
7519 ping_have_cheap_transparency,
7530 /* ping_exclude_EXIF, */
7533 /* ping_exclude_iTXt, */
7538 /* ping_exclude_tRNS, */
7540 ping_exclude_zCCP, /* hex-encoded iCCP */
7543 ping_preserve_colormap,
7544 ping_need_colortype_warning,
7568 ping_interlace_method,
7569 ping_compression_method,
7586 number_semitransparent,
7588 ping_pHYs_unit_type;
7591 ping_pHYs_x_resolution,
7592 ping_pHYs_y_resolution;
7594 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7595 " Enter WriteOnePNGImage()");
7597 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7598 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7599 if (image_info == (ImageInfo *) NULL)
7600 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7602 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7603 LockSemaphoreInfo(ping_semaphore);
7606 /* Initialize some stuff */
7609 ping_interlace_method=0,
7610 ping_compression_method=0,
7611 ping_filter_method=0,
7614 ping_background.red = 0;
7615 ping_background.green = 0;
7616 ping_background.blue = 0;
7617 ping_background.gray = 0;
7618 ping_background.index = 0;
7620 ping_trans_color.red=0;
7621 ping_trans_color.green=0;
7622 ping_trans_color.blue=0;
7623 ping_trans_color.gray=0;
7625 ping_pHYs_unit_type = 0;
7626 ping_pHYs_x_resolution = 0;
7627 ping_pHYs_y_resolution = 0;
7629 ping_have_blob=MagickFalse;
7630 ping_have_color=MagickTrue;
7631 ping_have_non_bw=MagickTrue;
7632 ping_have_PLTE=MagickFalse;
7633 ping_have_bKGD=MagickFalse;
7634 ping_have_pHYs=MagickFalse;
7635 ping_have_tRNS=MagickFalse;
7637 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7638 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7639 ping_exclude_date=mng_info->ping_exclude_date;
7640 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7641 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7642 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7643 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7644 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7645 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7646 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7647 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7648 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7649 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7650 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7651 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7653 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7654 ping_need_colortype_warning = MagickFalse;
7656 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7657 * i.e., eliminate the ICC profile and set image->rendering_intent.
7658 * Note that this will not involve any changes to the actual pixels
7659 * but merely passes information to applications that read the resulting
7662 if (ping_exclude_sRGB == MagickFalse)
7670 ResetImageProfileIterator(image);
7671 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7673 profile=GetImageProfile(image,name);
7675 if (profile != (StringInfo *) NULL)
7677 if ((LocaleCompare(name,"ICC") == 0) ||
7678 (LocaleCompare(name,"ICM") == 0))
7683 /* 0: not a known sRGB profile
7684 * 1: HP-Microsoft sRGB v2
7685 * 2: ICC sRGB v4 perceptual
7686 * 3: ICC sRGB v2 perceptual no black-compensation
7689 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7690 check_len[4] = {0, 3144, 60960, 3052};
7699 length=(png_uint_32) GetStringInfoLength(profile);
7701 for (icheck=3; icheck > 0; icheck--)
7703 if (length == check_len[icheck])
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " Got a %lu-byte ICC profile (potentially sRGB)",
7707 (unsigned long) length);
7709 data=GetStringInfoDatum(profile);
7710 profile_crc=crc32(0,data,length);
7712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7713 " with crc=%8x",(unsigned int) profile_crc);
7715 if (profile_crc == check_crc[icheck])
7717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 if (image->rendering_intent==UndefinedIntent)
7720 image->rendering_intent=PerceptualIntent;
7726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7727 " Got a %lu-byte ICC profile",
7728 (unsigned long) length);
7731 name=GetNextImageProfile(image);
7736 number_semitransparent = 0;
7737 number_transparent = 0;
7739 if (logging != MagickFalse)
7741 if (image->storage_class == UndefinedClass)
7742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7743 " storage_class=UndefinedClass");
7744 if (image->storage_class == DirectClass)
7745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7746 " storage_class=DirectClass");
7747 if (image->storage_class == PseudoClass)
7748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7749 " storage_class=PseudoClass");
7752 if (image->storage_class == PseudoClass &&
7753 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7754 (mng_info->write_png_colortype != 0 &&
7755 mng_info->write_png_colortype != 4)))
7757 (void) SyncImage(image,exception);
7758 image->storage_class = DirectClass;
7761 if (ping_preserve_colormap == MagickFalse)
7763 if (image->storage_class != PseudoClass && image->colormap != NULL)
7765 /* Free the bogus colormap; it can cause trouble later */
7766 if (logging != MagickFalse)
7767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7768 " Freeing bogus colormap");
7769 (void) RelinquishMagickMemory(image->colormap);
7770 image->colormap=NULL;
7774 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7775 (void) TransformImageColorspace(image,RGBColorspace,exception);
7778 Sometimes we get PseudoClass images whose RGB values don't match
7779 the colors in the colormap. This code syncs the RGB values.
7781 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7782 (void) SyncImage(image,exception);
7784 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7785 if (image->depth > 8)
7787 if (logging != MagickFalse)
7788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7789 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7795 /* Respect the -depth option */
7796 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7801 if (image->depth > 8)
7803 #if MAGICKCORE_QUANTUM_DEPTH > 16
7804 /* Scale to 16-bit */
7805 LBR16PacketRGBO(image->background_color);
7807 for (y=0; y < (ssize_t) image->rows; y++)
7809 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7811 if (r == (Quantum *) NULL)
7814 for (x=0; x < (ssize_t) image->columns; x++)
7817 r+=GetPixelChannels(image);
7820 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7824 if (image->storage_class == PseudoClass && image->colormap != NULL)
7826 for (i=0; i < (ssize_t) image->colors; i++)
7828 LBR16PacketRGBO(image->colormap[i]);
7831 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7834 else if (image->depth > 4)
7836 #if MAGICKCORE_QUANTUM_DEPTH > 8
7837 /* Scale to 8-bit */
7838 LBR08PacketRGBO(image->background_color);
7840 for (y=0; y < (ssize_t) image->rows; y++)
7842 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7844 if (r == (Quantum *) NULL)
7847 for (x=0; x < (ssize_t) image->columns; x++)
7850 r+=GetPixelChannels(image);
7853 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7857 if (image->storage_class == PseudoClass && image->colormap != NULL)
7859 for (i=0; i < (ssize_t) image->colors; i++)
7861 LBR08PacketRGBO(image->colormap[i]);
7864 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7867 if (image->depth > 2)
7869 /* Scale to 4-bit */
7870 LBR04PacketRGBO(image->background_color);
7872 for (y=0; y < (ssize_t) image->rows; y++)
7874 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7876 if (r == (Quantum *) NULL)
7879 for (x=0; x < (ssize_t) image->columns; x++)
7882 r+=GetPixelChannels(image);
7885 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7889 if (image->storage_class == PseudoClass && image->colormap != NULL)
7891 for (i=0; i < (ssize_t) image->colors; i++)
7893 LBR04PacketRGBO(image->colormap[i]);
7898 else if (image->depth > 1)
7900 /* Scale to 2-bit */
7901 LBR02PacketRGBO(image->background_color);
7903 for (y=0; y < (ssize_t) image->rows; y++)
7905 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7907 if (r == (Quantum *) NULL)
7910 for (x=0; x < (ssize_t) image->columns; x++)
7913 r+=GetPixelChannels(image);
7916 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7920 if (image->storage_class == PseudoClass && image->colormap != NULL)
7922 for (i=0; i < (ssize_t) image->colors; i++)
7924 LBR02PacketRGBO(image->colormap[i]);
7930 /* Scale to 1-bit */
7931 LBR01PacketRGBO(image->background_color);
7933 for (y=0; y < (ssize_t) image->rows; y++)
7935 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7937 if (r == (Quantum *) NULL)
7940 for (x=0; x < (ssize_t) image->columns; x++)
7943 r+=GetPixelChannels(image);
7946 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7950 if (image->storage_class == PseudoClass && image->colormap != NULL)
7952 for (i=0; i < (ssize_t) image->colors; i++)
7954 LBR01PacketRGBO(image->colormap[i]);
7960 /* To do: set to next higher multiple of 8 */
7961 if (image->depth < 8)
7964 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7965 /* PNG does not handle depths greater than 16 so reduce it even
7968 if (image->depth > 8)
7972 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7973 if (image->depth > 8)
7975 /* To do: fill low byte properly */
7979 if (image->depth == 16 && mng_info->write_png_depth != 16)
7980 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
7984 /* Normally we run this just once, but in the case of writing PNG8
7985 * we reduce the transparency to binary and run again, then if there
7986 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7987 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7988 * palette. Then (To do) we take care of a final reduction that is only
7989 * needed if there are still 256 colors present and one of them has both
7990 * transparent and opaque instances.
7993 tried_332 = MagickFalse;
7994 tried_333 = MagickFalse;
7995 tried_444 = MagickFalse;
8001 * Sometimes we get DirectClass images that have 256 colors or fewer.
8002 * This code will build a colormap.
8004 * Also, sometimes we get PseudoClass images with an out-of-date
8005 * colormap. This code will replace the colormap with a new one.
8006 * Sometimes we get PseudoClass images that have more than 256 colors.
8007 * This code will delete the colormap and change the image to
8010 * If image->matte is MagickFalse, we ignore the alpha channel
8011 * even though it sometimes contains left-over non-opaque values.
8013 * Also we gather some information (number of opaque, transparent,
8014 * and semitransparent pixels, and whether the image has any non-gray
8015 * pixels or only black-and-white pixels) that we might need later.
8017 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8018 * we need to check for bogus non-opaque values, at least.
8026 semitransparent[260],
8029 register const Quantum
8036 if (logging != MagickFalse)
8037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8038 " Enter BUILD_PALETTE:");
8040 if (logging != MagickFalse)
8042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8043 " image->columns=%.20g",(double) image->columns);
8044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8045 " image->rows=%.20g",(double) image->rows);
8046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " image->matte=%.20g",(double) image->matte);
8048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8049 " image->depth=%.20g",(double) image->depth);
8051 if (image->storage_class == PseudoClass && image->colormap != NULL)
8053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8054 " Original colormap:");
8055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056 " i (red,green,blue,alpha)");
8058 for (i=0; i < 256; i++)
8060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8061 " %d (%d,%d,%d,%d)",
8063 (int) image->colormap[i].red,
8064 (int) image->colormap[i].green,
8065 (int) image->colormap[i].blue,
8066 (int) image->colormap[i].alpha);
8069 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8074 " %d (%d,%d,%d,%d)",
8076 (int) image->colormap[i].red,
8077 (int) image->colormap[i].green,
8078 (int) image->colormap[i].blue,
8079 (int) image->colormap[i].alpha);
8084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8085 " image->colors=%d",(int) image->colors);
8087 if (image->colors == 0)
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " (zero means unknown)");
8091 if (ping_preserve_colormap == MagickFalse)
8092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8093 " Regenerate the colormap");
8098 number_semitransparent = 0;
8099 number_transparent = 0;
8101 for (y=0; y < (ssize_t) image->rows; y++)
8103 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8105 if (q == (Quantum *) NULL)
8108 for (x=0; x < (ssize_t) image->columns; x++)
8110 if (image->matte == MagickFalse ||
8111 GetPixelAlpha(image,q) == OpaqueAlpha)
8113 if (number_opaque < 259)
8115 if (number_opaque == 0)
8117 GetPixelInfoPixel(image, q, opaque);
8118 opaque[0].alpha=OpaqueAlpha;
8122 for (i=0; i< (ssize_t) number_opaque; i++)
8124 if (IsPixelEquivalent(image,q, opaque+i))
8128 if (i == (ssize_t) number_opaque && number_opaque < 259)
8131 GetPixelInfoPixel(image, q, opaque+i);
8132 opaque[i].alpha=OpaqueAlpha;
8136 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8138 if (number_transparent < 259)
8140 if (number_transparent == 0)
8142 GetPixelInfoPixel(image, q, transparent);
8143 ping_trans_color.red=(unsigned short)
8144 GetPixelRed(image,q);
8145 ping_trans_color.green=(unsigned short)
8146 GetPixelGreen(image,q);
8147 ping_trans_color.blue=(unsigned short)
8148 GetPixelBlue(image,q);
8149 ping_trans_color.gray=(unsigned short)
8150 GetPixelRed(image,q);
8151 number_transparent = 1;
8154 for (i=0; i< (ssize_t) number_transparent; i++)
8156 if (IsPixelEquivalent(image,q, transparent+i))
8160 if (i == (ssize_t) number_transparent &&
8161 number_transparent < 259)
8163 number_transparent++;
8164 GetPixelInfoPixel(image,q,transparent+i);
8170 if (number_semitransparent < 259)
8172 if (number_semitransparent == 0)
8174 GetPixelInfoPixel(image,q,semitransparent);
8175 number_semitransparent = 1;
8178 for (i=0; i< (ssize_t) number_semitransparent; i++)
8180 if (IsPixelEquivalent(image,q, semitransparent+i)
8181 && GetPixelAlpha(image,q) ==
8182 semitransparent[i].alpha)
8186 if (i == (ssize_t) number_semitransparent &&
8187 number_semitransparent < 259)
8189 number_semitransparent++;
8190 GetPixelInfoPixel(image, q, semitransparent+i);
8194 q+=GetPixelChannels(image);
8198 if (mng_info->write_png8 == MagickFalse &&
8199 ping_exclude_bKGD == MagickFalse)
8201 /* Add the background color to the palette, if it
8202 * isn't already there.
8204 if (logging != MagickFalse)
8206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 " Check colormap for background (%d,%d,%d)",
8208 (int) image->background_color.red,
8209 (int) image->background_color.green,
8210 (int) image->background_color.blue);
8212 for (i=0; i<number_opaque; i++)
8214 if (opaque[i].red == image->background_color.red &&
8215 opaque[i].green == image->background_color.green &&
8216 opaque[i].blue == image->background_color.blue)
8219 if (number_opaque < 259 && i == number_opaque)
8221 opaque[i] = image->background_color;
8222 ping_background.index = i;
8223 if (logging != MagickFalse)
8225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226 " background_color index is %d",(int) i);
8230 else if (logging != MagickFalse)
8231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8232 " No room in the colormap to add background color");
8235 image_colors=number_opaque+number_transparent+number_semitransparent;
8237 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8239 /* No room for the background color; remove it. */
8244 if (logging != MagickFalse)
8246 if (image_colors > 256)
8247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 " image has more than 256 colors");
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " image has %d colors",image_colors);
8255 if (ping_preserve_colormap != MagickFalse)
8258 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8260 ping_have_color=MagickFalse;
8261 ping_have_non_bw=MagickFalse;
8263 if(image_colors > 256)
8265 for (y=0; y < (ssize_t) image->rows; y++)
8267 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8269 if (q == (Quantum *) NULL)
8273 for (x=0; x < (ssize_t) image->columns; x++)
8275 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8276 GetPixelRed(image,s) != GetPixelBlue(image,s))
8278 ping_have_color=MagickTrue;
8279 ping_have_non_bw=MagickTrue;
8282 s+=GetPixelChannels(image);
8285 if (ping_have_color != MagickFalse)
8288 /* Worst case is black-and-white; we are looking at every
8292 if (ping_have_non_bw == MagickFalse)
8295 for (x=0; x < (ssize_t) image->columns; x++)
8297 if (GetPixelRed(image,s) != 0 &&
8298 GetPixelRed(image,s) != QuantumRange)
8300 ping_have_non_bw=MagickTrue;
8303 s+=GetPixelChannels(image);
8310 if (image_colors < 257)
8316 * Initialize image colormap.
8319 if (logging != MagickFalse)
8320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8321 " Sort the new colormap");
8323 /* Sort palette, transparent first */;
8327 for (i=0; i<number_transparent; i++)
8328 colormap[n++] = transparent[i];
8330 for (i=0; i<number_semitransparent; i++)
8331 colormap[n++] = semitransparent[i];
8333 for (i=0; i<number_opaque; i++)
8334 colormap[n++] = opaque[i];
8336 ping_background.index +=
8337 (number_transparent + number_semitransparent);
8339 /* image_colors < 257; search the colormap instead of the pixels
8340 * to get ping_have_color and ping_have_non_bw
8344 if (ping_have_color == MagickFalse)
8346 if (colormap[i].red != colormap[i].green ||
8347 colormap[i].red != colormap[i].blue)
8349 ping_have_color=MagickTrue;
8350 ping_have_non_bw=MagickTrue;
8355 if (ping_have_non_bw == MagickFalse)
8357 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8358 ping_have_non_bw=MagickTrue;
8362 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8363 (number_transparent == 0 && number_semitransparent == 0)) &&
8364 (((mng_info->write_png_colortype-1) ==
8365 PNG_COLOR_TYPE_PALETTE) ||
8366 (mng_info->write_png_colortype == 0)))
8368 if (logging != MagickFalse)
8370 if (n != (ssize_t) image_colors)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " image_colors (%d) and n (%d) don't match",
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 " AcquireImageColormap");
8379 image->colors = image_colors;
8381 if (AcquireImageColormap(image,image_colors,exception) ==
8383 ThrowWriterException(ResourceLimitError,
8384 "MemoryAllocationFailed");
8386 for (i=0; i< (ssize_t) image_colors; i++)
8387 image->colormap[i] = colormap[i];
8389 if (logging != MagickFalse)
8391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8392 " image->colors=%d (%d)",
8393 (int) image->colors, image_colors);
8395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Update the pixel indexes");
8399 /* Sync the pixel indices with the new colormap */
8401 for (y=0; y < (ssize_t) image->rows; y++)
8403 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8405 if (q == (Quantum *) NULL)
8408 for (x=0; x < (ssize_t) image->columns; x++)
8410 for (i=0; i< (ssize_t) image_colors; i++)
8412 if ((image->matte == MagickFalse ||
8413 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8414 image->colormap[i].red == GetPixelRed(image,q) &&
8415 image->colormap[i].green == GetPixelGreen(image,q) &&
8416 image->colormap[i].blue == GetPixelBlue(image,q))
8418 SetPixelIndex(image,i,q);
8422 q+=GetPixelChannels(image);
8425 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8431 if (logging != MagickFalse)
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434 " image->colors=%d", (int) image->colors);
8436 if (image->colormap != NULL)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " i (red,green,blue,alpha)");
8441 for (i=0; i < (ssize_t) image->colors; i++)
8443 if (i < 300 || i >= (ssize_t) image->colors - 10)
8445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8446 " %d (%d,%d,%d,%d)",
8448 (int) image->colormap[i].red,
8449 (int) image->colormap[i].green,
8450 (int) image->colormap[i].blue,
8451 (int) image->colormap[i].alpha);
8456 if (number_transparent < 257)
8457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8458 " number_transparent = %d",
8459 number_transparent);
8462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8463 " number_transparent > 256");
8465 if (number_opaque < 257)
8466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8467 " number_opaque = %d",
8471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8472 " number_opaque > 256");
8474 if (number_semitransparent < 257)
8475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8476 " number_semitransparent = %d",
8477 number_semitransparent);
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " number_semitransparent > 256");
8483 if (ping_have_non_bw == MagickFalse)
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " All pixels and the background are black or white");
8487 else if (ping_have_color == MagickFalse)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " All pixels and the background are gray");
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493 " At least one pixel or the background is non-gray");
8495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496 " Exit BUILD_PALETTE:");
8499 if (mng_info->write_png8 == MagickFalse)
8502 /* Make any reductions necessary for the PNG8 format */
8503 if (image_colors <= 256 &&
8504 image_colors != 0 && image->colormap != NULL &&
8505 number_semitransparent == 0 &&
8506 number_transparent <= 1)
8509 /* PNG8 can't have semitransparent colors so we threshold the
8510 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8511 * transparent color so if more than one is transparent we merge
8512 * them into image->background_color.
8514 if (number_semitransparent != 0 || number_transparent > 1)
8516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8517 " Thresholding the alpha channel to binary");
8519 for (y=0; y < (ssize_t) image->rows; y++)
8521 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8523 if (r == (Quantum *) NULL)
8526 for (x=0; x < (ssize_t) image->columns; x++)
8528 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8530 SetPixelInfoPixel(image,&image->background_color,r);
8531 SetPixelAlpha(image,TransparentAlpha,r);
8534 SetPixelAlpha(image,OpaqueAlpha,r);
8535 r+=GetPixelChannels(image);
8538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8541 if (image_colors != 0 && image_colors <= 256 &&
8542 image->colormap != NULL)
8543 for (i=0; i<image_colors; i++)
8544 image->colormap[i].alpha =
8545 (image->colormap[i].alpha > TransparentAlpha/2 ?
8546 TransparentAlpha : OpaqueAlpha);
8551 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8552 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8553 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8556 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8558 if (logging != MagickFalse)
8559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8560 " Quantizing the background color to 4-4-4");
8562 tried_444 = MagickTrue;
8564 LBR04PacketRGB(image->background_color);
8566 if (logging != MagickFalse)
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Quantizing the pixel colors to 4-4-4");
8570 if (image->colormap == NULL)
8572 for (y=0; y < (ssize_t) image->rows; y++)
8574 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8576 if (r == (Quantum *) NULL)
8579 for (x=0; x < (ssize_t) image->columns; x++)
8581 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8583 r+=GetPixelChannels(image);
8586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8591 else /* Should not reach this; colormap already exists and
8594 if (logging != MagickFalse)
8595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8596 " Quantizing the colormap to 4-4-4");
8598 for (i=0; i<image_colors; i++)
8600 LBR04PacketRGB(image->colormap[i]);
8606 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8608 if (logging != MagickFalse)
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " Quantizing the background color to 3-3-3");
8612 tried_333 = MagickTrue;
8614 LBR03PacketRGB(image->background_color);
8616 if (logging != MagickFalse)
8617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8618 " Quantizing the pixel colors to 3-3-3-1");
8620 if (image->colormap == NULL)
8622 for (y=0; y < (ssize_t) image->rows; y++)
8624 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8626 if (r == (Quantum *) NULL)
8629 for (x=0; x < (ssize_t) image->columns; x++)
8631 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8633 r+=GetPixelChannels(image);
8636 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8641 else /* Should not reach this; colormap already exists and
8644 if (logging != MagickFalse)
8645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8646 " Quantizing the colormap to 3-3-3-1");
8647 for (i=0; i<image_colors; i++)
8649 LBR03PacketRGB(image->colormap[i]);
8655 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8657 if (logging != MagickFalse)
8658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8659 " Quantizing the background color to 3-3-2");
8661 tried_332 = MagickTrue;
8663 /* Red and green were already done so we only quantize the blue
8667 LBR02PacketBlue(image->background_color);
8669 if (logging != MagickFalse)
8670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671 " Quantizing the pixel colors to 3-3-2-1");
8673 if (image->colormap == NULL)
8675 for (y=0; y < (ssize_t) image->rows; y++)
8677 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8679 if (r == (Quantum *) NULL)
8682 for (x=0; x < (ssize_t) image->columns; x++)
8684 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8686 r+=GetPixelChannels(image);
8689 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8694 else /* Should not reach this; colormap already exists and
8697 if (logging != MagickFalse)
8698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8699 " Quantizing the colormap to 3-3-2-1");
8700 for (i=0; i<image_colors; i++)
8702 LBR02PacketBlue(image->colormap[i]);
8709 if (image_colors == 0 || image_colors > 256)
8711 /* Take care of special case with 256 colors + 1 transparent
8712 * color. We don't need to quantize to 2-3-2-1; we only need to
8713 * eliminate one color, so we'll merge the two darkest red
8714 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8716 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8717 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8718 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8720 image->background_color.red=ScaleCharToQuantum(0x24);
8723 if (image->colormap == NULL)
8725 for (y=0; y < (ssize_t) image->rows; y++)
8727 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8729 if (r == (Quantum *) NULL)
8732 for (x=0; x < (ssize_t) image->columns; x++)
8734 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8735 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8736 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8737 GetPixelAlpha(image,r) == OpaqueAlpha)
8739 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8741 r+=GetPixelChannels(image);
8744 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8752 for (i=0; i<image_colors; i++)
8754 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8755 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8756 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8758 image->colormap[i].red=ScaleCharToQuantum(0x24);
8764 /* END OF BUILD_PALETTE */
8766 /* If we are excluding the tRNS chunk and there is transparency,
8767 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8770 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8771 (number_transparent != 0 || number_semitransparent != 0))
8773 unsigned int colortype=mng_info->write_png_colortype;
8775 if (ping_have_color == MagickFalse)
8776 mng_info->write_png_colortype = 5;
8779 mng_info->write_png_colortype = 7;
8781 if (colortype != 0 &&
8782 mng_info->write_png_colortype != colortype)
8783 ping_need_colortype_warning=MagickTrue;
8787 /* See if cheap transparency is possible. It is only possible
8788 * when there is a single transparent color, no semitransparent
8789 * color, and no opaque color that has the same RGB components
8790 * as the transparent color. We only need this information if
8791 * we are writing a PNG with colortype 0 or 2, and we have not
8792 * excluded the tRNS chunk.
8794 if (number_transparent == 1 &&
8795 mng_info->write_png_colortype < 4)
8797 ping_have_cheap_transparency = MagickTrue;
8799 if (number_semitransparent != 0)
8800 ping_have_cheap_transparency = MagickFalse;
8802 else if (image_colors == 0 || image_colors > 256 ||
8803 image->colormap == NULL)
8805 register const Quantum
8808 for (y=0; y < (ssize_t) image->rows; y++)
8810 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8812 if (q == (Quantum *) NULL)
8815 for (x=0; x < (ssize_t) image->columns; x++)
8817 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8818 (unsigned short) GetPixelRed(image,q) ==
8819 ping_trans_color.red &&
8820 (unsigned short) GetPixelGreen(image,q) ==
8821 ping_trans_color.green &&
8822 (unsigned short) GetPixelBlue(image,q) ==
8823 ping_trans_color.blue)
8825 ping_have_cheap_transparency = MagickFalse;
8829 q+=GetPixelChannels(image);
8832 if (ping_have_cheap_transparency == MagickFalse)
8838 /* Assuming that image->colormap[0] is the one transparent color
8839 * and that all others are opaque.
8841 if (image_colors > 1)
8842 for (i=1; i<image_colors; i++)
8843 if (image->colormap[i].red == image->colormap[0].red &&
8844 image->colormap[i].green == image->colormap[0].green &&
8845 image->colormap[i].blue == image->colormap[0].blue)
8847 ping_have_cheap_transparency = MagickFalse;
8852 if (logging != MagickFalse)
8854 if (ping_have_cheap_transparency == MagickFalse)
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " Cheap transparency is not possible.");
8859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8860 " Cheap transparency is possible.");
8864 ping_have_cheap_transparency = MagickFalse;
8866 image_depth=image->depth;
8868 quantum_info = (QuantumInfo *) NULL;
8870 image_colors=(int) image->colors;
8871 image_matte=image->matte;
8873 mng_info->IsPalette=image->storage_class == PseudoClass &&
8874 image_colors <= 256 && image->colormap != NULL;
8876 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8877 (image->colors == 0 || image->colormap == NULL))
8879 image_info=DestroyImageInfo(image_info);
8880 image=DestroyImage(image);
8881 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8882 "Cannot write PNG8 or color-type 3; colormap is NULL",
8883 "`%s'",IMimage->filename);
8884 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8885 UnlockSemaphoreInfo(ping_semaphore);
8887 return(MagickFalse);
8891 Allocate the PNG structures
8893 #ifdef PNG_USER_MEM_SUPPORTED
8894 error_info.image=image;
8895 error_info.exception=exception;
8896 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
8897 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8898 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8901 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
8902 MagickPNGErrorHandler,MagickPNGWarningHandler);
8905 if (ping == (png_struct *) NULL)
8906 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8908 ping_info=png_create_info_struct(ping);
8910 if (ping_info == (png_info *) NULL)
8912 png_destroy_write_struct(&ping,(png_info **) NULL);
8913 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8916 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8917 ping_pixels=(unsigned char *) NULL;
8919 if (setjmp(png_jmpbuf(ping)))
8925 if (image_info->verbose)
8926 (void) printf("PNG write has failed.\n");
8928 png_destroy_write_struct(&ping,&ping_info);
8929 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8930 UnlockSemaphoreInfo(ping_semaphore);
8932 if (ping_have_blob != MagickFalse)
8933 (void) CloseBlob(image);
8934 image_info=DestroyImageInfo(image_info);
8935 image=DestroyImage(image);
8936 return(MagickFalse);
8939 Prepare PNG for writing.
8941 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8942 if (mng_info->write_mng)
8943 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8946 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8947 if (mng_info->write_mng)
8948 png_permit_empty_plte(ping,MagickTrue);
8955 ping_width=(png_uint_32) image->columns;
8956 ping_height=(png_uint_32) image->rows;
8958 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8961 if (mng_info->write_png_depth != 0)
8962 image_depth=mng_info->write_png_depth;
8964 /* Adjust requested depth to next higher valid depth if necessary */
8965 if (image_depth > 8)
8968 if ((image_depth > 4) && (image_depth < 8))
8971 if (image_depth == 3)
8974 if (logging != MagickFalse)
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " width=%.20g",(double) ping_width);
8978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8979 " height=%.20g",(double) ping_height);
8980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8981 " image_matte=%.20g",(double) image->matte);
8982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8983 " image->depth=%.20g",(double) image->depth);
8984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8985 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8988 save_image_depth=image_depth;
8989 ping_bit_depth=(png_byte) save_image_depth;
8992 #if defined(PNG_pHYs_SUPPORTED)
8993 if (ping_exclude_pHYs == MagickFalse)
8995 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
8996 (!mng_info->write_mng || !mng_info->equal_physs))
8998 if (logging != MagickFalse)
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " Setting up pHYs chunk");
9002 if (image->units == PixelsPerInchResolution)
9004 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9005 ping_pHYs_x_resolution=
9006 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9007 ping_pHYs_y_resolution=
9008 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9011 else if (image->units == PixelsPerCentimeterResolution)
9013 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9014 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9015 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9020 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9021 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9022 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9025 if (logging != MagickFalse)
9026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9027 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9028 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9029 (int) ping_pHYs_unit_type);
9030 ping_have_pHYs = MagickTrue;
9035 if (ping_exclude_bKGD == MagickFalse)
9037 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9043 if (ping_bit_depth == 8)
9046 if (ping_bit_depth == 4)
9049 if (ping_bit_depth == 2)
9052 if (ping_bit_depth == 1)
9055 ping_background.red=(png_uint_16)
9056 (ScaleQuantumToShort(image->background_color.red) & mask);
9058 ping_background.green=(png_uint_16)
9059 (ScaleQuantumToShort(image->background_color.green) & mask);
9061 ping_background.blue=(png_uint_16)
9062 (ScaleQuantumToShort(image->background_color.blue) & mask);
9064 ping_background.gray=(png_uint_16) ping_background.green;
9067 if (logging != MagickFalse)
9069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " Setting up bKGD chunk (1)");
9071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9072 " background_color index is %d",
9073 (int) ping_background.index);
9075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9076 " ping_bit_depth=%d",ping_bit_depth);
9079 ping_have_bKGD = MagickTrue;
9083 Select the color type.
9088 if (mng_info->IsPalette && mng_info->write_png8)
9091 /* To do: make this a function cause it's used twice, except
9092 for reducing the sample depth from 8. */
9094 number_colors=image_colors;
9096 ping_have_tRNS=MagickFalse;
9101 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9103 if (logging != MagickFalse)
9104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9105 " Setting up PLTE chunk with %d colors (%d)",
9106 number_colors, image_colors);
9108 for (i=0; i < (ssize_t) number_colors; i++)
9110 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9111 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9112 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9113 if (logging != MagickFalse)
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115 #if MAGICKCORE_QUANTUM_DEPTH == 8
9116 " %3ld (%3d,%3d,%3d)",
9118 " %5ld (%5d,%5d,%5d)",
9120 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9124 ping_have_PLTE=MagickTrue;
9125 image_depth=ping_bit_depth;
9128 if (matte != MagickFalse)
9131 Identify which colormap entry is transparent.
9133 assert(number_colors <= 256);
9134 assert(image->colormap != NULL);
9136 for (i=0; i < (ssize_t) number_transparent; i++)
9137 ping_trans_alpha[i]=0;
9140 ping_num_trans=(unsigned short) (number_transparent +
9141 number_semitransparent);
9143 if (ping_num_trans == 0)
9144 ping_have_tRNS=MagickFalse;
9147 ping_have_tRNS=MagickTrue;
9150 if (ping_exclude_bKGD == MagickFalse)
9153 * Identify which colormap entry is the background color.
9156 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9157 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9160 ping_background.index=(png_byte) i;
9162 if (logging != MagickFalse)
9164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165 " background_color index is %d",
9166 (int) ping_background.index);
9169 } /* end of write_png8 */
9171 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
9173 image_matte=MagickFalse;
9174 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9177 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
9179 image_matte=MagickTrue;
9180 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9183 else /* mng_info->write_pngNN not specified */
9185 image_depth=ping_bit_depth;
9187 if (mng_info->write_png_colortype != 0)
9189 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9191 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9192 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9193 image_matte=MagickTrue;
9196 image_matte=MagickFalse;
9198 if (logging != MagickFalse)
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200 " PNG colortype %d was specified:",(int) ping_color_type);
9203 else /* write_png_colortype not specified */
9205 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " Selecting PNG colortype:");
9209 ping_color_type=(png_byte) ((matte != MagickFalse)?
9210 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9212 if (image_info->type == TrueColorType)
9214 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9215 image_matte=MagickFalse;
9218 if (image_info->type == TrueColorMatteType)
9220 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9221 image_matte=MagickTrue;
9224 if (image_info->type == PaletteType ||
9225 image_info->type == PaletteMatteType)
9226 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9228 if (mng_info->write_png_colortype == 0 &&
9229 (image_info->type == UndefinedType ||
9230 image_info->type == OptimizeType))
9232 if (ping_have_color == MagickFalse)
9234 if (image_matte == MagickFalse)
9236 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9237 image_matte=MagickFalse;
9242 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9243 image_matte=MagickTrue;
9248 if (image_matte == MagickFalse)
9250 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9251 image_matte=MagickFalse;
9256 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9257 image_matte=MagickTrue;
9264 if (logging != MagickFalse)
9265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9266 " Selected PNG colortype=%d",ping_color_type);
9268 if (ping_bit_depth < 8)
9270 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9271 ping_color_type == PNG_COLOR_TYPE_RGB ||
9272 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9276 old_bit_depth=ping_bit_depth;
9278 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9280 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9284 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9289 if (image->colors == 0)
9292 (void) ThrowMagickException(exception,
9293 GetMagickModule(),CoderError,
9294 "image has 0 colors", "`%s'","");
9297 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9298 ping_bit_depth <<= 1;
9301 if (logging != MagickFalse)
9303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9304 " Number of colors: %.20g",(double) image_colors);
9306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307 " Tentative PNG bit depth: %d",ping_bit_depth);
9310 if (ping_bit_depth < (int) mng_info->write_png_depth)
9311 ping_bit_depth = mng_info->write_png_depth;
9314 image_depth=ping_bit_depth;
9316 if (logging != MagickFalse)
9318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9319 " Tentative PNG color type: %.20g",(double) ping_color_type);
9321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9322 " image_info->type: %.20g",(double) image_info->type);
9324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9325 " image_depth: %.20g",(double) image_depth);
9327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329 " image->depth: %.20g",(double) image->depth);
9331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9332 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9335 if (matte != MagickFalse)
9337 if (mng_info->IsPalette)
9339 if (mng_info->write_png_colortype == 0)
9341 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9343 if (ping_have_color != MagickFalse)
9344 ping_color_type=PNG_COLOR_TYPE_RGBA;
9348 * Determine if there is any transparent color.
9350 if (number_transparent + number_semitransparent == 0)
9353 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9356 image_matte=MagickFalse;
9358 if (mng_info->write_png_colortype == 0)
9359 ping_color_type&=0x03;
9369 if (ping_bit_depth == 8)
9372 if (ping_bit_depth == 4)
9375 if (ping_bit_depth == 2)
9378 if (ping_bit_depth == 1)
9381 ping_trans_color.red=(png_uint_16)
9382 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9384 ping_trans_color.green=(png_uint_16)
9385 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9387 ping_trans_color.blue=(png_uint_16)
9388 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9390 ping_trans_color.gray=(png_uint_16)
9391 (ScaleQuantumToShort(GetPixelInfoIntensity(
9392 image->colormap)) & mask);
9394 ping_trans_color.index=(png_byte) 0;
9396 ping_have_tRNS=MagickTrue;
9399 if (ping_have_tRNS != MagickFalse)
9402 * Determine if there is one and only one transparent color
9403 * and if so if it is fully transparent.
9405 if (ping_have_cheap_transparency == MagickFalse)
9406 ping_have_tRNS=MagickFalse;
9409 if (ping_have_tRNS != MagickFalse)
9411 if (mng_info->write_png_colortype == 0)
9412 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9414 if (image_depth == 8)
9416 ping_trans_color.red&=0xff;
9417 ping_trans_color.green&=0xff;
9418 ping_trans_color.blue&=0xff;
9419 ping_trans_color.gray&=0xff;
9425 if (image_depth == 8)
9427 ping_trans_color.red&=0xff;
9428 ping_trans_color.green&=0xff;
9429 ping_trans_color.blue&=0xff;
9430 ping_trans_color.gray&=0xff;
9437 if (ping_have_tRNS != MagickFalse)
9438 image_matte=MagickFalse;
9440 if ((mng_info->IsPalette) &&
9441 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9442 ping_have_color == MagickFalse &&
9443 (image_matte == MagickFalse || image_depth >= 8))
9447 if (image_matte != MagickFalse)
9448 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9450 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9452 ping_color_type=PNG_COLOR_TYPE_GRAY;
9454 if (save_image_depth == 16 && image_depth == 8)
9456 if (logging != MagickFalse)
9458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9459 " Scaling ping_trans_color (0)");
9461 ping_trans_color.gray*=0x0101;
9465 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9466 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9468 if ((image_colors == 0) ||
9469 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9470 image_colors=(int) (one << image_depth);
9472 if (image_depth > 8)
9478 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9480 if(!mng_info->write_png_depth)
9484 while ((int) (one << ping_bit_depth)
9485 < (ssize_t) image_colors)
9486 ping_bit_depth <<= 1;
9490 else if (ping_color_type ==
9491 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9492 mng_info->IsPalette)
9494 /* Check if grayscale is reducible */
9497 depth_4_ok=MagickTrue,
9498 depth_2_ok=MagickTrue,
9499 depth_1_ok=MagickTrue;
9501 for (i=0; i < (ssize_t) image_colors; i++)
9506 intensity=ScaleQuantumToChar(image->colormap[i].red);
9508 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9509 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9510 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9511 depth_2_ok=depth_1_ok=MagickFalse;
9512 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9513 depth_1_ok=MagickFalse;
9516 if (depth_1_ok && mng_info->write_png_depth <= 1)
9519 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9522 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9527 image_depth=ping_bit_depth;
9532 if (mng_info->IsPalette)
9534 number_colors=image_colors;
9536 if (image_depth <= 8)
9541 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9543 if (mng_info->have_write_global_plte && matte == MagickFalse)
9545 png_set_PLTE(ping,ping_info,NULL,0);
9547 if (logging != MagickFalse)
9548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9549 " Setting up empty PLTE chunk");
9554 for (i=0; i < (ssize_t) number_colors; i++)
9556 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9557 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9558 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9561 if (logging != MagickFalse)
9562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9563 " Setting up PLTE chunk with %d colors",
9566 ping_have_PLTE=MagickTrue;
9569 /* color_type is PNG_COLOR_TYPE_PALETTE */
9570 if (mng_info->write_png_depth == 0)
9578 while ((one << ping_bit_depth) < (size_t) number_colors)
9579 ping_bit_depth <<= 1;
9584 if (matte != MagickFalse)
9587 * Set up trans_colors array.
9589 assert(number_colors <= 256);
9591 ping_num_trans=(unsigned short) (number_transparent +
9592 number_semitransparent);
9594 if (ping_num_trans == 0)
9595 ping_have_tRNS=MagickFalse;
9599 if (logging != MagickFalse)
9601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9602 " Scaling ping_trans_color (1)");
9604 ping_have_tRNS=MagickTrue;
9606 for (i=0; i < ping_num_trans; i++)
9608 ping_trans_alpha[i]= (png_byte)
9609 ScaleQuantumToChar(image->colormap[i].alpha);
9619 if (image_depth < 8)
9622 if ((save_image_depth == 16) && (image_depth == 8))
9624 if (logging != MagickFalse)
9626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9627 " Scaling ping_trans_color from (%d,%d,%d)",
9628 (int) ping_trans_color.red,
9629 (int) ping_trans_color.green,
9630 (int) ping_trans_color.blue);
9633 ping_trans_color.red*=0x0101;
9634 ping_trans_color.green*=0x0101;
9635 ping_trans_color.blue*=0x0101;
9636 ping_trans_color.gray*=0x0101;
9638 if (logging != MagickFalse)
9640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9642 (int) ping_trans_color.red,
9643 (int) ping_trans_color.green,
9644 (int) ping_trans_color.blue);
9649 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9650 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9653 Adjust background and transparency samples in sub-8-bit grayscale files.
9655 if (ping_bit_depth < 8 && ping_color_type ==
9656 PNG_COLOR_TYPE_GRAY)
9664 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9666 if (ping_exclude_bKGD == MagickFalse)
9669 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9670 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9671 &image->background_color))) +.5)));
9673 if (logging != MagickFalse)
9674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " Setting up bKGD chunk (2)");
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " background_color index is %d",
9678 (int) ping_background.index);
9680 ping_have_bKGD = MagickTrue;
9683 if (logging != MagickFalse)
9684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9685 " Scaling ping_trans_color.gray from %d",
9686 (int)ping_trans_color.gray);
9688 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9689 ping_trans_color.gray)+.5);
9691 if (logging != MagickFalse)
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693 " to %d", (int)ping_trans_color.gray);
9696 if (ping_exclude_bKGD == MagickFalse)
9698 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9701 Identify which colormap entry is the background color.
9704 number_colors=image_colors;
9706 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9707 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9710 ping_background.index=(png_byte) i;
9712 if (logging != MagickFalse)
9714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9715 " Setting up bKGD chunk with index=%d",(int) i);
9718 if (i < (ssize_t) number_colors)
9720 ping_have_bKGD = MagickTrue;
9722 if (logging != MagickFalse)
9724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9725 " background =(%d,%d,%d)",
9726 (int) ping_background.red,
9727 (int) ping_background.green,
9728 (int) ping_background.blue);
9732 else /* Can't happen */
9734 if (logging != MagickFalse)
9735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9736 " No room in PLTE to add bKGD color");
9737 ping_have_bKGD = MagickFalse;
9742 if (logging != MagickFalse)
9743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9744 " PNG color type: %d",ping_color_type);
9746 Initialize compression level and filtering.
9748 if (logging != MagickFalse)
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " Setting up deflate compression");
9753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9754 " Compression buffer size: 32768");
9757 png_set_compression_buffer_size(ping,32768L);
9759 if (logging != MagickFalse)
9760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9761 " Compression mem level: 9");
9763 png_set_compression_mem_level(ping, 9);
9765 /* Untangle the "-quality" setting:
9767 Undefined is 0; the default is used.
9772 0: Use Z_HUFFMAN_ONLY strategy with the
9773 zlib default compression level
9775 1-9: the zlib compression level
9779 0-4: the PNG filter method
9781 5: libpng adaptive filtering if compression level > 5
9782 libpng filter type "none" if compression level <= 5
9783 or if image is grayscale or palette
9785 6: libpng adaptive filtering
9787 7: "LOCO" filtering (intrapixel differing) if writing
9788 a MNG, othewise "none". Did not work in IM-6.7.0-9
9789 and earlier because of a missing "else".
9791 8: Z_RLE strategy, all filters
9792 Unused prior to IM-6.7.0-10, was same as 6
9794 9: Z_RLE strategy, no PNG filters
9795 Unused prior to IM-6.7.0-10, was same as 6
9797 Note that using the -quality option, not all combinations of
9798 PNG filter type, zlib compression level, and zlib compression
9799 strategy are possible. This will be addressed soon in a
9800 release that accomodates "-define png:compression-strategy", etc.
9804 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9809 if (mng_info->write_png_compression_strategy == 0)
9810 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9813 else if (mng_info->write_png_compression_level == 0)
9818 level=(int) MagickMin((ssize_t) quality/10,9);
9820 mng_info->write_png_compression_level = level+1;
9823 if (mng_info->write_png_compression_strategy == 0)
9825 if ((quality %10) == 8 || (quality %10) == 9)
9826 mng_info->write_png_compression_strategy=Z_RLE;
9829 if (mng_info->write_png_compression_filter == 0)
9830 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9832 if (logging != MagickFalse)
9834 if (mng_info->write_png_compression_level)
9835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9836 " Compression level: %d",
9837 (int) mng_info->write_png_compression_level-1);
9839 if (mng_info->write_png_compression_strategy)
9840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Compression strategy: %d",
9842 (int) mng_info->write_png_compression_strategy-1);
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Setting up filtering");
9847 if (mng_info->write_png_compression_filter == 6)
9848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9849 " Base filter method: ADAPTIVE");
9850 else if (mng_info->write_png_compression_filter == 0 ||
9851 mng_info->write_png_compression_filter == 1)
9852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9853 " Base filter method: NONE");
9855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9856 " Base filter method: %d",
9857 (int) mng_info->write_png_compression_filter-1);
9860 if (mng_info->write_png_compression_level != 0)
9861 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9863 if (mng_info->write_png_compression_filter == 6)
9865 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9866 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9870 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9872 else if (mng_info->write_png_compression_filter == 7 ||
9873 mng_info->write_png_compression_filter == 10)
9874 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9876 else if (mng_info->write_png_compression_filter == 8)
9878 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9879 if (mng_info->write_mng)
9881 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9882 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9883 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9886 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9889 else if (mng_info->write_png_compression_filter == 9)
9890 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9892 else if (mng_info->write_png_compression_filter != 0)
9893 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9894 mng_info->write_png_compression_filter-1);
9896 if (mng_info->write_png_compression_strategy != 0)
9897 png_set_compression_strategy(ping,
9898 mng_info->write_png_compression_strategy-1);
9900 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9901 if (ping_exclude_sRGB != MagickFalse ||
9902 (image->rendering_intent == UndefinedIntent))
9904 if ((ping_exclude_tEXt == MagickFalse ||
9905 ping_exclude_zTXt == MagickFalse) &&
9906 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9908 ResetImageProfileIterator(image);
9909 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9911 profile=GetImageProfile(image,name);
9913 if (profile != (StringInfo *) NULL)
9915 #ifdef PNG_WRITE_iCCP_SUPPORTED
9916 if ((LocaleCompare(name,"ICC") == 0) ||
9917 (LocaleCompare(name,"ICM") == 0))
9920 if (ping_exclude_iCCP == MagickFalse)
9922 png_set_iCCP(ping,ping_info,(png_charp) name,0,
9923 #if (PNG_LIBPNG_VER < 10500)
9924 (png_charp) GetStringInfoDatum(profile),
9926 (png_const_bytep) GetStringInfoDatum(profile),
9928 (png_uint_32) GetStringInfoLength(profile));
9934 if (ping_exclude_zCCP == MagickFalse)
9936 Magick_png_write_raw_profile(image_info,ping,ping_info,
9937 (unsigned char *) name,(unsigned char *) name,
9938 GetStringInfoDatum(profile),
9939 (png_uint_32) GetStringInfoLength(profile));
9943 if (logging != MagickFalse)
9944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9945 " Setting up text chunk with %s profile",name);
9947 name=GetNextImageProfile(image);
9952 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9953 if ((mng_info->have_write_global_srgb == 0) &&
9954 ((image->rendering_intent != UndefinedIntent) ||
9955 (image->colorspace == sRGBColorspace)))
9957 if (ping_exclude_sRGB == MagickFalse)
9960 Note image rendering intent.
9962 if (logging != MagickFalse)
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " Setting up sRGB chunk");
9966 (void) png_set_sRGB(ping,ping_info,(
9967 Magick_RenderingIntent_to_PNG_RenderingIntent(
9968 image->rendering_intent)));
9972 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9975 if (ping_exclude_gAMA == MagickFalse &&
9976 (ping_exclude_sRGB == MagickFalse ||
9977 (image->gamma < .45 || image->gamma > .46)))
9979 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9983 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9985 if (logging != MagickFalse)
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up gAMA chunk");
9989 png_set_gAMA(ping,ping_info,image->gamma);
9993 if (ping_exclude_cHRM == MagickFalse)
9995 if ((mng_info->have_write_global_chrm == 0) &&
9996 (image->chromaticity.red_primary.x != 0.0))
9999 Note image chromaticity.
10000 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10008 wp=image->chromaticity.white_point;
10009 rp=image->chromaticity.red_primary;
10010 gp=image->chromaticity.green_primary;
10011 bp=image->chromaticity.blue_primary;
10013 if (logging != MagickFalse)
10014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10015 " Setting up cHRM chunk");
10017 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10023 ping_interlace_method=image_info->interlace != NoInterlace;
10025 if (mng_info->write_mng)
10026 png_set_sig_bytes(ping,8);
10028 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10030 if (mng_info->write_png_colortype != 0)
10032 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10033 if (ping_have_color != MagickFalse)
10035 ping_color_type = PNG_COLOR_TYPE_RGB;
10037 if (ping_bit_depth < 8)
10041 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10042 if (ping_have_color != MagickFalse)
10043 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10046 if (ping_need_colortype_warning != MagickFalse ||
10047 ((mng_info->write_png_depth &&
10048 (int) mng_info->write_png_depth != ping_bit_depth) ||
10049 (mng_info->write_png_colortype &&
10050 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10051 mng_info->write_png_colortype != 7 &&
10052 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10054 if (logging != MagickFalse)
10056 if (ping_need_colortype_warning != MagickFalse)
10058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10059 " Image has transparency but tRNS chunk was excluded");
10062 if (mng_info->write_png_depth)
10064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10065 " Defined png:bit-depth=%u, Computed depth=%u",
10066 mng_info->write_png_depth,
10070 if (mng_info->write_png_colortype)
10072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10073 " Defined png:color-type=%u, Computed color type=%u",
10074 mng_info->write_png_colortype-1,
10080 "Cannot write image with defined png:bit-depth or png:color-type.");
10083 if (image_matte != MagickFalse && image->matte == MagickFalse)
10085 /* Add an opaque matte channel */
10086 image->matte = MagickTrue;
10087 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10089 if (logging != MagickFalse)
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Added an opaque matte channel");
10094 if (number_transparent != 0 || number_semitransparent != 0)
10096 if (ping_color_type < 4)
10098 ping_have_tRNS=MagickTrue;
10099 if (logging != MagickFalse)
10100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10101 " Setting ping_have_tRNS=MagickTrue.");
10105 if (logging != MagickFalse)
10106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10107 " Writing PNG header chunks");
10109 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10110 ping_bit_depth,ping_color_type,
10111 ping_interlace_method,ping_compression_method,
10112 ping_filter_method);
10114 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10116 png_set_PLTE(ping,ping_info,palette,number_colors);
10118 if (logging != MagickFalse)
10120 for (i=0; i< (ssize_t) number_colors; i++)
10122 if (i < ping_num_trans)
10123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10124 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10126 (int) palette[i].red,
10127 (int) palette[i].green,
10128 (int) palette[i].blue,
10130 (int) ping_trans_alpha[i]);
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133 " PLTE[%d] = (%d,%d,%d)",
10135 (int) palette[i].red,
10136 (int) palette[i].green,
10137 (int) palette[i].blue);
10142 if (ping_exclude_bKGD == MagickFalse)
10144 if (ping_have_bKGD != MagickFalse)
10146 png_set_bKGD(ping,ping_info,&ping_background);
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " Setting up bKGD chunk");
10151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10152 " background color = (%d,%d,%d)",
10153 (int) ping_background.red,
10154 (int) ping_background.green,
10155 (int) ping_background.blue);
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " index = %d, gray=%d",
10158 (int) ping_background.index,
10159 (int) ping_background.gray);
10164 if (ping_exclude_pHYs == MagickFalse)
10166 if (ping_have_pHYs != MagickFalse)
10168 png_set_pHYs(ping,ping_info,
10169 ping_pHYs_x_resolution,
10170 ping_pHYs_y_resolution,
10171 ping_pHYs_unit_type);
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " Setting up pHYs chunk");
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " x_resolution=%lu",
10179 (unsigned long) ping_pHYs_x_resolution);
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " y_resolution=%lu",
10182 (unsigned long) ping_pHYs_y_resolution);
10183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10185 (unsigned long) ping_pHYs_unit_type);
10190 #if defined(PNG_oFFs_SUPPORTED)
10191 if (ping_exclude_oFFs == MagickFalse)
10193 if (image->page.x || image->page.y)
10195 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10196 (png_int_32) image->page.y, 0);
10198 if (logging != MagickFalse)
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10201 (int) image->page.x, (int) image->page.y);
10206 if (mng_info->need_blob != MagickFalse)
10208 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10210 png_error(ping,"WriteBlob Failed");
10212 ping_have_blob=MagickTrue;
10215 png_write_info_before_PLTE(ping, ping_info);
10217 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10219 if (logging != MagickFalse)
10221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10222 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10225 if (ping_color_type == 3)
10226 (void) png_set_tRNS(ping, ping_info,
10233 (void) png_set_tRNS(ping, ping_info,
10236 &ping_trans_color);
10238 if (logging != MagickFalse)
10240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10241 " tRNS color =(%d,%d,%d)",
10242 (int) ping_trans_color.red,
10243 (int) ping_trans_color.green,
10244 (int) ping_trans_color.blue);
10249 /* write any png-chunk-b profiles */
10250 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10252 png_write_info(ping,ping_info);
10254 /* write any PNG-chunk-m profiles */
10255 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10257 if (ping_exclude_vpAg == MagickFalse)
10259 if ((image->page.width != 0 && image->page.width != image->columns) ||
10260 (image->page.height != 0 && image->page.height != image->rows))
10265 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10266 PNGType(chunk,mng_vpAg);
10267 LogPNGChunk(logging,mng_vpAg,9L);
10268 PNGLong(chunk+4,(png_uint_32) image->page.width);
10269 PNGLong(chunk+8,(png_uint_32) image->page.height);
10270 chunk[12]=0; /* unit = pixels */
10271 (void) WriteBlob(image,13,chunk);
10272 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10276 #if (PNG_LIBPNG_VER == 10206)
10277 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10278 #define PNG_HAVE_IDAT 0x04
10279 ping->mode |= PNG_HAVE_IDAT;
10280 #undef PNG_HAVE_IDAT
10283 png_set_packing(ping);
10287 rowbytes=image->columns;
10288 if (image_depth > 8)
10290 switch (ping_color_type)
10292 case PNG_COLOR_TYPE_RGB:
10296 case PNG_COLOR_TYPE_GRAY_ALPHA:
10300 case PNG_COLOR_TYPE_RGBA:
10308 if (logging != MagickFalse)
10310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311 " Writing PNG image data");
10313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10314 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10316 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10317 sizeof(*ping_pixels));
10319 if (ping_pixels == (unsigned char *) NULL)
10320 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10323 Initialize image scanlines.
10325 if (setjmp(png_jmpbuf(ping)))
10331 if (image_info->verbose)
10332 (void) printf("PNG write has failed.\n");
10334 png_destroy_write_struct(&ping,&ping_info);
10335 if (quantum_info != (QuantumInfo *) NULL)
10336 quantum_info=DestroyQuantumInfo(quantum_info);
10337 if (ping_pixels != (unsigned char *) NULL)
10338 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10339 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10340 UnlockSemaphoreInfo(ping_semaphore);
10342 if (ping_have_blob != MagickFalse)
10343 (void) CloseBlob(image);
10344 image_info=DestroyImageInfo(image_info);
10345 image=DestroyImage(image);
10346 return(MagickFalse);
10348 quantum_info=AcquireQuantumInfo(image_info,image);
10349 if (quantum_info == (QuantumInfo *) NULL)
10350 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10351 quantum_info->format=UndefinedQuantumFormat;
10352 quantum_info->depth=image_depth;
10353 num_passes=png_set_interlace_handling(ping);
10355 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10356 !mng_info->write_png32) &&
10357 (mng_info->IsPalette ||
10358 (image_info->type == BilevelType)) &&
10359 image_matte == MagickFalse &&
10360 ping_have_non_bw == MagickFalse)
10362 /* Palette, Bilevel, or Opaque Monochrome */
10363 register const Quantum
10366 quantum_info->depth=8;
10367 for (pass=0; pass < num_passes; pass++)
10370 Convert PseudoClass image to a PNG monochrome image.
10372 for (y=0; y < (ssize_t) image->rows; y++)
10374 if (logging != MagickFalse && y == 0)
10375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10376 " Writing row of pixels (0)");
10378 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10380 if (p == (const Quantum *) NULL)
10383 if (mng_info->IsPalette)
10385 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10386 quantum_info,GrayQuantum,ping_pixels,exception);
10387 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10388 mng_info->write_png_depth &&
10389 mng_info->write_png_depth != old_bit_depth)
10391 /* Undo pixel scaling */
10392 for (i=0; i < (ssize_t) image->columns; i++)
10393 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10394 >> (8-old_bit_depth));
10400 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10401 quantum_info,RedQuantum,ping_pixels,exception);
10404 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10405 for (i=0; i < (ssize_t) image->columns; i++)
10406 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10409 if (logging != MagickFalse && y == 0)
10410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10411 " Writing row of pixels (1)");
10413 png_write_row(ping,ping_pixels);
10415 if (image->previous == (Image *) NULL)
10417 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10418 if (status == MagickFalse)
10424 else /* Not Palette, Bilevel, or Opaque Monochrome */
10426 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10427 !mng_info->write_png32) &&
10428 (image_matte != MagickFalse ||
10429 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10430 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10432 register const Quantum
10435 for (pass=0; pass < num_passes; pass++)
10438 for (y=0; y < (ssize_t) image->rows; y++)
10440 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10442 if (p == (const Quantum *) NULL)
10445 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10447 if (mng_info->IsPalette)
10448 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10449 quantum_info,GrayQuantum,ping_pixels,exception);
10452 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10453 quantum_info,RedQuantum,ping_pixels,exception);
10455 if (logging != MagickFalse && y == 0)
10456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10457 " Writing GRAY PNG pixels (2)");
10460 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10462 if (logging != MagickFalse && y == 0)
10463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10464 " Writing GRAY_ALPHA PNG pixels (2)");
10466 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10467 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10470 if (logging != MagickFalse && y == 0)
10471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10472 " Writing row of pixels (2)");
10474 png_write_row(ping,ping_pixels);
10477 if (image->previous == (Image *) NULL)
10479 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10480 if (status == MagickFalse)
10488 register const Quantum
10491 for (pass=0; pass < num_passes; pass++)
10493 if ((image_depth > 8) || (mng_info->write_png24 ||
10494 mng_info->write_png32 ||
10495 (!mng_info->write_png8 && !mng_info->IsPalette)))
10497 for (y=0; y < (ssize_t) image->rows; y++)
10499 p=GetVirtualPixels(image,0,y,image->columns,1,
10502 if (p == (const Quantum *) NULL)
10505 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10507 if (image->storage_class == DirectClass)
10508 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10509 quantum_info,RedQuantum,ping_pixels,exception);
10512 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10513 quantum_info,GrayQuantum,ping_pixels,exception);
10516 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10518 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10519 quantum_info,GrayAlphaQuantum,ping_pixels,
10522 if (logging != MagickFalse && y == 0)
10523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10524 " Writing GRAY_ALPHA PNG pixels (3)");
10527 else if (image_matte != MagickFalse)
10528 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10529 quantum_info,RGBAQuantum,ping_pixels,exception);
10532 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10533 quantum_info,RGBQuantum,ping_pixels,exception);
10535 if (logging != MagickFalse && y == 0)
10536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " Writing row of pixels (3)");
10539 png_write_row(ping,ping_pixels);
10544 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10545 mng_info->write_png32 ||
10546 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10548 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10549 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10551 if (logging != MagickFalse)
10552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10553 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10555 quantum_info->depth=8;
10559 for (y=0; y < (ssize_t) image->rows; y++)
10561 if (logging != MagickFalse && y == 0)
10562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10563 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10565 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10567 if (p == (const Quantum *) NULL)
10570 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10572 quantum_info->depth=image->depth;
10574 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10575 quantum_info,GrayQuantum,ping_pixels,exception);
10578 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10580 if (logging != MagickFalse && y == 0)
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10582 " Writing GRAY_ALPHA PNG pixels (4)");
10584 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10585 quantum_info,GrayAlphaQuantum,ping_pixels,
10591 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10592 quantum_info,IndexQuantum,ping_pixels,exception);
10594 if (logging != MagickFalse && y <= 2)
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10597 " Writing row of non-gray pixels (4)");
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10601 (int)ping_pixels[0],(int)ping_pixels[1]);
10604 png_write_row(ping,ping_pixels);
10608 if (image->previous == (Image *) NULL)
10610 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10611 if (status == MagickFalse)
10618 if (quantum_info != (QuantumInfo *) NULL)
10619 quantum_info=DestroyQuantumInfo(quantum_info);
10621 if (logging != MagickFalse)
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " Wrote PNG image data");
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627 " Width: %.20g",(double) ping_width);
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 " Height: %.20g",(double) ping_height);
10632 if (mng_info->write_png_depth)
10634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10635 " Defined png:bit-depth: %d",mng_info->write_png_depth);
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " PNG bit-depth written: %d",ping_bit_depth);
10641 if (mng_info->write_png_colortype)
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10644 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " PNG color-type written: %d",ping_color_type);
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " PNG Interlace method: %d",ping_interlace_method);
10654 Generate text chunks after IDAT.
10656 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10658 ResetImagePropertyIterator(image);
10659 property=GetNextImageProperty(image);
10660 while (property != (const char *) NULL)
10665 value=GetImageProperty(image,property,exception);
10667 /* Don't write any "png:" properties; those are just for "identify" */
10668 if (LocaleNCompare(property,"png:",4) != 0 &&
10670 /* Suppress density and units if we wrote a pHYs chunk */
10671 (ping_exclude_pHYs != MagickFalse ||
10672 LocaleCompare(property,"density") != 0 ||
10673 LocaleCompare(property,"units") != 0) &&
10675 /* Suppress the IM-generated Date:create and Date:modify */
10676 (ping_exclude_date == MagickFalse ||
10677 LocaleNCompare(property, "Date:",5) != 0))
10679 if (value != (const char *) NULL)
10681 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10682 text[0].key=(char *) property;
10683 text[0].text=(char *) value;
10684 text[0].text_length=strlen(value);
10686 if (ping_exclude_tEXt != MagickFalse)
10687 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10689 else if (ping_exclude_zTXt != MagickFalse)
10690 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10694 text[0].compression=image_info->compression == NoCompression ||
10695 (image_info->compression == UndefinedCompression &&
10696 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10697 PNG_TEXT_COMPRESSION_zTXt ;
10700 if (logging != MagickFalse)
10702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10703 " Setting up text chunk");
10705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10706 " keyword: %s",text[0].key);
10709 png_set_text(ping,ping_info,text,1);
10710 png_free(ping,text);
10713 property=GetNextImageProperty(image);
10717 /* write any PNG-chunk-e profiles */
10718 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10720 if (logging != MagickFalse)
10721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10722 " Writing PNG end info");
10724 png_write_end(ping,ping_info);
10726 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10728 if (mng_info->page.x || mng_info->page.y ||
10729 (ping_width != mng_info->page.width) ||
10730 (ping_height != mng_info->page.height))
10736 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10738 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10739 PNGType(chunk,mng_FRAM);
10740 LogPNGChunk(logging,mng_FRAM,27L);
10742 chunk[5]=0; /* frame name separator (no name) */
10743 chunk[6]=1; /* flag for changing delay, for next frame only */
10744 chunk[7]=0; /* flag for changing frame timeout */
10745 chunk[8]=1; /* flag for changing frame clipping for next frame */
10746 chunk[9]=0; /* flag for changing frame sync_id */
10747 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10748 chunk[14]=0; /* clipping boundaries delta type */
10749 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10751 (png_uint_32) (mng_info->page.x + ping_width));
10752 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10754 (png_uint_32) (mng_info->page.y + ping_height));
10755 (void) WriteBlob(image,31,chunk);
10756 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10757 mng_info->old_framing_mode=4;
10758 mng_info->framing_mode=1;
10762 mng_info->framing_mode=3;
10764 if (mng_info->write_mng && !mng_info->need_fram &&
10765 ((int) image->dispose == 3))
10766 (void) ThrowMagickException(exception,GetMagickModule(),
10767 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10768 "`%s'",image->filename);
10771 Free PNG resources.
10774 png_destroy_write_struct(&ping,&ping_info);
10776 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10778 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10779 UnlockSemaphoreInfo(ping_semaphore);
10782 if (ping_have_blob != MagickFalse)
10783 (void) CloseBlob(image);
10785 image_info=DestroyImageInfo(image_info);
10786 image=DestroyImage(image);
10788 /* Store bit depth actually written */
10789 s[0]=(char) ping_bit_depth;
10792 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
10794 if (logging != MagickFalse)
10795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796 " exit WriteOnePNGImage()");
10798 return(MagickTrue);
10799 /* End write one PNG image */
10803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10807 % W r i t e P N G I m a g e %
10811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10813 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10814 % Multiple-image Network Graphics (MNG) image file.
10816 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10818 % The format of the WritePNGImage method is:
10820 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10821 % Image *image,ExceptionInfo *exception)
10823 % A description of each parameter follows:
10825 % o image_info: the image info.
10827 % o image: The image.
10829 % o exception: return any errors or warnings in this structure.
10831 % Returns MagickTrue on success, MagickFalse on failure.
10833 % Communicating with the PNG encoder:
10835 % While the datastream written is always in PNG format and normally would
10836 % be given the "png" file extension, this method also writes the following
10837 % pseudo-formats which are subsets of png:
10839 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10840 % a depth greater than 8, the depth is reduced. If transparency
10841 % is present, the tRNS chunk must only have values 0 and 255
10842 % (i.e., transparency is binary: fully opaque or fully
10843 % transparent). If other values are present they will be
10844 % 50%-thresholded to binary transparency. If more than 256
10845 % colors are present, they will be quantized to the 4-4-4-1,
10846 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10847 % of any resulting fully-transparent pixels is changed to
10848 % the image's background color.
10850 % If you want better quantization or dithering of the colors
10851 % or alpha than that, you need to do it before calling the
10852 % PNG encoder. The pixels contain 8-bit indices even if
10853 % they could be represented with 1, 2, or 4 bits. Grayscale
10854 % images will be written as indexed PNG files even though the
10855 % PNG grayscale type might be slightly more efficient. Please
10856 % note that writing to the PNG8 format may result in loss
10857 % of color and alpha data.
10859 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10860 % chunk can be present to convey binary transparency by naming
10861 % one of the colors as transparent. The only loss incurred
10862 % is reduction of sample depth to 8. If the image has more
10863 % than one transparent color, has semitransparent pixels, or
10864 % has an opaque pixel with the same RGB components as the
10865 % transparent color, an image is not written.
10867 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10868 % transparency is permitted, i.e., the alpha sample for
10869 % each pixel can have any value from 0 to 255. The alpha
10870 % channel is present even if the image is fully opaque.
10871 % The only loss in data is the reduction of the sample depth
10874 % o -define: For more precise control of the PNG output, you can use the
10875 % Image options "png:bit-depth" and "png:color-type". These
10876 % can be set from the commandline with "-define" and also
10877 % from the application programming interfaces. The options
10878 % are case-independent and are converted to lowercase before
10879 % being passed to this encoder.
10881 % png:color-type can be 0, 2, 3, 4, or 6.
10883 % When png:color-type is 0 (Grayscale), png:bit-depth can
10884 % be 1, 2, 4, 8, or 16.
10886 % When png:color-type is 2 (RGB), png:bit-depth can
10889 % When png:color-type is 3 (Indexed), png:bit-depth can
10890 % be 1, 2, 4, or 8. This refers to the number of bits
10891 % used to store the index. The color samples always have
10892 % bit-depth 8 in indexed PNG files.
10894 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10895 % png:bit-depth can be 8 or 16.
10897 % If the image cannot be written without loss with the requested bit-depth
10898 % and color-type, a PNG file will not be written, and the encoder will
10899 % return MagickFalse.
10901 % Since image encoders should not be responsible for the "heavy lifting",
10902 % the user should make sure that ImageMagick has already reduced the
10903 % image depth and number of colors and limit transparency to binary
10904 % transparency prior to attempting to write the image with depth, color,
10905 % or transparency limitations.
10907 % Note that another definition, "png:bit-depth-written" exists, but it
10908 % is not intended for external use. It is only used internally by the
10909 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10911 % It is possible to request that the PNG encoder write previously-formatted
10912 % ancillary chunks in the output PNG file, using the "-profile" commandline
10913 % option as shown below or by setting the profile via a programming
10916 % -profile PNG-chunk-x:<file>
10918 % where x is a location flag and <file> is a file containing the chunk
10919 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10920 % This encoder will compute the chunk length and CRC, so those must not
10921 % be included in the file.
10923 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10924 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10925 % of the same type, then add a short unique string after the "x" to prevent
10926 % subsequent profiles from overwriting the preceding ones, e.g.,
10928 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10930 % As of version 6.6.6 the following optimizations are always done:
10932 % o 32-bit depth is reduced to 16.
10933 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10934 % high byte and low byte are identical.
10935 % o Palette is sorted to remove unused entries and to put a
10936 % transparent color first, if BUILD_PNG_PALETTE is defined.
10937 % o Opaque matte channel is removed when writing an indexed PNG.
10938 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10939 % this can be done without loss and a larger bit depth N was not
10940 % requested via the "-define png:bit-depth=N" option.
10941 % o If matte channel is present but only one transparent color is
10942 % present, RGB+tRNS is written instead of RGBA
10943 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10944 % was requested when converting an opaque image).
10946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10948 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10949 Image *image,ExceptionInfo *exception)
10954 have_mng_structure,
10970 assert(image_info != (const ImageInfo *) NULL);
10971 assert(image_info->signature == MagickSignature);
10972 assert(image != (Image *) NULL);
10973 assert(image->signature == MagickSignature);
10974 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10975 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10977 Allocate a MngInfo structure.
10979 have_mng_structure=MagickFalse;
10980 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10982 if (mng_info == (MngInfo *) NULL)
10983 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10986 Initialize members of the MngInfo structure.
10988 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10989 mng_info->image=image;
10990 mng_info->equal_backgrounds=MagickTrue;
10991 have_mng_structure=MagickTrue;
10993 /* See if user has requested a specific PNG subformat */
10995 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10996 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10997 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10999 if (mng_info->write_png8)
11001 mng_info->write_png_colortype = /* 3 */ 4;
11002 mng_info->write_png_depth = 8;
11006 if (mng_info->write_png24)
11008 mng_info->write_png_colortype = /* 2 */ 3;
11009 mng_info->write_png_depth = 8;
11012 if (image->matte == MagickTrue)
11013 (void) SetImageType(image,TrueColorMatteType,exception);
11016 (void) SetImageType(image,TrueColorType,exception);
11018 (void) SyncImage(image,exception);
11021 if (mng_info->write_png32)
11023 mng_info->write_png_colortype = /* 6 */ 7;
11024 mng_info->write_png_depth = 8;
11027 if (image->matte == MagickTrue)
11028 (void) SetImageType(image,TrueColorMatteType,exception);
11031 (void) SetImageType(image,TrueColorType,exception);
11033 (void) SyncImage(image,exception);
11036 value=GetImageOption(image_info,"png:bit-depth");
11038 if (value != (char *) NULL)
11040 if (LocaleCompare(value,"1") == 0)
11041 mng_info->write_png_depth = 1;
11043 else if (LocaleCompare(value,"2") == 0)
11044 mng_info->write_png_depth = 2;
11046 else if (LocaleCompare(value,"4") == 0)
11047 mng_info->write_png_depth = 4;
11049 else if (LocaleCompare(value,"8") == 0)
11050 mng_info->write_png_depth = 8;
11052 else if (LocaleCompare(value,"16") == 0)
11053 mng_info->write_png_depth = 16;
11056 (void) ThrowMagickException(exception,
11057 GetMagickModule(),CoderWarning,
11058 "ignoring invalid defined png:bit-depth",
11061 if (logging != MagickFalse)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11066 value=GetImageOption(image_info,"png:color-type");
11068 if (value != (char *) NULL)
11070 /* We must store colortype+1 because 0 is a valid colortype */
11071 if (LocaleCompare(value,"0") == 0)
11072 mng_info->write_png_colortype = 1;
11074 else if (LocaleCompare(value,"1") == 0)
11075 mng_info->write_png_colortype = 2;
11077 else if (LocaleCompare(value,"2") == 0)
11078 mng_info->write_png_colortype = 3;
11080 else if (LocaleCompare(value,"3") == 0)
11081 mng_info->write_png_colortype = 4;
11083 else if (LocaleCompare(value,"4") == 0)
11084 mng_info->write_png_colortype = 5;
11086 else if (LocaleCompare(value,"6") == 0)
11087 mng_info->write_png_colortype = 7;
11090 (void) ThrowMagickException(exception,
11091 GetMagickModule(),CoderWarning,
11092 "ignoring invalid defined png:color-type",
11095 if (logging != MagickFalse)
11096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11097 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11100 /* Check for chunks to be excluded:
11102 * The default is to not exclude any known chunks except for any
11103 * listed in the "unused_chunks" array, above.
11105 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11106 * define (in the image properties or in the image artifacts)
11107 * or via a mng_info member. For convenience, in addition
11108 * to or instead of a comma-separated list of chunks, the
11109 * "exclude-chunk" string can be simply "all" or "none".
11111 * The exclude-chunk define takes priority over the mng_info.
11113 * A "png:include-chunk" define takes priority over both the
11114 * mng_info and the "png:exclude-chunk" define. Like the
11115 * "exclude-chunk" string, it can define "all" or "none" as
11116 * well as a comma-separated list. Chunks that are unknown to
11117 * ImageMagick are always excluded, regardless of their "copy-safe"
11118 * status according to the PNG specification, and even if they
11119 * appear in the "include-chunk" list. Such defines appearing among
11120 * the image options take priority over those found among the image
11123 * Finally, all chunks listed in the "unused_chunks" array are
11124 * automatically excluded, regardless of the other instructions
11127 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11128 * will not be written and the gAMA chunk will only be written if it
11129 * is not between .45 and .46, or approximately (1.0/2.2).
11131 * If you exclude tRNS and the image has transparency, the colortype
11132 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11134 * The -strip option causes StripImage() to set the png:include-chunk
11135 * artifact to "none,trns,gama".
11138 mng_info->ping_exclude_bKGD=MagickFalse;
11139 mng_info->ping_exclude_cHRM=MagickFalse;
11140 mng_info->ping_exclude_date=MagickFalse;
11141 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11142 mng_info->ping_exclude_gAMA=MagickFalse;
11143 mng_info->ping_exclude_iCCP=MagickFalse;
11144 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11145 mng_info->ping_exclude_oFFs=MagickFalse;
11146 mng_info->ping_exclude_pHYs=MagickFalse;
11147 mng_info->ping_exclude_sRGB=MagickFalse;
11148 mng_info->ping_exclude_tEXt=MagickFalse;
11149 mng_info->ping_exclude_tRNS=MagickFalse;
11150 mng_info->ping_exclude_vpAg=MagickFalse;
11151 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11152 mng_info->ping_exclude_zTXt=MagickFalse;
11154 mng_info->ping_preserve_colormap=MagickFalse;
11156 value=GetImageArtifact(image,"png:preserve-colormap");
11158 value=GetImageOption(image_info,"png:preserve-colormap");
11160 mng_info->ping_preserve_colormap=MagickTrue;
11162 /* Thes compression-level, compression-strategy, and compression-filter
11163 * defines take precedence over values from the -quality option.
11165 value=GetImageArtifact(image,"png:compression-level");
11167 value=GetImageOption(image_info,"png:compression-level");
11170 /* We have to add 1 to everything because 0 is a valid input,
11171 * and we want to use 0 (the default) to mean undefined.
11173 if (LocaleCompare(value,"0") == 0)
11174 mng_info->write_png_compression_level = 1;
11176 if (LocaleCompare(value,"1") == 0)
11177 mng_info->write_png_compression_level = 2;
11179 else if (LocaleCompare(value,"2") == 0)
11180 mng_info->write_png_compression_level = 3;
11182 else if (LocaleCompare(value,"3") == 0)
11183 mng_info->write_png_compression_level = 4;
11185 else if (LocaleCompare(value,"4") == 0)
11186 mng_info->write_png_compression_level = 5;
11188 else if (LocaleCompare(value,"5") == 0)
11189 mng_info->write_png_compression_level = 6;
11191 else if (LocaleCompare(value,"6") == 0)
11192 mng_info->write_png_compression_level = 7;
11194 else if (LocaleCompare(value,"7") == 0)
11195 mng_info->write_png_compression_level = 8;
11197 else if (LocaleCompare(value,"8") == 0)
11198 mng_info->write_png_compression_level = 9;
11200 else if (LocaleCompare(value,"9") == 0)
11201 mng_info->write_png_compression_level = 10;
11204 (void) ThrowMagickException(exception,
11205 GetMagickModule(),CoderWarning,
11206 "ignoring invalid defined png:compression-level",
11210 value=GetImageArtifact(image,"png:compression-strategy");
11212 value=GetImageOption(image_info,"png:compression-strategy");
11216 if (LocaleCompare(value,"0") == 0)
11217 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11219 else if (LocaleCompare(value,"1") == 0)
11220 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11222 else if (LocaleCompare(value,"2") == 0)
11223 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11225 else if (LocaleCompare(value,"3") == 0)
11226 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11227 mng_info->write_png_compression_strategy = Z_RLE+1;
11229 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11232 else if (LocaleCompare(value,"4") == 0)
11233 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11234 mng_info->write_png_compression_strategy = Z_FIXED+1;
11236 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11240 (void) ThrowMagickException(exception,
11241 GetMagickModule(),CoderWarning,
11242 "ignoring invalid defined png:compression-strategy",
11246 value=GetImageArtifact(image,"png:compression-filter");
11248 value=GetImageOption(image_info,"png:compression-filter");
11252 /* To do: combinations of filters allowed by libpng
11253 * masks 0x08 through 0xf8
11255 * Implement this as a comma-separated list of 0,1,2,3,4,5
11256 * where 5 is a special case meaning PNG_ALL_FILTERS.
11259 if (LocaleCompare(value,"0") == 0)
11260 mng_info->write_png_compression_filter = 1;
11262 if (LocaleCompare(value,"1") == 0)
11263 mng_info->write_png_compression_filter = 2;
11265 else if (LocaleCompare(value,"2") == 0)
11266 mng_info->write_png_compression_filter = 3;
11268 else if (LocaleCompare(value,"3") == 0)
11269 mng_info->write_png_compression_filter = 4;
11271 else if (LocaleCompare(value,"4") == 0)
11272 mng_info->write_png_compression_filter = 5;
11274 else if (LocaleCompare(value,"5") == 0)
11275 mng_info->write_png_compression_filter = 6;
11278 (void) ThrowMagickException(exception,
11279 GetMagickModule(),CoderWarning,
11280 "ignoring invalid defined png:compression-filter",
11284 excluding=MagickFalse;
11286 for (source=0; source<1; source++)
11290 value=GetImageArtifact(image,"png:exclude-chunk");
11293 value=GetImageArtifact(image,"png:exclude-chunks");
11297 value=GetImageOption(image_info,"png:exclude-chunk");
11300 value=GetImageOption(image_info,"png:exclude-chunks");
11309 excluding=MagickTrue;
11311 if (logging != MagickFalse)
11314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11315 " png:exclude-chunk=%s found in image artifacts.\n", value);
11317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11318 " png:exclude-chunk=%s found in image properties.\n", value);
11321 last=strlen(value);
11323 for (i=0; i<(int) last; i+=5)
11326 if (LocaleNCompare(value+i,"all",3) == 0)
11328 mng_info->ping_exclude_bKGD=MagickTrue;
11329 mng_info->ping_exclude_cHRM=MagickTrue;
11330 mng_info->ping_exclude_date=MagickTrue;
11331 mng_info->ping_exclude_EXIF=MagickTrue;
11332 mng_info->ping_exclude_gAMA=MagickTrue;
11333 mng_info->ping_exclude_iCCP=MagickTrue;
11334 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11335 mng_info->ping_exclude_oFFs=MagickTrue;
11336 mng_info->ping_exclude_pHYs=MagickTrue;
11337 mng_info->ping_exclude_sRGB=MagickTrue;
11338 mng_info->ping_exclude_tEXt=MagickTrue;
11339 mng_info->ping_exclude_tRNS=MagickTrue;
11340 mng_info->ping_exclude_vpAg=MagickTrue;
11341 mng_info->ping_exclude_zCCP=MagickTrue;
11342 mng_info->ping_exclude_zTXt=MagickTrue;
11346 if (LocaleNCompare(value+i,"none",4) == 0)
11348 mng_info->ping_exclude_bKGD=MagickFalse;
11349 mng_info->ping_exclude_cHRM=MagickFalse;
11350 mng_info->ping_exclude_date=MagickFalse;
11351 mng_info->ping_exclude_EXIF=MagickFalse;
11352 mng_info->ping_exclude_gAMA=MagickFalse;
11353 mng_info->ping_exclude_iCCP=MagickFalse;
11354 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11355 mng_info->ping_exclude_oFFs=MagickFalse;
11356 mng_info->ping_exclude_pHYs=MagickFalse;
11357 mng_info->ping_exclude_sRGB=MagickFalse;
11358 mng_info->ping_exclude_tEXt=MagickFalse;
11359 mng_info->ping_exclude_tRNS=MagickFalse;
11360 mng_info->ping_exclude_vpAg=MagickFalse;
11361 mng_info->ping_exclude_zCCP=MagickFalse;
11362 mng_info->ping_exclude_zTXt=MagickFalse;
11365 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11366 mng_info->ping_exclude_bKGD=MagickTrue;
11368 if (LocaleNCompare(value+i,"chrm",4) == 0)
11369 mng_info->ping_exclude_cHRM=MagickTrue;
11371 if (LocaleNCompare(value+i,"date",4) == 0)
11372 mng_info->ping_exclude_date=MagickTrue;
11374 if (LocaleNCompare(value+i,"exif",4) == 0)
11375 mng_info->ping_exclude_EXIF=MagickTrue;
11377 if (LocaleNCompare(value+i,"gama",4) == 0)
11378 mng_info->ping_exclude_gAMA=MagickTrue;
11380 if (LocaleNCompare(value+i,"iccp",4) == 0)
11381 mng_info->ping_exclude_iCCP=MagickTrue;
11384 if (LocaleNCompare(value+i,"itxt",4) == 0)
11385 mng_info->ping_exclude_iTXt=MagickTrue;
11388 if (LocaleNCompare(value+i,"gama",4) == 0)
11389 mng_info->ping_exclude_gAMA=MagickTrue;
11391 if (LocaleNCompare(value+i,"offs",4) == 0)
11392 mng_info->ping_exclude_oFFs=MagickTrue;
11394 if (LocaleNCompare(value+i,"phys",4) == 0)
11395 mng_info->ping_exclude_pHYs=MagickTrue;
11397 if (LocaleNCompare(value+i,"srgb",4) == 0)
11398 mng_info->ping_exclude_sRGB=MagickTrue;
11400 if (LocaleNCompare(value+i,"text",4) == 0)
11401 mng_info->ping_exclude_tEXt=MagickTrue;
11403 if (LocaleNCompare(value+i,"trns",4) == 0)
11404 mng_info->ping_exclude_tRNS=MagickTrue;
11406 if (LocaleNCompare(value+i,"vpag",4) == 0)
11407 mng_info->ping_exclude_vpAg=MagickTrue;
11409 if (LocaleNCompare(value+i,"zccp",4) == 0)
11410 mng_info->ping_exclude_zCCP=MagickTrue;
11412 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11413 mng_info->ping_exclude_zTXt=MagickTrue;
11419 for (source=0; source<1; source++)
11423 value=GetImageArtifact(image,"png:include-chunk");
11426 value=GetImageArtifact(image,"png:include-chunks");
11430 value=GetImageOption(image_info,"png:include-chunk");
11433 value=GetImageOption(image_info,"png:include-chunks");
11441 excluding=MagickTrue;
11443 if (logging != MagickFalse)
11446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11447 " png:include-chunk=%s found in image artifacts.\n", value);
11449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11450 " png:include-chunk=%s found in image properties.\n", value);
11453 last=strlen(value);
11455 for (i=0; i<(int) last; i+=5)
11457 if (LocaleNCompare(value+i,"all",3) == 0)
11459 mng_info->ping_exclude_bKGD=MagickFalse;
11460 mng_info->ping_exclude_cHRM=MagickFalse;
11461 mng_info->ping_exclude_date=MagickFalse;
11462 mng_info->ping_exclude_EXIF=MagickFalse;
11463 mng_info->ping_exclude_gAMA=MagickFalse;
11464 mng_info->ping_exclude_iCCP=MagickFalse;
11465 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11466 mng_info->ping_exclude_oFFs=MagickFalse;
11467 mng_info->ping_exclude_pHYs=MagickFalse;
11468 mng_info->ping_exclude_sRGB=MagickFalse;
11469 mng_info->ping_exclude_tEXt=MagickFalse;
11470 mng_info->ping_exclude_tRNS=MagickFalse;
11471 mng_info->ping_exclude_vpAg=MagickFalse;
11472 mng_info->ping_exclude_zCCP=MagickFalse;
11473 mng_info->ping_exclude_zTXt=MagickFalse;
11477 if (LocaleNCompare(value+i,"none",4) == 0)
11479 mng_info->ping_exclude_bKGD=MagickTrue;
11480 mng_info->ping_exclude_cHRM=MagickTrue;
11481 mng_info->ping_exclude_date=MagickTrue;
11482 mng_info->ping_exclude_EXIF=MagickTrue;
11483 mng_info->ping_exclude_gAMA=MagickTrue;
11484 mng_info->ping_exclude_iCCP=MagickTrue;
11485 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11486 mng_info->ping_exclude_oFFs=MagickTrue;
11487 mng_info->ping_exclude_pHYs=MagickTrue;
11488 mng_info->ping_exclude_sRGB=MagickTrue;
11489 mng_info->ping_exclude_tEXt=MagickTrue;
11490 mng_info->ping_exclude_tRNS=MagickTrue;
11491 mng_info->ping_exclude_vpAg=MagickTrue;
11492 mng_info->ping_exclude_zCCP=MagickTrue;
11493 mng_info->ping_exclude_zTXt=MagickTrue;
11496 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11497 mng_info->ping_exclude_bKGD=MagickFalse;
11499 if (LocaleNCompare(value+i,"chrm",4) == 0)
11500 mng_info->ping_exclude_cHRM=MagickFalse;
11502 if (LocaleNCompare(value+i,"date",4) == 0)
11503 mng_info->ping_exclude_date=MagickFalse;
11505 if (LocaleNCompare(value+i,"exif",4) == 0)
11506 mng_info->ping_exclude_EXIF=MagickFalse;
11508 if (LocaleNCompare(value+i,"gama",4) == 0)
11509 mng_info->ping_exclude_gAMA=MagickFalse;
11511 if (LocaleNCompare(value+i,"iccp",4) == 0)
11512 mng_info->ping_exclude_iCCP=MagickFalse;
11515 if (LocaleNCompare(value+i,"itxt",4) == 0)
11516 mng_info->ping_exclude_iTXt=MagickFalse;
11519 if (LocaleNCompare(value+i,"gama",4) == 0)
11520 mng_info->ping_exclude_gAMA=MagickFalse;
11522 if (LocaleNCompare(value+i,"offs",4) == 0)
11523 mng_info->ping_exclude_oFFs=MagickFalse;
11525 if (LocaleNCompare(value+i,"phys",4) == 0)
11526 mng_info->ping_exclude_pHYs=MagickFalse;
11528 if (LocaleNCompare(value+i,"srgb",4) == 0)
11529 mng_info->ping_exclude_sRGB=MagickFalse;
11531 if (LocaleNCompare(value+i,"text",4) == 0)
11532 mng_info->ping_exclude_tEXt=MagickFalse;
11534 if (LocaleNCompare(value+i,"trns",4) == 0)
11535 mng_info->ping_exclude_tRNS=MagickFalse;
11537 if (LocaleNCompare(value+i,"vpag",4) == 0)
11538 mng_info->ping_exclude_vpAg=MagickFalse;
11540 if (LocaleNCompare(value+i,"zccp",4) == 0)
11541 mng_info->ping_exclude_zCCP=MagickFalse;
11543 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11544 mng_info->ping_exclude_zTXt=MagickFalse;
11550 if (excluding != MagickFalse && logging != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 " Chunks to be excluded from the output png:");
11554 if (mng_info->ping_exclude_bKGD != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 if (mng_info->ping_exclude_cHRM != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11560 if (mng_info->ping_exclude_date != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11563 if (mng_info->ping_exclude_EXIF != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11566 if (mng_info->ping_exclude_gAMA != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 if (mng_info->ping_exclude_iCCP != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 if (mng_info->ping_exclude_iTXt != MagickFalse)
11574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11577 if (mng_info->ping_exclude_oFFs != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580 if (mng_info->ping_exclude_pHYs != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11583 if (mng_info->ping_exclude_sRGB != MagickFalse)
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11586 if (mng_info->ping_exclude_tEXt != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11589 if (mng_info->ping_exclude_tRNS != MagickFalse)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11592 if (mng_info->ping_exclude_vpAg != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11595 if (mng_info->ping_exclude_zCCP != MagickFalse)
11596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11598 if (mng_info->ping_exclude_zTXt != MagickFalse)
11599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11603 mng_info->need_blob = MagickTrue;
11605 status=WriteOnePNGImage(mng_info,image_info,image,exception);
11607 MngInfoFreeStruct(mng_info,&have_mng_structure);
11609 if (logging != MagickFalse)
11610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11615 #if defined(JNG_SUPPORTED)
11617 /* Write one JNG image */
11618 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11619 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
11640 jng_alpha_compression_method,
11641 jng_alpha_sample_depth,
11649 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11650 " Enter WriteOneJNGImage()");
11652 blob=(unsigned char *) NULL;
11653 jpeg_image=(Image *) NULL;
11654 jpeg_image_info=(ImageInfo *) NULL;
11657 transparent=image_info->type==GrayscaleMatteType ||
11658 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
11660 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11662 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11664 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11665 image_info->quality;
11667 if (jng_alpha_quality >= 1000)
11668 jng_alpha_quality /= 1000;
11674 /* Create JPEG blob, image, and image_info */
11675 if (logging != MagickFalse)
11676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11677 " Creating jpeg_image_info for alpha.");
11679 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11681 if (jpeg_image_info == (ImageInfo *) NULL)
11682 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11684 if (logging != MagickFalse)
11685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11686 " Creating jpeg_image.");
11688 jpeg_image=SeparateImage(image,AlphaChannel,exception);
11689 if (jpeg_image == (Image *) NULL)
11690 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11691 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11692 jpeg_image->matte=MagickFalse;
11693 jpeg_image->quality=jng_alpha_quality;
11694 jpeg_image_info->type=GrayscaleType;
11695 (void) SetImageType(jpeg_image,GrayscaleType,exception);
11696 (void) AcquireUniqueFilename(jpeg_image->filename);
11697 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11698 "%s",jpeg_image->filename);
11702 jng_alpha_compression_method=0;
11704 jng_alpha_sample_depth=0;
11707 /* To do: check bit depth of PNG alpha channel */
11709 /* Check if image is grayscale. */
11710 if (image_info->type != TrueColorMatteType && image_info->type !=
11711 TrueColorType && ImageIsGray(image,exception))
11714 if (logging != MagickFalse)
11716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11717 " JNG Quality = %d",(int) jng_quality);
11718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11719 " JNG Color Type = %d",jng_color_type);
11722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11725 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11727 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11733 if (jng_alpha_compression_method==0)
11738 /* Encode alpha as a grayscale PNG blob */
11739 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11741 if (logging != MagickFalse)
11742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11743 " Creating PNG blob.");
11746 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11747 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11748 jpeg_image_info->interlace=NoInterlace;
11750 /* Exclude all ancillary chunks */
11751 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11753 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11756 /* Retrieve sample depth used */
11757 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
11758 if (value != (char *) NULL)
11759 jng_alpha_sample_depth= (unsigned int) value[0];
11763 /* Encode alpha as a grayscale JPEG blob */
11765 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11768 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11769 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11770 jpeg_image_info->interlace=NoInterlace;
11771 if (logging != MagickFalse)
11772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11773 " Creating blob.");
11774 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11776 jng_alpha_sample_depth=8;
11778 if (logging != MagickFalse)
11779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780 " Successfully read jpeg_image into a blob, length=%.20g.",
11784 /* Destroy JPEG image and image_info */
11785 jpeg_image=DestroyImage(jpeg_image);
11786 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11787 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11790 /* Write JHDR chunk */
11791 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11792 PNGType(chunk,mng_JHDR);
11793 LogPNGChunk(logging,mng_JHDR,16L);
11794 PNGLong(chunk+4,(png_uint_32) image->columns);
11795 PNGLong(chunk+8,(png_uint_32) image->rows);
11796 chunk[12]=jng_color_type;
11797 chunk[13]=8; /* sample depth */
11798 chunk[14]=8; /*jng_image_compression_method */
11799 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11800 chunk[16]=jng_alpha_sample_depth;
11801 chunk[17]=jng_alpha_compression_method;
11802 chunk[18]=0; /*jng_alpha_filter_method */
11803 chunk[19]=0; /*jng_alpha_interlace_method */
11804 (void) WriteBlob(image,20,chunk);
11805 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11806 if (logging != MagickFalse)
11808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11809 " JNG width:%15lu",(unsigned long) image->columns);
11811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11812 " JNG height:%14lu",(unsigned long) image->rows);
11814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11815 " JNG color type:%10d",jng_color_type);
11817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11818 " JNG sample depth:%8d",8);
11820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11821 " JNG compression:%9d",8);
11823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11824 " JNG interlace:%11d",0);
11826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11827 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11830 " JNG alpha compression:%3d",jng_alpha_compression_method);
11832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11833 " JNG alpha filter:%8d",0);
11835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836 " JNG alpha interlace:%5d",0);
11839 /* Write any JNG-chunk-b profiles */
11840 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11843 Write leading ancillary chunks
11849 Write JNG bKGD chunk
11860 if (jng_color_type == 8 || jng_color_type == 12)
11864 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11865 PNGType(chunk,mng_bKGD);
11866 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11867 red=ScaleQuantumToChar(image->background_color.red);
11868 green=ScaleQuantumToChar(image->background_color.green);
11869 blue=ScaleQuantumToChar(image->background_color.blue);
11876 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11877 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11880 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11883 Write JNG sRGB chunk
11885 (void) WriteBlobMSBULong(image,1L);
11886 PNGType(chunk,mng_sRGB);
11887 LogPNGChunk(logging,mng_sRGB,1L);
11889 if (image->rendering_intent != UndefinedIntent)
11890 chunk[4]=(unsigned char)
11891 Magick_RenderingIntent_to_PNG_RenderingIntent(
11892 (image->rendering_intent));
11895 chunk[4]=(unsigned char)
11896 Magick_RenderingIntent_to_PNG_RenderingIntent(
11897 (PerceptualIntent));
11899 (void) WriteBlob(image,5,chunk);
11900 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11904 if (image->gamma != 0.0)
11907 Write JNG gAMA chunk
11909 (void) WriteBlobMSBULong(image,4L);
11910 PNGType(chunk,mng_gAMA);
11911 LogPNGChunk(logging,mng_gAMA,4L);
11912 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11913 (void) WriteBlob(image,8,chunk);
11914 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11917 if ((mng_info->equal_chrms == MagickFalse) &&
11918 (image->chromaticity.red_primary.x != 0.0))
11924 Write JNG cHRM chunk
11926 (void) WriteBlobMSBULong(image,32L);
11927 PNGType(chunk,mng_cHRM);
11928 LogPNGChunk(logging,mng_cHRM,32L);
11929 primary=image->chromaticity.white_point;
11930 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11931 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11932 primary=image->chromaticity.red_primary;
11933 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11934 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11935 primary=image->chromaticity.green_primary;
11936 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11937 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11938 primary=image->chromaticity.blue_primary;
11939 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11940 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11941 (void) WriteBlob(image,36,chunk);
11942 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11946 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
11949 Write JNG pHYs chunk
11951 (void) WriteBlobMSBULong(image,9L);
11952 PNGType(chunk,mng_pHYs);
11953 LogPNGChunk(logging,mng_pHYs,9L);
11954 if (image->units == PixelsPerInchResolution)
11956 PNGLong(chunk+4,(png_uint_32)
11957 (image->resolution.x*100.0/2.54+0.5));
11959 PNGLong(chunk+8,(png_uint_32)
11960 (image->resolution.y*100.0/2.54+0.5));
11967 if (image->units == PixelsPerCentimeterResolution)
11969 PNGLong(chunk+4,(png_uint_32)
11970 (image->resolution.x*100.0+0.5));
11972 PNGLong(chunk+8,(png_uint_32)
11973 (image->resolution.y*100.0+0.5));
11980 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11981 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
11985 (void) WriteBlob(image,13,chunk);
11986 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11989 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11992 Write JNG oFFs chunk
11994 (void) WriteBlobMSBULong(image,9L);
11995 PNGType(chunk,mng_oFFs);
11996 LogPNGChunk(logging,mng_oFFs,9L);
11997 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11998 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12000 (void) WriteBlob(image,13,chunk);
12001 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12003 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12005 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12006 PNGType(chunk,mng_vpAg);
12007 LogPNGChunk(logging,mng_vpAg,9L);
12008 PNGLong(chunk+4,(png_uint_32) image->page.width);
12009 PNGLong(chunk+8,(png_uint_32) image->page.height);
12010 chunk[12]=0; /* unit = pixels */
12011 (void) WriteBlob(image,13,chunk);
12012 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12018 if (jng_alpha_compression_method==0)
12026 /* Write IDAT chunk header */
12027 if (logging != MagickFalse)
12028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12029 " Write IDAT chunks from blob, length=%.20g.",(double)
12032 /* Copy IDAT chunks */
12035 for (i=8; i<(ssize_t) length; i+=len+12)
12037 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12040 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12042 /* Found an IDAT chunk. */
12043 (void) WriteBlobMSBULong(image,(size_t) len);
12044 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12045 (void) WriteBlob(image,(size_t) len+4,p);
12046 (void) WriteBlobMSBULong(image,
12047 crc32(0,p,(uInt) len+4));
12052 if (logging != MagickFalse)
12053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12054 " Skipping %c%c%c%c chunk, length=%.20g.",
12055 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12062 /* Write JDAA chunk header */
12063 if (logging != MagickFalse)
12064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12065 " Write JDAA chunk, length=%.20g.",(double) length);
12066 (void) WriteBlobMSBULong(image,(size_t) length);
12067 PNGType(chunk,mng_JDAA);
12068 LogPNGChunk(logging,mng_JDAA,length);
12069 /* Write JDAT chunk(s) data */
12070 (void) WriteBlob(image,4,chunk);
12071 (void) WriteBlob(image,length,blob);
12072 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12075 blob=(unsigned char *) RelinquishMagickMemory(blob);
12078 /* Encode image as a JPEG blob */
12079 if (logging != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12081 " Creating jpeg_image_info.");
12082 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12083 if (jpeg_image_info == (ImageInfo *) NULL)
12084 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12086 if (logging != MagickFalse)
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12088 " Creating jpeg_image.");
12090 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12091 if (jpeg_image == (Image *) NULL)
12092 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12093 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12095 (void) AcquireUniqueFilename(jpeg_image->filename);
12096 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12097 jpeg_image->filename);
12099 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12102 if (logging != MagickFalse)
12103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12104 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12105 (double) jpeg_image->rows);
12107 if (jng_color_type == 8 || jng_color_type == 12)
12108 jpeg_image_info->type=GrayscaleType;
12110 jpeg_image_info->quality=jng_quality;
12111 jpeg_image->quality=jng_quality;
12112 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12113 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12115 if (logging != MagickFalse)
12116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12117 " Creating blob.");
12119 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12121 if (logging != MagickFalse)
12123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12124 " Successfully read jpeg_image into a blob, length=%.20g.",
12127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12128 " Write JDAT chunk, length=%.20g.",(double) length);
12131 /* Write JDAT chunk(s) */
12132 (void) WriteBlobMSBULong(image,(size_t) length);
12133 PNGType(chunk,mng_JDAT);
12134 LogPNGChunk(logging,mng_JDAT,length);
12135 (void) WriteBlob(image,4,chunk);
12136 (void) WriteBlob(image,length,blob);
12137 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12139 jpeg_image=DestroyImage(jpeg_image);
12140 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12141 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12142 blob=(unsigned char *) RelinquishMagickMemory(blob);
12144 /* Write any JNG-chunk-e profiles */
12145 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12147 /* Write IEND chunk */
12148 (void) WriteBlobMSBULong(image,0L);
12149 PNGType(chunk,mng_IEND);
12150 LogPNGChunk(logging,mng_IEND,0);
12151 (void) WriteBlob(image,4,chunk);
12152 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12154 if (logging != MagickFalse)
12155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12156 " exit WriteOneJNGImage()");
12163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12167 % W r i t e J N G I m a g e %
12171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12173 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12175 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12177 % The format of the WriteJNGImage method is:
12179 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12180 % Image *image,ExceptionInfo *exception)
12182 % A description of each parameter follows:
12184 % o image_info: the image info.
12186 % o image: The image.
12188 % o exception: return any errors or warnings in this structure.
12190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12192 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12193 ExceptionInfo *exception)
12196 have_mng_structure,
12206 assert(image_info != (const ImageInfo *) NULL);
12207 assert(image_info->signature == MagickSignature);
12208 assert(image != (Image *) NULL);
12209 assert(image->signature == MagickSignature);
12210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12211 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12212 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12213 if (status == MagickFalse)
12217 Allocate a MngInfo structure.
12219 have_mng_structure=MagickFalse;
12220 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12221 if (mng_info == (MngInfo *) NULL)
12222 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12224 Initialize members of the MngInfo structure.
12226 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12227 mng_info->image=image;
12228 have_mng_structure=MagickTrue;
12230 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12232 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12233 (void) CloseBlob(image);
12235 (void) CatchImageException(image);
12236 MngInfoFreeStruct(mng_info,&have_mng_structure);
12237 if (logging != MagickFalse)
12238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12243 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12244 ExceptionInfo *exception)
12253 have_mng_structure,
12256 volatile MagickBooleanType
12268 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12269 defined(PNG_MNG_FEATURES_SUPPORTED)
12272 all_images_are_gray,
12282 volatile unsigned int
12293 #if (PNG_LIBPNG_VER < 10200)
12294 if (image_info->verbose)
12295 printf("Your PNG library (libpng-%s) is rather old.\n",
12296 PNG_LIBPNG_VER_STRING);
12302 assert(image_info != (const ImageInfo *) NULL);
12303 assert(image_info->signature == MagickSignature);
12304 assert(image != (Image *) NULL);
12305 assert(image->signature == MagickSignature);
12306 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12307 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12308 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12309 if (status == MagickFalse)
12313 Allocate a MngInfo structure.
12315 have_mng_structure=MagickFalse;
12316 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12317 if (mng_info == (MngInfo *) NULL)
12318 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12320 Initialize members of the MngInfo structure.
12322 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12323 mng_info->image=image;
12324 have_mng_structure=MagickTrue;
12325 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12328 * See if user has requested a specific PNG subformat to be used
12329 * for all of the PNGs in the MNG being written, e.g.,
12331 * convert *.png png8:animation.mng
12333 * To do: check -define png:bit_depth and png:color_type as well,
12334 * or perhaps use mng:bit_depth and mng:color_type instead for
12338 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12339 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12340 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12342 write_jng=MagickFalse;
12343 if (image_info->compression == JPEGCompression)
12344 write_jng=MagickTrue;
12346 mng_info->adjoin=image_info->adjoin &&
12347 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12349 if (logging != MagickFalse)
12351 /* Log some info about the input */
12355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12356 " Checking input image(s)");
12358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12359 " Image_info depth: %.20g",(double) image_info->depth);
12361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12362 " Type: %d",image_info->type);
12365 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12368 " Scene: %.20g",(double) scene++);
12370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12371 " Image depth: %.20g",(double) p->depth);
12374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12381 if (p->storage_class == PseudoClass)
12382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12383 " Storage class: PseudoClass");
12386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12387 " Storage class: DirectClass");
12390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12391 " Number of colors: %.20g",(double) p->colors);
12394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12395 " Number of colors: unspecified");
12397 if (mng_info->adjoin == MagickFalse)
12402 use_global_plte=MagickFalse;
12403 all_images_are_gray=MagickFalse;
12404 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12405 need_local_plte=MagickTrue;
12407 need_defi=MagickFalse;
12408 need_matte=MagickFalse;
12409 mng_info->framing_mode=1;
12410 mng_info->old_framing_mode=1;
12413 if (image_info->page != (char *) NULL)
12416 Determine image bounding box.
12418 SetGeometry(image,&mng_info->page);
12419 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12420 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12432 mng_info->page=image->page;
12433 need_geom=MagickTrue;
12434 if (mng_info->page.width || mng_info->page.height)
12435 need_geom=MagickFalse;
12437 Check all the scenes.
12439 initial_delay=image->delay;
12440 need_iterations=MagickFalse;
12441 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12442 mng_info->equal_physs=MagickTrue,
12443 mng_info->equal_gammas=MagickTrue;
12444 mng_info->equal_srgbs=MagickTrue;
12445 mng_info->equal_backgrounds=MagickTrue;
12447 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12448 defined(PNG_MNG_FEATURES_SUPPORTED)
12449 all_images_are_gray=MagickTrue;
12450 mng_info->equal_palettes=MagickFalse;
12451 need_local_plte=MagickFalse;
12453 for (next_image=image; next_image != (Image *) NULL; )
12457 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12458 mng_info->page.width=next_image->columns+next_image->page.x;
12460 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12461 mng_info->page.height=next_image->rows+next_image->page.y;
12464 if (next_image->page.x || next_image->page.y)
12465 need_defi=MagickTrue;
12467 if (next_image->matte)
12468 need_matte=MagickTrue;
12470 if ((int) next_image->dispose >= BackgroundDispose)
12471 if (next_image->matte || next_image->page.x || next_image->page.y ||
12472 ((next_image->columns < mng_info->page.width) &&
12473 (next_image->rows < mng_info->page.height)))
12474 mng_info->need_fram=MagickTrue;
12476 if (next_image->iterations)
12477 need_iterations=MagickTrue;
12479 final_delay=next_image->delay;
12481 if (final_delay != initial_delay || final_delay > 1UL*
12482 next_image->ticks_per_second)
12483 mng_info->need_fram=1;
12485 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12486 defined(PNG_MNG_FEATURES_SUPPORTED)
12488 check for global palette possibility.
12490 if (image->matte != MagickFalse)
12491 need_local_plte=MagickTrue;
12493 if (need_local_plte == 0)
12495 if (ImageIsGray(image,exception) == MagickFalse)
12496 all_images_are_gray=MagickFalse;
12497 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12498 if (use_global_plte == 0)
12499 use_global_plte=mng_info->equal_palettes;
12500 need_local_plte=!mng_info->equal_palettes;
12503 if (GetNextImageInList(next_image) != (Image *) NULL)
12505 if (next_image->background_color.red !=
12506 next_image->next->background_color.red ||
12507 next_image->background_color.green !=
12508 next_image->next->background_color.green ||
12509 next_image->background_color.blue !=
12510 next_image->next->background_color.blue)
12511 mng_info->equal_backgrounds=MagickFalse;
12513 if (next_image->gamma != next_image->next->gamma)
12514 mng_info->equal_gammas=MagickFalse;
12516 if (next_image->rendering_intent !=
12517 next_image->next->rendering_intent)
12518 mng_info->equal_srgbs=MagickFalse;
12520 if ((next_image->units != next_image->next->units) ||
12521 (next_image->resolution.x != next_image->next->resolution.x) ||
12522 (next_image->resolution.y != next_image->next->resolution.y))
12523 mng_info->equal_physs=MagickFalse;
12525 if (mng_info->equal_chrms)
12527 if (next_image->chromaticity.red_primary.x !=
12528 next_image->next->chromaticity.red_primary.x ||
12529 next_image->chromaticity.red_primary.y !=
12530 next_image->next->chromaticity.red_primary.y ||
12531 next_image->chromaticity.green_primary.x !=
12532 next_image->next->chromaticity.green_primary.x ||
12533 next_image->chromaticity.green_primary.y !=
12534 next_image->next->chromaticity.green_primary.y ||
12535 next_image->chromaticity.blue_primary.x !=
12536 next_image->next->chromaticity.blue_primary.x ||
12537 next_image->chromaticity.blue_primary.y !=
12538 next_image->next->chromaticity.blue_primary.y ||
12539 next_image->chromaticity.white_point.x !=
12540 next_image->next->chromaticity.white_point.x ||
12541 next_image->chromaticity.white_point.y !=
12542 next_image->next->chromaticity.white_point.y)
12543 mng_info->equal_chrms=MagickFalse;
12547 next_image=GetNextImageInList(next_image);
12549 if (image_count < 2)
12551 mng_info->equal_backgrounds=MagickFalse;
12552 mng_info->equal_chrms=MagickFalse;
12553 mng_info->equal_gammas=MagickFalse;
12554 mng_info->equal_srgbs=MagickFalse;
12555 mng_info->equal_physs=MagickFalse;
12556 use_global_plte=MagickFalse;
12557 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12558 need_local_plte=MagickTrue;
12560 need_iterations=MagickFalse;
12563 if (mng_info->need_fram == MagickFalse)
12566 Only certain framing rates 100/n are exactly representable without
12567 the FRAM chunk but we'll allow some slop in VLC files
12569 if (final_delay == 0)
12571 if (need_iterations != MagickFalse)
12574 It's probably a GIF with loop; don't run it *too* fast.
12576 if (mng_info->adjoin)
12579 (void) ThrowMagickException(exception,GetMagickModule(),
12581 "input has zero delay between all frames; assuming",
12586 mng_info->ticks_per_second=0;
12588 if (final_delay != 0)
12589 mng_info->ticks_per_second=(png_uint_32)
12590 (image->ticks_per_second/final_delay);
12591 if (final_delay > 50)
12592 mng_info->ticks_per_second=2;
12594 if (final_delay > 75)
12595 mng_info->ticks_per_second=1;
12597 if (final_delay > 125)
12598 mng_info->need_fram=MagickTrue;
12600 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12601 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12602 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12603 1UL*image->ticks_per_second))
12604 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12607 if (mng_info->need_fram != MagickFalse)
12608 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12610 If pseudocolor, we should also check to see if all the
12611 palettes are identical and write a global PLTE if they are.
12615 Write the MNG version 1.0 signature and MHDR chunk.
12617 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12618 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12619 PNGType(chunk,mng_MHDR);
12620 LogPNGChunk(logging,mng_MHDR,28L);
12621 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12622 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12623 PNGLong(chunk+12,mng_info->ticks_per_second);
12624 PNGLong(chunk+16,0L); /* layer count=unknown */
12625 PNGLong(chunk+20,0L); /* frame count=unknown */
12626 PNGLong(chunk+24,0L); /* play time=unknown */
12631 if (need_defi || mng_info->need_fram || use_global_plte)
12632 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12635 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12640 if (need_defi || mng_info->need_fram || use_global_plte)
12641 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12644 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12652 if (need_defi || mng_info->need_fram || use_global_plte)
12653 PNGLong(chunk+28,11L); /* simplicity=LC */
12656 PNGLong(chunk+28,9L); /* simplicity=VLC */
12661 if (need_defi || mng_info->need_fram || use_global_plte)
12662 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12665 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12668 (void) WriteBlob(image,32,chunk);
12669 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12670 option=GetImageOption(image_info,"mng:need-cacheoff");
12671 if (option != (const char *) NULL)
12677 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12679 PNGType(chunk,mng_nEED);
12680 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12681 (void) WriteBlobMSBULong(image,(size_t) length);
12682 LogPNGChunk(logging,mng_nEED,(size_t) length);
12684 (void) WriteBlob(image,length,chunk);
12685 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12687 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12688 (GetNextImageInList(image) != (Image *) NULL) &&
12689 (image->iterations != 1))
12692 Write MNG TERM chunk
12694 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12695 PNGType(chunk,mng_TERM);
12696 LogPNGChunk(logging,mng_TERM,10L);
12697 chunk[4]=3; /* repeat animation */
12698 chunk[5]=0; /* show last frame when done */
12699 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12700 final_delay/MagickMax(image->ticks_per_second,1)));
12702 if (image->iterations == 0)
12703 PNGLong(chunk+10,PNG_UINT_31_MAX);
12706 PNGLong(chunk+10,(png_uint_32) image->iterations);
12708 if (logging != MagickFalse)
12710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12711 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12712 final_delay/MagickMax(image->ticks_per_second,1)));
12714 if (image->iterations == 0)
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12720 " Image iterations: %.20g",(double) image->iterations);
12722 (void) WriteBlob(image,14,chunk);
12723 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12726 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12728 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12729 mng_info->equal_srgbs)
12732 Write MNG sRGB chunk
12734 (void) WriteBlobMSBULong(image,1L);
12735 PNGType(chunk,mng_sRGB);
12736 LogPNGChunk(logging,mng_sRGB,1L);
12738 if (image->rendering_intent != UndefinedIntent)
12739 chunk[4]=(unsigned char)
12740 Magick_RenderingIntent_to_PNG_RenderingIntent(
12741 (image->rendering_intent));
12744 chunk[4]=(unsigned char)
12745 Magick_RenderingIntent_to_PNG_RenderingIntent(
12746 (PerceptualIntent));
12748 (void) WriteBlob(image,5,chunk);
12749 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12750 mng_info->have_write_global_srgb=MagickTrue;
12755 if (image->gamma && mng_info->equal_gammas)
12758 Write MNG gAMA chunk
12760 (void) WriteBlobMSBULong(image,4L);
12761 PNGType(chunk,mng_gAMA);
12762 LogPNGChunk(logging,mng_gAMA,4L);
12763 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12764 (void) WriteBlob(image,8,chunk);
12765 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12766 mng_info->have_write_global_gama=MagickTrue;
12768 if (mng_info->equal_chrms)
12774 Write MNG cHRM chunk
12776 (void) WriteBlobMSBULong(image,32L);
12777 PNGType(chunk,mng_cHRM);
12778 LogPNGChunk(logging,mng_cHRM,32L);
12779 primary=image->chromaticity.white_point;
12780 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12781 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12782 primary=image->chromaticity.red_primary;
12783 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12784 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12785 primary=image->chromaticity.green_primary;
12786 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12787 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12788 primary=image->chromaticity.blue_primary;
12789 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12790 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12791 (void) WriteBlob(image,36,chunk);
12792 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12793 mng_info->have_write_global_chrm=MagickTrue;
12796 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
12799 Write MNG pHYs chunk
12801 (void) WriteBlobMSBULong(image,9L);
12802 PNGType(chunk,mng_pHYs);
12803 LogPNGChunk(logging,mng_pHYs,9L);
12805 if (image->units == PixelsPerInchResolution)
12807 PNGLong(chunk+4,(png_uint_32)
12808 (image->resolution.x*100.0/2.54+0.5));
12810 PNGLong(chunk+8,(png_uint_32)
12811 (image->resolution.y*100.0/2.54+0.5));
12818 if (image->units == PixelsPerCentimeterResolution)
12820 PNGLong(chunk+4,(png_uint_32)
12821 (image->resolution.x*100.0+0.5));
12823 PNGLong(chunk+8,(png_uint_32)
12824 (image->resolution.y*100.0+0.5));
12831 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12832 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12836 (void) WriteBlob(image,13,chunk);
12837 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12840 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12841 or does not cover the entire frame.
12843 if (write_mng && (image->matte || image->page.x > 0 ||
12844 image->page.y > 0 || (image->page.width &&
12845 (image->page.width+image->page.x < mng_info->page.width))
12846 || (image->page.height && (image->page.height+image->page.y
12847 < mng_info->page.height))))
12849 (void) WriteBlobMSBULong(image,6L);
12850 PNGType(chunk,mng_BACK);
12851 LogPNGChunk(logging,mng_BACK,6L);
12852 red=ScaleQuantumToShort(image->background_color.red);
12853 green=ScaleQuantumToShort(image->background_color.green);
12854 blue=ScaleQuantumToShort(image->background_color.blue);
12855 PNGShort(chunk+4,red);
12856 PNGShort(chunk+6,green);
12857 PNGShort(chunk+8,blue);
12858 (void) WriteBlob(image,10,chunk);
12859 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12860 if (mng_info->equal_backgrounds)
12862 (void) WriteBlobMSBULong(image,6L);
12863 PNGType(chunk,mng_bKGD);
12864 LogPNGChunk(logging,mng_bKGD,6L);
12865 (void) WriteBlob(image,10,chunk);
12866 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12870 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12871 if ((need_local_plte == MagickFalse) &&
12872 (image->storage_class == PseudoClass) &&
12873 (all_images_are_gray == MagickFalse))
12879 Write MNG PLTE chunk
12881 data_length=3*image->colors;
12882 (void) WriteBlobMSBULong(image,data_length);
12883 PNGType(chunk,mng_PLTE);
12884 LogPNGChunk(logging,mng_PLTE,data_length);
12886 for (i=0; i < (ssize_t) image->colors; i++)
12888 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12889 image->colormap[i].red) & 0xff);
12890 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12891 image->colormap[i].green) & 0xff);
12892 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12893 image->colormap[i].blue) & 0xff);
12896 (void) WriteBlob(image,data_length+4,chunk);
12897 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12898 mng_info->have_write_global_plte=MagickTrue;
12904 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12905 defined(PNG_MNG_FEATURES_SUPPORTED)
12906 mng_info->equal_palettes=MagickFalse;
12910 if (mng_info->adjoin)
12912 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12913 defined(PNG_MNG_FEATURES_SUPPORTED)
12915 If we aren't using a global palette for the entire MNG, check to
12916 see if we can use one for two or more consecutive images.
12918 if (need_local_plte && use_global_plte && !all_images_are_gray)
12920 if (mng_info->IsPalette)
12923 When equal_palettes is true, this image has the same palette
12924 as the previous PseudoClass image
12926 mng_info->have_write_global_plte=mng_info->equal_palettes;
12927 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12928 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12931 Write MNG PLTE chunk
12936 data_length=3*image->colors;
12937 (void) WriteBlobMSBULong(image,data_length);
12938 PNGType(chunk,mng_PLTE);
12939 LogPNGChunk(logging,mng_PLTE,data_length);
12941 for (i=0; i < (ssize_t) image->colors; i++)
12943 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12944 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12945 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12948 (void) WriteBlob(image,data_length+4,chunk);
12949 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12950 (uInt) (data_length+4)));
12951 mng_info->have_write_global_plte=MagickTrue;
12955 mng_info->have_write_global_plte=MagickFalse;
12966 previous_x=mng_info->page.x;
12967 previous_y=mng_info->page.y;
12974 mng_info->page=image->page;
12975 if ((mng_info->page.x != previous_x) ||
12976 (mng_info->page.y != previous_y))
12978 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12979 PNGType(chunk,mng_DEFI);
12980 LogPNGChunk(logging,mng_DEFI,12L);
12981 chunk[4]=0; /* object 0 MSB */
12982 chunk[5]=0; /* object 0 LSB */
12983 chunk[6]=0; /* visible */
12984 chunk[7]=0; /* abstract */
12985 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12986 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12987 (void) WriteBlob(image,16,chunk);
12988 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12993 mng_info->write_mng=write_mng;
12995 if ((int) image->dispose >= 3)
12996 mng_info->framing_mode=3;
12998 if (mng_info->need_fram && mng_info->adjoin &&
12999 ((image->delay != mng_info->delay) ||
13000 (mng_info->framing_mode != mng_info->old_framing_mode)))
13002 if (image->delay == mng_info->delay)
13005 Write a MNG FRAM chunk with the new framing mode.
13007 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13008 PNGType(chunk,mng_FRAM);
13009 LogPNGChunk(logging,mng_FRAM,1L);
13010 chunk[4]=(unsigned char) mng_info->framing_mode;
13011 (void) WriteBlob(image,5,chunk);
13012 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13017 Write a MNG FRAM chunk with the delay.
13019 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13020 PNGType(chunk,mng_FRAM);
13021 LogPNGChunk(logging,mng_FRAM,10L);
13022 chunk[4]=(unsigned char) mng_info->framing_mode;
13023 chunk[5]=0; /* frame name separator (no name) */
13024 chunk[6]=2; /* flag for changing default delay */
13025 chunk[7]=0; /* flag for changing frame timeout */
13026 chunk[8]=0; /* flag for changing frame clipping */
13027 chunk[9]=0; /* flag for changing frame sync_id */
13028 PNGLong(chunk+10,(png_uint_32)
13029 ((mng_info->ticks_per_second*
13030 image->delay)/MagickMax(image->ticks_per_second,1)));
13031 (void) WriteBlob(image,14,chunk);
13032 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13033 mng_info->delay=(png_uint_32) image->delay;
13035 mng_info->old_framing_mode=mng_info->framing_mode;
13038 #if defined(JNG_SUPPORTED)
13039 if (image_info->compression == JPEGCompression)
13044 if (logging != MagickFalse)
13045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13046 " Writing JNG object.");
13047 /* To do: specify the desired alpha compression method. */
13048 write_info=CloneImageInfo(image_info);
13049 write_info->compression=UndefinedCompression;
13050 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13051 write_info=DestroyImageInfo(write_info);
13056 if (logging != MagickFalse)
13057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13058 " Writing PNG object.");
13060 mng_info->need_blob = MagickFalse;
13061 mng_info->ping_preserve_colormap = MagickFalse;
13063 /* We don't want any ancillary chunks written */
13064 mng_info->ping_exclude_bKGD=MagickTrue;
13065 mng_info->ping_exclude_cHRM=MagickTrue;
13066 mng_info->ping_exclude_date=MagickTrue;
13067 mng_info->ping_exclude_EXIF=MagickTrue;
13068 mng_info->ping_exclude_gAMA=MagickTrue;
13069 mng_info->ping_exclude_iCCP=MagickTrue;
13070 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13071 mng_info->ping_exclude_oFFs=MagickTrue;
13072 mng_info->ping_exclude_pHYs=MagickTrue;
13073 mng_info->ping_exclude_sRGB=MagickTrue;
13074 mng_info->ping_exclude_tEXt=MagickTrue;
13075 mng_info->ping_exclude_tRNS=MagickTrue;
13076 mng_info->ping_exclude_vpAg=MagickTrue;
13077 mng_info->ping_exclude_zCCP=MagickTrue;
13078 mng_info->ping_exclude_zTXt=MagickTrue;
13080 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13083 if (status == MagickFalse)
13085 MngInfoFreeStruct(mng_info,&have_mng_structure);
13086 (void) CloseBlob(image);
13087 return(MagickFalse);
13089 (void) CatchImageException(image);
13090 if (GetNextImageInList(image) == (Image *) NULL)
13092 image=SyncNextImageInList(image);
13093 status=SetImageProgress(image,SaveImagesTag,scene++,
13094 GetImageListLength(image));
13096 if (status == MagickFalse)
13099 } while (mng_info->adjoin);
13103 while (GetPreviousImageInList(image) != (Image *) NULL)
13104 image=GetPreviousImageInList(image);
13106 Write the MEND chunk.
13108 (void) WriteBlobMSBULong(image,0x00000000L);
13109 PNGType(chunk,mng_MEND);
13110 LogPNGChunk(logging,mng_MEND,0L);
13111 (void) WriteBlob(image,4,chunk);
13112 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13115 Relinquish resources.
13117 (void) CloseBlob(image);
13118 MngInfoFreeStruct(mng_info,&have_mng_structure);
13120 if (logging != MagickFalse)
13121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13123 return(MagickTrue);
13125 #else /* PNG_LIBPNG_VER > 10011 */
13127 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13130 printf("Your PNG library is too old: You have libpng-%s\n",
13131 PNG_LIBPNG_VER_STRING);
13133 ThrowBinaryException(CoderError,"PNG library is too old",
13134 image_info->filename);
13137 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13139 return(WritePNGImage(image_info,image));
13141 #endif /* PNG_LIBPNG_VER > 10011 */