2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/color-private.h"
52 #include "MagickCore/colormap.h"
53 #include "MagickCore/colorspace.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/constitute.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/histogram.h"
61 #include "MagickCore/image.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/layer.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/MagickCore.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/module.h"
69 #include "MagickCore/monitor.h"
70 #include "MagickCore/monitor-private.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/pixel.h"
73 #include "MagickCore/pixel-accessor.h"
74 #include "MagickCore/profile.h"
75 #include "MagickCore/property.h"
76 #include "MagickCore/quantum-private.h"
77 #include "MagickCore/resource_.h"
78 #include "MagickCore/semaphore.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/static.h"
81 #include "MagickCore/statistic.h"
82 #include "MagickCore/string_.h"
83 #include "MagickCore/string-private.h"
84 #include "MagickCore/transform.h"
85 #include "MagickCore/utility.h"
86 #if defined(MAGICKCORE_PNG_DELEGATE)
88 /* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
90 * migration to libpng-1.5, remove these defines and then
91 * fix any code that generates warnings.
93 /* #define PNG_DEPRECATED Use of this function is deprecated */
94 /* #define PNG_USE_RESULT The result of this function must be checked */
95 /* #define PNG_NORETURN This function does not return */
96 /* #define PNG_ALLOCATED The result of the function is new memory */
97 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
99 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
100 #define PNG_PTR_NORETURN
105 /* ImageMagick differences */
106 #define first_scene scene
108 #if PNG_LIBPNG_VER > 10011
110 Optional declarations. Define or undefine them as you like.
112 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
115 Features under construction. Define these to work on them.
117 #undef MNG_OBJECT_BUFFERS
118 #undef MNG_BASI_SUPPORTED
119 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
121 #if defined(MAGICKCORE_JPEG_DELEGATE)
122 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
124 #if !defined(RGBColorMatchExact)
125 #define IsPNGColorEqual(color,target) \
126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
131 /* Macros for left-bit-replication to ensure that pixels
132 * and PixelPackets all have the image->depth, and for use
133 * in PNG8 quantization.
137 /* LBR01: Replicate top bit */
139 #define LBR01PixelPacketRed(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 LBR01PixelPacketRed((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 LBR01Green(pixel) \
173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
176 #define LBR01Blue(pixel) \
177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
180 #define LBR01Alpha(pixel) \
181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
184 #define LBR01RGB(pixel) \
186 LBR01PixelRed((pixel)); \
187 LBR01Green((pixel)); \
188 LBR01Blue((pixel)); \
191 #define LBR01RGBA(pixel) \
194 LBR01Alpha((pixel)); \
197 /* LBR02: Replicate top 2 bits */
199 #define LBR02PixelPacketRed(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 LBR02PixelPacketRed((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 LBR02Green(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 LBR02Blue(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 LBR02Alpha(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 LBR02RGB(pixel) \
272 LBR02PixelRed((pixel)); \
273 LBR02Green((pixel)); \
274 LBR02Blue((pixel)); \
277 #define LBR02RGBA(pixel) \
280 LBR02Alpha((pixel)); \
283 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
286 #define LBR03PixelPacketRed(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 LBR03PixelPacketRed((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 LBR04PixelPacketRed(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 LBR04PixelPacketRed((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 LBR04Green(pixel) \
386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
391 #define LBR04Blue(pixel) \
393 unsigned char lbr_bits= \
394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
398 #define LBR04Alpha(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 LBR04RGB(pixel) \
408 LBR04PixelRed((pixel)); \
409 LBR04Green((pixel)); \
410 LBR04Blue((pixel)); \
413 #define LBR04RGBA(pixel) \
416 LBR04Alpha((pixel)); \
420 /* LBR08: Replicate top 8 bits */
422 #define LBR08PixelPacketRed(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 LBR08PixelPacketRed((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 LBR08Green(pixel) \
465 unsigned char lbr_bits= \
466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
470 #define LBR08Blue(pixel) \
472 unsigned char lbr_bits= \
473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
477 #define LBR08Alpha(pixel) \
479 unsigned char lbr_bits= \
480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
485 #define LBR08RGB(pixel) \
487 LBR08PixelRed((pixel)); \
488 LBR08Green((pixel)); \
489 LBR08Blue((pixel)); \
492 #define LBR08RGBA(pixel) \
495 LBR08Alpha((pixel)); \
499 /* LBR16: Replicate top 16 bits */
501 #define LBR16PixelPacketRed(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 LBR16PixelPacketRed((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 LBR16Green(pixel) \
544 unsigned short lbr_bits= \
545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
549 #define LBR16Blue(pixel) \
551 unsigned short lbr_bits= \
552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
556 #define LBR16Alpha(pixel) \
558 unsigned short lbr_bits= \
559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
564 #define LBR16RGB(pixel) \
566 LBR16PixelRed((pixel)); \
567 LBR16Green((pixel)); \
568 LBR16Blue((pixel)); \
571 #define LBR16RGBA(pixel) \
574 LBR16Alpha((pixel)); \
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
582 #ifndef SETJMP_IS_THREAD_SAFE
583 #define PNG_SETJMP_NOT_THREAD_SAFE
586 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
588 *ping_semaphore = (SemaphoreInfo *) NULL;
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
596 #define MNG_MAX_OBJECTS 256
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
602 o global PLTE too short
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
615 #ifdef PNG_MNG_FEATURES_SUPPORTED
616 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617 # define PNG_READ_EMPTY_PLTE_SUPPORTED
619 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
629 #ifndef PNG_UINT_31_MAX
630 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
639 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
674 #if defined(JNG_SUPPORTED)
675 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
684 Other known chunks that are not yet supported by ImageMagick:
685 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
695 typedef struct _MngBox
704 typedef struct _MngPair
711 #ifdef MNG_OBJECT_BUFFERS
712 typedef struct _MngBuffer
744 typedef struct _MngInfo
747 #ifdef MNG_OBJECT_BUFFERS
749 *ob[MNG_MAX_OBJECTS];
760 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
767 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
805 object_clip[MNG_MAX_OBJECTS];
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
825 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
854 global_phys_unit_type,
869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
876 #ifdef MNG_BASI_SUPPORTED
884 basi_compression_method,
886 basi_interlace_method,
909 /* Added at version 6.6.6-7 */
917 /* ping_exclude_iTXt, */
924 ping_exclude_zCCP, /* hex-encoded iCCP */
926 ping_preserve_colormap;
932 Forward declarations.
934 static MagickBooleanType
935 WritePNGImage(const ImageInfo *,Image *);
937 static MagickBooleanType
938 WriteMNGImage(const ImageInfo *,Image *);
940 #if defined(JNG_SUPPORTED)
941 static MagickBooleanType
942 WriteJNGImage(const ImageInfo *,Image *);
945 #if PNG_LIBPNG_VER > 10011
948 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949 static MagickBooleanType
950 LosslessReduceDepthOK(Image *image)
952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
959 * We don't use the method GetImageDepth() because it doesn't check
960 * background and doesn't handle PseudoClass specially.
963 #define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
967 ok_to_reduce=MagickFalse;
969 if (image->depth >= 16)
976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
996 if (ok_to_reduce == MagickFalse)
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1010 for (y=0; y < (ssize_t) image->rows; y++)
1012 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1014 if (p == (const Quantum *) NULL)
1016 ok_to_reduce = MagickFalse;
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1026 MagickTrue : MagickFalse;
1028 if (ok_to_reduce == MagickFalse)
1031 p+=GetPixelChannels(image);
1038 if (ok_to_reduce != MagickFalse)
1040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1041 " OK to reduce PNG bit depth to 8 without loss of info");
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1046 " Not OK to reduce PNG bit depth to 8 without loss of info");
1050 return ok_to_reduce;
1052 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1055 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1059 case PerceptualIntent:
1062 case RelativeIntent:
1065 case SaturationIntent:
1068 case AbsoluteIntent:
1076 static RenderingIntent
1077 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1079 switch (ping_intent)
1082 return PerceptualIntent;
1085 return RelativeIntent;
1088 return SaturationIntent;
1091 return AbsoluteIntent;
1094 return UndefinedIntent;
1098 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1106 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120 % I m a g e I s G r a y %
1124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126 % Like IsImageGray except does not change DirectClass to PseudoClass %
1128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1130 static MagickBooleanType ImageIsGray(Image *image)
1132 register const Quantum
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1145 if (image->storage_class == PseudoClass)
1147 for (i=0; i < (ssize_t) image->colors; i++)
1148 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
1149 return(MagickFalse);
1152 for (y=0; y < (ssize_t) image->rows; y++)
1154 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1155 if (p == (const Quantum *) NULL)
1156 return(MagickFalse);
1157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1159 if (IsPixelGray(image,p) == MagickFalse)
1160 return(MagickFalse);
1161 p+=GetPixelChannels(image);
1166 #endif /* PNG_LIBPNG_VER > 10011 */
1167 #endif /* MAGICKCORE_PNG_DELEGATE */
1170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 % IsMNG() returns MagickTrue if the image format type, identified by the
1181 % magick string, is MNG.
1183 % The format of the IsMNG method is:
1185 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1187 % A description of each parameter follows:
1189 % o magick: compare image format pattern against these bytes.
1191 % o length: Specifies the length of the magick string.
1195 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1198 return(MagickFalse);
1200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1203 return(MagickFalse);
1207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217 % IsJNG() returns MagickTrue if the image format type, identified by the
1218 % magick string, is JNG.
1220 % The format of the IsJNG method is:
1222 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1224 % A description of each parameter follows:
1226 % o magick: compare image format pattern against these bytes.
1228 % o length: Specifies the length of the magick string.
1232 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1235 return(MagickFalse);
1237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1240 return(MagickFalse);
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 % IsPNG() returns MagickTrue if the image format type, identified by the
1255 % magick string, is PNG.
1257 % The format of the IsPNG method is:
1259 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1261 % A description of each parameter follows:
1263 % o magick: compare image format pattern against these bytes.
1265 % o length: Specifies the length of the magick string.
1268 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1271 return(MagickFalse);
1273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1276 return(MagickFalse);
1279 #if defined(MAGICKCORE_PNG_DELEGATE)
1280 #if defined(__cplusplus) || defined(c_plusplus)
1284 #if (PNG_LIBPNG_VER > 10011)
1285 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1299 static void PNGLong(png_bytep p,png_uint_32 value)
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1307 #if defined(JNG_SUPPORTED)
1308 static void PNGsLong(png_bytep p,png_int_32 value)
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1317 static void PNGShort(png_bytep p,png_uint_16 value)
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1323 static void PNGType(png_bytep p,png_bytep type)
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1328 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
1336 #endif /* PNG_LIBPNG_VER > 10011 */
1338 #if defined(__cplusplus) || defined(c_plusplus)
1342 #if PNG_LIBPNG_VER > 10011
1344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1348 % R e a d P N G I m a g e %
1352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1354 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355 % Multiple-image Network Graphics (MNG) image file and returns it. It
1356 % allocates the memory necessary for the new Image structure and returns a
1357 % pointer to the new image or set of images.
1359 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1361 % The format of the ReadPNGImage method is:
1363 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1365 % A description of each parameter follows:
1367 % o image_info: the image info.
1369 % o exception: return any errors or warnings in this structure.
1371 % To do, more or less in chronological order (as of version 5.5.2,
1372 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1374 % Get 16-bit cheap transparency working.
1376 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1378 % Preserve all unknown and not-yet-handled known chunks found in input
1379 % PNG file and copy them into output PNG files according to the PNG
1382 % (At this point, PNG encoding should be in full MNG compliance)
1384 % Provide options for choice of background to use when the MNG BACK
1385 % chunk is not present or is not mandatory (i.e., leave transparent,
1386 % user specified, MNG BACK, PNG bKGD)
1388 % Implement LOOP/ENDL [done, but could do discretionary loops more
1389 % efficiently by linking in the duplicate frames.].
1391 % Decode and act on the MHDR simplicity profile (offer option to reject
1392 % files or attempt to process them anyway when the profile isn't LC or VLC).
1394 % Upgrade to full MNG without Delta-PNG.
1396 % o BACK [done a while ago except for background image ID]
1397 % o MOVE [done 15 May 1999]
1398 % o CLIP [done 15 May 1999]
1399 % o DISC [done 19 May 1999]
1400 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401 % o SEEK [partially done 19 May 1999 (discard function only)]
1405 % o MNG-level tEXt/iTXt/zTXt
1410 % o iTXt (wait for libpng implementation).
1412 % Use the scene signature to discover when an identical scene is
1413 % being reused, and just point to the original image->exception instead
1414 % of storing another set of pixels. This not specific to MNG
1415 % but could be applied generally.
1417 % Upgrade to full MNG with Delta-PNG.
1419 % JNG tEXt/iTXt/zTXt
1421 % We will not attempt to read files containing the CgBI chunk.
1422 % They are really Xcode files meant for display on the iPhone.
1423 % These are not valid PNG files and it is impossible to recover
1424 % the original PNG from files that have been converted to Xcode-PNG,
1425 % since irretrievable loss of color data has occurred due to the
1426 % use of premultiplied alpha.
1429 #if defined(__cplusplus) || defined(c_plusplus)
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1438 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1443 image=(Image *) png_get_io_ptr(png_ptr);
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1455 (void) FormatLocaleString(msg,MaxTextExtent,
1456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1464 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1474 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1534 Skip the bKGD data byte and CRC.
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1550 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1555 image=(Image *) png_get_io_ptr(png_ptr);
1561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1568 static void png_flush_data(png_structp png_ptr)
1573 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574 static int PalettesAreEqual(Image *a,Image *b)
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
1582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
1585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
1588 for (i=0; i < (ssize_t) a->colors; i++)
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1596 return((int) MagickTrue);
1600 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1605 #ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
1611 if (mng_info->ob[i]->reference_count == 0)
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
1628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1629 mng_info->object_clip[i].top=0;
1630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1634 static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
1637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
1645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
1649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1654 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1660 if (box.left < box2.left)
1663 if (box.top < box2.top)
1666 if (box.right > box2.right)
1667 box.right=box2.right;
1669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
1675 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1687 if (delta_type != 0)
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1698 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1704 Read two ssize_ts from CLON, MOVE or PAST chunk
1706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1709 if (delta_type != 0)
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1718 static long mng_get_long(unsigned char *p)
1720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1723 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1728 image=(Image *) png_get_error_ptr(ping);
1730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1732 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1734 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1735 message,"`%s'",image->filename);
1737 #if (PNG_LIBPNG_VER < 10500)
1738 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1739 * are building with libpng-1.4.x and can be ignored.
1741 longjmp(ping->jmpbuf,1);
1743 png_longjmp(ping,1);
1747 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1752 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1753 png_error(ping, message);
1755 image=(Image *) png_get_error_ptr(ping);
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1758 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1760 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1761 message,"`%s'",image->filename);
1764 #ifdef PNG_USER_MEM_SUPPORTED
1765 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1767 #if (PNG_LIBPNG_VER < 10011)
1772 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1775 png_error("Insufficient memory.");
1780 return((png_voidp) AcquireMagickMemory((size_t) size));
1785 Free a pointer. It is removed from the list at the same time.
1787 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1790 ptr=RelinquishMagickMemory(ptr);
1791 return((png_free_ptr) NULL);
1795 #if defined(__cplusplus) || defined(c_plusplus)
1800 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1801 png_textp text,int ii)
1806 register unsigned char
1820 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1828 /* look for newline */
1832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1836 length=(png_uint_32) StringToLong(sp);
1838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1841 while (*sp != ' ' && *sp != '\n')
1844 /* allocate space */
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1852 profile=AcquireStringInfo(length);
1854 if (profile == (StringInfo *) NULL)
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1866 for (i=0; i < (ssize_t) nibbles; i++)
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1884 (*dp++)+=unhex[(int) *sp++];
1887 We have already read "Raw profile type.
1889 (void) SetImageProfile(image,&text[ii].key[17],profile);
1890 profile=DestroyStringInfo(profile);
1892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1898 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1905 /* The unknown chunk structure contains the chunk data:
1910 Note that libpng has already taken care of the CRC handling.
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1918 /* recognized vpAg */
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1928 image->page.width=(size_t) ((chunk->data[0] << 24) |
1929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1931 image->page.height=(size_t) ((chunk->data[4] << 24) |
1932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1945 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1949 % R e a d O n e P N G I m a g e %
1953 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1955 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956 % (minus the 8-byte signature) and returns it. It allocates the memory
1957 % necessary for the new Image structure and returns a pointer to the new
1960 % The format of the ReadOnePNGImage method is:
1962 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963 % ExceptionInfo *exception)
1965 % A description of each parameter follows:
1967 % o mng_info: Specifies a pointer to a MngInfo structure.
1969 % o image_info: the image info.
1971 % o exception: return any errors or warnings in this structure.
1974 static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1977 /* Read one PNG image */
1979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1994 ping_interlace_method,
1995 ping_compression_method,
2043 register unsigned char
2060 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2061 png_byte unused_chunks[]=
2063 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2064 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2065 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2066 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2067 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2068 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2072 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2073 " Enter ReadOnePNGImage()");
2075 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2076 LockSemaphoreInfo(ping_semaphore);
2079 #if (PNG_LIBPNG_VER < 10200)
2080 if (image_info->verbose)
2081 printf("Your PNG library (libpng-%s) is rather old.\n",
2082 PNG_LIBPNG_VER_STRING);
2085 #if (PNG_LIBPNG_VER >= 10400)
2086 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2087 if (image_info->verbose)
2089 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2090 PNG_LIBPNG_VER_STRING);
2091 printf("Please update it.\n");
2097 quantum_info = (QuantumInfo *) NULL;
2098 image=mng_info->image;
2100 if (logging != MagickFalse)
2101 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2102 " image->matte=%d",(int) image->matte);
2104 /* Set to an out-of-range color unless tRNS chunk is present */
2105 transparent_color.red=65537;
2106 transparent_color.green=65537;
2107 transparent_color.blue=65537;
2108 transparent_color.alpha=65537;
2112 num_raw_profiles = 0;
2115 Allocate the PNG structures
2117 #ifdef PNG_USER_MEM_SUPPORTED
2118 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
2119 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2120 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2122 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
2123 MagickPNGErrorHandler,MagickPNGWarningHandler);
2125 if (ping == (png_struct *) NULL)
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2128 ping_info=png_create_info_struct(ping);
2130 if (ping_info == (png_info *) NULL)
2132 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2136 end_info=png_create_info_struct(ping);
2138 if (end_info == (png_info *) NULL)
2140 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2144 ping_pixels=(unsigned char *) NULL;
2146 if (setjmp(png_jmpbuf(ping)))
2149 PNG image is corrupt.
2151 png_destroy_read_struct(&ping,&ping_info,&end_info);
2152 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2153 UnlockSemaphoreInfo(ping_semaphore);
2155 if (logging != MagickFalse)
2156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " exit ReadOnePNGImage() with error.");
2159 if (image != (Image *) NULL)
2161 InheritException(exception,&image->exception);
2165 return(GetFirstImageInList(image));
2168 Prepare PNG for reading.
2171 mng_info->image_found++;
2172 png_set_sig_bytes(ping,8);
2174 if (LocaleCompare(image_info->magick,"MNG") == 0)
2176 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2177 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2178 png_set_read_fn(ping,image,png_get_data);
2180 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2181 png_permit_empty_plte(ping,MagickTrue);
2182 png_set_read_fn(ping,image,png_get_data);
2184 mng_info->image=image;
2185 mng_info->bytes_in_read_buffer=0;
2186 mng_info->found_empty_plte=MagickFalse;
2187 mng_info->have_saved_bkgd_index=MagickFalse;
2188 png_set_read_fn(ping,mng_info,mng_get_data);
2194 png_set_read_fn(ping,image,png_get_data);
2196 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2197 /* Ignore unused chunks and all unknown chunks except for vpAg */
2198 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2199 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2200 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2201 (int)sizeof(unused_chunks)/5);
2202 /* Callback for other unknown chunks */
2203 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2206 #if (PNG_LIBPNG_VER < 10400)
2207 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2208 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2209 /* Disable thread-unsafe features of pnggccrd */
2210 if (png_access_version_number() >= 10200)
2212 png_uint_32 mmx_disable_mask=0;
2213 png_uint_32 asm_flags;
2215 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2218 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2219 asm_flags=png_get_asm_flags(ping);
2220 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2225 png_read_info(ping,ping_info);
2227 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2228 &ping_bit_depth,&ping_color_type,
2229 &ping_interlace_method,&ping_compression_method,
2230 &ping_filter_method);
2232 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2235 (void) png_get_bKGD(ping, ping_info, &ping_background);
2237 if (ping_bit_depth < 8)
2239 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2241 png_set_packing(ping);
2246 image->depth=ping_bit_depth;
2247 image->depth=GetImageQuantumDepth(image,MagickFalse);
2248 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2249 if (logging != MagickFalse)
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2252 " PNG width: %.20g, height: %.20g",
2253 (double) ping_width, (double) ping_height);
2255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG color_type: %d, bit_depth: %d",
2257 ping_color_type, ping_bit_depth);
2259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG compression_method: %d",
2261 ping_compression_method);
2263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " PNG interlace_method: %d, filter_method: %d",
2265 ping_interlace_method,ping_filter_method);
2268 #ifdef PNG_READ_iCCP_SUPPORTED
2269 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2274 #if (PNG_LIBPNG_VER < 10500)
2288 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2291 if (profile_length != 0)
2296 if (logging != MagickFalse)
2297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2298 " Reading PNG iCCP chunk.");
2299 profile=AcquireStringInfo(profile_length);
2300 SetStringInfoDatum(profile,(const unsigned char *) info);
2301 (void) SetImageProfile(image,"icc",profile);
2302 profile=DestroyStringInfo(profile);
2306 #if defined(PNG_READ_sRGB_SUPPORTED)
2308 if (mng_info->have_global_srgb)
2309 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2310 (mng_info->global_srgb_intent);
2312 if (png_get_sRGB(ping,ping_info,&intent))
2314 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2317 if (logging != MagickFalse)
2318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2319 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2324 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2325 if (mng_info->have_global_gama)
2326 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2328 if (png_get_gAMA(ping,ping_info,&file_gamma))
2330 image->gamma=(float) file_gamma;
2331 if (logging != MagickFalse)
2332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2333 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2336 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2338 if (mng_info->have_global_chrm != MagickFalse)
2340 (void) png_set_cHRM(ping,ping_info,
2341 mng_info->global_chrm.white_point.x,
2342 mng_info->global_chrm.white_point.y,
2343 mng_info->global_chrm.red_primary.x,
2344 mng_info->global_chrm.red_primary.y,
2345 mng_info->global_chrm.green_primary.x,
2346 mng_info->global_chrm.green_primary.y,
2347 mng_info->global_chrm.blue_primary.x,
2348 mng_info->global_chrm.blue_primary.y);
2352 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2354 (void) png_get_cHRM(ping,ping_info,
2355 &image->chromaticity.white_point.x,
2356 &image->chromaticity.white_point.y,
2357 &image->chromaticity.red_primary.x,
2358 &image->chromaticity.red_primary.y,
2359 &image->chromaticity.green_primary.x,
2360 &image->chromaticity.green_primary.y,
2361 &image->chromaticity.blue_primary.x,
2362 &image->chromaticity.blue_primary.y);
2364 if (logging != MagickFalse)
2365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2366 " Reading PNG cHRM chunk.");
2369 if (image->rendering_intent != UndefinedIntent)
2371 png_set_sRGB(ping,ping_info,
2372 Magick_RenderingIntent_to_PNG_RenderingIntent
2373 (image->rendering_intent));
2374 png_set_gAMA(ping,ping_info,0.45455f);
2375 png_set_cHRM(ping,ping_info,
2376 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2377 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2379 #if defined(PNG_oFFs_SUPPORTED)
2380 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2382 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2383 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2385 if (logging != MagickFalse)
2386 if (image->page.x || image->page.y)
2387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2388 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2389 image->page.x,(double) image->page.y);
2392 #if defined(PNG_pHYs_SUPPORTED)
2393 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2395 if (mng_info->have_global_phys)
2397 png_set_pHYs(ping,ping_info,
2398 mng_info->global_x_pixels_per_unit,
2399 mng_info->global_y_pixels_per_unit,
2400 mng_info->global_phys_unit_type);
2404 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2407 Set image resolution.
2409 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2411 image->x_resolution=(double) x_resolution;
2412 image->y_resolution=(double) y_resolution;
2414 if (unit_type == PNG_RESOLUTION_METER)
2416 image->units=PixelsPerCentimeterResolution;
2417 image->x_resolution=(double) x_resolution/100.0;
2418 image->y_resolution=(double) y_resolution/100.0;
2421 if (logging != MagickFalse)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2423 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2424 (double) x_resolution,(double) y_resolution,unit_type);
2428 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2436 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2438 if ((number_colors == 0) &&
2439 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2441 if (mng_info->global_plte_length)
2443 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2444 (int) mng_info->global_plte_length);
2446 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2447 if (mng_info->global_trns_length)
2449 if (mng_info->global_trns_length >
2450 mng_info->global_plte_length)
2451 (void) ThrowMagickException(&image->exception,
2452 GetMagickModule(),CoderError,
2453 "global tRNS has more entries than global PLTE",
2454 "`%s'",image_info->filename);
2455 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2456 (int) mng_info->global_trns_length,NULL);
2458 #ifdef PNG_READ_bKGD_SUPPORTED
2460 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2461 mng_info->have_saved_bkgd_index ||
2463 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2468 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2469 if (mng_info->have_saved_bkgd_index)
2470 background.index=mng_info->saved_bkgd_index;
2472 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2473 background.index=ping_background->index;
2475 background.red=(png_uint_16)
2476 mng_info->global_plte[background.index].red;
2478 background.green=(png_uint_16)
2479 mng_info->global_plte[background.index].green;
2481 background.blue=(png_uint_16)
2482 mng_info->global_plte[background.index].blue;
2484 background.gray=(png_uint_16)
2485 mng_info->global_plte[background.index].green;
2487 png_set_bKGD(ping,ping_info,&background);
2492 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2493 CoderError,"No global PLTE in file","`%s'",
2494 image_info->filename);
2498 #ifdef PNG_READ_bKGD_SUPPORTED
2499 if (mng_info->have_global_bkgd &&
2500 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2501 image->background_color=mng_info->mng_global_bkgd;
2503 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2509 Set image background color.
2511 if (logging != MagickFalse)
2512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2513 " Reading PNG bKGD chunk.");
2515 /* Scale background components to 16-bit, then scale
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " raw ping_background=(%d,%d,%d).",ping_background->red,
2521 ping_background->green,ping_background->blue);
2525 if (ping_bit_depth == 1)
2528 else if (ping_bit_depth == 2)
2531 else if (ping_bit_depth == 4)
2534 if (ping_bit_depth <= 8)
2537 ping_background->red *= bkgd_scale;
2538 ping_background->green *= bkgd_scale;
2539 ping_background->blue *= bkgd_scale;
2541 if (logging != MagickFalse)
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " bkgd_scale=%d.",bkgd_scale);
2546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2547 " ping_background=(%d,%d,%d).",ping_background->red,
2548 ping_background->green,ping_background->blue);
2551 image->background_color.red=
2552 ScaleShortToQuantum(ping_background->red);
2554 image->background_color.green=
2555 ScaleShortToQuantum(ping_background->green);
2557 image->background_color.blue=
2558 ScaleShortToQuantum(ping_background->blue);
2560 image->background_color.alpha=OpaqueAlpha;
2562 if (logging != MagickFalse)
2563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2564 " image->background_color=(%.20g,%.20g,%.20g).",
2565 (double) image->background_color.red,
2566 (double) image->background_color.green,
2567 (double) image->background_color.blue);
2569 #endif /* PNG_READ_bKGD_SUPPORTED */
2571 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2574 Image has a tRNS chunk.
2582 if (logging != MagickFalse)
2583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2584 " Reading PNG tRNS chunk.");
2586 max_sample = (int) ((one << ping_bit_depth) - 1);
2588 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2589 (int)ping_trans_color->gray > max_sample) ||
2590 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2591 ((int)ping_trans_color->red > max_sample ||
2592 (int)ping_trans_color->green > max_sample ||
2593 (int)ping_trans_color->blue > max_sample)))
2595 if (logging != MagickFalse)
2596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2597 " Ignoring PNG tRNS chunk with out-of-range sample.");
2598 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2599 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2600 image->matte=MagickFalse;
2607 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2609 /* Scale transparent_color to short */
2610 transparent_color.red= scale_to_short*ping_trans_color->red;
2611 transparent_color.green= scale_to_short*ping_trans_color->green;
2612 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2613 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2615 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2617 if (logging != MagickFalse)
2619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2620 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2623 " scaled graylevel is %d.",transparent_color.alpha);
2625 transparent_color.red=transparent_color.alpha;
2626 transparent_color.green=transparent_color.alpha;
2627 transparent_color.blue=transparent_color.alpha;
2631 #if defined(PNG_READ_sBIT_SUPPORTED)
2632 if (mng_info->have_global_sbit)
2634 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2635 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2638 num_passes=png_set_interlace_handling(ping);
2640 png_read_update_info(ping,ping_info);
2642 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2645 Initialize image structure.
2647 mng_info->image_box.left=0;
2648 mng_info->image_box.right=(ssize_t) ping_width;
2649 mng_info->image_box.top=0;
2650 mng_info->image_box.bottom=(ssize_t) ping_height;
2651 if (mng_info->mng_type == 0)
2653 mng_info->mng_width=ping_width;
2654 mng_info->mng_height=ping_height;
2655 mng_info->frame=mng_info->image_box;
2656 mng_info->clip=mng_info->image_box;
2661 image->page.y=mng_info->y_off[mng_info->object_id];
2664 image->compression=ZipCompression;
2665 image->columns=ping_width;
2666 image->rows=ping_height;
2667 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2668 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2673 image->storage_class=PseudoClass;
2675 image->colors=one << ping_bit_depth;
2676 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2677 if (image->colors > 256)
2680 if (image->colors > 65536L)
2681 image->colors=65536L;
2683 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2691 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2692 image->colors=(size_t) number_colors;
2694 if (logging != MagickFalse)
2695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2696 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2700 if (image->storage_class == PseudoClass)
2703 Initialize image colormap.
2705 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2706 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2708 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2716 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2718 for (i=0; i < (ssize_t) number_colors; i++)
2720 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2721 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2722 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2725 for ( ; i < (ssize_t) image->colors; i++)
2727 image->colormap[i].red=0;
2728 image->colormap[i].green=0;
2729 image->colormap[i].blue=0;
2738 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2743 for (i=0; i < (ssize_t) image->colors; i++)
2745 image->colormap[i].red=(Quantum) (i*scale);
2746 image->colormap[i].green=(Quantum) (i*scale);
2747 image->colormap[i].blue=(Quantum) (i*scale);
2752 /* Set some properties for reporting by "identify" */
2757 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2758 ping_interlace_method in value */
2760 (void) FormatLocaleString(msg,MaxTextExtent,
2761 "%d, %d",(int) ping_width, (int) ping_height);
2762 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
2764 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2765 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2767 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2768 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2770 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2771 (int) ping_interlace_method);
2772 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
2776 Read image scanlines.
2778 if (image->delay != 0)
2779 mng_info->scenes_found++;
2781 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2782 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2783 (image_info->first_scene+image_info->number_scenes))))
2785 if (logging != MagickFalse)
2786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2787 " Skipping PNG image data for scene %.20g",(double)
2788 mng_info->scenes_found-1);
2789 png_destroy_read_struct(&ping,&ping_info,&end_info);
2790 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2791 UnlockSemaphoreInfo(ping_semaphore);
2793 if (logging != MagickFalse)
2794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2795 " exit ReadOnePNGImage().");
2800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " Reading PNG IDAT chunk(s)");
2805 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2806 ping_rowbytes*sizeof(*ping_pixels));
2809 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2810 sizeof(*ping_pixels));
2812 if (ping_pixels == (unsigned char *) NULL)
2813 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2815 if (logging != MagickFalse)
2816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2817 " Converting PNG pixels to pixel packets");
2819 Convert PNG pixels to pixel packets.
2821 if (setjmp(png_jmpbuf(ping)))
2824 PNG image is corrupt.
2826 png_destroy_read_struct(&ping,&ping_info,&end_info);
2827 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2828 UnlockSemaphoreInfo(ping_semaphore);
2830 if (quantum_info != (QuantumInfo *) NULL)
2831 quantum_info = DestroyQuantumInfo(quantum_info);
2833 if (ping_pixels != (unsigned char *) NULL)
2834 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2836 if (logging != MagickFalse)
2837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2838 " exit ReadOnePNGImage() with error.");
2840 if (image != (Image *) NULL)
2842 InheritException(exception,&image->exception);
2846 return(GetFirstImageInList(image));
2849 quantum_info=AcquireQuantumInfo(image_info,image);
2851 if (quantum_info == (QuantumInfo *) NULL)
2852 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2857 found_transparent_pixel;
2859 found_transparent_pixel=MagickFalse;
2861 if (image->storage_class == DirectClass)
2863 for (pass=0; pass < num_passes; pass++)
2866 Convert image to DirectClass pixel packets.
2868 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2872 depth=(ssize_t) ping_bit_depth;
2874 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2875 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2876 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2877 MagickTrue : MagickFalse;
2879 for (y=0; y < (ssize_t) image->rows; y++)
2882 row_offset=ping_rowbytes*y;
2887 png_read_row(ping,ping_pixels+row_offset,NULL);
2888 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2890 if (q == (const Quantum *) NULL)
2893 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2894 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2895 GrayQuantum,ping_pixels+row_offset,exception);
2897 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2898 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2899 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2901 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2902 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2903 RGBAQuantum,ping_pixels+row_offset,exception);
2905 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2906 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2907 IndexQuantum,ping_pixels+row_offset,exception);
2909 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2910 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2911 RGBQuantum,ping_pixels+row_offset,exception);
2913 if (found_transparent_pixel == MagickFalse)
2915 /* Is there a transparent pixel in the row? */
2916 if (y== 0 && logging != MagickFalse)
2917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2918 " Looking for cheap transparent pixel");
2920 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2922 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2923 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2924 (GetPixelAlpha(image,q) != OpaqueAlpha))
2926 if (logging != MagickFalse)
2927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2930 found_transparent_pixel = MagickTrue;
2933 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2934 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2935 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2936 transparent_color.red &&
2937 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2938 transparent_color.green &&
2939 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2940 transparent_color.blue))
2942 if (logging != MagickFalse)
2943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2945 found_transparent_pixel = MagickTrue;
2948 q+=GetPixelChannels(image);
2952 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2954 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2957 if (status == MagickFalse)
2960 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2964 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2966 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2967 if (status == MagickFalse)
2973 else /* image->storage_class != DirectClass */
2975 for (pass=0; pass < num_passes; pass++)
2984 Convert grayscale image to PseudoClass pixel packets.
2986 if (logging != MagickFalse)
2987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2988 " Converting grayscale pixels to pixel packets");
2990 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2991 MagickTrue : MagickFalse;
2993 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2994 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2996 if (quantum_scanline == (Quantum *) NULL)
2997 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2999 for (y=0; y < (ssize_t) image->rows; y++)
3002 row_offset=ping_rowbytes*y;
3007 png_read_row(ping,ping_pixels+row_offset,NULL);
3008 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3010 if (q == (const Quantum *) NULL)
3013 p=ping_pixels+row_offset;
3016 switch (ping_bit_depth)
3023 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
3025 for (bit=7; bit >= 0; bit--)
3026 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3030 if ((image->columns % 8) != 0)
3032 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
3033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
3041 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
3043 *r++=(*p >> 6) & 0x03;
3044 *r++=(*p >> 4) & 0x03;
3045 *r++=(*p >> 2) & 0x03;
3049 if ((image->columns % 4) != 0)
3051 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
3052 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
3060 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
3062 *r++=(*p >> 4) & 0x0f;
3066 if ((image->columns % 2) != 0)
3067 *r++=(*p++ >> 4) & 0x0f;
3074 if (ping_color_type == 4)
3075 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3078 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3079 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3080 found_transparent_pixel = MagickTrue;
3081 q+=GetPixelChannels(image);
3085 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3093 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3095 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3099 if (image->colors > 256)
3100 quantum=((*p++) << 8);
3106 *r=ScaleShortToQuantum(quantum);
3109 if (ping_color_type == 4)
3111 if (image->colors > 256)
3112 quantum=((*p++) << 8);
3117 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3118 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3119 found_transparent_pixel = MagickTrue;
3120 q+=GetPixelChannels(image);
3123 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3125 p++; /* strip low byte */
3127 if (ping_color_type == 4)
3129 SetPixelAlpha(image,*p++,q);
3130 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3131 found_transparent_pixel = MagickTrue;
3133 q+=GetPixelChannels(image);
3146 Transfer image scanline.
3150 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3152 if (q == (const Quantum *) NULL)
3154 for (x=0; x < (ssize_t) image->columns; x++)
3156 SetPixelIndex(image,*r++,q);
3157 q+=GetPixelChannels(image);
3160 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3163 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3165 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3168 if (status == MagickFalse)
3173 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3175 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3177 if (status == MagickFalse)
3181 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3184 image->matte=found_transparent_pixel;
3186 if (logging != MagickFalse)
3188 if (found_transparent_pixel != MagickFalse)
3189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3190 " Found transparent pixel");
3193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3194 " No transparent pixel was found");
3196 ping_color_type&=0x03;
3201 if (quantum_info != (QuantumInfo *) NULL)
3202 quantum_info=DestroyQuantumInfo(quantum_info);
3204 if (image->storage_class == PseudoClass)
3210 image->matte=MagickFalse;
3211 (void) SyncImage(image);
3215 png_read_end(ping,end_info);
3217 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3218 (ssize_t) image_info->first_scene && image->delay != 0)
3220 png_destroy_read_struct(&ping,&ping_info,&end_info);
3221 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3223 (void) SetImageBackgroundColor(image);
3224 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3225 UnlockSemaphoreInfo(ping_semaphore);
3227 if (logging != MagickFalse)
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " exit ReadOnePNGImage() early.");
3233 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3239 Image has a transparent background.
3241 storage_class=image->storage_class;
3242 image->matte=MagickTrue;
3244 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3246 if (storage_class == PseudoClass)
3248 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3250 for (x=0; x < ping_num_trans; x++)
3252 image->colormap[x].alpha =
3253 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3257 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3259 for (x=0; x < (int) image->colors; x++)
3261 if (ScaleQuantumToShort(image->colormap[x].red) ==
3262 transparent_color.alpha)
3264 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3268 (void) SyncImage(image);
3271 #if 1 /* Should have already been done above, but glennrp problem P10
3276 for (y=0; y < (ssize_t) image->rows; y++)
3278 image->storage_class=storage_class;
3279 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3281 if (q == (const Quantum *) NULL)
3285 /* Caution: on a Q8 build, this does not distinguish between
3286 * 16-bit colors that differ only in the low byte
3288 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3290 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3291 transparent_color.red &&
3292 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3293 transparent_color.green &&
3294 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3295 transparent_color.blue)
3297 SetPixelAlpha(image,TransparentAlpha,q);
3300 #if 0 /* I have not found a case where this is needed. */
3303 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3307 q+=GetPixelChannels(image);
3310 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3316 image->storage_class=DirectClass;
3319 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3320 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3321 image->colorspace=GRAYColorspace;
3323 for (j = 0; j < 2; j++)
3326 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3327 MagickTrue : MagickFalse;
3329 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3330 MagickTrue : MagickFalse;
3332 if (status != MagickFalse)
3333 for (i=0; i < (ssize_t) num_text; i++)
3335 /* Check for a profile */
3337 if (logging != MagickFalse)
3338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3339 " Reading PNG text chunk");
3341 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3343 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3352 length=text[i].text_length;
3353 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3355 if (value == (char *) NULL)
3357 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3358 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3363 (void) ConcatenateMagickString(value,text[i].text,length+2);
3365 /* Don't save "density" or "units" property if we have a pHYs
3368 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3369 (LocaleCompare(text[i].key,"density") != 0 &&
3370 LocaleCompare(text[i].key,"units") != 0))
3371 (void) SetImageProperty(image,text[i].key,value);
3373 if (logging != MagickFalse)
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " length: %lu",(unsigned long) length);
3377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3378 " Keyword: %s",text[i].key);
3381 value=DestroyString(value);
3384 num_text_total += num_text;
3387 #ifdef MNG_OBJECT_BUFFERS
3389 Store the object if necessary.
3391 if (object_id && !mng_info->frozen[object_id])
3393 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3396 create a new object buffer.
3398 mng_info->ob[object_id]=(MngBuffer *)
3399 AcquireMagickMemory(sizeof(MngBuffer));
3401 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3403 mng_info->ob[object_id]->image=(Image *) NULL;
3404 mng_info->ob[object_id]->reference_count=1;
3408 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3409 mng_info->ob[object_id]->frozen)
3411 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3412 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3413 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3416 if (mng_info->ob[object_id]->frozen)
3417 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3418 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3419 "`%s'",image->filename);
3425 if (mng_info->ob[object_id]->image != (Image *) NULL)
3426 mng_info->ob[object_id]->image=DestroyImage
3427 (mng_info->ob[object_id]->image);
3429 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3432 if (mng_info->ob[object_id]->image != (Image *) NULL)
3433 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3436 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3437 ResourceLimitError,"Cloning image for object buffer failed",
3438 "`%s'",image->filename);
3440 if (ping_width > 250000L || ping_height > 250000L)
3441 png_error(ping,"PNG Image dimensions are too large.");
3443 mng_info->ob[object_id]->width=ping_width;
3444 mng_info->ob[object_id]->height=ping_height;
3445 mng_info->ob[object_id]->color_type=ping_color_type;
3446 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3447 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3448 mng_info->ob[object_id]->compression_method=
3449 ping_compression_method;
3450 mng_info->ob[object_id]->filter_method=ping_filter_method;
3452 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3461 Copy the PLTE to the object buffer.
3463 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3464 mng_info->ob[object_id]->plte_length=number_colors;
3466 for (i=0; i < number_colors; i++)
3468 mng_info->ob[object_id]->plte[i]=plte[i];
3473 mng_info->ob[object_id]->plte_length=0;
3478 /* Set image->matte to MagickTrue if the input colortype supports
3479 * alpha or if a valid tRNS chunk is present, no matter whether there
3480 * is actual transparency present.
3482 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3483 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3484 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3485 MagickTrue : MagickFalse;
3487 /* Set more properties for identify to retrieve */
3492 if (num_text_total != 0)
3494 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3495 (void) FormatLocaleString(msg,MaxTextExtent,
3496 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3497 (void) SetImageProperty(image,"PNG:text ",msg);
3500 if (num_raw_profiles != 0)
3502 (void) FormatLocaleString(msg,MaxTextExtent,
3503 "%d were found", num_raw_profiles);
3504 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3507 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3509 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3510 "chunk was found (see Chromaticity, above)");
3511 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3514 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3516 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3517 "chunk was found (see Background color, above)");
3518 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3521 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3524 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3525 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3527 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3528 (void) SetImageProperty(image,"PNG:tRNS ",msg);
3530 #if defined(PNG_sRGB_SUPPORTED)
3531 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3533 (void) FormatLocaleString(msg,MaxTextExtent,
3534 "intent=%d (See Rendering intent)",
3536 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3540 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3542 (void) FormatLocaleString(msg,MaxTextExtent,
3543 "gamma=%.8g (See Gamma, above)",
3545 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3548 #if defined(PNG_pHYs_SUPPORTED)
3549 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3551 (void) FormatLocaleString(msg,MaxTextExtent,
3552 "x_res=%.10g, y_res=%.10g, units=%d",
3553 (double) x_resolution,(double) y_resolution, unit_type);
3554 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3558 #if defined(PNG_oFFs_SUPPORTED)
3559 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3561 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3562 (double) image->page.x,(double) image->page.y);
3563 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3567 if ((image->page.width != 0 && image->page.width != image->columns) ||
3568 (image->page.height != 0 && image->page.height != image->rows))
3570 (void) FormatLocaleString(msg,MaxTextExtent,
3571 "width=%.20g, height=%.20g",
3572 (double) image->page.width,(double) image->page.height);
3573 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3578 Relinquish resources.
3580 png_destroy_read_struct(&ping,&ping_info,&end_info);
3582 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3583 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3584 UnlockSemaphoreInfo(ping_semaphore);
3587 if (logging != MagickFalse)
3588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3589 " exit ReadOnePNGImage()");
3593 /* end of reading one PNG image */
3596 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3611 magic_number[MaxTextExtent];
3619 assert(image_info != (const ImageInfo *) NULL);
3620 assert(image_info->signature == MagickSignature);
3622 if (image_info->debug != MagickFalse)
3623 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3624 image_info->filename);
3626 assert(exception != (ExceptionInfo *) NULL);
3627 assert(exception->signature == MagickSignature);
3628 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3629 image=AcquireImage(image_info);
3630 mng_info=(MngInfo *) NULL;
3631 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3633 if (status == MagickFalse)
3634 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3637 Verify PNG signature.
3639 count=ReadBlob(image,8,(unsigned char *) magic_number);
3641 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3642 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3645 Allocate a MngInfo structure.
3647 have_mng_structure=MagickFalse;
3648 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3650 if (mng_info == (MngInfo *) NULL)
3651 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3654 Initialize members of the MngInfo structure.
3656 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3657 mng_info->image=image;
3658 have_mng_structure=MagickTrue;
3661 image=ReadOnePNGImage(mng_info,image_info,exception);
3662 MngInfoFreeStruct(mng_info,&have_mng_structure);
3664 if (image == (Image *) NULL)
3666 if (previous != (Image *) NULL)
3668 if (previous->signature != MagickSignature)
3669 ThrowReaderException(CorruptImageError,"CorruptImage");
3671 (void) CloseBlob(previous);
3672 (void) DestroyImageList(previous);
3675 if (logging != MagickFalse)
3676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3677 "exit ReadPNGImage() with error");
3679 return((Image *) NULL);
3682 (void) CloseBlob(image);
3684 if ((image->columns == 0) || (image->rows == 0))
3686 if (logging != MagickFalse)
3687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3688 "exit ReadPNGImage() with error.");
3690 ThrowReaderException(CorruptImageError,"CorruptImage");
3693 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3695 (void) SetImageType(image,TrueColorType);
3696 image->matte=MagickFalse;
3699 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3700 (void) SetImageType(image,TrueColorMatteType);
3702 if (logging != MagickFalse)
3703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3704 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3705 (double) image->page.width,(double) image->page.height,
3706 (double) image->page.x,(double) image->page.y);
3708 if (logging != MagickFalse)
3709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3716 #if defined(JNG_SUPPORTED)
3718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3722 % R e a d O n e J N G I m a g e %
3726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3728 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3729 % (minus the 8-byte signature) and returns it. It allocates the memory
3730 % necessary for the new Image structure and returns a pointer to the new
3733 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3735 % The format of the ReadOneJNGImage method is:
3737 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3738 % ExceptionInfo *exception)
3740 % A description of each parameter follows:
3742 % o mng_info: Specifies a pointer to a MngInfo structure.
3744 % o image_info: the image info.
3746 % o exception: return any errors or warnings in this structure.
3749 static Image *ReadOneJNGImage(MngInfo *mng_info,
3750 const ImageInfo *image_info, ExceptionInfo *exception)
3777 jng_image_sample_depth,
3778 jng_image_compression_method,
3779 jng_image_interlace_method,
3780 jng_alpha_sample_depth,
3781 jng_alpha_compression_method,
3782 jng_alpha_filter_method,
3783 jng_alpha_interlace_method;
3785 register const Quantum
3795 register unsigned char
3806 jng_alpha_compression_method=0;
3807 jng_alpha_sample_depth=8;
3811 alpha_image=(Image *) NULL;
3812 color_image=(Image *) NULL;
3813 alpha_image_info=(ImageInfo *) NULL;
3814 color_image_info=(ImageInfo *) NULL;
3816 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3817 " Enter ReadOneJNGImage()");
3819 image=mng_info->image;
3821 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
3824 Allocate next image structure.
3826 if (logging != MagickFalse)
3827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3828 " AcquireNextImage()");
3830 AcquireNextImage(image_info,image);
3832 if (GetNextImageInList(image) == (Image *) NULL)
3833 return((Image *) NULL);
3835 image=SyncNextImageInList(image);
3837 mng_info->image=image;
3840 Signature bytes have already been read.
3843 read_JSEP=MagickFalse;
3844 reading_idat=MagickFalse;
3845 skip_to_iend=MagickFalse;
3849 type[MaxTextExtent];
3858 Read a new JNG chunk.
3860 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3861 2*GetBlobSize(image));
3863 if (status == MagickFalse)
3867 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3868 length=ReadBlobMSBLong(image);
3869 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3871 if (logging != MagickFalse)
3872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3873 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3874 type[0],type[1],type[2],type[3],(double) length);
3876 if (length > PNG_UINT_31_MAX || count == 0)
3877 ThrowReaderException(CorruptImageError,"CorruptImage");
3880 chunk=(unsigned char *) NULL;
3884 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3886 if (chunk == (unsigned char *) NULL)
3887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3889 for (i=0; i < (ssize_t) length; i++)
3890 chunk[i]=(unsigned char) ReadBlobByte(image);
3895 (void) ReadBlobMSBLong(image); /* read crc word */
3900 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3905 if (memcmp(type,mng_JHDR,4) == 0)
3909 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3910 (p[2] << 8) | p[3]);
3911 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3912 (p[6] << 8) | p[7]);
3913 jng_color_type=p[8];
3914 jng_image_sample_depth=p[9];
3915 jng_image_compression_method=p[10];
3916 jng_image_interlace_method=p[11];
3918 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3921 jng_alpha_sample_depth=p[12];
3922 jng_alpha_compression_method=p[13];
3923 jng_alpha_filter_method=p[14];
3924 jng_alpha_interlace_method=p[15];
3926 if (logging != MagickFalse)
3928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3929 " jng_width: %16lu",(unsigned long) jng_width);
3931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3932 " jng_width: %16lu",(unsigned long) jng_height);
3934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3935 " jng_color_type: %16d",jng_color_type);
3937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3938 " jng_image_sample_depth: %3d",
3939 jng_image_sample_depth);
3941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3942 " jng_image_compression_method:%3d",
3943 jng_image_compression_method);
3945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3946 " jng_image_interlace_method: %3d",
3947 jng_image_interlace_method);
3949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3950 " jng_alpha_sample_depth: %3d",
3951 jng_alpha_sample_depth);
3953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3954 " jng_alpha_compression_method:%3d",
3955 jng_alpha_compression_method);
3957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3958 " jng_alpha_filter_method: %3d",
3959 jng_alpha_filter_method);
3961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3962 " jng_alpha_interlace_method: %3d",
3963 jng_alpha_interlace_method);
3968 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3974 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3975 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3976 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3979 o create color_image
3980 o open color_blob, attached to color_image
3981 o if (color type has alpha)
3982 open alpha_blob, attached to alpha_image
3985 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3987 if (color_image_info == (ImageInfo *) NULL)
3988 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3990 GetImageInfo(color_image_info);
3991 color_image=AcquireImage(color_image_info);
3993 if (color_image == (Image *) NULL)
3994 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3996 if (logging != MagickFalse)
3997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " Creating color_blob.");
4000 (void) AcquireUniqueFilename(color_image->filename);
4001 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4004 if (status == MagickFalse)
4005 return((Image *) NULL);
4007 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4009 alpha_image_info=(ImageInfo *)
4010 AcquireMagickMemory(sizeof(ImageInfo));
4012 if (alpha_image_info == (ImageInfo *) NULL)
4013 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4015 GetImageInfo(alpha_image_info);
4016 alpha_image=AcquireImage(alpha_image_info);
4018 if (alpha_image == (Image *) NULL)
4020 alpha_image=DestroyImage(alpha_image);
4021 ThrowReaderException(ResourceLimitError,
4022 "MemoryAllocationFailed");
4025 if (logging != MagickFalse)
4026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4027 " Creating alpha_blob.");
4029 (void) AcquireUniqueFilename(alpha_image->filename);
4030 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4033 if (status == MagickFalse)
4034 return((Image *) NULL);
4036 if (jng_alpha_compression_method == 0)
4041 if (logging != MagickFalse)
4042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4043 " Writing IHDR chunk to alpha_blob.");
4045 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4046 "\211PNG\r\n\032\n");
4048 (void) WriteBlobMSBULong(alpha_image,13L);
4049 PNGType(data,mng_IHDR);
4050 LogPNGChunk(logging,mng_IHDR,13L);
4051 PNGLong(data+4,jng_width);
4052 PNGLong(data+8,jng_height);
4053 data[12]=jng_alpha_sample_depth;
4054 data[13]=0; /* color_type gray */
4055 data[14]=0; /* compression method 0 */
4056 data[15]=0; /* filter_method 0 */
4057 data[16]=0; /* interlace_method 0 */
4058 (void) WriteBlob(alpha_image,17,data);
4059 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4062 reading_idat=MagickTrue;
4065 if (memcmp(type,mng_JDAT,4) == 0)
4067 /* Copy chunk to color_image->blob */
4069 if (logging != MagickFalse)
4070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4071 " Copying JDAT chunk data to color_blob.");
4073 (void) WriteBlob(color_image,length,chunk);
4076 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4081 if (memcmp(type,mng_IDAT,4) == 0)
4086 /* Copy IDAT header and chunk data to alpha_image->blob */
4088 if (image_info->ping == MagickFalse)
4090 if (logging != MagickFalse)
4091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4092 " Copying IDAT chunk data to alpha_blob.");
4094 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4095 PNGType(data,mng_IDAT);
4096 LogPNGChunk(logging,mng_IDAT,length);
4097 (void) WriteBlob(alpha_image,4,data);
4098 (void) WriteBlob(alpha_image,length,chunk);
4099 (void) WriteBlobMSBULong(alpha_image,
4100 crc32(crc32(0,data,4),chunk,(uInt) length));
4104 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4109 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4111 /* Copy chunk data to alpha_image->blob */
4113 if (image_info->ping == MagickFalse)
4115 if (logging != MagickFalse)
4116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4117 " Copying JDAA chunk data to alpha_blob.");
4119 (void) WriteBlob(alpha_image,length,chunk);
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4128 if (memcmp(type,mng_JSEP,4) == 0)
4130 read_JSEP=MagickTrue;
4133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4138 if (memcmp(type,mng_bKGD,4) == 0)
4142 image->background_color.red=ScaleCharToQuantum(p[1]);
4143 image->background_color.green=image->background_color.red;
4144 image->background_color.blue=image->background_color.red;
4149 image->background_color.red=ScaleCharToQuantum(p[1]);
4150 image->background_color.green=ScaleCharToQuantum(p[3]);
4151 image->background_color.blue=ScaleCharToQuantum(p[5]);
4154 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4158 if (memcmp(type,mng_gAMA,4) == 0)
4161 image->gamma=((float) mng_get_long(p))*0.00001;
4163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4167 if (memcmp(type,mng_cHRM,4) == 0)
4171 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4172 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4173 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4174 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4175 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4176 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4177 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4178 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4185 if (memcmp(type,mng_sRGB,4) == 0)
4189 image->rendering_intent=
4190 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4191 image->gamma=0.45455f;
4192 image->chromaticity.red_primary.x=0.6400f;
4193 image->chromaticity.red_primary.y=0.3300f;
4194 image->chromaticity.green_primary.x=0.3000f;
4195 image->chromaticity.green_primary.y=0.6000f;
4196 image->chromaticity.blue_primary.x=0.1500f;
4197 image->chromaticity.blue_primary.y=0.0600f;
4198 image->chromaticity.white_point.x=0.3127f;
4199 image->chromaticity.white_point.y=0.3290f;
4202 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4206 if (memcmp(type,mng_oFFs,4) == 0)
4210 image->page.x=(ssize_t) mng_get_long(p);
4211 image->page.y=(ssize_t) mng_get_long(&p[4]);
4213 if ((int) p[8] != 0)
4215 image->page.x/=10000;
4216 image->page.y/=10000;
4221 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4226 if (memcmp(type,mng_pHYs,4) == 0)
4230 image->x_resolution=(double) mng_get_long(p);
4231 image->y_resolution=(double) mng_get_long(&p[4]);
4232 if ((int) p[8] == PNG_RESOLUTION_METER)
4234 image->units=PixelsPerCentimeterResolution;
4235 image->x_resolution=image->x_resolution/100.0f;
4236 image->y_resolution=image->y_resolution/100.0f;
4240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4245 if (memcmp(type,mng_iCCP,4) == 0)
4249 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4258 if (memcmp(type,mng_IEND,4))
4268 Finish up reading image data:
4270 o read main image from color_blob.
4274 o if (color_type has alpha)
4275 if alpha_encoding is PNG
4276 read secondary image from alpha_blob via ReadPNG
4277 if alpha_encoding is JPEG
4278 read secondary image from alpha_blob via ReadJPEG
4282 o copy intensity of secondary image into
4283 alpha samples of main image.
4285 o destroy the secondary image.
4288 (void) CloseBlob(color_image);
4290 if (logging != MagickFalse)
4291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4292 " Reading jng_image from color_blob.");
4294 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4295 color_image->filename);
4297 color_image_info->ping=MagickFalse; /* To do: avoid this */
4298 jng_image=ReadImage(color_image_info,exception);
4300 if (jng_image == (Image *) NULL)
4301 return((Image *) NULL);
4303 (void) RelinquishUniqueFileResource(color_image->filename);
4304 color_image=DestroyImage(color_image);
4305 color_image_info=DestroyImageInfo(color_image_info);
4307 if (jng_image == (Image *) NULL)
4308 return((Image *) NULL);
4310 if (logging != MagickFalse)
4311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4312 " Copying jng_image pixels to main image.");
4314 image->rows=jng_height;
4315 image->columns=jng_width;
4317 for (y=0; y < (ssize_t) image->rows; y++)
4319 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4320 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4321 for (x=(ssize_t) image->columns; x != 0; x--)
4323 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4324 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4325 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4326 q+=GetPixelChannels(image);
4327 s+=GetPixelChannels(jng_image);
4330 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4334 jng_image=DestroyImage(jng_image);
4336 if (image_info->ping == MagickFalse)
4338 if (jng_color_type >= 12)
4340 if (jng_alpha_compression_method == 0)
4344 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4345 PNGType(data,mng_IEND);
4346 LogPNGChunk(logging,mng_IEND,0L);
4347 (void) WriteBlob(alpha_image,4,data);
4348 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4351 (void) CloseBlob(alpha_image);
4353 if (logging != MagickFalse)
4354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4355 " Reading alpha from alpha_blob.");
4357 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4358 "%s",alpha_image->filename);
4360 jng_image=ReadImage(alpha_image_info,exception);
4362 if (jng_image != (Image *) NULL)
4363 for (y=0; y < (ssize_t) image->rows; y++)
4365 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4367 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4369 if (image->matte != MagickFalse)
4370 for (x=(ssize_t) image->columns; x != 0; x--)
4372 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4373 q+=GetPixelChannels(image);
4374 s+=GetPixelChannels(jng_image);
4378 for (x=(ssize_t) image->columns; x != 0; x--)
4380 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4381 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4382 image->matte=MagickTrue;
4383 q+=GetPixelChannels(image);
4384 s+=GetPixelChannels(jng_image);
4387 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4390 (void) RelinquishUniqueFileResource(alpha_image->filename);
4391 alpha_image=DestroyImage(alpha_image);
4392 alpha_image_info=DestroyImageInfo(alpha_image_info);
4393 if (jng_image != (Image *) NULL)
4394 jng_image=DestroyImage(jng_image);
4398 /* Read the JNG image. */
4400 if (mng_info->mng_type == 0)
4402 mng_info->mng_width=jng_width;
4403 mng_info->mng_height=jng_height;
4406 if (image->page.width == 0 && image->page.height == 0)
4408 image->page.width=jng_width;
4409 image->page.height=jng_height;
4412 if (image->page.x == 0 && image->page.y == 0)
4414 image->page.x=mng_info->x_off[mng_info->object_id];
4415 image->page.y=mng_info->y_off[mng_info->object_id];
4420 image->page.y=mng_info->y_off[mng_info->object_id];
4423 mng_info->image_found++;
4424 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4425 2*GetBlobSize(image));
4427 if (logging != MagickFalse)
4428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4429 " exit ReadOneJNGImage()");
4435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4439 % R e a d J N G I m a g e %
4443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4445 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4446 % (including the 8-byte signature) and returns it. It allocates the memory
4447 % necessary for the new Image structure and returns a pointer to the new
4450 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4452 % The format of the ReadJNGImage method is:
4454 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4457 % A description of each parameter follows:
4459 % o image_info: the image info.
4461 % o exception: return any errors or warnings in this structure.
4465 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4480 magic_number[MaxTextExtent];
4488 assert(image_info != (const ImageInfo *) NULL);
4489 assert(image_info->signature == MagickSignature);
4490 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4491 assert(exception != (ExceptionInfo *) NULL);
4492 assert(exception->signature == MagickSignature);
4493 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4494 image=AcquireImage(image_info);
4495 mng_info=(MngInfo *) NULL;
4496 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4498 if (status == MagickFalse)
4499 return((Image *) NULL);
4501 if (LocaleCompare(image_info->magick,"JNG") != 0)
4502 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4504 /* Verify JNG signature. */
4506 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4508 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4509 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4511 /* Allocate a MngInfo structure. */
4513 have_mng_structure=MagickFalse;
4514 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4516 if (mng_info == (MngInfo *) NULL)
4517 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4519 /* Initialize members of the MngInfo structure. */
4521 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4522 have_mng_structure=MagickTrue;
4524 mng_info->image=image;
4526 image=ReadOneJNGImage(mng_info,image_info,exception);
4527 MngInfoFreeStruct(mng_info,&have_mng_structure);
4529 if (image == (Image *) NULL)
4531 if (IsImageObject(previous) != MagickFalse)
4533 (void) CloseBlob(previous);
4534 (void) DestroyImageList(previous);
4537 if (logging != MagickFalse)
4538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4539 "exit ReadJNGImage() with error");
4541 return((Image *) NULL);
4543 (void) CloseBlob(image);
4545 if (image->columns == 0 || image->rows == 0)
4547 if (logging != MagickFalse)
4548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4549 "exit ReadJNGImage() with error");
4551 ThrowReaderException(CorruptImageError,"CorruptImage");
4554 if (logging != MagickFalse)
4555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4561 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4564 page_geometry[MaxTextExtent];
4597 #if defined(MNG_INSERT_LAYERS)
4599 mng_background_color;
4602 register unsigned char
4617 #if defined(MNG_INSERT_LAYERS)
4622 volatile unsigned int
4623 #ifdef MNG_OBJECT_BUFFERS
4624 mng_background_object=0,
4626 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4629 default_frame_timeout,
4631 #if defined(MNG_INSERT_LAYERS)
4637 /* These delays are all measured in image ticks_per_second,
4638 * not in MNG ticks_per_second
4641 default_frame_delay,
4645 #if defined(MNG_INSERT_LAYERS)
4654 previous_fb.bottom=0;
4656 previous_fb.right=0;
4658 default_fb.bottom=0;
4662 /* Open image file. */
4664 assert(image_info != (const ImageInfo *) NULL);
4665 assert(image_info->signature == MagickSignature);
4666 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4667 assert(exception != (ExceptionInfo *) NULL);
4668 assert(exception->signature == MagickSignature);
4669 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4670 image=AcquireImage(image_info);
4671 mng_info=(MngInfo *) NULL;
4672 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4674 if (status == MagickFalse)
4675 return((Image *) NULL);
4677 first_mng_object=MagickFalse;
4679 have_mng_structure=MagickFalse;
4681 /* Allocate a MngInfo structure. */
4683 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4685 if (mng_info == (MngInfo *) NULL)
4686 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4688 /* Initialize members of the MngInfo structure. */
4690 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4691 mng_info->image=image;
4692 have_mng_structure=MagickTrue;
4694 if (LocaleCompare(image_info->magick,"MNG") == 0)
4697 magic_number[MaxTextExtent];
4699 /* Verify MNG signature. */
4700 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4701 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4702 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4704 /* Initialize some nonzero members of the MngInfo structure. */
4705 for (i=0; i < MNG_MAX_OBJECTS; i++)
4707 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4708 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4710 mng_info->exists[0]=MagickTrue;
4713 first_mng_object=MagickTrue;
4715 #if defined(MNG_INSERT_LAYERS)
4716 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4718 default_frame_delay=0;
4719 default_frame_timeout=0;
4722 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4724 skip_to_iend=MagickFalse;
4725 term_chunk_found=MagickFalse;
4726 mng_info->framing_mode=1;
4727 #if defined(MNG_INSERT_LAYERS)
4728 mandatory_back=MagickFalse;
4730 #if defined(MNG_INSERT_LAYERS)
4731 mng_background_color=image->background_color;
4733 default_fb=mng_info->frame;
4734 previous_fb=mng_info->frame;
4738 type[MaxTextExtent];
4740 if (LocaleCompare(image_info->magick,"MNG") == 0)
4749 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4750 length=ReadBlobMSBLong(image);
4751 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4753 if (logging != MagickFalse)
4754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4755 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4756 type[0],type[1],type[2],type[3],(double) length);
4758 if (length > PNG_UINT_31_MAX)
4762 ThrowReaderException(CorruptImageError,"CorruptImage");
4765 chunk=(unsigned char *) NULL;
4769 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4771 if (chunk == (unsigned char *) NULL)
4772 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4774 for (i=0; i < (ssize_t) length; i++)
4775 chunk[i]=(unsigned char) ReadBlobByte(image);
4780 (void) ReadBlobMSBLong(image); /* read crc word */
4782 #if !defined(JNG_SUPPORTED)
4783 if (memcmp(type,mng_JHDR,4) == 0)
4785 skip_to_iend=MagickTrue;
4787 if (mng_info->jhdr_warning == 0)
4788 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4789 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4791 mng_info->jhdr_warning++;
4794 if (memcmp(type,mng_DHDR,4) == 0)
4796 skip_to_iend=MagickTrue;
4798 if (mng_info->dhdr_warning == 0)
4799 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4800 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4802 mng_info->dhdr_warning++;
4804 if (memcmp(type,mng_MEND,4) == 0)
4809 if (memcmp(type,mng_IEND,4) == 0)
4810 skip_to_iend=MagickFalse;
4813 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4815 if (logging != MagickFalse)
4816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4822 if (memcmp(type,mng_MHDR,4) == 0)
4824 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4825 (p[2] << 8) | p[3]);
4827 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4828 (p[6] << 8) | p[7]);
4830 if (logging != MagickFalse)
4832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4833 " MNG width: %.20g",(double) mng_info->mng_width);
4834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4835 " MNG height: %.20g",(double) mng_info->mng_height);
4839 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4841 if (mng_info->ticks_per_second == 0)
4842 default_frame_delay=0;
4845 default_frame_delay=1UL*image->ticks_per_second/
4846 mng_info->ticks_per_second;
4848 frame_delay=default_frame_delay;
4854 simplicity=(size_t) mng_get_long(p);
4857 mng_type=1; /* Full MNG */
4859 if ((simplicity != 0) && ((simplicity | 11) == 11))
4860 mng_type=2; /* LC */
4862 if ((simplicity != 0) && ((simplicity | 9) == 9))
4863 mng_type=3; /* VLC */
4865 #if defined(MNG_INSERT_LAYERS)
4867 insert_layers=MagickTrue;
4869 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4871 /* Allocate next image structure. */
4872 AcquireNextImage(image_info,image);
4874 if (GetNextImageInList(image) == (Image *) NULL)
4875 return((Image *) NULL);
4877 image=SyncNextImageInList(image);
4878 mng_info->image=image;
4881 if ((mng_info->mng_width > 65535L) ||
4882 (mng_info->mng_height > 65535L))
4883 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4885 (void) FormatLocaleString(page_geometry,MaxTextExtent,
4886 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4887 mng_info->mng_height);
4889 mng_info->frame.left=0;
4890 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4891 mng_info->frame.top=0;
4892 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4893 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4895 for (i=0; i < MNG_MAX_OBJECTS; i++)
4896 mng_info->object_clip[i]=mng_info->frame;
4898 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4902 if (memcmp(type,mng_TERM,4) == 0)
4913 final_delay=(png_uint_32) mng_get_long(&p[2]);
4914 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4916 if (mng_iterations == PNG_UINT_31_MAX)
4919 image->iterations=mng_iterations;
4920 term_chunk_found=MagickTrue;
4923 if (logging != MagickFalse)
4925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4926 " repeat=%d",repeat);
4928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4929 " final_delay=%.20g",(double) final_delay);
4931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4932 " image->iterations=%.20g",(double) image->iterations);
4935 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4938 if (memcmp(type,mng_DEFI,4) == 0)
4941 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4942 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4945 object_id=(p[0] << 8) | p[1];
4947 if (mng_type == 2 && object_id != 0)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4952 if (object_id > MNG_MAX_OBJECTS)
4955 Instead ofsuing a warning we should allocate a larger
4956 MngInfo structure and continue.
4958 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4959 CoderError,"object id too large","`%s'",image->filename);
4960 object_id=MNG_MAX_OBJECTS;
4963 if (mng_info->exists[object_id])
4964 if (mng_info->frozen[object_id])
4966 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4967 (void) ThrowMagickException(&image->exception,
4968 GetMagickModule(),CoderError,
4969 "DEFI cannot redefine a frozen MNG object","`%s'",
4974 mng_info->exists[object_id]=MagickTrue;
4977 mng_info->invisible[object_id]=p[2];
4980 Extract object offset info.
4984 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4985 (p[5] << 16) | (p[6] << 8) | p[7]);
4987 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4988 (p[9] << 16) | (p[10] << 8) | p[11]);
4990 if (logging != MagickFalse)
4992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4993 " x_off[%d]: %.20g",object_id,(double)
4994 mng_info->x_off[object_id]);
4996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4997 " y_off[%d]: %.20g",object_id,(double)
4998 mng_info->y_off[object_id]);
5003 Extract object clipping info.
5006 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5009 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5012 if (memcmp(type,mng_bKGD,4) == 0)
5014 mng_info->have_global_bkgd=MagickFalse;
5018 mng_info->mng_global_bkgd.red=
5019 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5021 mng_info->mng_global_bkgd.green=
5022 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5024 mng_info->mng_global_bkgd.blue=
5025 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5027 mng_info->have_global_bkgd=MagickTrue;
5030 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5033 if (memcmp(type,mng_BACK,4) == 0)
5035 #if defined(MNG_INSERT_LAYERS)
5037 mandatory_back=p[6];
5042 if (mandatory_back && length > 5)
5044 mng_background_color.red=
5045 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5047 mng_background_color.green=
5048 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5050 mng_background_color.blue=
5051 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5053 mng_background_color.alpha=OpaqueAlpha;
5056 #ifdef MNG_OBJECT_BUFFERS
5058 mng_background_object=(p[7] << 8) | p[8];
5061 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5065 if (memcmp(type,mng_PLTE,4) == 0)
5067 /* Read global PLTE. */
5069 if (length && (length < 769))
5071 if (mng_info->global_plte == (png_colorp) NULL)
5072 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5073 sizeof(*mng_info->global_plte));
5075 for (i=0; i < (ssize_t) (length/3); i++)
5077 mng_info->global_plte[i].red=p[3*i];
5078 mng_info->global_plte[i].green=p[3*i+1];
5079 mng_info->global_plte[i].blue=p[3*i+2];
5082 mng_info->global_plte_length=(unsigned int) (length/3);
5085 for ( ; i < 256; i++)
5087 mng_info->global_plte[i].red=i;
5088 mng_info->global_plte[i].green=i;
5089 mng_info->global_plte[i].blue=i;
5093 mng_info->global_plte_length=256;
5096 mng_info->global_plte_length=0;
5098 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5102 if (memcmp(type,mng_tRNS,4) == 0)
5104 /* read global tRNS */
5107 for (i=0; i < (ssize_t) length; i++)
5108 mng_info->global_trns[i]=p[i];
5111 for ( ; i < 256; i++)
5112 mng_info->global_trns[i]=255;
5114 mng_info->global_trns_length=(unsigned int) length;
5115 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5118 if (memcmp(type,mng_gAMA,4) == 0)
5125 igamma=mng_get_long(p);
5126 mng_info->global_gamma=((float) igamma)*0.00001;
5127 mng_info->have_global_gama=MagickTrue;
5131 mng_info->have_global_gama=MagickFalse;
5133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5137 if (memcmp(type,mng_cHRM,4) == 0)
5139 /* Read global cHRM */
5143 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5144 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5145 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5146 mng_info->global_chrm.red_primary.y=0.00001*
5147 mng_get_long(&p[12]);
5148 mng_info->global_chrm.green_primary.x=0.00001*
5149 mng_get_long(&p[16]);
5150 mng_info->global_chrm.green_primary.y=0.00001*
5151 mng_get_long(&p[20]);
5152 mng_info->global_chrm.blue_primary.x=0.00001*
5153 mng_get_long(&p[24]);
5154 mng_info->global_chrm.blue_primary.y=0.00001*
5155 mng_get_long(&p[28]);
5156 mng_info->have_global_chrm=MagickTrue;
5159 mng_info->have_global_chrm=MagickFalse;
5161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5165 if (memcmp(type,mng_sRGB,4) == 0)
5172 mng_info->global_srgb_intent=
5173 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5174 mng_info->have_global_srgb=MagickTrue;
5177 mng_info->have_global_srgb=MagickFalse;
5179 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5183 if (memcmp(type,mng_iCCP,4) == 0)
5191 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5196 if (memcmp(type,mng_FRAM,4) == 0)
5199 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5200 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5203 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5204 image->delay=frame_delay;
5206 frame_delay=default_frame_delay;
5207 frame_timeout=default_frame_timeout;
5212 mng_info->framing_mode=p[0];
5214 if (logging != MagickFalse)
5215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5216 " Framing_mode=%d",mng_info->framing_mode);
5220 /* Note the delay and frame clipping boundaries. */
5222 p++; /* framing mode */
5224 while (*p && ((p-chunk) < (ssize_t) length))
5225 p++; /* frame name */
5227 p++; /* frame name terminator */
5229 if ((p-chunk) < (ssize_t) (length-4))
5236 change_delay=(*p++);
5237 change_timeout=(*p++);
5238 change_clipping=(*p++);
5239 p++; /* change_sync */
5243 frame_delay=1UL*image->ticks_per_second*
5246 if (mng_info->ticks_per_second != 0)
5247 frame_delay/=mng_info->ticks_per_second;
5250 frame_delay=PNG_UINT_31_MAX;
5252 if (change_delay == 2)
5253 default_frame_delay=frame_delay;
5257 if (logging != MagickFalse)
5258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5259 " Framing_delay=%.20g",(double) frame_delay);
5264 frame_timeout=1UL*image->ticks_per_second*
5267 if (mng_info->ticks_per_second != 0)
5268 frame_timeout/=mng_info->ticks_per_second;
5271 frame_timeout=PNG_UINT_31_MAX;
5273 if (change_delay == 2)
5274 default_frame_timeout=frame_timeout;
5278 if (logging != MagickFalse)
5279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5280 " Framing_timeout=%.20g",(double) frame_timeout);
5283 if (change_clipping)
5285 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5289 if (logging != MagickFalse)
5290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5291 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5292 (double) fb.left,(double) fb.right,(double) fb.top,
5293 (double) fb.bottom);
5295 if (change_clipping == 2)
5301 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5303 subframe_width=(size_t) (mng_info->clip.right
5304 -mng_info->clip.left);
5306 subframe_height=(size_t) (mng_info->clip.bottom
5307 -mng_info->clip.top);
5309 Insert a background layer behind the frame if framing_mode is 4.
5311 #if defined(MNG_INSERT_LAYERS)
5312 if (logging != MagickFalse)
5313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5314 " subframe_width=%.20g, subframe_height=%.20g",(double)
5315 subframe_width,(double) subframe_height);
5317 if (insert_layers && (mng_info->framing_mode == 4) &&
5318 (subframe_width) && (subframe_height))
5320 /* Allocate next image structure. */
5321 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5323 AcquireNextImage(image_info,image);
5325 if (GetNextImageInList(image) == (Image *) NULL)
5327 image=DestroyImageList(image);
5328 MngInfoFreeStruct(mng_info,&have_mng_structure);
5329 return((Image *) NULL);
5332 image=SyncNextImageInList(image);
5335 mng_info->image=image;
5337 if (term_chunk_found)
5339 image->start_loop=MagickTrue;
5340 image->iterations=mng_iterations;
5341 term_chunk_found=MagickFalse;
5345 image->start_loop=MagickFalse;
5347 image->columns=subframe_width;
5348 image->rows=subframe_height;
5349 image->page.width=subframe_width;
5350 image->page.height=subframe_height;
5351 image->page.x=mng_info->clip.left;
5352 image->page.y=mng_info->clip.top;
5353 image->background_color=mng_background_color;
5354 image->matte=MagickFalse;
5356 (void) SetImageBackgroundColor(image);
5358 if (logging != MagickFalse)
5359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5360 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5361 (double) mng_info->clip.left,(double) mng_info->clip.right,
5362 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5365 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5368 if (memcmp(type,mng_CLIP,4) == 0)
5377 first_object=(p[0] << 8) | p[1];
5378 last_object=(p[2] << 8) | p[3];
5380 for (i=(int) first_object; i <= (int) last_object; i++)
5382 if (mng_info->exists[i] && !mng_info->frozen[i])
5387 box=mng_info->object_clip[i];
5388 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5392 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5395 if (memcmp(type,mng_SAVE,4) == 0)
5397 for (i=1; i < MNG_MAX_OBJECTS; i++)
5398 if (mng_info->exists[i])
5400 mng_info->frozen[i]=MagickTrue;
5401 #ifdef MNG_OBJECT_BUFFERS
5402 if (mng_info->ob[i] != (MngBuffer *) NULL)
5403 mng_info->ob[i]->frozen=MagickTrue;
5408 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5413 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5415 /* Read DISC or SEEK. */
5417 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5419 for (i=1; i < MNG_MAX_OBJECTS; i++)
5420 MngInfoDiscardObject(mng_info,i);
5428 for (j=0; j < (ssize_t) length; j+=2)
5430 i=p[j] << 8 | p[j+1];
5431 MngInfoDiscardObject(mng_info,i);
5436 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5441 if (memcmp(type,mng_MOVE,4) == 0)
5449 first_object=(p[0] << 8) | p[1];
5450 last_object=(p[2] << 8) | p[3];
5451 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5453 if (mng_info->exists[i] && !mng_info->frozen[i])
5461 old_pair.a=mng_info->x_off[i];
5462 old_pair.b=mng_info->y_off[i];
5463 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5464 mng_info->x_off[i]=new_pair.a;
5465 mng_info->y_off[i]=new_pair.b;
5469 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5473 if (memcmp(type,mng_LOOP,4) == 0)
5475 ssize_t loop_iters=1;
5476 loop_level=chunk[0];
5477 mng_info->loop_active[loop_level]=1; /* mark loop active */
5479 /* Record starting point. */
5480 loop_iters=mng_get_long(&chunk[1]);
5482 if (logging != MagickFalse)
5483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5484 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5485 (double) loop_iters);
5487 if (loop_iters == 0)
5488 skipping_loop=loop_level;
5492 mng_info->loop_jump[loop_level]=TellBlob(image);
5493 mng_info->loop_count[loop_level]=loop_iters;
5496 mng_info->loop_iteration[loop_level]=0;
5497 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5501 if (memcmp(type,mng_ENDL,4) == 0)
5503 loop_level=chunk[0];
5505 if (skipping_loop > 0)
5507 if (skipping_loop == loop_level)
5510 Found end of zero-iteration loop.
5513 mng_info->loop_active[loop_level]=0;
5519 if (mng_info->loop_active[loop_level] == 1)
5521 mng_info->loop_count[loop_level]--;
5522 mng_info->loop_iteration[loop_level]++;
5524 if (logging != MagickFalse)
5525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5526 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5527 (double) loop_level,(double)
5528 mng_info->loop_count[loop_level]);
5530 if (mng_info->loop_count[loop_level] != 0)
5532 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5536 ThrowReaderException(CorruptImageError,
5537 "ImproperImageHeader");
5548 mng_info->loop_active[loop_level]=0;
5550 for (i=0; i < loop_level; i++)
5551 if (mng_info->loop_active[i] == 1)
5552 last_level=(short) i;
5553 loop_level=last_level;
5558 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5562 if (memcmp(type,mng_CLON,4) == 0)
5564 if (mng_info->clon_warning == 0)
5565 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5566 CoderError,"CLON is not implemented yet","`%s'",
5569 mng_info->clon_warning++;
5572 if (memcmp(type,mng_MAGN,4) == 0)
5587 magn_first=(p[0] << 8) | p[1];
5593 magn_last=(p[2] << 8) | p[3];
5596 magn_last=magn_first;
5597 #ifndef MNG_OBJECT_BUFFERS
5598 if (magn_first || magn_last)
5599 if (mng_info->magn_warning == 0)
5601 (void) ThrowMagickException(&image->exception,
5602 GetMagickModule(),CoderError,
5603 "MAGN is not implemented yet for nonzero objects",
5604 "`%s'",image->filename);
5606 mng_info->magn_warning++;
5616 magn_mx=(p[5] << 8) | p[6];
5625 magn_my=(p[7] << 8) | p[8];
5634 magn_ml=(p[9] << 8) | p[10];
5643 magn_mr=(p[11] << 8) | p[12];
5652 magn_mt=(p[13] << 8) | p[14];
5661 magn_mb=(p[15] << 8) | p[16];
5673 magn_methy=magn_methx;
5676 if (magn_methx > 5 || magn_methy > 5)
5677 if (mng_info->magn_warning == 0)
5679 (void) ThrowMagickException(&image->exception,
5680 GetMagickModule(),CoderError,
5681 "Unknown MAGN method in MNG datastream","`%s'",
5684 mng_info->magn_warning++;
5686 #ifdef MNG_OBJECT_BUFFERS
5687 /* Magnify existing objects in the range magn_first to magn_last */
5689 if (magn_first == 0 || magn_last == 0)
5691 /* Save the magnification factors for object 0 */
5692 mng_info->magn_mb=magn_mb;
5693 mng_info->magn_ml=magn_ml;
5694 mng_info->magn_mr=magn_mr;
5695 mng_info->magn_mt=magn_mt;
5696 mng_info->magn_mx=magn_mx;
5697 mng_info->magn_my=magn_my;
5698 mng_info->magn_methx=magn_methx;
5699 mng_info->magn_methy=magn_methy;
5703 if (memcmp(type,mng_PAST,4) == 0)
5705 if (mng_info->past_warning == 0)
5706 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5707 CoderError,"PAST is not implemented yet","`%s'",
5710 mng_info->past_warning++;
5713 if (memcmp(type,mng_SHOW,4) == 0)
5715 if (mng_info->show_warning == 0)
5716 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5717 CoderError,"SHOW is not implemented yet","`%s'",
5720 mng_info->show_warning++;
5723 if (memcmp(type,mng_sBIT,4) == 0)
5726 mng_info->have_global_sbit=MagickFalse;
5730 mng_info->global_sbit.gray=p[0];
5731 mng_info->global_sbit.red=p[0];
5732 mng_info->global_sbit.green=p[1];
5733 mng_info->global_sbit.blue=p[2];
5734 mng_info->global_sbit.alpha=p[3];
5735 mng_info->have_global_sbit=MagickTrue;
5738 if (memcmp(type,mng_pHYs,4) == 0)
5742 mng_info->global_x_pixels_per_unit=
5743 (size_t) mng_get_long(p);
5744 mng_info->global_y_pixels_per_unit=
5745 (size_t) mng_get_long(&p[4]);
5746 mng_info->global_phys_unit_type=p[8];
5747 mng_info->have_global_phys=MagickTrue;
5751 mng_info->have_global_phys=MagickFalse;
5753 if (memcmp(type,mng_pHYg,4) == 0)
5755 if (mng_info->phyg_warning == 0)
5756 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5757 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5759 mng_info->phyg_warning++;
5761 if (memcmp(type,mng_BASI,4) == 0)
5763 skip_to_iend=MagickTrue;
5765 if (mng_info->basi_warning == 0)
5766 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5767 CoderError,"BASI is not implemented yet","`%s'",
5770 mng_info->basi_warning++;
5771 #ifdef MNG_BASI_SUPPORTED
5772 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5773 (p[2] << 8) | p[3]);
5774 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5775 (p[6] << 8) | p[7]);
5776 basi_color_type=p[8];
5777 basi_compression_method=p[9];
5778 basi_filter_type=p[10];
5779 basi_interlace_method=p[11];
5781 basi_red=(p[12] << 8) & p[13];
5787 basi_green=(p[14] << 8) & p[15];
5793 basi_blue=(p[16] << 8) & p[17];
5799 basi_alpha=(p[18] << 8) & p[19];
5803 if (basi_sample_depth == 16)
5810 basi_viewable=p[20];
5816 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5820 if (memcmp(type,mng_IHDR,4)
5821 #if defined(JNG_SUPPORTED)
5822 && memcmp(type,mng_JHDR,4)
5826 /* Not an IHDR or JHDR chunk */
5828 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5833 if (logging != MagickFalse)
5834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5835 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5837 mng_info->exists[object_id]=MagickTrue;
5838 mng_info->viewable[object_id]=MagickTrue;
5840 if (mng_info->invisible[object_id])
5842 if (logging != MagickFalse)
5843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5844 " Skipping invisible object");
5846 skip_to_iend=MagickTrue;
5847 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5850 #if defined(MNG_INSERT_LAYERS)
5852 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5854 image_width=(size_t) mng_get_long(p);
5855 image_height=(size_t) mng_get_long(&p[4]);
5857 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5860 Insert a transparent background layer behind the entire animation
5861 if it is not full screen.
5863 #if defined(MNG_INSERT_LAYERS)
5864 if (insert_layers && mng_type && first_mng_object)
5866 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5867 (image_width < mng_info->mng_width) ||
5868 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5869 (image_height < mng_info->mng_height) ||
5870 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5872 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5875 Allocate next image structure.
5877 AcquireNextImage(image_info,image);
5879 if (GetNextImageInList(image) == (Image *) NULL)
5881 image=DestroyImageList(image);
5882 MngInfoFreeStruct(mng_info,&have_mng_structure);
5883 return((Image *) NULL);
5886 image=SyncNextImageInList(image);
5888 mng_info->image=image;
5890 if (term_chunk_found)
5892 image->start_loop=MagickTrue;
5893 image->iterations=mng_iterations;
5894 term_chunk_found=MagickFalse;
5898 image->start_loop=MagickFalse;
5900 /* Make a background rectangle. */
5903 image->columns=mng_info->mng_width;
5904 image->rows=mng_info->mng_height;
5905 image->page.width=mng_info->mng_width;
5906 image->page.height=mng_info->mng_height;
5909 image->background_color=mng_background_color;
5910 (void) SetImageBackgroundColor(image);
5911 if (logging != MagickFalse)
5912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5913 " Inserted transparent background layer, W=%.20g, H=%.20g",
5914 (double) mng_info->mng_width,(double) mng_info->mng_height);
5918 Insert a background layer behind the upcoming image if
5919 framing_mode is 3, and we haven't already inserted one.
5921 if (insert_layers && (mng_info->framing_mode == 3) &&
5922 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5923 (simplicity & 0x08)))
5925 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5928 Allocate next image structure.
5930 AcquireNextImage(image_info,image);
5932 if (GetNextImageInList(image) == (Image *) NULL)
5934 image=DestroyImageList(image);
5935 MngInfoFreeStruct(mng_info,&have_mng_structure);
5936 return((Image *) NULL);
5939 image=SyncNextImageInList(image);
5942 mng_info->image=image;
5944 if (term_chunk_found)
5946 image->start_loop=MagickTrue;
5947 image->iterations=mng_iterations;
5948 term_chunk_found=MagickFalse;
5952 image->start_loop=MagickFalse;
5955 image->columns=subframe_width;
5956 image->rows=subframe_height;
5957 image->page.width=subframe_width;
5958 image->page.height=subframe_height;
5959 image->page.x=mng_info->clip.left;
5960 image->page.y=mng_info->clip.top;
5961 image->background_color=mng_background_color;
5962 image->matte=MagickFalse;
5963 (void) SetImageBackgroundColor(image);
5965 if (logging != MagickFalse)
5966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5967 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5968 (double) mng_info->clip.left,(double) mng_info->clip.right,
5969 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5971 #endif /* MNG_INSERT_LAYERS */
5972 first_mng_object=MagickFalse;
5974 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5977 Allocate next image structure.
5979 AcquireNextImage(image_info,image);
5981 if (GetNextImageInList(image) == (Image *) NULL)
5983 image=DestroyImageList(image);
5984 MngInfoFreeStruct(mng_info,&have_mng_structure);
5985 return((Image *) NULL);
5988 image=SyncNextImageInList(image);
5990 mng_info->image=image;
5991 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5992 GetBlobSize(image));
5994 if (status == MagickFalse)
5997 if (term_chunk_found)
5999 image->start_loop=MagickTrue;
6000 term_chunk_found=MagickFalse;
6004 image->start_loop=MagickFalse;
6006 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6008 image->delay=frame_delay;
6009 frame_delay=default_frame_delay;
6015 image->page.width=mng_info->mng_width;
6016 image->page.height=mng_info->mng_height;
6017 image->page.x=mng_info->x_off[object_id];
6018 image->page.y=mng_info->y_off[object_id];
6019 image->iterations=mng_iterations;
6022 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6025 if (logging != MagickFalse)
6026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6027 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6030 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6033 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6037 mng_info->image=image;
6038 mng_info->mng_type=mng_type;
6039 mng_info->object_id=object_id;
6041 if (memcmp(type,mng_IHDR,4) == 0)
6042 image=ReadOnePNGImage(mng_info,image_info,exception);
6044 #if defined(JNG_SUPPORTED)
6046 image=ReadOneJNGImage(mng_info,image_info,exception);
6049 if (image == (Image *) NULL)
6051 if (IsImageObject(previous) != MagickFalse)
6053 (void) DestroyImageList(previous);
6054 (void) CloseBlob(previous);
6057 MngInfoFreeStruct(mng_info,&have_mng_structure);
6058 return((Image *) NULL);
6061 if (image->columns == 0 || image->rows == 0)
6063 (void) CloseBlob(image);
6064 image=DestroyImageList(image);
6065 MngInfoFreeStruct(mng_info,&have_mng_structure);
6066 return((Image *) NULL);
6069 mng_info->image=image;
6076 if (mng_info->magn_methx || mng_info->magn_methy)
6082 if (logging != MagickFalse)
6083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6084 " Processing MNG MAGN chunk");
6086 if (mng_info->magn_methx == 1)
6088 magnified_width=mng_info->magn_ml;
6090 if (image->columns > 1)
6091 magnified_width += mng_info->magn_mr;
6093 if (image->columns > 2)
6094 magnified_width += (png_uint_32)
6095 ((image->columns-2)*(mng_info->magn_mx));
6100 magnified_width=(png_uint_32) image->columns;
6102 if (image->columns > 1)
6103 magnified_width += mng_info->magn_ml-1;
6105 if (image->columns > 2)
6106 magnified_width += mng_info->magn_mr-1;
6108 if (image->columns > 3)
6109 magnified_width += (png_uint_32)
6110 ((image->columns-3)*(mng_info->magn_mx-1));
6113 if (mng_info->magn_methy == 1)
6115 magnified_height=mng_info->magn_mt;
6117 if (image->rows > 1)
6118 magnified_height += mng_info->magn_mb;
6120 if (image->rows > 2)
6121 magnified_height += (png_uint_32)
6122 ((image->rows-2)*(mng_info->magn_my));
6127 magnified_height=(png_uint_32) image->rows;
6129 if (image->rows > 1)
6130 magnified_height += mng_info->magn_mt-1;
6132 if (image->rows > 2)
6133 magnified_height += mng_info->magn_mb-1;
6135 if (image->rows > 3)
6136 magnified_height += (png_uint_32)
6137 ((image->rows-3)*(mng_info->magn_my-1));
6140 if (magnified_height > image->rows ||
6141 magnified_width > image->columns)
6168 /* Allocate next image structure. */
6170 if (logging != MagickFalse)
6171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6172 " Allocate magnified image");
6174 AcquireNextImage(image_info,image);
6176 if (GetNextImageInList(image) == (Image *) NULL)
6178 image=DestroyImageList(image);
6179 MngInfoFreeStruct(mng_info,&have_mng_structure);
6180 return((Image *) NULL);
6183 large_image=SyncNextImageInList(image);
6185 large_image->columns=magnified_width;
6186 large_image->rows=magnified_height;
6188 magn_methx=mng_info->magn_methx;
6189 magn_methy=mng_info->magn_methy;
6191 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6192 #define QM unsigned short
6193 if (magn_methx != 1 || magn_methy != 1)
6196 Scale pixels to unsigned shorts to prevent
6197 overflow of intermediate values of interpolations
6199 for (y=0; y < (ssize_t) image->rows; y++)
6201 q=GetAuthenticPixels(image,0,y,image->columns,1,
6204 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6206 SetPixelRed(image,ScaleQuantumToShort(
6207 GetPixelRed(image,q)),q);
6208 SetPixelGreen(image,ScaleQuantumToShort(
6209 GetPixelGreen(image,q)),q);
6210 SetPixelBlue(image,ScaleQuantumToShort(
6211 GetPixelBlue(image,q)),q);
6212 SetPixelAlpha(image,ScaleQuantumToShort(
6213 GetPixelAlpha(image,q)),q);
6214 q+=GetPixelChannels(image);
6217 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6225 if (image->matte != MagickFalse)
6226 (void) SetImageBackgroundColor(large_image);
6230 large_image->background_color.alpha=OpaqueAlpha;
6231 (void) SetImageBackgroundColor(large_image);
6233 if (magn_methx == 4)
6236 if (magn_methx == 5)
6239 if (magn_methy == 4)
6242 if (magn_methy == 5)
6246 /* magnify the rows into the right side of the large image */
6248 if (logging != MagickFalse)
6249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6250 " Magnify the rows to %.20g",(double) large_image->rows);
6251 m=(ssize_t) mng_info->magn_mt;
6253 length=(size_t) image->columns;
6254 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6255 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6257 if ((prev == (Quantum *) NULL) ||
6258 (next == (Quantum *) NULL))
6260 image=DestroyImageList(image);
6261 MngInfoFreeStruct(mng_info,&have_mng_structure);
6262 ThrowReaderException(ResourceLimitError,
6263 "MemoryAllocationFailed");
6266 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6267 (void) CopyMagickMemory(next,n,length);
6269 for (y=0; y < (ssize_t) image->rows; y++)
6272 m=(ssize_t) mng_info->magn_mt;
6274 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6275 m=(ssize_t) mng_info->magn_mb;
6277 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6278 m=(ssize_t) mng_info->magn_mb;
6280 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6284 m=(ssize_t) mng_info->magn_my;
6290 if (y < (ssize_t) image->rows-1)
6292 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6294 (void) CopyMagickMemory(next,n,length);
6297 for (i=0; i < m; i++, yy++)
6302 assert(yy < (ssize_t) large_image->rows);
6305 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6307 q+=(large_image->columns-image->columns);
6309 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6311 /* To do: get color as function of indexes[x] */
6313 if (image->storage_class == PseudoClass)
6318 if (magn_methy <= 1)
6320 /* replicate previous */
6321 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6322 SetPixelGreen(large_image,GetPixelGreen(image,
6324 SetPixelBlue(large_image,GetPixelBlue(image,
6326 SetPixelAlpha(large_image,GetPixelAlpha(image,
6330 else if (magn_methy == 2 || magn_methy == 4)
6334 SetPixelRed(large_image,GetPixelRed(image,
6336 SetPixelGreen(large_image,GetPixelGreen(image,
6338 SetPixelBlue(large_image,GetPixelBlue(image,
6340 SetPixelAlpha(large_image,GetPixelAlpha(image,
6347 SetPixelRed(large_image,((QM) (((ssize_t)
6348 (2*i*(GetPixelRed(image,n)
6349 -GetPixelRed(image,pixels)+m))/
6351 +GetPixelRed(image,pixels)))),q);
6352 SetPixelGreen(large_image,((QM) (((ssize_t)
6353 (2*i*(GetPixelGreen(image,n)
6354 -GetPixelGreen(image,pixels)+m))/
6356 +GetPixelGreen(image,pixels)))),q);
6357 SetPixelBlue(large_image,((QM) (((ssize_t)
6358 (2*i*(GetPixelBlue(image,n)
6359 -GetPixelBlue(image,pixels)+m))/
6361 +GetPixelBlue(image,pixels)))),q);
6363 if (image->matte != MagickFalse)
6364 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6365 (2*i*(GetPixelAlpha(image,n)
6366 -GetPixelAlpha(image,pixels)+m))
6368 GetPixelAlpha(image,pixels)))),q);
6371 if (magn_methy == 4)
6373 /* Replicate nearest */
6374 if (i <= ((m+1) << 1))
6375 SetPixelAlpha(large_image,GetPixelAlpha(image,
6378 SetPixelAlpha(large_image,GetPixelAlpha(image,
6383 else /* if (magn_methy == 3 || magn_methy == 5) */
6385 /* Replicate nearest */
6386 if (i <= ((m+1) << 1))
6388 SetPixelRed(large_image,GetPixelRed(image,
6390 SetPixelGreen(large_image,GetPixelGreen(image,
6392 SetPixelBlue(large_image,GetPixelBlue(image,
6394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6400 SetPixelRed(large_image,GetPixelRed(image,n),q);
6401 SetPixelGreen(large_image,GetPixelGreen(image,n),
6403 SetPixelBlue(large_image,GetPixelBlue(image,n),
6405 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6409 if (magn_methy == 5)
6411 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6412 (GetPixelAlpha(image,n)
6413 -GetPixelAlpha(image,pixels))
6414 +m))/((ssize_t) (m*2))
6415 +GetPixelAlpha(image,pixels)),q);
6418 n+=GetPixelChannels(image);
6419 q+=GetPixelChannels(large_image);
6420 pixels+=GetPixelChannels(image);
6423 if (SyncAuthenticPixels(large_image,exception) == 0)
6429 prev=(Quantum *) RelinquishMagickMemory(prev);
6430 next=(Quantum *) RelinquishMagickMemory(next);
6432 length=image->columns;
6434 if (logging != MagickFalse)
6435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6436 " Delete original image");
6438 DeleteImageFromList(&image);
6442 mng_info->image=image;
6444 /* magnify the columns */
6445 if (logging != MagickFalse)
6446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6447 " Magnify the columns to %.20g",(double) image->columns);
6449 for (y=0; y < (ssize_t) image->rows; y++)
6454 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6455 pixels=q+(image->columns-length)*GetPixelChannels(image);
6456 n=pixels+GetPixelChannels(image);
6458 for (x=(ssize_t) (image->columns-length);
6459 x < (ssize_t) image->columns; x++)
6461 /* To do: Rewrite using Get/Set***PixelChannel() */
6463 if (x == (ssize_t) (image->columns-length))
6464 m=(ssize_t) mng_info->magn_ml;
6466 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6467 m=(ssize_t) mng_info->magn_mr;
6469 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6470 m=(ssize_t) mng_info->magn_mr;
6472 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6476 m=(ssize_t) mng_info->magn_mx;
6478 for (i=0; i < m; i++)
6480 if (magn_methx <= 1)
6482 /* replicate previous */
6483 SetPixelRed(image,GetPixelRed(image,pixels),q);
6484 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6485 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6486 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6489 else if (magn_methx == 2 || magn_methx == 4)
6493 SetPixelRed(image,GetPixelRed(image,pixels),q);
6494 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6495 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6496 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6499 /* To do: Rewrite using Get/Set***PixelChannel() */
6503 SetPixelRed(image,(QM) ((2*i*(
6504 GetPixelRed(image,n)
6505 -GetPixelRed(image,pixels))+m)
6507 GetPixelRed(image,pixels)),q);
6509 SetPixelGreen(image,(QM) ((2*i*(
6510 GetPixelGreen(image,n)
6511 -GetPixelGreen(image,pixels))+m)
6513 GetPixelGreen(image,pixels)),q);
6515 SetPixelBlue(image,(QM) ((2*i*(
6516 GetPixelBlue(image,n)
6517 -GetPixelBlue(image,pixels))+m)
6519 GetPixelBlue(image,pixels)),q);
6520 if (image->matte != MagickFalse)
6521 SetPixelAlpha(image,(QM) ((2*i*(
6522 GetPixelAlpha(image,n)
6523 -GetPixelAlpha(image,pixels))+m)
6525 GetPixelAlpha(image,pixels)),q);
6528 if (magn_methx == 4)
6530 /* Replicate nearest */
6531 if (i <= ((m+1) << 1))
6533 SetPixelAlpha(image,
6534 GetPixelAlpha(image,pixels)+0,q);
6538 SetPixelAlpha(image,
6539 GetPixelAlpha(image,n)+0,q);
6544 else /* if (magn_methx == 3 || magn_methx == 5) */
6546 /* Replicate nearest */
6547 if (i <= ((m+1) << 1))
6549 SetPixelRed(image,GetPixelRed(image,pixels),q);
6550 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6551 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6552 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6557 SetPixelRed(image,GetPixelRed(image,n),q);
6558 SetPixelGreen(image,GetPixelGreen(image,n),q);
6559 SetPixelBlue(image,GetPixelBlue(image,n),q);
6560 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6563 if (magn_methx == 5)
6566 SetPixelAlpha(image,
6567 (QM) ((2*i*( GetPixelAlpha(image,n)
6568 -GetPixelAlpha(image,pixels))+m)/
6570 +GetPixelAlpha(image,pixels)),q);
6573 q+=GetPixelChannels(image);
6575 n+=GetPixelChannels(image);
6576 p+=GetPixelChannels(image);
6579 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6582 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6583 if (magn_methx != 1 || magn_methy != 1)
6586 Rescale pixels to Quantum
6588 for (y=0; y < (ssize_t) image->rows; y++)
6590 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6592 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6594 SetPixelRed(image,ScaleShortToQuantum(
6595 GetPixelRed(image,q)),q);
6596 SetPixelGreen(image,ScaleShortToQuantum(
6597 GetPixelGreen(image,q)),q);
6598 SetPixelBlue(image,ScaleShortToQuantum(
6599 GetPixelBlue(image,q)),q);
6600 SetPixelAlpha(image,ScaleShortToQuantum(
6601 GetPixelAlpha(image,q)),q);
6602 q+=GetPixelChannels(image);
6605 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6610 if (logging != MagickFalse)
6611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6612 " Finished MAGN processing");
6617 Crop_box is with respect to the upper left corner of the MNG.
6619 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6620 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6621 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6622 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6623 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6624 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6625 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6626 if ((crop_box.left != (mng_info->image_box.left
6627 +mng_info->x_off[object_id])) ||
6628 (crop_box.right != (mng_info->image_box.right
6629 +mng_info->x_off[object_id])) ||
6630 (crop_box.top != (mng_info->image_box.top
6631 +mng_info->y_off[object_id])) ||
6632 (crop_box.bottom != (mng_info->image_box.bottom
6633 +mng_info->y_off[object_id])))
6635 if (logging != MagickFalse)
6636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6637 " Crop the PNG image");
6639 if ((crop_box.left < crop_box.right) &&
6640 (crop_box.top < crop_box.bottom))
6649 Crop_info is with respect to the upper left corner of
6652 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6653 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6654 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6655 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6656 image->page.width=image->columns;
6657 image->page.height=image->rows;
6660 im=CropImage(image,&crop_info,exception);
6662 if (im != (Image *) NULL)
6664 image->columns=im->columns;
6665 image->rows=im->rows;
6666 im=DestroyImage(im);
6667 image->page.width=image->columns;
6668 image->page.height=image->rows;
6669 image->page.x=crop_box.left;
6670 image->page.y=crop_box.top;
6677 No pixels in crop area. The MNG spec still requires
6678 a layer, though, so make a single transparent pixel in
6679 the top left corner.
6684 (void) SetImageBackgroundColor(image);
6685 image->page.width=1;
6686 image->page.height=1;
6691 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6692 image=mng_info->image;
6696 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6697 /* PNG does not handle depths greater than 16 so reduce it even
6700 if (image->depth > 16)
6704 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6705 if (LosslessReduceDepthOK(image) != MagickFalse)
6709 GetImageException(image,exception);
6711 if (image_info->number_scenes != 0)
6713 if (mng_info->scenes_found >
6714 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6718 if (logging != MagickFalse)
6719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6720 " Finished reading image datastream.");
6722 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6724 (void) CloseBlob(image);
6726 if (logging != MagickFalse)
6727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6728 " Finished reading all image datastreams.");
6730 #if defined(MNG_INSERT_LAYERS)
6731 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6732 (mng_info->mng_height))
6735 Insert a background layer if nothing else was found.
6737 if (logging != MagickFalse)
6738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6739 " No images found. Inserting a background layer.");
6741 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6744 Allocate next image structure.
6746 AcquireNextImage(image_info,image);
6747 if (GetNextImageInList(image) == (Image *) NULL)
6749 image=DestroyImageList(image);
6750 MngInfoFreeStruct(mng_info,&have_mng_structure);
6752 if (logging != MagickFalse)
6753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6754 " Allocation failed, returning NULL.");
6756 return((Image *) NULL);
6758 image=SyncNextImageInList(image);
6760 image->columns=mng_info->mng_width;
6761 image->rows=mng_info->mng_height;
6762 image->page.width=mng_info->mng_width;
6763 image->page.height=mng_info->mng_height;
6766 image->background_color=mng_background_color;
6767 image->matte=MagickFalse;
6769 if (image_info->ping == MagickFalse)
6770 (void) SetImageBackgroundColor(image);
6772 mng_info->image_found++;
6775 image->iterations=mng_iterations;
6777 if (mng_iterations == 1)
6778 image->start_loop=MagickTrue;
6780 while (GetPreviousImageInList(image) != (Image *) NULL)
6783 if (image_count > 10*mng_info->image_found)
6785 if (logging != MagickFalse)
6786 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6788 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6789 CoderError,"Linked list is corrupted, beginning of list not found",
6790 "`%s'",image_info->filename);
6792 return((Image *) NULL);
6795 image=GetPreviousImageInList(image);
6797 if (GetNextImageInList(image) == (Image *) NULL)
6799 if (logging != MagickFalse)
6800 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6802 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6803 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6804 image_info->filename);
6808 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6809 GetNextImageInList(image) ==
6812 if (logging != MagickFalse)
6813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6814 " First image null");
6816 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6817 CoderError,"image->next for first image is NULL but shouldn't be.",
6818 "`%s'",image_info->filename);
6821 if (mng_info->image_found == 0)
6823 if (logging != MagickFalse)
6824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6825 " No visible images found.");
6827 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6828 CoderError,"No visible images in file","`%s'",image_info->filename);
6830 if (image != (Image *) NULL)
6831 image=DestroyImageList(image);
6833 MngInfoFreeStruct(mng_info,&have_mng_structure);
6834 return((Image *) NULL);
6837 if (mng_info->ticks_per_second)
6838 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6839 final_delay/mng_info->ticks_per_second;
6842 image->start_loop=MagickTrue;
6844 /* Find final nonzero image delay */
6845 final_image_delay=0;
6847 while (GetNextImageInList(image) != (Image *) NULL)
6850 final_image_delay=image->delay;
6852 image=GetNextImageInList(image);
6855 if (final_delay < final_image_delay)
6856 final_delay=final_image_delay;
6858 image->delay=final_delay;
6860 if (logging != MagickFalse)
6861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6862 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6863 (double) final_delay);
6865 if (logging != MagickFalse)
6871 image=GetFirstImageInList(image);
6873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6874 " Before coalesce:");
6876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6877 " scene 0 delay=%.20g",(double) image->delay);
6879 while (GetNextImageInList(image) != (Image *) NULL)
6881 image=GetNextImageInList(image);
6882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6883 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6887 image=GetFirstImageInList(image);
6888 #ifdef MNG_COALESCE_LAYERS
6898 if (logging != MagickFalse)
6899 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6902 next_image=CoalesceImages(image,&image->exception);
6904 if (next_image == (Image *) NULL)
6905 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6907 image=DestroyImageList(image);
6910 for (next=image; next != (Image *) NULL; next=next_image)
6912 next->page.width=mng_info->mng_width;
6913 next->page.height=mng_info->mng_height;
6916 next->scene=scene++;
6917 next_image=GetNextImageInList(next);
6919 if (next_image == (Image *) NULL)
6922 if (next->delay == 0)
6925 next_image->previous=GetPreviousImageInList(next);
6926 if (GetPreviousImageInList(next) == (Image *) NULL)
6929 next->previous->next=next_image;
6930 next=DestroyImage(next);
6936 while (GetNextImageInList(image) != (Image *) NULL)
6937 image=GetNextImageInList(image);
6939 image->dispose=BackgroundDispose;
6941 if (logging != MagickFalse)
6947 image=GetFirstImageInList(image);
6949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6950 " After coalesce:");
6952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6953 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6954 (double) image->dispose);
6956 while (GetNextImageInList(image) != (Image *) NULL)
6958 image=GetNextImageInList(image);
6960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6961 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6962 (double) image->delay,(double) image->dispose);
6966 image=GetFirstImageInList(image);
6967 MngInfoFreeStruct(mng_info,&have_mng_structure);
6968 have_mng_structure=MagickFalse;
6970 if (logging != MagickFalse)
6971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6973 return(GetFirstImageInList(image));
6975 #else /* PNG_LIBPNG_VER > 10011 */
6976 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6978 printf("Your PNG library is too old: You have libpng-%s\n",
6979 PNG_LIBPNG_VER_STRING);
6981 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6982 "PNG library is too old","`%s'",image_info->filename);
6984 return(Image *) NULL;
6987 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6989 return(ReadPNGImage(image_info,exception));
6991 #endif /* PNG_LIBPNG_VER > 10011 */
6995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6999 % R e g i s t e r P N G I m a g e %
7003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7005 % RegisterPNGImage() adds properties for the PNG image format to
7006 % the list of supported formats. The properties include the image format
7007 % tag, a method to read and/or write the format, whether the format
7008 % supports the saving of more than one frame to the same file or blob,
7009 % whether the format supports native in-memory I/O, and a brief
7010 % description of the format.
7012 % The format of the RegisterPNGImage method is:
7014 % size_t RegisterPNGImage(void)
7017 ModuleExport size_t RegisterPNGImage(void)
7020 version[MaxTextExtent];
7028 "See http://www.libpng.org/ for details about the PNG format."
7033 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7039 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7045 #if defined(PNG_LIBPNG_VER_STRING)
7046 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7047 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7049 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7051 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7052 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7057 entry=SetMagickInfo("MNG");
7058 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7060 #if defined(MAGICKCORE_PNG_DELEGATE)
7061 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7062 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7065 entry->magick=(IsImageFormatHandler *) IsMNG;
7066 entry->description=ConstantString("Multiple-image Network Graphics");
7068 if (*version != '\0')
7069 entry->version=ConstantString(version);
7071 entry->module=ConstantString("PNG");
7072 entry->note=ConstantString(MNGNote);
7073 (void) RegisterMagickInfo(entry);
7075 entry=SetMagickInfo("PNG");
7077 #if defined(MAGICKCORE_PNG_DELEGATE)
7078 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7079 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7082 entry->magick=(IsImageFormatHandler *) IsPNG;
7083 entry->adjoin=MagickFalse;
7084 entry->description=ConstantString("Portable Network Graphics");
7085 entry->module=ConstantString("PNG");
7087 if (*version != '\0')
7088 entry->version=ConstantString(version);
7090 entry->note=ConstantString(PNGNote);
7091 (void) RegisterMagickInfo(entry);
7093 entry=SetMagickInfo("PNG8");
7095 #if defined(MAGICKCORE_PNG_DELEGATE)
7096 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7097 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7100 entry->magick=(IsImageFormatHandler *) IsPNG;
7101 entry->adjoin=MagickFalse;
7102 entry->description=ConstantString(
7103 "8-bit indexed with optional binary transparency");
7104 entry->module=ConstantString("PNG");
7105 (void) RegisterMagickInfo(entry);
7107 entry=SetMagickInfo("PNG24");
7110 #if defined(ZLIB_VERSION)
7111 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7112 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7114 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7116 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7117 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7121 if (*version != '\0')
7122 entry->version=ConstantString(version);
7124 #if defined(MAGICKCORE_PNG_DELEGATE)
7125 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7126 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7129 entry->magick=(IsImageFormatHandler *) IsPNG;
7130 entry->adjoin=MagickFalse;
7131 entry->description=ConstantString("opaque 24-bit RGB");
7132 entry->module=ConstantString("PNG");
7133 (void) RegisterMagickInfo(entry);
7135 entry=SetMagickInfo("PNG32");
7137 #if defined(MAGICKCORE_PNG_DELEGATE)
7138 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7139 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7142 entry->magick=(IsImageFormatHandler *) IsPNG;
7143 entry->adjoin=MagickFalse;
7144 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7145 entry->module=ConstantString("PNG");
7146 (void) RegisterMagickInfo(entry);
7148 entry=SetMagickInfo("JNG");
7150 #if defined(JNG_SUPPORTED)
7151 #if defined(MAGICKCORE_PNG_DELEGATE)
7152 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7153 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7157 entry->magick=(IsImageFormatHandler *) IsJNG;
7158 entry->adjoin=MagickFalse;
7159 entry->description=ConstantString("JPEG Network Graphics");
7160 entry->module=ConstantString("PNG");
7161 entry->note=ConstantString(JNGNote);
7162 (void) RegisterMagickInfo(entry);
7164 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7165 ping_semaphore=AllocateSemaphoreInfo();
7168 return(MagickImageCoderSignature);
7172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7176 % U n r e g i s t e r P N G I m a g e %
7180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7182 % UnregisterPNGImage() removes format registrations made by the
7183 % PNG module from the list of supported formats.
7185 % The format of the UnregisterPNGImage method is:
7187 % UnregisterPNGImage(void)
7190 ModuleExport void UnregisterPNGImage(void)
7192 (void) UnregisterMagickInfo("MNG");
7193 (void) UnregisterMagickInfo("PNG");
7194 (void) UnregisterMagickInfo("PNG8");
7195 (void) UnregisterMagickInfo("PNG24");
7196 (void) UnregisterMagickInfo("PNG32");
7197 (void) UnregisterMagickInfo("JNG");
7199 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7200 if (ping_semaphore != (SemaphoreInfo *) NULL)
7201 DestroySemaphoreInfo(&ping_semaphore);
7205 #if defined(MAGICKCORE_PNG_DELEGATE)
7206 #if PNG_LIBPNG_VER > 10011
7208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7212 % W r i t e M N G I m a g e %
7216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7218 % WriteMNGImage() writes an image in the Portable Network Graphics
7219 % Group's "Multiple-image Network Graphics" encoded image format.
7221 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7223 % The format of the WriteMNGImage method is:
7225 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7227 % A description of each parameter follows.
7229 % o image_info: the image info.
7231 % o image: The image.
7234 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7235 % "To do" under ReadPNGImage):
7237 % Preserve all unknown and not-yet-handled known chunks found in input
7238 % PNG file and copy them into output PNG files according to the PNG
7241 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7243 % Improve selection of color type (use indexed-colour or indexed-colour
7244 % with tRNS when 256 or fewer unique RGBA values are present).
7246 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7247 % This will be complicated if we limit ourselves to generating MNG-LC
7248 % files. For now we ignore disposal method 3 and simply overlay the next
7251 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7252 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7253 % [mostly done 15 June 1999 but still need to take care of tRNS]
7255 % Check for identical sRGB and replace with a global sRGB (and remove
7256 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7257 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7258 % local gAMA/cHRM with local sRGB if appropriate).
7260 % Check for identical sBIT chunks and write global ones.
7262 % Provide option to skip writing the signature tEXt chunks.
7264 % Use signatures to detect identical objects and reuse the first
7265 % instance of such objects instead of writing duplicate objects.
7267 % Use a smaller-than-32k value of compression window size when
7270 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7271 % ancillary text chunks and save profiles.
7273 % Provide an option to force LC files (to ensure exact framing rate)
7276 % Provide an option to force VLC files instead of LC, even when offsets
7277 % are present. This will involve expanding the embedded images with a
7278 % transparent region at the top and/or left.
7282 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7283 png_info *ping_info, unsigned char *profile_type, unsigned char
7284 *profile_description, unsigned char *profile_data, png_uint_32 length)
7303 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7305 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7308 if (image_info->verbose)
7310 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7311 (char *) profile_type, (double) length);
7314 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7315 description_length=(png_uint_32) strlen((const char *) profile_description);
7316 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7317 + description_length);
7318 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7319 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7320 text[0].key[0]='\0';
7321 (void) ConcatenateMagickString(text[0].key,
7322 "Raw profile type ",MaxTextExtent);
7323 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7327 (void) CopyMagickString(dp,(const char *) profile_description,
7329 dp+=description_length;
7331 (void) FormatLocaleString(dp,allocated_length-
7332 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7335 for (i=0; i < (ssize_t) length; i++)
7339 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7340 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7345 text[0].text_length=(png_size_t) (dp-text[0].text);
7346 text[0].compression=image_info->compression == NoCompression ||
7347 (image_info->compression == UndefinedCompression &&
7348 text[0].text_length < 128) ? -1 : 0;
7350 if (text[0].text_length <= allocated_length)
7351 png_set_text(ping,ping_info,text,1);
7353 png_free(ping,text[0].text);
7354 png_free(ping,text[0].key);
7355 png_free(ping,text);
7358 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7359 const char *string, MagickBooleanType logging)
7372 ResetImageProfileIterator(image);
7374 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7376 profile=GetImageProfile(image,name);
7378 if (profile != (const StringInfo *) NULL)
7383 if (LocaleNCompare(name,string,11) == 0)
7385 if (logging != MagickFalse)
7386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7387 " Found %s profile",name);
7389 ping_profile=CloneStringInfo(profile);
7390 data=GetStringInfoDatum(ping_profile),
7391 length=(png_uint_32) GetStringInfoLength(ping_profile);
7396 (void) WriteBlobMSBULong(image,length-5); /* data length */
7397 (void) WriteBlob(image,length-1,data+1);
7398 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7399 ping_profile=DestroyStringInfo(ping_profile);
7403 name=GetNextImageProfile(image);
7410 /* Write one PNG image */
7411 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7412 const ImageInfo *IMimage_info,Image *IMimage)
7436 ping_trans_alpha[256];
7464 ping_have_cheap_transparency,
7475 /* ping_exclude_EXIF, */
7478 /* ping_exclude_iTXt, */
7483 /* ping_exclude_tRNS, */
7485 ping_exclude_zCCP, /* hex-encoded iCCP */
7488 ping_preserve_colormap,
7489 ping_need_colortype_warning,
7510 ping_interlace_method,
7511 ping_compression_method,
7528 number_semitransparent,
7530 ping_pHYs_unit_type;
7533 ping_pHYs_x_resolution,
7534 ping_pHYs_y_resolution;
7536 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7537 " Enter WriteOnePNGImage()");
7539 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7540 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7541 if (image_info == (ImageInfo *) NULL)
7542 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7544 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7545 LockSemaphoreInfo(ping_semaphore);
7548 /* Initialize some stuff */
7551 ping_interlace_method=0,
7552 ping_compression_method=0,
7553 ping_filter_method=0,
7556 ping_background.red = 0;
7557 ping_background.green = 0;
7558 ping_background.blue = 0;
7559 ping_background.gray = 0;
7560 ping_background.index = 0;
7562 ping_trans_color.red=0;
7563 ping_trans_color.green=0;
7564 ping_trans_color.blue=0;
7565 ping_trans_color.gray=0;
7567 ping_pHYs_unit_type = 0;
7568 ping_pHYs_x_resolution = 0;
7569 ping_pHYs_y_resolution = 0;
7571 ping_have_blob=MagickFalse;
7572 ping_have_color=MagickTrue;
7573 ping_have_non_bw=MagickTrue;
7574 ping_have_PLTE=MagickFalse;
7575 ping_have_bKGD=MagickFalse;
7576 ping_have_pHYs=MagickFalse;
7577 ping_have_tRNS=MagickFalse;
7579 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7580 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7581 ping_exclude_date=mng_info->ping_exclude_date;
7582 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7583 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7584 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7585 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7586 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7587 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7588 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7589 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7590 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7591 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7592 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7593 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7595 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7596 ping_need_colortype_warning = MagickFalse;
7599 number_semitransparent = 0;
7600 number_transparent = 0;
7602 if (logging != MagickFalse)
7604 if (image->storage_class == UndefinedClass)
7605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606 " storage_class=UndefinedClass");
7607 if (image->storage_class == DirectClass)
7608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7609 " storage_class=DirectClass");
7610 if (image->storage_class == PseudoClass)
7611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7612 " storage_class=PseudoClass");
7615 if (ping_preserve_colormap == MagickFalse)
7617 if (image->storage_class != PseudoClass && image->colormap != NULL)
7619 /* Free the bogus colormap; it can cause trouble later */
7620 if (logging != MagickFalse)
7621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7622 " Freeing bogus colormap");
7623 (void *) RelinquishMagickMemory(image->colormap);
7624 image->colormap=NULL;
7628 if (IsRGBColorspace(image->colorspace) == MagickFalse)
7629 (void) TransformImageColorspace(image,RGBColorspace);
7632 Sometimes we get PseudoClass images whose RGB values don't match
7633 the colors in the colormap. This code syncs the RGB values.
7635 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7636 (void) SyncImage(image);
7638 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7639 if (image->depth > 8)
7641 if (logging != MagickFalse)
7642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7643 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7649 /* Respect the -depth option */
7650 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7658 exception=(&image->exception);
7660 if (image->depth > 8)
7662 #if MAGICKCORE_QUANTUM_DEPTH > 16
7663 /* Scale to 16-bit */
7664 LBR16PacketRGBO(image->background_color);
7666 for (y=0; y < (ssize_t) image->rows; y++)
7668 r=GetAuthenticPixels(image,0,y,image->columns,1,
7671 if (r == (Quantum *) NULL)
7674 for (x=0; x < (ssize_t) image->columns; x++)
7680 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7684 if (image->storage_class == PseudoClass && image->colormap != NULL)
7686 for (i=0; i < (ssize_t) image->colors; i++)
7688 LBR16PacketRGBO(image->colormap[i]);
7691 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7694 else if (image->depth > 4)
7696 #if MAGICKCORE_QUANTUM_DEPTH > 8
7697 /* Scale to 8-bit */
7698 LBR08PacketRGBO(image->background_color);
7700 for (y=0; y < (ssize_t) image->rows; y++)
7702 r=GetAuthenticPixels(image,0,y,image->columns,1,
7705 if (r == (Quantum *) NULL)
7708 for (x=0; x < (ssize_t) image->columns; x++)
7714 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7718 if (image->storage_class == PseudoClass && image->colormap != NULL)
7720 for (i=0; i < (ssize_t) image->colors; i++)
7722 LBR08PacketRGBO(image->colormap[i]);
7725 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7728 if (image->depth > 2)
7730 /* Scale to 4-bit */
7731 LBR04PacketRGBO(image->background_color);
7733 for (y=0; y < (ssize_t) image->rows; y++)
7735 r=GetAuthenticPixels(image,0,y,image->columns,1,
7738 if (r == (Quantum *) NULL)
7741 for (x=0; x < (ssize_t) image->columns; x++)
7747 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7751 if (image->storage_class == PseudoClass && image->colormap != NULL)
7753 for (i=0; i < (ssize_t) image->colors; i++)
7755 LBR04PacketRGBO(image->colormap[i]);
7760 else if (image->depth > 1)
7762 /* Scale to 2-bit */
7763 LBR02PacketRGBO(image->background_color);
7765 for (y=0; y < (ssize_t) image->rows; y++)
7767 r=GetAuthenticPixels(image,0,y,image->columns,1,
7770 if (r == (Quantum *) NULL)
7773 for (x=0; x < (ssize_t) image->columns; x++)
7779 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7783 if (image->storage_class == PseudoClass && image->colormap != NULL)
7785 for (i=0; i < (ssize_t) image->colors; i++)
7787 LBR02PacketRGBO(image->colormap[i]);
7793 /* Scale to 1-bit */
7794 LBR01PacketRGBO(image->background_color);
7796 for (y=0; y < (ssize_t) image->rows; y++)
7798 r=GetAuthenticPixels(image,0,y,image->columns,1,
7801 if (r == (Quantum *) NULL)
7804 for (x=0; x < (ssize_t) image->columns; x++)
7810 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7814 if (image->storage_class == PseudoClass && image->colormap != NULL)
7816 for (i=0; i < (ssize_t) image->colors; i++)
7818 LBR01PacketRGBO(image->colormap[i]);
7824 /* To do: set to next higher multiple of 8 */
7825 if (image->depth < 8)
7828 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7829 /* PNG does not handle depths greater than 16 so reduce it even
7832 if (image->depth > 8)
7836 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7837 if (image->depth == 16 && mng_info->write_png_depth != 16)
7838 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7842 /* Normally we run this just once, but in the case of writing PNG8
7843 * we reduce the transparency to binary and run again, then if there
7844 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7845 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7846 * palette. Then (To do) we take care of a final reduction that is only
7847 * needed if there are still 256 colors present and one of them has both
7848 * transparent and opaque instances.
7851 tried_332 = MagickFalse;
7852 tried_333 = MagickFalse;
7853 tried_444 = MagickFalse;
7859 * Sometimes we get DirectClass images that have 256 colors or fewer.
7860 * This code will build a colormap.
7862 * Also, sometimes we get PseudoClass images with an out-of-date
7863 * colormap. This code will replace the colormap with a new one.
7864 * Sometimes we get PseudoClass images that have more than 256 colors.
7865 * This code will delete the colormap and change the image to
7868 * If image->matte is MagickFalse, we ignore the alpha channel
7869 * even though it sometimes contains left-over non-opaque values.
7871 * Also we gather some information (number of opaque, transparent,
7872 * and semitransparent pixels, and whether the image has any non-gray
7873 * pixels or only black-and-white pixels) that we might need later.
7875 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7876 * we need to check for bogus non-opaque values, at least.
7887 semitransparent[260],
7890 register const Quantum
7897 if (logging != MagickFalse)
7898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7899 " Enter BUILD_PALETTE:");
7901 if (logging != MagickFalse)
7903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7904 " image->columns=%.20g",(double) image->columns);
7905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7906 " image->rows=%.20g",(double) image->rows);
7907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7908 " image->matte=%.20g",(double) image->matte);
7909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7910 " image->depth=%.20g",(double) image->depth);
7912 if (image->storage_class == PseudoClass && image->colormap != NULL)
7914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7915 " Original colormap:");
7916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7917 " i (red,green,blue,alpha)");
7919 for (i=0; i < 256; i++)
7921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7922 " %d (%d,%d,%d,%d)",
7924 (int) image->colormap[i].red,
7925 (int) image->colormap[i].green,
7926 (int) image->colormap[i].blue,
7927 (int) image->colormap[i].alpha);
7930 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7935 " %d (%d,%d,%d,%d)",
7937 (int) image->colormap[i].red,
7938 (int) image->colormap[i].green,
7939 (int) image->colormap[i].blue,
7940 (int) image->colormap[i].alpha);
7945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7946 " image->colors=%d",(int) image->colors);
7948 if (image->colors == 0)
7949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7950 " (zero means unknown)");
7952 if (ping_preserve_colormap == MagickFalse)
7953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7954 " Regenerate the colormap");
7957 exception=(&image->exception);
7961 number_semitransparent = 0;
7962 number_transparent = 0;
7964 for (y=0; y < (ssize_t) image->rows; y++)
7966 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7968 if (q == (const Quantum *) NULL)
7971 for (x=0; x < (ssize_t) image->columns; x++)
7973 if (image->matte == MagickFalse ||
7974 GetPixelAlpha(image,q) == OpaqueAlpha)
7976 if (number_opaque < 259)
7978 if (number_opaque == 0)
7980 GetPixelPacket(image, q, opaque);
7981 opaque[0].alpha=OpaqueAlpha;
7985 for (i=0; i< (ssize_t) number_opaque; i++)
7987 if (IsPixelEquivalent(image,q, opaque+i))
7991 if (i == (ssize_t) number_opaque &&
7992 number_opaque < 259)
7995 GetPixelPacket(image, q, opaque+i);
7996 opaque[i].alpha=OpaqueAlpha;
8000 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8002 if (number_transparent < 259)
8004 if (number_transparent == 0)
8006 GetPixelPacket(image, q, transparent);
8007 ping_trans_color.red=(unsigned short)
8008 GetPixelRed(image,q);
8009 ping_trans_color.green=(unsigned short)
8010 GetPixelGreen(image,q);
8011 ping_trans_color.blue=(unsigned short)
8012 GetPixelBlue(image,q);
8013 ping_trans_color.gray=(unsigned short)
8014 GetPixelRed(image,q);
8015 number_transparent = 1;
8018 for (i=0; i< (ssize_t) number_transparent; i++)
8020 if (IsPixelEquivalent(image,q, transparent+i))
8024 if (i == (ssize_t) number_transparent &&
8025 number_transparent < 259)
8027 number_transparent++;
8028 GetPixelPacket(image,q,transparent+i);
8034 if (number_semitransparent < 259)
8036 if (number_semitransparent == 0)
8038 GetPixelPacket(image,q,semitransparent);
8039 number_semitransparent = 1;
8042 for (i=0; i< (ssize_t) number_semitransparent; i++)
8044 if (IsPixelEquivalent(image,q, semitransparent+i)
8045 && GetPixelAlpha(image,q) ==
8046 semitransparent[i].alpha)
8050 if (i == (ssize_t) number_semitransparent &&
8051 number_semitransparent < 259)
8053 number_semitransparent++;
8054 GetPixelPacket(image, q, semitransparent+i);
8058 q+=GetPixelChannels(image);
8062 if (ping_exclude_bKGD == MagickFalse)
8064 /* Add the background color to the palette, if it
8065 * isn't already there.
8067 if (logging != MagickFalse)
8069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8070 " Check colormap for background (%d,%d,%d)",
8071 (int) image->background_color.red,
8072 (int) image->background_color.green,
8073 (int) image->background_color.blue);
8075 for (i=0; i<number_opaque; i++)
8077 if (opaque[i].red == image->background_color.red &&
8078 opaque[i].green == image->background_color.green &&
8079 opaque[i].blue == image->background_color.blue)
8082 if (number_opaque < 259 && i == number_opaque)
8084 opaque[i] = image->background_color;
8085 ping_background.index = i;
8086 if (logging != MagickFalse)
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " background_color index is %d",(int) i);
8093 else if (logging != MagickFalse)
8094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8095 " No room in the colormap to add background color");
8098 image_colors=number_opaque+number_transparent+number_semitransparent;
8100 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8102 /* No room for the background color; remove it. */
8107 if (logging != MagickFalse)
8109 if (image_colors > 256)
8110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8111 " image has more than 256 colors");
8114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8115 " image has %d colors",image_colors);
8118 if (ping_preserve_colormap != MagickFalse)
8121 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8123 ping_have_color=MagickFalse;
8124 ping_have_non_bw=MagickFalse;
8126 if(image_colors > 256)
8128 for (y=0; y < (ssize_t) image->rows; y++)
8130 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8132 if (q == (const Quantum *) NULL)
8136 for (x=0; x < (ssize_t) image->columns; x++)
8138 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8139 GetPixelRed(image,s) != GetPixelBlue(image,s))
8141 ping_have_color=MagickTrue;
8142 ping_have_non_bw=MagickTrue;
8145 s+=GetPixelChannels(image);
8148 if (ping_have_color != MagickFalse)
8151 /* Worst case is black-and-white; we are looking at every
8155 if (ping_have_non_bw == MagickFalse)
8158 for (x=0; x < (ssize_t) image->columns; x++)
8160 if (GetPixelRed(image,s) != 0 &&
8161 GetPixelRed(image,s) != QuantumRange)
8163 ping_have_non_bw=MagickTrue;
8166 s+=GetPixelChannels(image);
8173 if (image_colors < 257)
8179 * Initialize image colormap.
8182 if (logging != MagickFalse)
8183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8184 " Sort the new colormap");
8186 /* Sort palette, transparent first */;
8190 for (i=0; i<number_transparent; i++)
8191 colormap[n++] = transparent[i];
8193 for (i=0; i<number_semitransparent; i++)
8194 colormap[n++] = semitransparent[i];
8196 for (i=0; i<number_opaque; i++)
8197 colormap[n++] = opaque[i];
8199 ping_background.index +=
8200 (number_transparent + number_semitransparent);
8202 /* image_colors < 257; search the colormap instead of the pixels
8203 * to get ping_have_color and ping_have_non_bw
8207 if (ping_have_color == MagickFalse)
8209 if (colormap[i].red != colormap[i].green ||
8210 colormap[i].red != colormap[i].blue)
8212 ping_have_color=MagickTrue;
8213 ping_have_non_bw=MagickTrue;
8218 if (ping_have_non_bw == MagickFalse)
8220 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8221 ping_have_non_bw=MagickTrue;
8225 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8226 (number_transparent == 0 && number_semitransparent == 0)) &&
8227 (((mng_info->write_png_colortype-1) ==
8228 PNG_COLOR_TYPE_PALETTE) ||
8229 (mng_info->write_png_colortype == 0)))
8231 if (logging != MagickFalse)
8233 if (n != (ssize_t) image_colors)
8234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8235 " image_colors (%d) and n (%d) don't match",
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " AcquireImageColormap");
8242 image->colors = image_colors;
8244 if (AcquireImageColormap(image,image_colors) ==
8246 ThrowWriterException(ResourceLimitError,
8247 "MemoryAllocationFailed");
8249 for (i=0; i< (ssize_t) image_colors; i++)
8250 image->colormap[i] = colormap[i];
8252 if (logging != MagickFalse)
8254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8255 " image->colors=%d (%d)",
8256 (int) image->colors, image_colors);
8258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8259 " Update the pixel indexes");
8262 /* Sync the pixel indices with the new colormap */
8264 for (y=0; y < (ssize_t) image->rows; y++)
8266 q=GetAuthenticPixels(image,0,y,image->columns,1,
8269 if (q == (const Quantum *) NULL)
8273 for (x=0; x < (ssize_t) image->columns; x++)
8275 for (i=0; i< (ssize_t) image_colors; i++)
8277 if ((image->matte == MagickFalse ||
8278 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8279 image->colormap[i].red == GetPixelRed(image,q) &&
8280 image->colormap[i].green == GetPixelGreen(image,q) &&
8281 image->colormap[i].blue == GetPixelBlue(image,q))
8283 SetPixelIndex(image,i,q);
8287 q+=GetPixelChannels(image);
8290 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8296 if (logging != MagickFalse)
8298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8299 " image->colors=%d", (int) image->colors);
8301 if (image->colormap != NULL)
8303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8304 " i (red,green,blue,alpha)");
8306 for (i=0; i < (ssize_t) image->colors; i++)
8308 if (i < 300 || i >= (ssize_t) image->colors - 10)
8310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8311 " %d (%d,%d,%d,%d)",
8313 (int) image->colormap[i].red,
8314 (int) image->colormap[i].green,
8315 (int) image->colormap[i].blue,
8316 (int) image->colormap[i].alpha);
8321 if (number_transparent < 257)
8322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8323 " number_transparent = %d",
8324 number_transparent);
8327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8328 " number_transparent > 256");
8330 if (number_opaque < 257)
8331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332 " number_opaque = %d",
8336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " number_opaque > 256");
8339 if (number_semitransparent < 257)
8340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8341 " number_semitransparent = %d",
8342 number_semitransparent);
8345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346 " number_semitransparent > 256");
8348 if (ping_have_non_bw == MagickFalse)
8349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8350 " All pixels and the background are black or white");
8352 else if (ping_have_color == MagickFalse)
8353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8354 " All pixels and the background are gray");
8357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " At least one pixel or the background is non-gray");
8360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8361 " Exit BUILD_PALETTE:");
8364 if (mng_info->write_png8 == MagickFalse)
8367 /* Make any reductions necessary for the PNG8 format */
8368 if (image_colors <= 256 &&
8369 image_colors != 0 && image->colormap != NULL &&
8370 number_semitransparent == 0 &&
8371 number_transparent <= 1)
8374 /* PNG8 can't have semitransparent colors so we threshold the
8375 * alpha to 0 or OpaqueAlpha
8377 if (number_semitransparent != 0)
8379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8380 " Thresholding the alpha channel to binary");
8382 for (y=0; y < (ssize_t) image->rows; y++)
8384 r=GetAuthenticPixels(image,0,y,image->columns,1,
8387 if (r == (Quantum *) NULL)
8390 for (x=0; x < (ssize_t) image->columns; x++)
8392 if (GetPixelAlpha(image,r) > TransparentAlpha/2)
8394 SetPixelPacket(image,&image->background_color,r);
8395 SetPixelAlpha(image,TransparentAlpha,r);
8398 SetPixelAlpha(image,OpaqueAlpha,r);
8399 r+=GetPixelChannels(image);
8402 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8405 if (image_colors != 0 && image_colors <= 256 &&
8406 image->colormap != NULL)
8407 for (i=0; i<image_colors; i++)
8408 image->colormap[i].alpha =
8409 (image->colormap[i].alpha > TransparentAlpha/2 ?
8410 TransparentAlpha : OpaqueAlpha);
8415 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8416 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8417 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8420 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8422 if (logging != MagickFalse)
8423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8424 " Quantizing the background color to 4-4-4");
8426 tried_444 = MagickTrue;
8428 LBR04PacketRGB(image->background_color);
8430 if (logging != MagickFalse)
8431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8432 " Quantizing the pixel colors to 4-4-4");
8434 if (image->colormap == NULL)
8436 for (y=0; y < (ssize_t) image->rows; y++)
8438 r=GetAuthenticPixels(image,0,y,image->columns,1,
8441 if (r == (Quantum *) NULL)
8444 for (x=0; x < (ssize_t) image->columns; x++)
8446 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8451 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8456 else /* Should not reach this; colormap already exists and
8459 if (logging != MagickFalse)
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " Quantizing the colormap to 4-4-4");
8463 for (i=0; i<image_colors; i++)
8465 LBR04PacketRGB(image->colormap[i]);
8471 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8473 if (logging != MagickFalse)
8474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8475 " Quantizing the background color to 3-3-3");
8477 tried_333 = MagickTrue;
8479 LBR03PacketRGB(image->background_color);
8481 if (logging != MagickFalse)
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " Quantizing the pixel colors to 3-3-3-1");
8485 if (image->colormap == NULL)
8487 for (y=0; y < (ssize_t) image->rows; y++)
8489 r=GetAuthenticPixels(image,0,y,image->columns,1,
8492 if (r == (Quantum *) NULL)
8495 for (x=0; x < (ssize_t) image->columns; x++)
8497 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8502 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8507 else /* Should not reach this; colormap already exists and
8510 if (logging != MagickFalse)
8511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8512 " Quantizing the colormap to 3-3-3-1");
8513 for (i=0; i<image_colors; i++)
8515 LBR03PacketRGB(image->colormap[i]);
8521 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8523 if (logging != MagickFalse)
8524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525 " Quantizing the background color to 3-3-2");
8527 tried_332 = MagickTrue;
8529 /* Red and green were already done so we only quantize the blue
8533 LBR02PacketBlue(image->background_color);
8535 if (logging != MagickFalse)
8536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8537 " Quantizing the pixel colors to 3-3-2-1");
8539 if (image->colormap == NULL)
8541 for (y=0; y < (ssize_t) image->rows; y++)
8543 r=GetAuthenticPixels(image,0,y,image->columns,1,
8546 if (r == (Quantum *) NULL)
8549 for (x=0; x < (ssize_t) image->columns; x++)
8551 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8556 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8561 else /* Should not reach this; colormap already exists and
8564 if (logging != MagickFalse)
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Quantizing the colormap to 3-3-2-1");
8567 for (i=0; i<image_colors; i++)
8569 LBR02PacketBlue(image->colormap[i]);
8576 if (image_colors == 0 || image_colors > 256)
8578 /* Take care of special case with 256 colors + 1 transparent
8579 * color. We don't need to quantize to 2-3-2-1; we only need to
8580 * eliminate one color, so we'll merge the two darkest red
8581 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8583 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8584 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8585 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8587 image->background_color.red=ScaleCharToQuantum(0x24);
8590 if (image->colormap == NULL)
8592 for (y=0; y < (ssize_t) image->rows; y++)
8594 r=GetAuthenticPixels(image,0,y,image->columns,1,
8597 if (r == (Quantum *) NULL)
8600 for (x=0; x < (ssize_t) image->columns; x++)
8602 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8603 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8604 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8605 GetPixelAlpha(image,r) == OpaqueAlpha)
8607 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8609 r+=GetPixelChannels(image);
8612 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8620 for (i=0; i<image_colors; i++)
8622 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8623 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8624 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8626 image->colormap[i].red=ScaleCharToQuantum(0x24);
8632 /* END OF BUILD_PALETTE */
8634 /* If we are excluding the tRNS chunk and there is transparency,
8635 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8638 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8639 (number_transparent != 0 || number_semitransparent != 0))
8641 unsigned int colortype=mng_info->write_png_colortype;
8643 if (ping_have_color == MagickFalse)
8644 mng_info->write_png_colortype = 5;
8647 mng_info->write_png_colortype = 7;
8649 if (colortype != 0 &&
8650 mng_info->write_png_colortype != colortype)
8651 ping_need_colortype_warning=MagickTrue;
8655 /* See if cheap transparency is possible. It is only possible
8656 * when there is a single transparent color, no semitransparent
8657 * color, and no opaque color that has the same RGB components
8658 * as the transparent color. We only need this information if
8659 * we are writing a PNG with colortype 0 or 2, and we have not
8660 * excluded the tRNS chunk.
8662 if (number_transparent == 1 &&
8663 mng_info->write_png_colortype < 4)
8665 ping_have_cheap_transparency = MagickTrue;
8667 if (number_semitransparent != 0)
8668 ping_have_cheap_transparency = MagickFalse;
8670 else if (image_colors == 0 || image_colors > 256 ||
8671 image->colormap == NULL)
8676 register const Quantum
8679 exception=(&image->exception);
8681 for (y=0; y < (ssize_t) image->rows; y++)
8683 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8685 if (q == (const Quantum *) NULL)
8688 for (x=0; x < (ssize_t) image->columns; x++)
8690 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8691 (unsigned short) GetPixelRed(image,q) ==
8692 ping_trans_color.red &&
8693 (unsigned short) GetPixelGreen(image,q) ==
8694 ping_trans_color.green &&
8695 (unsigned short) GetPixelBlue(image,q) ==
8696 ping_trans_color.blue)
8698 ping_have_cheap_transparency = MagickFalse;
8702 q+=GetPixelChannels(image);
8705 if (ping_have_cheap_transparency == MagickFalse)
8711 /* Assuming that image->colormap[0] is the one transparent color
8712 * and that all others are opaque.
8714 if (image_colors > 1)
8715 for (i=1; i<image_colors; i++)
8716 if (image->colormap[i].red == image->colormap[0].red &&
8717 image->colormap[i].green == image->colormap[0].green &&
8718 image->colormap[i].blue == image->colormap[0].blue)
8720 ping_have_cheap_transparency = MagickFalse;
8725 if (logging != MagickFalse)
8727 if (ping_have_cheap_transparency == MagickFalse)
8728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8729 " Cheap transparency is not possible.");
8732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8733 " Cheap transparency is possible.");
8737 ping_have_cheap_transparency = MagickFalse;
8739 image_depth=image->depth;
8741 quantum_info = (QuantumInfo *) NULL;
8743 image_colors=(int) image->colors;
8744 image_matte=image->matte;
8746 mng_info->IsPalette=image->storage_class == PseudoClass &&
8747 image_colors <= 256 && image->colormap != NULL;
8749 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8750 (image->colors == 0 || image->colormap == NULL))
8752 image_info=DestroyImageInfo(image_info);
8753 image=DestroyImage(image);
8754 (void) ThrowMagickException(&IMimage->exception,
8755 GetMagickModule(),CoderError,
8756 "Cannot write PNG8 or color-type 3; colormap is NULL",
8757 "`%s'",IMimage->filename);
8758 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8759 UnlockSemaphoreInfo(ping_semaphore);
8761 return(MagickFalse);
8765 Allocate the PNG structures
8767 #ifdef PNG_USER_MEM_SUPPORTED
8768 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8769 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8770 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8773 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8774 MagickPNGErrorHandler,MagickPNGWarningHandler);
8777 if (ping == (png_struct *) NULL)
8778 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8780 ping_info=png_create_info_struct(ping);
8782 if (ping_info == (png_info *) NULL)
8784 png_destroy_write_struct(&ping,(png_info **) NULL);
8785 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8788 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8789 ping_pixels=(unsigned char *) NULL;
8791 if (setjmp(png_jmpbuf(ping)))
8797 if (image_info->verbose)
8798 (void) printf("PNG write has failed.\n");
8800 png_destroy_write_struct(&ping,&ping_info);
8801 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8802 UnlockSemaphoreInfo(ping_semaphore);
8804 if (ping_have_blob != MagickFalse)
8805 (void) CloseBlob(image);
8806 image_info=DestroyImageInfo(image_info);
8807 image=DestroyImage(image);
8808 return(MagickFalse);
8811 Prepare PNG for writing.
8813 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8814 if (mng_info->write_mng)
8815 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8818 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8819 if (mng_info->write_mng)
8820 png_permit_empty_plte(ping,MagickTrue);
8827 ping_width=(png_uint_32) image->columns;
8828 ping_height=(png_uint_32) image->rows;
8830 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8833 if (mng_info->write_png_depth != 0)
8834 image_depth=mng_info->write_png_depth;
8836 /* Adjust requested depth to next higher valid depth if necessary */
8837 if (image_depth > 8)
8840 if ((image_depth > 4) && (image_depth < 8))
8843 if (image_depth == 3)
8846 if (logging != MagickFalse)
8848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8849 " width=%.20g",(double) ping_width);
8850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8851 " height=%.20g",(double) ping_height);
8852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8853 " image_matte=%.20g",(double) image->matte);
8854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8855 " image->depth=%.20g",(double) image->depth);
8856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8860 save_image_depth=image_depth;
8861 ping_bit_depth=(png_byte) save_image_depth;
8864 #if defined(PNG_pHYs_SUPPORTED)
8865 if (ping_exclude_pHYs == MagickFalse)
8867 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8868 (!mng_info->write_mng || !mng_info->equal_physs))
8870 if (logging != MagickFalse)
8871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8872 " Setting up pHYs chunk");
8874 if (image->units == PixelsPerInchResolution)
8876 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8877 ping_pHYs_x_resolution=
8878 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8879 ping_pHYs_y_resolution=
8880 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8883 else if (image->units == PixelsPerCentimeterResolution)
8885 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8886 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8887 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8892 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8893 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8894 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8897 if (logging != MagickFalse)
8898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8900 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8901 (int) ping_pHYs_unit_type);
8902 ping_have_pHYs = MagickTrue;
8907 if (ping_exclude_bKGD == MagickFalse)
8909 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8915 if (ping_bit_depth == 8)
8918 if (ping_bit_depth == 4)
8921 if (ping_bit_depth == 2)
8924 if (ping_bit_depth == 1)
8927 ping_background.red=(png_uint_16)
8928 (ScaleQuantumToShort(image->background_color.red) & mask);
8930 ping_background.green=(png_uint_16)
8931 (ScaleQuantumToShort(image->background_color.green) & mask);
8933 ping_background.blue=(png_uint_16)
8934 (ScaleQuantumToShort(image->background_color.blue) & mask);
8936 ping_background.gray=(png_uint_16) ping_background.green;
8939 if (logging != MagickFalse)
8941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " Setting up bKGD chunk (1)");
8943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944 " background_color index is %d",
8945 (int) ping_background.index);
8947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8948 " ping_bit_depth=%d",ping_bit_depth);
8951 ping_have_bKGD = MagickTrue;
8955 Select the color type.
8960 if (mng_info->IsPalette && mng_info->write_png8)
8963 /* To do: make this a function cause it's used twice, except
8964 for reducing the sample depth from 8. */
8966 number_colors=image_colors;
8968 ping_have_tRNS=MagickFalse;
8973 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8975 if (logging != MagickFalse)
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " Setting up PLTE chunk with %d colors (%d)",
8978 number_colors, image_colors);
8980 for (i=0; i < (ssize_t) number_colors; i++)
8982 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8983 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8984 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8985 if (logging != MagickFalse)
8986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8987 #if MAGICKCORE_QUANTUM_DEPTH == 8
8988 " %3ld (%3d,%3d,%3d)",
8990 " %5ld (%5d,%5d,%5d)",
8992 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8996 ping_have_PLTE=MagickTrue;
8997 image_depth=ping_bit_depth;
9000 if (matte != MagickFalse)
9003 Identify which colormap entry is transparent.
9005 assert(number_colors <= 256);
9006 assert(image->colormap != NULL);
9008 for (i=0; i < (ssize_t) number_transparent; i++)
9009 ping_trans_alpha[i]=0;
9012 ping_num_trans=(unsigned short) (number_transparent +
9013 number_semitransparent);
9015 if (ping_num_trans == 0)
9016 ping_have_tRNS=MagickFalse;
9019 ping_have_tRNS=MagickTrue;
9022 if (ping_exclude_bKGD == MagickFalse)
9025 * Identify which colormap entry is the background color.
9028 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9029 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9032 ping_background.index=(png_byte) i;
9034 if (logging != MagickFalse)
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " background_color index is %d",
9038 (int) ping_background.index);
9041 } /* end of write_png8 */
9043 else if (mng_info->write_png24)
9045 image_matte=MagickFalse;
9046 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9049 else if (mng_info->write_png32)
9051 image_matte=MagickTrue;
9052 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9055 else /* mng_info->write_pngNN not specified */
9057 image_depth=ping_bit_depth;
9059 if (mng_info->write_png_colortype != 0)
9061 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9063 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9064 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9065 image_matte=MagickTrue;
9068 image_matte=MagickFalse;
9070 if (logging != MagickFalse)
9071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9072 " PNG colortype %d was specified:",(int) ping_color_type);
9075 else /* write_png_colortype not specified */
9077 if (logging != MagickFalse)
9078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9079 " Selecting PNG colortype:");
9081 ping_color_type=(png_byte) ((matte != MagickFalse)?
9082 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9084 if (image_info->type == TrueColorType)
9086 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9087 image_matte=MagickFalse;
9090 if (image_info->type == TrueColorMatteType)
9092 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9093 image_matte=MagickTrue;
9096 if (image_info->type == PaletteType ||
9097 image_info->type == PaletteMatteType)
9098 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9100 if (mng_info->write_png_colortype == 0 &&
9101 (image_info->type == UndefinedType ||
9102 image_info->type == OptimizeType))
9104 if (ping_have_color == MagickFalse)
9106 if (image_matte == MagickFalse)
9108 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9109 image_matte=MagickFalse;
9114 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9115 image_matte=MagickTrue;
9120 if (image_matte == MagickFalse)
9122 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9123 image_matte=MagickFalse;
9128 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9129 image_matte=MagickTrue;
9136 if (logging != MagickFalse)
9137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9138 " Selected PNG colortype=%d",ping_color_type);
9140 if (ping_bit_depth < 8)
9142 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9143 ping_color_type == PNG_COLOR_TYPE_RGB ||
9144 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9148 old_bit_depth=ping_bit_depth;
9150 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9152 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9156 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9161 if (image->colors == 0)
9164 (void) ThrowMagickException(&image->exception,
9165 GetMagickModule(),CoderError,
9166 "image has 0 colors", "`%s'","");
9169 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9170 ping_bit_depth <<= 1;
9173 if (logging != MagickFalse)
9175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9176 " Number of colors: %.20g",(double) image_colors);
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Tentative PNG bit depth: %d",ping_bit_depth);
9182 if (ping_bit_depth < (int) mng_info->write_png_depth)
9183 ping_bit_depth = mng_info->write_png_depth;
9186 image_depth=ping_bit_depth;
9188 if (logging != MagickFalse)
9190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9191 " Tentative PNG color type: %.20g",(double) ping_color_type);
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194 " image_info->type: %.20g",(double) image_info->type);
9196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197 " image_depth: %.20g",(double) image_depth);
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201 " image->depth: %.20g",(double) image->depth);
9203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9204 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9207 if (matte != MagickFalse)
9209 if (mng_info->IsPalette)
9211 if (mng_info->write_png_colortype == 0)
9213 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9215 if (ping_have_color != MagickFalse)
9216 ping_color_type=PNG_COLOR_TYPE_RGBA;
9220 * Determine if there is any transparent color.
9222 if (number_transparent + number_semitransparent == 0)
9225 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9228 image_matte=MagickFalse;
9230 if (mng_info->write_png_colortype == 0)
9231 ping_color_type&=0x03;
9241 if (ping_bit_depth == 8)
9244 if (ping_bit_depth == 4)
9247 if (ping_bit_depth == 2)
9250 if (ping_bit_depth == 1)
9253 ping_trans_color.red=(png_uint_16)
9254 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9256 ping_trans_color.green=(png_uint_16)
9257 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9259 ping_trans_color.blue=(png_uint_16)
9260 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9262 ping_trans_color.gray=(png_uint_16)
9263 (ScaleQuantumToShort(GetPixelPacketIntensity(
9264 image->colormap)) & mask);
9266 ping_trans_color.index=(png_byte) 0;
9268 ping_have_tRNS=MagickTrue;
9271 if (ping_have_tRNS != MagickFalse)
9274 * Determine if there is one and only one transparent color
9275 * and if so if it is fully transparent.
9277 if (ping_have_cheap_transparency == MagickFalse)
9278 ping_have_tRNS=MagickFalse;
9281 if (ping_have_tRNS != MagickFalse)
9283 if (mng_info->write_png_colortype == 0)
9284 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9286 if (image_depth == 8)
9288 ping_trans_color.red&=0xff;
9289 ping_trans_color.green&=0xff;
9290 ping_trans_color.blue&=0xff;
9291 ping_trans_color.gray&=0xff;
9297 if (image_depth == 8)
9299 ping_trans_color.red&=0xff;
9300 ping_trans_color.green&=0xff;
9301 ping_trans_color.blue&=0xff;
9302 ping_trans_color.gray&=0xff;
9309 if (ping_have_tRNS != MagickFalse)
9310 image_matte=MagickFalse;
9312 if ((mng_info->IsPalette) &&
9313 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9314 ping_have_color == MagickFalse &&
9315 (image_matte == MagickFalse || image_depth >= 8))
9319 if (image_matte != MagickFalse)
9320 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9322 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9324 ping_color_type=PNG_COLOR_TYPE_GRAY;
9326 if (save_image_depth == 16 && image_depth == 8)
9328 if (logging != MagickFalse)
9330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331 " Scaling ping_trans_color (0)");
9333 ping_trans_color.gray*=0x0101;
9337 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9338 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9340 if ((image_colors == 0) ||
9341 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9342 image_colors=(int) (one << image_depth);
9344 if (image_depth > 8)
9350 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9352 if(!mng_info->write_png_depth)
9356 while ((int) (one << ping_bit_depth)
9357 < (ssize_t) image_colors)
9358 ping_bit_depth <<= 1;
9362 else if (ping_color_type ==
9363 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9364 mng_info->IsPalette)
9366 /* Check if grayscale is reducible */
9369 depth_4_ok=MagickTrue,
9370 depth_2_ok=MagickTrue,
9371 depth_1_ok=MagickTrue;
9373 for (i=0; i < (ssize_t) image_colors; i++)
9378 intensity=ScaleQuantumToChar(image->colormap[i].red);
9380 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9381 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9382 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9383 depth_2_ok=depth_1_ok=MagickFalse;
9384 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9385 depth_1_ok=MagickFalse;
9388 if (depth_1_ok && mng_info->write_png_depth <= 1)
9391 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9394 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9399 image_depth=ping_bit_depth;
9404 if (mng_info->IsPalette)
9406 number_colors=image_colors;
9408 if (image_depth <= 8)
9413 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9415 if (mng_info->have_write_global_plte && matte == MagickFalse)
9417 png_set_PLTE(ping,ping_info,NULL,0);
9419 if (logging != MagickFalse)
9420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9421 " Setting up empty PLTE chunk");
9426 for (i=0; i < (ssize_t) number_colors; i++)
9428 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9429 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9430 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9433 if (logging != MagickFalse)
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435 " Setting up PLTE chunk with %d colors",
9438 ping_have_PLTE=MagickTrue;
9441 /* color_type is PNG_COLOR_TYPE_PALETTE */
9442 if (mng_info->write_png_depth == 0)
9450 while ((one << ping_bit_depth) < (ssize_t) number_colors)
9451 ping_bit_depth <<= 1;
9456 if (matte != MagickFalse)
9459 * Set up trans_colors array.
9461 assert(number_colors <= 256);
9463 ping_num_trans=(unsigned short) (number_transparent +
9464 number_semitransparent);
9466 if (ping_num_trans == 0)
9467 ping_have_tRNS=MagickFalse;
9471 if (logging != MagickFalse)
9473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9474 " Scaling ping_trans_color (1)");
9476 ping_have_tRNS=MagickTrue;
9478 for (i=0; i < ping_num_trans; i++)
9480 ping_trans_alpha[i]= (png_byte)
9481 ScaleQuantumToChar(image->colormap[i].alpha);
9491 if (image_depth < 8)
9494 if ((save_image_depth == 16) && (image_depth == 8))
9496 if (logging != MagickFalse)
9498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9499 " Scaling ping_trans_color from (%d,%d,%d)",
9500 (int) ping_trans_color.red,
9501 (int) ping_trans_color.green,
9502 (int) ping_trans_color.blue);
9505 ping_trans_color.red*=0x0101;
9506 ping_trans_color.green*=0x0101;
9507 ping_trans_color.blue*=0x0101;
9508 ping_trans_color.gray*=0x0101;
9510 if (logging != MagickFalse)
9512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9514 (int) ping_trans_color.red,
9515 (int) ping_trans_color.green,
9516 (int) ping_trans_color.blue);
9521 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9522 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9525 Adjust background and transparency samples in sub-8-bit grayscale files.
9527 if (ping_bit_depth < 8 && ping_color_type ==
9528 PNG_COLOR_TYPE_GRAY)
9536 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9538 if (ping_exclude_bKGD == MagickFalse)
9541 ping_background.gray=(png_uint_16)
9542 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9545 if (logging != MagickFalse)
9546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9547 " Setting up bKGD chunk (2)");
9548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9549 " background_color index is %d",
9550 (int) ping_background.index);
9552 ping_have_bKGD = MagickTrue;
9555 if (logging != MagickFalse)
9556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9557 " Scaling ping_trans_color.gray from %d",
9558 (int)ping_trans_color.gray);
9560 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9561 ping_trans_color.gray)+.5);
9563 if (logging != MagickFalse)
9564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9565 " to %d", (int)ping_trans_color.gray);
9568 if (ping_exclude_bKGD == MagickFalse)
9570 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9573 Identify which colormap entry is the background color.
9576 number_colors=image_colors;
9578 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9579 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9582 ping_background.index=(png_byte) i;
9584 if (logging != MagickFalse)
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " Setting up bKGD chunk with index=%d",(int) i);
9590 if (i < (ssize_t) number_colors)
9592 ping_have_bKGD = MagickTrue;
9594 if (logging != MagickFalse)
9596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9597 " background =(%d,%d,%d)",
9598 (int) ping_background.red,
9599 (int) ping_background.green,
9600 (int) ping_background.blue);
9604 else /* Can't happen */
9606 if (logging != MagickFalse)
9607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9608 " No room in PLTE to add bKGD color");
9609 ping_have_bKGD = MagickFalse;
9614 if (logging != MagickFalse)
9615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9616 " PNG color type: %d",ping_color_type);
9618 Initialize compression level and filtering.
9620 if (logging != MagickFalse)
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Setting up deflate compression");
9625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9626 " Compression buffer size: 32768");
9629 png_set_compression_buffer_size(ping,32768L);
9631 if (logging != MagickFalse)
9632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9633 " Compression mem level: 9");
9635 /* Untangle the "-quality" setting:
9637 Undefined is 0; the default is used.
9642 0: Use Z_HUFFMAN_ONLY strategy with the
9643 zlib default compression level
9645 1-9: the zlib compression level
9649 0-4: the PNG filter method
9651 5: libpng adaptive filtering if compression level > 5
9652 libpng filter type "none" if compression level <= 5
9653 or if image is grayscale or palette
9655 6: libpng adaptive filtering
9657 7: "LOCO" filtering (intrapixel differing) if writing
9658 a MNG, othewise "none". Did not work in IM-6.7.0-9
9659 and earlier because of a missing "else".
9661 8: Z_RLE strategy, all filters
9662 Unused prior to IM-6.7.0-10, was same as 6
9664 9: Z_RLE strategy, no PNG filters
9665 Unused prior to IM-6.7.0-10, was same as 6
9667 Note that using the -quality option, not all combinations of
9668 PNG filter type, zlib compression level, and zlib compression
9669 strategy are possible. This will be addressed soon in a
9670 release that accomodates "-define PNG:compression-strategy",
9675 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9680 if (mng_info->write_png_compression_strategy == 0)
9681 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9684 else if (mng_info->write_png_compression_level == 0)
9689 level=(int) MagickMin((ssize_t) quality/10,9);
9691 mng_info->write_png_compression_level = level+1;
9694 if (mng_info->write_png_compression_strategy == 0)
9696 if ((quality %10) == 8 || (quality %10) == 9)
9697 mng_info->write_png_compression_strategy=Z_RLE;
9700 if (mng_info->write_png_compression_filter == 0)
9701 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9703 if (logging != MagickFalse)
9705 if (mng_info->write_png_compression_level)
9706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9707 " Compression level: %d",
9708 (int) mng_info->write_png_compression_level-1);
9710 if (mng_info->write_png_compression_strategy)
9711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712 " Compression strategy: %d",
9713 (int) mng_info->write_png_compression_strategy-1);
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 " Setting up filtering");
9718 if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
9719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720 " Base filter method: ADAPTIVE");
9721 else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " Base filter method: NONE");
9725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9726 " Base filter method: %d",
9727 (int) mng_info->write_png_compression_filter-1);
9730 if (mng_info->write_png_compression_level != 0)
9731 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9733 if (mng_info->write_png_compression_filter == 6)
9735 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9736 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9738 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9740 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9743 if (mng_info->write_png_compression_filter == 7 ||
9744 mng_info->write_png_compression_filter == 10)
9745 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9747 else if (mng_info->write_png_compression_filter == 8)
9749 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9750 if (mng_info->write_mng)
9752 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9753 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9754 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9757 png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9760 else if (mng_info->write_png_compression_filter == 9)
9761 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9763 else if (mng_info->write_png_compression_filter != 0)
9764 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9765 mng_info->write_png_compression_filter-1);
9767 if (mng_info->write_png_compression_strategy != 0)
9768 png_set_compression_strategy(ping,
9769 mng_info->write_png_compression_strategy-1);
9772 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9773 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9775 ResetImageProfileIterator(image);
9776 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9778 profile=GetImageProfile(image,name);
9780 if (profile != (StringInfo *) NULL)
9782 #ifdef PNG_WRITE_iCCP_SUPPORTED
9783 if ((LocaleCompare(name,"ICC") == 0) ||
9784 (LocaleCompare(name,"ICM") == 0))
9787 if (ping_exclude_iCCP == MagickFalse)
9789 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9790 #if (PNG_LIBPNG_VER < 10500)
9791 (png_charp) GetStringInfoDatum(profile),
9793 (png_const_bytep) GetStringInfoDatum(profile),
9795 (png_uint_32) GetStringInfoLength(profile));
9801 if (ping_exclude_zCCP == MagickFalse)
9803 Magick_png_write_raw_profile(image_info,ping,ping_info,
9804 (unsigned char *) name,(unsigned char *) name,
9805 GetStringInfoDatum(profile),
9806 (png_uint_32) GetStringInfoLength(profile));
9810 if (logging != MagickFalse)
9811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9812 " Setting up text chunk with %s profile",name);
9814 name=GetNextImageProfile(image);
9818 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9819 if ((mng_info->have_write_global_srgb == 0) &&
9820 ((image->rendering_intent != UndefinedIntent) ||
9821 (image->colorspace == sRGBColorspace)))
9823 if (ping_exclude_sRGB == MagickFalse)
9826 Note image rendering intent.
9828 if (logging != MagickFalse)
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Setting up sRGB chunk");
9832 (void) png_set_sRGB(ping,ping_info,(
9833 Magick_RenderingIntent_to_PNG_RenderingIntent(
9834 image->rendering_intent)));
9836 if (ping_exclude_gAMA == MagickFalse)
9837 png_set_gAMA(ping,ping_info,0.45455);
9841 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9844 if (ping_exclude_gAMA == MagickFalse &&
9845 (ping_exclude_sRGB == MagickFalse ||
9846 (image->gamma < .45 || image->gamma > .46)))
9848 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9852 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9854 if (logging != MagickFalse)
9855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9856 " Setting up gAMA chunk");
9858 png_set_gAMA(ping,ping_info,image->gamma);
9862 if (ping_exclude_cHRM == MagickFalse)
9864 if ((mng_info->have_write_global_chrm == 0) &&
9865 (image->chromaticity.red_primary.x != 0.0))
9868 Note image chromaticity.
9869 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9877 wp=image->chromaticity.white_point;
9878 rp=image->chromaticity.red_primary;
9879 gp=image->chromaticity.green_primary;
9880 bp=image->chromaticity.blue_primary;
9882 if (logging != MagickFalse)
9883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9884 " Setting up cHRM chunk");
9886 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9892 ping_interlace_method=image_info->interlace != NoInterlace;
9894 if (mng_info->write_mng)
9895 png_set_sig_bytes(ping,8);
9897 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9899 if (mng_info->write_png_colortype != 0)
9901 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9902 if (ping_have_color != MagickFalse)
9904 ping_color_type = PNG_COLOR_TYPE_RGB;
9906 if (ping_bit_depth < 8)
9910 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9911 if (ping_have_color != MagickFalse)
9912 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9915 if (ping_need_colortype_warning != MagickFalse ||
9916 ((mng_info->write_png_depth &&
9917 (int) mng_info->write_png_depth != ping_bit_depth) ||
9918 (mng_info->write_png_colortype &&
9919 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9920 mng_info->write_png_colortype != 7 &&
9921 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9923 if (logging != MagickFalse)
9925 if (ping_need_colortype_warning != MagickFalse)
9927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928 " Image has transparency but tRNS chunk was excluded");
9931 if (mng_info->write_png_depth)
9933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9934 " Defined PNG:bit-depth=%u, Computed depth=%u",
9935 mng_info->write_png_depth,
9939 if (mng_info->write_png_colortype)
9941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " Defined PNG:color-type=%u, Computed color type=%u",
9943 mng_info->write_png_colortype-1,
9949 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9952 if (image_matte != MagickFalse && image->matte == MagickFalse)
9954 /* Add an opaque matte channel */
9955 image->matte = MagickTrue;
9956 (void) SetImageOpacity(image,0);
9958 if (logging != MagickFalse)
9959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9960 " Added an opaque matte channel");
9963 if (number_transparent != 0 || number_semitransparent != 0)
9965 if (ping_color_type < 4)
9967 ping_have_tRNS=MagickTrue;
9968 if (logging != MagickFalse)
9969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9970 " Setting ping_have_tRNS=MagickTrue.");
9974 if (logging != MagickFalse)
9975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9976 " Writing PNG header chunks");
9978 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9979 ping_bit_depth,ping_color_type,
9980 ping_interlace_method,ping_compression_method,
9981 ping_filter_method);
9983 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9985 png_set_PLTE(ping,ping_info,palette,number_colors);
9987 if (logging != MagickFalse)
9989 for (i=0; i< (ssize_t) number_colors; i++)
9991 if (i < ping_num_trans)
9992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9995 (int) palette[i].red,
9996 (int) palette[i].green,
9997 (int) palette[i].blue,
9999 (int) ping_trans_alpha[i]);
10001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10002 " PLTE[%d] = (%d,%d,%d)",
10004 (int) palette[i].red,
10005 (int) palette[i].green,
10006 (int) palette[i].blue);
10011 if (ping_exclude_bKGD == MagickFalse)
10013 if (ping_have_bKGD != MagickFalse)
10015 png_set_bKGD(ping,ping_info,&ping_background);
10018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019 " Setting up bKGD chunk");
10020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10021 " background color = (%d,%d,%d)",
10022 (int) ping_background.red,
10023 (int) ping_background.green,
10024 (int) ping_background.blue);
10025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10026 " index = %d, gray=%d",
10027 (int) ping_background.index,
10028 (int) ping_background.gray);
10033 if (ping_exclude_pHYs == MagickFalse)
10035 if (ping_have_pHYs != MagickFalse)
10037 png_set_pHYs(ping,ping_info,
10038 ping_pHYs_x_resolution,
10039 ping_pHYs_y_resolution,
10040 ping_pHYs_unit_type);
10044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10045 " Setting up pHYs chunk");
10046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10047 " x_resolution=%lu",
10048 (unsigned long) ping_pHYs_x_resolution);
10049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050 " y_resolution=%lu",
10051 (unsigned long) ping_pHYs_y_resolution);
10052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10054 (unsigned long) ping_pHYs_unit_type);
10059 #if defined(PNG_oFFs_SUPPORTED)
10060 if (ping_exclude_oFFs == MagickFalse)
10062 if (image->page.x || image->page.y)
10064 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10065 (png_int_32) image->page.y, 0);
10067 if (logging != MagickFalse)
10068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10069 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10070 (int) image->page.x, (int) image->page.y);
10075 if (mng_info->need_blob != MagickFalse)
10077 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10079 png_error(ping,"WriteBlob Failed");
10081 ping_have_blob=MagickTrue;
10084 png_write_info_before_PLTE(ping, ping_info);
10086 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10088 if (logging != MagickFalse)
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10094 if (ping_color_type == 3)
10095 (void) png_set_tRNS(ping, ping_info,
10102 (void) png_set_tRNS(ping, ping_info,
10105 &ping_trans_color);
10107 if (logging != MagickFalse)
10109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10110 " tRNS color =(%d,%d,%d)",
10111 (int) ping_trans_color.red,
10112 (int) ping_trans_color.green,
10113 (int) ping_trans_color.blue);
10118 /* write any png-chunk-b profiles */
10119 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10121 png_write_info(ping,ping_info);
10123 /* write any PNG-chunk-m profiles */
10124 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10126 if (ping_exclude_vpAg == MagickFalse)
10128 if ((image->page.width != 0 && image->page.width != image->columns) ||
10129 (image->page.height != 0 && image->page.height != image->rows))
10134 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10135 PNGType(chunk,mng_vpAg);
10136 LogPNGChunk(logging,mng_vpAg,9L);
10137 PNGLong(chunk+4,(png_uint_32) image->page.width);
10138 PNGLong(chunk+8,(png_uint_32) image->page.height);
10139 chunk[12]=0; /* unit = pixels */
10140 (void) WriteBlob(image,13,chunk);
10141 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10145 #if (PNG_LIBPNG_VER == 10206)
10146 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10147 #define PNG_HAVE_IDAT 0x04
10148 ping->mode |= PNG_HAVE_IDAT;
10149 #undef PNG_HAVE_IDAT
10152 png_set_packing(ping);
10156 rowbytes=image->columns;
10157 if (image_depth > 8)
10159 switch (ping_color_type)
10161 case PNG_COLOR_TYPE_RGB:
10165 case PNG_COLOR_TYPE_GRAY_ALPHA:
10169 case PNG_COLOR_TYPE_RGBA:
10177 if (logging != MagickFalse)
10179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10180 " Writing PNG image data");
10182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10183 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10185 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10186 sizeof(*ping_pixels));
10188 if (ping_pixels == (unsigned char *) NULL)
10189 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10192 Initialize image scanlines.
10194 if (setjmp(png_jmpbuf(ping)))
10200 if (image_info->verbose)
10201 (void) printf("PNG write has failed.\n");
10203 png_destroy_write_struct(&ping,&ping_info);
10204 if (quantum_info != (QuantumInfo *) NULL)
10205 quantum_info=DestroyQuantumInfo(quantum_info);
10206 if (ping_pixels != (unsigned char *) NULL)
10207 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10208 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10209 UnlockSemaphoreInfo(ping_semaphore);
10211 if (ping_have_blob != MagickFalse)
10212 (void) CloseBlob(image);
10213 image_info=DestroyImageInfo(image_info);
10214 image=DestroyImage(image);
10215 return(MagickFalse);
10217 quantum_info=AcquireQuantumInfo(image_info,image);
10218 if (quantum_info == (QuantumInfo *) NULL)
10219 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10220 quantum_info->format=UndefinedQuantumFormat;
10221 quantum_info->depth=image_depth;
10222 num_passes=png_set_interlace_handling(ping);
10224 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10225 !mng_info->write_png32) &&
10226 (mng_info->IsPalette ||
10227 (image_info->type == BilevelType)) &&
10228 image_matte == MagickFalse &&
10229 ping_have_non_bw == MagickFalse)
10231 /* Palette, Bilevel, or Opaque Monochrome */
10232 register const Quantum
10235 quantum_info->depth=8;
10236 for (pass=0; pass < num_passes; pass++)
10239 Convert PseudoClass image to a PNG monochrome image.
10241 for (y=0; y < (ssize_t) image->rows; y++)
10243 if (logging != MagickFalse && y == 0)
10244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10245 " Writing row of pixels (0)");
10247 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10249 if (p == (const Quantum *) NULL)
10252 if (mng_info->IsPalette)
10254 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10255 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10256 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10257 mng_info->write_png_depth &&
10258 mng_info->write_png_depth != old_bit_depth)
10260 /* Undo pixel scaling */
10261 for (i=0; i < (ssize_t) image->columns; i++)
10262 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10263 >> (8-old_bit_depth));
10269 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10270 quantum_info,RedQuantum,ping_pixels,&image->exception);
10273 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10274 for (i=0; i < (ssize_t) image->columns; i++)
10275 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10278 if (logging != MagickFalse && y == 0)
10279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10280 " Writing row of pixels (1)");
10282 png_write_row(ping,ping_pixels);
10284 if (image->previous == (Image *) NULL)
10286 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10287 if (status == MagickFalse)
10293 else /* Not Palette, Bilevel, or Opaque Monochrome */
10295 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10296 !mng_info->write_png32) &&
10297 (image_matte != MagickFalse ||
10298 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10299 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10301 register const Quantum
10304 for (pass=0; pass < num_passes; pass++)
10307 for (y=0; y < (ssize_t) image->rows; y++)
10309 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10311 if (p == (const Quantum *) NULL)
10314 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10316 if (mng_info->IsPalette)
10317 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10318 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10321 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10322 quantum_info,RedQuantum,ping_pixels,&image->exception);
10324 if (logging != MagickFalse && y == 0)
10325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10326 " Writing GRAY PNG pixels (2)");
10329 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10331 if (logging != MagickFalse && y == 0)
10332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10333 " Writing GRAY_ALPHA PNG pixels (2)");
10335 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10336 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
10339 if (logging != MagickFalse && y == 0)
10340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10341 " Writing row of pixels (2)");
10343 png_write_row(ping,ping_pixels);
10346 if (image->previous == (Image *) NULL)
10348 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10349 if (status == MagickFalse)
10357 register const Quantum
10360 for (pass=0; pass < num_passes; pass++)
10362 if ((image_depth > 8) || (mng_info->write_png24 ||
10363 mng_info->write_png32 ||
10364 (!mng_info->write_png8 && !mng_info->IsPalette)))
10366 for (y=0; y < (ssize_t) image->rows; y++)
10368 p=GetVirtualPixels(image,0,y,image->columns,1,
10369 &image->exception);
10371 if (p == (const Quantum *) NULL)
10374 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10376 if (image->storage_class == DirectClass)
10377 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10378 quantum_info,RedQuantum,ping_pixels,&image->exception);
10381 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10382 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10385 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10387 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10388 quantum_info,GrayAlphaQuantum,ping_pixels,
10389 &image->exception);
10391 if (logging != MagickFalse && y == 0)
10392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10393 " Writing GRAY_ALPHA PNG pixels (3)");
10396 else if (image_matte != MagickFalse)
10397 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10398 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
10401 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10402 quantum_info,RGBQuantum,ping_pixels,&image->exception);
10404 if (logging != MagickFalse && y == 0)
10405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10406 " Writing row of pixels (3)");
10408 png_write_row(ping,ping_pixels);
10413 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10414 mng_info->write_png32 ||
10415 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10417 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10418 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10420 if (logging != MagickFalse)
10421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10422 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10424 quantum_info->depth=8;
10428 for (y=0; y < (ssize_t) image->rows; y++)
10430 if (logging != MagickFalse && y == 0)
10431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10432 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10434 p=GetVirtualPixels(image,0,y,image->columns,1,
10435 &image->exception);
10437 if (p == (const Quantum *) NULL)
10440 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10442 quantum_info->depth=image->depth;
10444 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10445 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10448 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10450 if (logging != MagickFalse && y == 0)
10451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452 " Writing GRAY_ALPHA PNG pixels (4)");
10454 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10455 quantum_info,GrayAlphaQuantum,ping_pixels,
10456 &image->exception);
10461 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10462 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10464 if (logging != MagickFalse && y <= 2)
10466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10467 " Writing row of non-gray pixels (4)");
10469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10470 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10471 (int)ping_pixels[0],(int)ping_pixels[1]);
10474 png_write_row(ping,ping_pixels);
10478 if (image->previous == (Image *) NULL)
10480 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10481 if (status == MagickFalse)
10488 if (quantum_info != (QuantumInfo *) NULL)
10489 quantum_info=DestroyQuantumInfo(quantum_info);
10491 if (logging != MagickFalse)
10493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10494 " Wrote PNG image data");
10496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10497 " Width: %.20g",(double) ping_width);
10499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10500 " Height: %.20g",(double) ping_height);
10502 if (mng_info->write_png_depth)
10504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10505 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10509 " PNG bit-depth written: %d",ping_bit_depth);
10511 if (mng_info->write_png_colortype)
10513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10518 " PNG color-type written: %d",ping_color_type);
10520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10521 " PNG Interlace method: %d",ping_interlace_method);
10524 Generate text chunks after IDAT.
10526 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10528 ResetImagePropertyIterator(image);
10529 property=GetNextImageProperty(image);
10530 while (property != (const char *) NULL)
10535 value=GetImageProperty(image,property);
10537 /* Don't write any "png:" properties; those are just for "identify" */
10538 if (LocaleNCompare(property,"png:",4) != 0 &&
10540 /* Suppress density and units if we wrote a pHYs chunk */
10541 (ping_exclude_pHYs != MagickFalse ||
10542 LocaleCompare(property,"density") != 0 ||
10543 LocaleCompare(property,"units") != 0) &&
10545 /* Suppress the IM-generated Date:create and Date:modify */
10546 (ping_exclude_date == MagickFalse ||
10547 LocaleNCompare(property, "Date:",5) != 0))
10549 if (value != (const char *) NULL)
10551 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10552 text[0].key=(char *) property;
10553 text[0].text=(char *) value;
10554 text[0].text_length=strlen(value);
10556 if (ping_exclude_tEXt != MagickFalse)
10557 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10559 else if (ping_exclude_zTXt != MagickFalse)
10560 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10564 text[0].compression=image_info->compression == NoCompression ||
10565 (image_info->compression == UndefinedCompression &&
10566 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10567 PNG_TEXT_COMPRESSION_zTXt ;
10570 if (logging != MagickFalse)
10572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10573 " Setting up text chunk");
10575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576 " keyword: %s",text[0].key);
10579 png_set_text(ping,ping_info,text,1);
10580 png_free(ping,text);
10583 property=GetNextImageProperty(image);
10587 /* write any PNG-chunk-e profiles */
10588 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10590 if (logging != MagickFalse)
10591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592 " Writing PNG end info");
10594 png_write_end(ping,ping_info);
10596 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10598 if (mng_info->page.x || mng_info->page.y ||
10599 (ping_width != mng_info->page.width) ||
10600 (ping_height != mng_info->page.height))
10606 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10608 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10609 PNGType(chunk,mng_FRAM);
10610 LogPNGChunk(logging,mng_FRAM,27L);
10612 chunk[5]=0; /* frame name separator (no name) */
10613 chunk[6]=1; /* flag for changing delay, for next frame only */
10614 chunk[7]=0; /* flag for changing frame timeout */
10615 chunk[8]=1; /* flag for changing frame clipping for next frame */
10616 chunk[9]=0; /* flag for changing frame sync_id */
10617 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10618 chunk[14]=0; /* clipping boundaries delta type */
10619 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10621 (png_uint_32) (mng_info->page.x + ping_width));
10622 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10624 (png_uint_32) (mng_info->page.y + ping_height));
10625 (void) WriteBlob(image,31,chunk);
10626 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10627 mng_info->old_framing_mode=4;
10628 mng_info->framing_mode=1;
10632 mng_info->framing_mode=3;
10634 if (mng_info->write_mng && !mng_info->need_fram &&
10635 ((int) image->dispose == 3))
10636 (void) ThrowMagickException(&image->exception,GetMagickModule(),
10637 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10638 "`%s'",image->filename);
10641 Free PNG resources.
10644 png_destroy_write_struct(&ping,&ping_info);
10646 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10648 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10649 UnlockSemaphoreInfo(ping_semaphore);
10652 if (ping_have_blob != MagickFalse)
10653 (void) CloseBlob(image);
10655 image_info=DestroyImageInfo(image_info);
10656 image=DestroyImage(image);
10658 /* Store bit depth actually written */
10659 s[0]=(char) ping_bit_depth;
10662 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10664 if (logging != MagickFalse)
10665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10666 " exit WriteOnePNGImage()");
10668 return(MagickTrue);
10669 /* End write one PNG image */
10673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10677 % W r i t e P N G I m a g e %
10681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10683 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10684 % Multiple-image Network Graphics (MNG) image file.
10686 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10688 % The format of the WritePNGImage method is:
10690 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10692 % A description of each parameter follows:
10694 % o image_info: the image info.
10696 % o image: The image.
10698 % Returns MagickTrue on success, MagickFalse on failure.
10700 % Communicating with the PNG encoder:
10702 % While the datastream written is always in PNG format and normally would
10703 % be given the "png" file extension, this method also writes the following
10704 % pseudo-formats which are subsets of PNG:
10706 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10707 % a depth greater than 8, the depth is reduced. If transparency
10708 % is present, the tRNS chunk must only have values 0 and 255
10709 % (i.e., transparency is binary: fully opaque or fully
10710 % transparent). If other values are present they will be
10711 % 50%-thresholded to binary transparency. If more than 256
10712 % colors are present, they will be quantized to the 4-4-4-1,
10713 % 3-3-3-1, or 3-3-2-1 palette.
10715 % If you want better quantization or dithering of the colors
10716 % or alpha than that, you need to do it before calling the
10717 % PNG encoder. The pixels contain 8-bit indices even if
10718 % they could be represented with 1, 2, or 4 bits. Grayscale
10719 % images will be written as indexed PNG files even though the
10720 % PNG grayscale type might be slightly more efficient. Please
10721 % note that writing to the PNG8 format may result in loss
10722 % of color and alpha data.
10724 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10725 % chunk can be present to convey binary transparency by naming
10726 % one of the colors as transparent. The only loss incurred
10727 % is reduction of sample depth to 8. If the image has more
10728 % than one transparent color, has semitransparent pixels, or
10729 % has an opaque pixel with the same RGB components as the
10730 % transparent color, an image is not written.
10732 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10733 % transparency is permitted, i.e., the alpha sample for
10734 % each pixel can have any value from 0 to 255. The alpha
10735 % channel is present even if the image is fully opaque.
10736 % The only loss in data is the reduction of the sample depth
10739 % o -define: For more precise control of the PNG output, you can use the
10740 % Image options "png:bit-depth" and "png:color-type". These
10741 % can be set from the commandline with "-define" and also
10742 % from the application programming interfaces. The options
10743 % are case-independent and are converted to lowercase before
10744 % being passed to this encoder.
10746 % png:color-type can be 0, 2, 3, 4, or 6.
10748 % When png:color-type is 0 (Grayscale), png:bit-depth can
10749 % be 1, 2, 4, 8, or 16.
10751 % When png:color-type is 2 (RGB), png:bit-depth can
10754 % When png:color-type is 3 (Indexed), png:bit-depth can
10755 % be 1, 2, 4, or 8. This refers to the number of bits
10756 % used to store the index. The color samples always have
10757 % bit-depth 8 in indexed PNG files.
10759 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10760 % png:bit-depth can be 8 or 16.
10762 % If the image cannot be written without loss with the requested bit-depth
10763 % and color-type, a PNG file will not be written, and the encoder will
10764 % return MagickFalse.
10766 % Since image encoders should not be responsible for the "heavy lifting",
10767 % the user should make sure that ImageMagick has already reduced the
10768 % image depth and number of colors and limit transparency to binary
10769 % transparency prior to attempting to write the image with depth, color,
10770 % or transparency limitations.
10772 % Note that another definition, "png:bit-depth-written" exists, but it
10773 % is not intended for external use. It is only used internally by the
10774 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10776 % It is possible to request that the PNG encoder write previously-formatted
10777 % ancillary chunks in the output PNG file, using the "-profile" commandline
10778 % option as shown below or by setting the profile via a programming
10781 % -profile PNG-chunk-x:<file>
10783 % where x is a location flag and <file> is a file containing the chunk
10784 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10785 % This encoder will compute the chunk length and CRC, so those must not
10786 % be included in the file.
10788 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10789 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10790 % of the same type, then add a short unique string after the "x" to prevent
10791 % subsequent profiles from overwriting the preceding ones, e.g.,
10793 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10795 % As of version 6.6.6 the following optimizations are always done:
10797 % o 32-bit depth is reduced to 16.
10798 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10799 % high byte and low byte are identical.
10800 % o Palette is sorted to remove unused entries and to put a
10801 % transparent color first, if BUILD_PNG_PALETTE is defined.
10802 % o Opaque matte channel is removed when writing an indexed PNG.
10803 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10804 % this can be done without loss and a larger bit depth N was not
10805 % requested via the "-define PNG:bit-depth=N" option.
10806 % o If matte channel is present but only one transparent color is
10807 % present, RGB+tRNS is written instead of RGBA
10808 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10809 % was requested when converting an opaque image).
10811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10813 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10819 have_mng_structure,
10835 assert(image_info != (const ImageInfo *) NULL);
10836 assert(image_info->signature == MagickSignature);
10837 assert(image != (Image *) NULL);
10838 assert(image->signature == MagickSignature);
10839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10840 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10842 Allocate a MngInfo structure.
10844 have_mng_structure=MagickFalse;
10845 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10847 if (mng_info == (MngInfo *) NULL)
10848 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10851 Initialize members of the MngInfo structure.
10853 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10854 mng_info->image=image;
10855 mng_info->equal_backgrounds=MagickTrue;
10856 have_mng_structure=MagickTrue;
10858 /* See if user has requested a specific PNG subformat */
10860 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10861 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10862 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10864 if (mng_info->write_png8)
10866 mng_info->write_png_colortype = /* 3 */ 4;
10867 mng_info->write_png_depth = 8;
10871 if (mng_info->write_png24)
10873 mng_info->write_png_colortype = /* 2 */ 3;
10874 mng_info->write_png_depth = 8;
10877 if (image->matte == MagickTrue)
10878 (void) SetImageType(image,TrueColorMatteType);
10881 (void) SetImageType(image,TrueColorType);
10883 (void) SyncImage(image);
10886 if (mng_info->write_png32)
10888 mng_info->write_png_colortype = /* 6 */ 7;
10889 mng_info->write_png_depth = 8;
10892 if (image->matte == MagickTrue)
10893 (void) SetImageType(image,TrueColorMatteType);
10896 (void) SetImageType(image,TrueColorType);
10898 (void) SyncImage(image);
10901 value=GetImageOption(image_info,"png:bit-depth");
10903 if (value != (char *) NULL)
10905 if (LocaleCompare(value,"1") == 0)
10906 mng_info->write_png_depth = 1;
10908 else if (LocaleCompare(value,"2") == 0)
10909 mng_info->write_png_depth = 2;
10911 else if (LocaleCompare(value,"4") == 0)
10912 mng_info->write_png_depth = 4;
10914 else if (LocaleCompare(value,"8") == 0)
10915 mng_info->write_png_depth = 8;
10917 else if (LocaleCompare(value,"16") == 0)
10918 mng_info->write_png_depth = 16;
10921 (void) ThrowMagickException(&image->exception,
10922 GetMagickModule(),CoderWarning,
10923 "ignoring invalid defined png:bit-depth",
10926 if (logging != MagickFalse)
10927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10928 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10931 value=GetImageOption(image_info,"png:color-type");
10933 if (value != (char *) NULL)
10935 /* We must store colortype+1 because 0 is a valid colortype */
10936 if (LocaleCompare(value,"0") == 0)
10937 mng_info->write_png_colortype = 1;
10939 else if (LocaleCompare(value,"2") == 0)
10940 mng_info->write_png_colortype = 3;
10942 else if (LocaleCompare(value,"3") == 0)
10943 mng_info->write_png_colortype = 4;
10945 else if (LocaleCompare(value,"4") == 0)
10946 mng_info->write_png_colortype = 5;
10948 else if (LocaleCompare(value,"6") == 0)
10949 mng_info->write_png_colortype = 7;
10952 (void) ThrowMagickException(&image->exception,
10953 GetMagickModule(),CoderWarning,
10954 "ignoring invalid defined png:color-type",
10957 if (logging != MagickFalse)
10958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10959 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10962 /* Check for chunks to be excluded:
10964 * The default is to not exclude any known chunks except for any
10965 * listed in the "unused_chunks" array, above.
10967 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10968 * define (in the image properties or in the image artifacts)
10969 * or via a mng_info member. For convenience, in addition
10970 * to or instead of a comma-separated list of chunks, the
10971 * "exclude-chunk" string can be simply "all" or "none".
10973 * The exclude-chunk define takes priority over the mng_info.
10975 * A "PNG:include-chunk" define takes priority over both the
10976 * mng_info and the "PNG:exclude-chunk" define. Like the
10977 * "exclude-chunk" string, it can define "all" or "none" as
10978 * well as a comma-separated list. Chunks that are unknown to
10979 * ImageMagick are always excluded, regardless of their "copy-safe"
10980 * status according to the PNG specification, and even if they
10981 * appear in the "include-chunk" list.
10983 * Finally, all chunks listed in the "unused_chunks" array are
10984 * automatically excluded, regardless of the other instructions
10987 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10988 * will not be written and the gAMA chunk will only be written if it
10989 * is not between .45 and .46, or approximately (1.0/2.2).
10991 * If you exclude tRNS and the image has transparency, the colortype
10992 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10994 * The -strip option causes StripImage() to set the png:include-chunk
10995 * artifact to "none,gama".
10998 mng_info->ping_exclude_bKGD=MagickFalse;
10999 mng_info->ping_exclude_cHRM=MagickFalse;
11000 mng_info->ping_exclude_date=MagickFalse;
11001 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11002 mng_info->ping_exclude_gAMA=MagickFalse;
11003 mng_info->ping_exclude_iCCP=MagickFalse;
11004 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11005 mng_info->ping_exclude_oFFs=MagickFalse;
11006 mng_info->ping_exclude_pHYs=MagickFalse;
11007 mng_info->ping_exclude_sRGB=MagickFalse;
11008 mng_info->ping_exclude_tEXt=MagickFalse;
11009 mng_info->ping_exclude_tRNS=MagickFalse;
11010 mng_info->ping_exclude_vpAg=MagickFalse;
11011 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11012 mng_info->ping_exclude_zTXt=MagickFalse;
11014 mng_info->ping_preserve_colormap=MagickFalse;
11016 value=GetImageArtifact(image,"png:preserve-colormap");
11018 value=GetImageOption(image_info,"png:preserve-colormap");
11020 mng_info->ping_preserve_colormap=MagickTrue;
11022 /* Thes compression-level, compression-strategy, and compression-filter
11023 * defines take precedence over values from the -quality option.
11025 value=GetImageArtifact(image,"png:compression-level");
11027 value=GetImageOption(image_info,"png:compression-level");
11030 /* We have to add 1 to everything because 0 is a valid input,
11031 * and we want to use 0 (the default) to mean undefined.
11033 if (LocaleCompare(value,"0") == 0)
11034 mng_info->write_png_compression_level = 1;
11036 if (LocaleCompare(value,"1") == 0)
11037 mng_info->write_png_compression_level = 2;
11039 else if (LocaleCompare(value,"2") == 0)
11040 mng_info->write_png_compression_level = 3;
11042 else if (LocaleCompare(value,"3") == 0)
11043 mng_info->write_png_compression_level = 4;
11045 else if (LocaleCompare(value,"4") == 0)
11046 mng_info->write_png_compression_level = 5;
11048 else if (LocaleCompare(value,"5") == 0)
11049 mng_info->write_png_compression_level = 6;
11051 else if (LocaleCompare(value,"6") == 0)
11052 mng_info->write_png_compression_level = 7;
11054 else if (LocaleCompare(value,"7") == 0)
11055 mng_info->write_png_compression_level = 8;
11057 else if (LocaleCompare(value,"8") == 0)
11058 mng_info->write_png_compression_level = 9;
11060 else if (LocaleCompare(value,"9") == 0)
11061 mng_info->write_png_compression_level = 10;
11064 (void) ThrowMagickException(&image->exception,
11065 GetMagickModule(),CoderWarning,
11066 "ignoring invalid defined png:compression-level",
11070 value=GetImageArtifact(image,"png:compression-strategy");
11072 value=GetImageOption(image_info,"png:compression-strategy");
11076 if (LocaleCompare(value,"0") == 0)
11077 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11079 else if (LocaleCompare(value,"1") == 0)
11080 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11082 else if (LocaleCompare(value,"2") == 0)
11083 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11085 else if (LocaleCompare(value,"3") == 0)
11086 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11087 mng_info->write_png_compression_strategy = Z_RLE+1;
11089 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11092 else if (LocaleCompare(value,"4") == 0)
11093 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11094 mng_info->write_png_compression_strategy = Z_FIXED+1;
11096 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11100 (void) ThrowMagickException(&image->exception,
11101 GetMagickModule(),CoderWarning,
11102 "ignoring invalid defined png:compression-strategy",
11106 value=GetImageArtifact(image,"png:compression-filter");
11108 value=GetImageOption(image_info,"png:compression-filter");
11112 /* To do: combinations of filters allowed by libpng
11113 * masks 0x08 through 0xf8
11115 * Implement this as a comma-separated list of 0,1,2,3,4,5
11116 * where 5 is a special case meaning PNG_ALL_FILTERS.
11119 if (LocaleCompare(value,"0") == 0)
11120 mng_info->write_png_compression_filter = 1;
11122 if (LocaleCompare(value,"1") == 0)
11123 mng_info->write_png_compression_filter = 2;
11125 else if (LocaleCompare(value,"2") == 0)
11126 mng_info->write_png_compression_filter = 3;
11128 else if (LocaleCompare(value,"3") == 0)
11129 mng_info->write_png_compression_filter = 4;
11131 else if (LocaleCompare(value,"4") == 0)
11132 mng_info->write_png_compression_filter = 5;
11134 else if (LocaleCompare(value,"5") == 0)
11135 mng_info->write_png_compression_filter = 6;
11137 else if (LocaleCompare(value,"6") == 0)
11138 mng_info->write_png_compression_filter = 7;
11140 else if (LocaleCompare(value,"7") == 0)
11141 mng_info->write_png_compression_filter = 8;
11143 else if (LocaleCompare(value,"8") == 0)
11144 mng_info->write_png_compression_filter = 9;
11146 else if (LocaleCompare(value,"9") == 0)
11147 mng_info->write_png_compression_filter = 10;
11150 (void) ThrowMagickException(&image->exception,
11151 GetMagickModule(),CoderWarning,
11152 "ignoring invalid defined png:compression-filter",
11156 excluding=MagickFalse;
11158 for (source=0; source<1; source++)
11162 value=GetImageArtifact(image,"png:exclude-chunk");
11165 value=GetImageArtifact(image,"png:exclude-chunks");
11169 value=GetImageOption(image_info,"png:exclude-chunk");
11172 value=GetImageOption(image_info,"png:exclude-chunks");
11181 excluding=MagickTrue;
11183 if (logging != MagickFalse)
11186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187 " png:exclude-chunk=%s found in image artifacts.\n", value);
11189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190 " png:exclude-chunk=%s found in image properties.\n", value);
11193 last=strlen(value);
11195 for (i=0; i<(int) last; i+=5)
11198 if (LocaleNCompare(value+i,"all",3) == 0)
11200 mng_info->ping_exclude_bKGD=MagickTrue;
11201 mng_info->ping_exclude_cHRM=MagickTrue;
11202 mng_info->ping_exclude_date=MagickTrue;
11203 mng_info->ping_exclude_EXIF=MagickTrue;
11204 mng_info->ping_exclude_gAMA=MagickTrue;
11205 mng_info->ping_exclude_iCCP=MagickTrue;
11206 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11207 mng_info->ping_exclude_oFFs=MagickTrue;
11208 mng_info->ping_exclude_pHYs=MagickTrue;
11209 mng_info->ping_exclude_sRGB=MagickTrue;
11210 mng_info->ping_exclude_tEXt=MagickTrue;
11211 mng_info->ping_exclude_tRNS=MagickTrue;
11212 mng_info->ping_exclude_vpAg=MagickTrue;
11213 mng_info->ping_exclude_zCCP=MagickTrue;
11214 mng_info->ping_exclude_zTXt=MagickTrue;
11218 if (LocaleNCompare(value+i,"none",4) == 0)
11220 mng_info->ping_exclude_bKGD=MagickFalse;
11221 mng_info->ping_exclude_cHRM=MagickFalse;
11222 mng_info->ping_exclude_date=MagickFalse;
11223 mng_info->ping_exclude_EXIF=MagickFalse;
11224 mng_info->ping_exclude_gAMA=MagickFalse;
11225 mng_info->ping_exclude_iCCP=MagickFalse;
11226 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11227 mng_info->ping_exclude_oFFs=MagickFalse;
11228 mng_info->ping_exclude_pHYs=MagickFalse;
11229 mng_info->ping_exclude_sRGB=MagickFalse;
11230 mng_info->ping_exclude_tEXt=MagickFalse;
11231 mng_info->ping_exclude_tRNS=MagickFalse;
11232 mng_info->ping_exclude_vpAg=MagickFalse;
11233 mng_info->ping_exclude_zCCP=MagickFalse;
11234 mng_info->ping_exclude_zTXt=MagickFalse;
11237 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11238 mng_info->ping_exclude_bKGD=MagickTrue;
11240 if (LocaleNCompare(value+i,"chrm",4) == 0)
11241 mng_info->ping_exclude_cHRM=MagickTrue;
11243 if (LocaleNCompare(value+i,"date",4) == 0)
11244 mng_info->ping_exclude_date=MagickTrue;
11246 if (LocaleNCompare(value+i,"exif",4) == 0)
11247 mng_info->ping_exclude_EXIF=MagickTrue;
11249 if (LocaleNCompare(value+i,"gama",4) == 0)
11250 mng_info->ping_exclude_gAMA=MagickTrue;
11252 if (LocaleNCompare(value+i,"iccp",4) == 0)
11253 mng_info->ping_exclude_iCCP=MagickTrue;
11256 if (LocaleNCompare(value+i,"itxt",4) == 0)
11257 mng_info->ping_exclude_iTXt=MagickTrue;
11260 if (LocaleNCompare(value+i,"gama",4) == 0)
11261 mng_info->ping_exclude_gAMA=MagickTrue;
11263 if (LocaleNCompare(value+i,"offs",4) == 0)
11264 mng_info->ping_exclude_oFFs=MagickTrue;
11266 if (LocaleNCompare(value+i,"phys",4) == 0)
11267 mng_info->ping_exclude_pHYs=MagickTrue;
11269 if (LocaleNCompare(value+i,"srgb",4) == 0)
11270 mng_info->ping_exclude_sRGB=MagickTrue;
11272 if (LocaleNCompare(value+i,"text",4) == 0)
11273 mng_info->ping_exclude_tEXt=MagickTrue;
11275 if (LocaleNCompare(value+i,"trns",4) == 0)
11276 mng_info->ping_exclude_tRNS=MagickTrue;
11278 if (LocaleNCompare(value+i,"vpag",4) == 0)
11279 mng_info->ping_exclude_vpAg=MagickTrue;
11281 if (LocaleNCompare(value+i,"zccp",4) == 0)
11282 mng_info->ping_exclude_zCCP=MagickTrue;
11284 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11285 mng_info->ping_exclude_zTXt=MagickTrue;
11291 for (source=0; source<1; source++)
11295 value=GetImageArtifact(image,"png:include-chunk");
11298 value=GetImageArtifact(image,"png:include-chunks");
11302 value=GetImageOption(image_info,"png:include-chunk");
11305 value=GetImageOption(image_info,"png:include-chunks");
11313 excluding=MagickTrue;
11315 if (logging != MagickFalse)
11318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319 " png:include-chunk=%s found in image artifacts.\n", value);
11321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11322 " png:include-chunk=%s found in image properties.\n", value);
11325 last=strlen(value);
11327 for (i=0; i<(int) last; i+=5)
11329 if (LocaleNCompare(value+i,"all",3) == 0)
11331 mng_info->ping_exclude_bKGD=MagickFalse;
11332 mng_info->ping_exclude_cHRM=MagickFalse;
11333 mng_info->ping_exclude_date=MagickFalse;
11334 mng_info->ping_exclude_EXIF=MagickFalse;
11335 mng_info->ping_exclude_gAMA=MagickFalse;
11336 mng_info->ping_exclude_iCCP=MagickFalse;
11337 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11338 mng_info->ping_exclude_oFFs=MagickFalse;
11339 mng_info->ping_exclude_pHYs=MagickFalse;
11340 mng_info->ping_exclude_sRGB=MagickFalse;
11341 mng_info->ping_exclude_tEXt=MagickFalse;
11342 mng_info->ping_exclude_tRNS=MagickFalse;
11343 mng_info->ping_exclude_vpAg=MagickFalse;
11344 mng_info->ping_exclude_zCCP=MagickFalse;
11345 mng_info->ping_exclude_zTXt=MagickFalse;
11349 if (LocaleNCompare(value+i,"none",4) == 0)
11351 mng_info->ping_exclude_bKGD=MagickTrue;
11352 mng_info->ping_exclude_cHRM=MagickTrue;
11353 mng_info->ping_exclude_date=MagickTrue;
11354 mng_info->ping_exclude_EXIF=MagickTrue;
11355 mng_info->ping_exclude_gAMA=MagickTrue;
11356 mng_info->ping_exclude_iCCP=MagickTrue;
11357 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11358 mng_info->ping_exclude_oFFs=MagickTrue;
11359 mng_info->ping_exclude_pHYs=MagickTrue;
11360 mng_info->ping_exclude_sRGB=MagickTrue;
11361 mng_info->ping_exclude_tEXt=MagickTrue;
11362 mng_info->ping_exclude_tRNS=MagickTrue;
11363 mng_info->ping_exclude_vpAg=MagickTrue;
11364 mng_info->ping_exclude_zCCP=MagickTrue;
11365 mng_info->ping_exclude_zTXt=MagickTrue;
11368 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11369 mng_info->ping_exclude_bKGD=MagickFalse;
11371 if (LocaleNCompare(value+i,"chrm",4) == 0)
11372 mng_info->ping_exclude_cHRM=MagickFalse;
11374 if (LocaleNCompare(value+i,"date",4) == 0)
11375 mng_info->ping_exclude_date=MagickFalse;
11377 if (LocaleNCompare(value+i,"exif",4) == 0)
11378 mng_info->ping_exclude_EXIF=MagickFalse;
11380 if (LocaleNCompare(value+i,"gama",4) == 0)
11381 mng_info->ping_exclude_gAMA=MagickFalse;
11383 if (LocaleNCompare(value+i,"iccp",4) == 0)
11384 mng_info->ping_exclude_iCCP=MagickFalse;
11387 if (LocaleNCompare(value+i,"itxt",4) == 0)
11388 mng_info->ping_exclude_iTXt=MagickFalse;
11391 if (LocaleNCompare(value+i,"gama",4) == 0)
11392 mng_info->ping_exclude_gAMA=MagickFalse;
11394 if (LocaleNCompare(value+i,"offs",4) == 0)
11395 mng_info->ping_exclude_oFFs=MagickFalse;
11397 if (LocaleNCompare(value+i,"phys",4) == 0)
11398 mng_info->ping_exclude_pHYs=MagickFalse;
11400 if (LocaleNCompare(value+i,"srgb",4) == 0)
11401 mng_info->ping_exclude_sRGB=MagickFalse;
11403 if (LocaleNCompare(value+i,"text",4) == 0)
11404 mng_info->ping_exclude_tEXt=MagickFalse;
11406 if (LocaleNCompare(value+i,"trns",4) == 0)
11407 mng_info->ping_exclude_tRNS=MagickFalse;
11409 if (LocaleNCompare(value+i,"vpag",4) == 0)
11410 mng_info->ping_exclude_vpAg=MagickFalse;
11412 if (LocaleNCompare(value+i,"zccp",4) == 0)
11413 mng_info->ping_exclude_zCCP=MagickFalse;
11415 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11416 mng_info->ping_exclude_zTXt=MagickFalse;
11422 if (excluding != MagickFalse && logging != MagickFalse)
11424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11425 " Chunks to be excluded from the output PNG:");
11426 if (mng_info->ping_exclude_bKGD != MagickFalse)
11427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 if (mng_info->ping_exclude_cHRM != MagickFalse)
11430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11432 if (mng_info->ping_exclude_date != MagickFalse)
11433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11435 if (mng_info->ping_exclude_EXIF != MagickFalse)
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11438 if (mng_info->ping_exclude_gAMA != MagickFalse)
11439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11441 if (mng_info->ping_exclude_iCCP != MagickFalse)
11442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11445 if (mng_info->ping_exclude_iTXt != MagickFalse)
11446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11449 if (mng_info->ping_exclude_oFFs != MagickFalse)
11450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11452 if (mng_info->ping_exclude_pHYs != MagickFalse)
11453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11455 if (mng_info->ping_exclude_sRGB != MagickFalse)
11456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11458 if (mng_info->ping_exclude_tEXt != MagickFalse)
11459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11461 if (mng_info->ping_exclude_tRNS != MagickFalse)
11462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464 if (mng_info->ping_exclude_vpAg != MagickFalse)
11465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11467 if (mng_info->ping_exclude_zCCP != MagickFalse)
11468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11470 if (mng_info->ping_exclude_zTXt != MagickFalse)
11471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11475 mng_info->need_blob = MagickTrue;
11477 status=WriteOnePNGImage(mng_info,image_info,image);
11479 MngInfoFreeStruct(mng_info,&have_mng_structure);
11481 if (logging != MagickFalse)
11482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11487 #if defined(JNG_SUPPORTED)
11489 /* Write one JNG image */
11490 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11491 const ImageInfo *image_info,Image *image)
11512 jng_alpha_compression_method,
11513 jng_alpha_sample_depth,
11520 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11521 " Enter WriteOneJNGImage()");
11523 blob=(unsigned char *) NULL;
11524 jpeg_image=(Image *) NULL;
11525 jpeg_image_info=(ImageInfo *) NULL;
11528 transparent=image_info->type==GrayscaleMatteType ||
11529 image_info->type==TrueColorMatteType;
11531 jng_alpha_sample_depth=0;
11532 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11533 jng_alpha_compression_method=0;
11535 if (image->matte != MagickFalse)
11537 /* if any pixels are transparent */
11538 transparent=MagickTrue;
11539 if (image_info->compression==JPEGCompression)
11540 jng_alpha_compression_method=8;
11547 /* Create JPEG blob, image, and image_info */
11548 if (logging != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 " Creating jpeg_image_info for alpha.");
11552 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11554 if (jpeg_image_info == (ImageInfo *) NULL)
11555 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11557 if (logging != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " Creating jpeg_image.");
11561 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11563 if (jpeg_image == (Image *) NULL)
11564 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11566 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11567 PushPixelChannelMap(jpeg_image,AlphaChannel);
11568 status=SeparateImage(jpeg_image);
11569 PopPixelChannelMap(jpeg_image);
11570 jpeg_image->matte=MagickFalse;
11572 if (jng_quality >= 1000)
11573 jpeg_image_info->quality=jng_quality/1000;
11576 jpeg_image_info->quality=jng_quality;
11578 jpeg_image_info->type=GrayscaleType;
11579 (void) SetImageType(jpeg_image,GrayscaleType);
11580 (void) AcquireUniqueFilename(jpeg_image->filename);
11581 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11582 "%s",jpeg_image->filename);
11585 /* To do: check bit depth of PNG alpha channel */
11587 /* Check if image is grayscale. */
11588 if (image_info->type != TrueColorMatteType && image_info->type !=
11589 TrueColorType && ImageIsGray(image))
11594 if (jng_alpha_compression_method==0)
11599 /* Encode alpha as a grayscale PNG blob */
11600 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11601 &image->exception);
11602 if (logging != MagickFalse)
11603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11604 " Creating PNG blob.");
11607 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11608 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11609 jpeg_image_info->interlace=NoInterlace;
11611 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11612 &image->exception);
11614 /* Retrieve sample depth used */
11615 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11616 if (value != (char *) NULL)
11617 jng_alpha_sample_depth= (unsigned int) value[0];
11621 /* Encode alpha as a grayscale JPEG blob */
11623 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11624 &image->exception);
11626 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11627 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11628 jpeg_image_info->interlace=NoInterlace;
11629 if (logging != MagickFalse)
11630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631 " Creating blob.");
11632 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11633 &image->exception);
11634 jng_alpha_sample_depth=8;
11636 if (logging != MagickFalse)
11637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11638 " Successfully read jpeg_image into a blob, length=%.20g.",
11642 /* Destroy JPEG image and image_info */
11643 jpeg_image=DestroyImage(jpeg_image);
11644 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11645 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11648 /* Write JHDR chunk */
11649 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11650 PNGType(chunk,mng_JHDR);
11651 LogPNGChunk(logging,mng_JHDR,16L);
11652 PNGLong(chunk+4,(png_uint_32) image->columns);
11653 PNGLong(chunk+8,(png_uint_32) image->rows);
11654 chunk[12]=jng_color_type;
11655 chunk[13]=8; /* sample depth */
11656 chunk[14]=8; /*jng_image_compression_method */
11657 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11658 chunk[16]=jng_alpha_sample_depth;
11659 chunk[17]=jng_alpha_compression_method;
11660 chunk[18]=0; /*jng_alpha_filter_method */
11661 chunk[19]=0; /*jng_alpha_interlace_method */
11662 (void) WriteBlob(image,20,chunk);
11663 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11664 if (logging != MagickFalse)
11666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11667 " JNG width:%15lu",(unsigned long) image->columns);
11669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670 " JNG height:%14lu",(unsigned long) image->rows);
11672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673 " JNG color type:%10d",jng_color_type);
11675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11676 " JNG sample depth:%8d",8);
11678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11679 " JNG compression:%9d",8);
11681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11682 " JNG interlace:%11d",0);
11684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11685 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11688 " JNG alpha compression:%3d",jng_alpha_compression_method);
11690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11691 " JNG alpha filter:%8d",0);
11693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11694 " JNG alpha interlace:%5d",0);
11697 /* Write any JNG-chunk-b profiles */
11698 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11701 Write leading ancillary chunks
11707 Write JNG bKGD chunk
11718 if (jng_color_type == 8 || jng_color_type == 12)
11722 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11723 PNGType(chunk,mng_bKGD);
11724 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11725 red=ScaleQuantumToChar(image->background_color.red);
11726 green=ScaleQuantumToChar(image->background_color.green);
11727 blue=ScaleQuantumToChar(image->background_color.blue);
11734 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11735 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11738 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11741 Write JNG sRGB chunk
11743 (void) WriteBlobMSBULong(image,1L);
11744 PNGType(chunk,mng_sRGB);
11745 LogPNGChunk(logging,mng_sRGB,1L);
11747 if (image->rendering_intent != UndefinedIntent)
11748 chunk[4]=(unsigned char)
11749 Magick_RenderingIntent_to_PNG_RenderingIntent(
11750 (image->rendering_intent));
11753 chunk[4]=(unsigned char)
11754 Magick_RenderingIntent_to_PNG_RenderingIntent(
11755 (PerceptualIntent));
11757 (void) WriteBlob(image,5,chunk);
11758 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11762 if (image->gamma != 0.0)
11765 Write JNG gAMA chunk
11767 (void) WriteBlobMSBULong(image,4L);
11768 PNGType(chunk,mng_gAMA);
11769 LogPNGChunk(logging,mng_gAMA,4L);
11770 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11771 (void) WriteBlob(image,8,chunk);
11772 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11775 if ((mng_info->equal_chrms == MagickFalse) &&
11776 (image->chromaticity.red_primary.x != 0.0))
11782 Write JNG cHRM chunk
11784 (void) WriteBlobMSBULong(image,32L);
11785 PNGType(chunk,mng_cHRM);
11786 LogPNGChunk(logging,mng_cHRM,32L);
11787 primary=image->chromaticity.white_point;
11788 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11789 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11790 primary=image->chromaticity.red_primary;
11791 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11792 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11793 primary=image->chromaticity.green_primary;
11794 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11795 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11796 primary=image->chromaticity.blue_primary;
11797 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11798 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11799 (void) WriteBlob(image,36,chunk);
11800 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11804 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11807 Write JNG pHYs chunk
11809 (void) WriteBlobMSBULong(image,9L);
11810 PNGType(chunk,mng_pHYs);
11811 LogPNGChunk(logging,mng_pHYs,9L);
11812 if (image->units == PixelsPerInchResolution)
11814 PNGLong(chunk+4,(png_uint_32)
11815 (image->x_resolution*100.0/2.54+0.5));
11817 PNGLong(chunk+8,(png_uint_32)
11818 (image->y_resolution*100.0/2.54+0.5));
11825 if (image->units == PixelsPerCentimeterResolution)
11827 PNGLong(chunk+4,(png_uint_32)
11828 (image->x_resolution*100.0+0.5));
11830 PNGLong(chunk+8,(png_uint_32)
11831 (image->y_resolution*100.0+0.5));
11838 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11839 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11843 (void) WriteBlob(image,13,chunk);
11844 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11847 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11850 Write JNG oFFs chunk
11852 (void) WriteBlobMSBULong(image,9L);
11853 PNGType(chunk,mng_oFFs);
11854 LogPNGChunk(logging,mng_oFFs,9L);
11855 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11856 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11858 (void) WriteBlob(image,13,chunk);
11859 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11861 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11863 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11864 PNGType(chunk,mng_vpAg);
11865 LogPNGChunk(logging,mng_vpAg,9L);
11866 PNGLong(chunk+4,(png_uint_32) image->page.width);
11867 PNGLong(chunk+8,(png_uint_32) image->page.height);
11868 chunk[12]=0; /* unit = pixels */
11869 (void) WriteBlob(image,13,chunk);
11870 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11876 if (jng_alpha_compression_method==0)
11884 /* Write IDAT chunk header */
11885 if (logging != MagickFalse)
11886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11887 " Write IDAT chunks from blob, length=%.20g.",(double)
11890 /* Copy IDAT chunks */
11893 for (i=8; i<(ssize_t) length; i+=len+12)
11895 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11898 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11900 /* Found an IDAT chunk. */
11901 (void) WriteBlobMSBULong(image,(size_t) len);
11902 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11903 (void) WriteBlob(image,(size_t) len+4,p);
11904 (void) WriteBlobMSBULong(image,
11905 crc32(0,p,(uInt) len+4));
11910 if (logging != MagickFalse)
11911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11912 " Skipping %c%c%c%c chunk, length=%.20g.",
11913 *(p),*(p+1),*(p+2),*(p+3),(double) len);
11920 /* Write JDAA chunk header */
11921 if (logging != MagickFalse)
11922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11923 " Write JDAA chunk, length=%.20g.",(double) length);
11924 (void) WriteBlobMSBULong(image,(size_t) length);
11925 PNGType(chunk,mng_JDAA);
11926 LogPNGChunk(logging,mng_JDAA,length);
11927 /* Write JDAT chunk(s) data */
11928 (void) WriteBlob(image,4,chunk);
11929 (void) WriteBlob(image,length,blob);
11930 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11933 blob=(unsigned char *) RelinquishMagickMemory(blob);
11936 /* Encode image as a JPEG blob */
11937 if (logging != MagickFalse)
11938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11939 " Creating jpeg_image_info.");
11940 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11941 if (jpeg_image_info == (ImageInfo *) NULL)
11942 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11944 if (logging != MagickFalse)
11945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11946 " Creating jpeg_image.");
11948 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11949 if (jpeg_image == (Image *) NULL)
11950 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11951 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11953 (void) AcquireUniqueFilename(jpeg_image->filename);
11954 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
11955 jpeg_image->filename);
11957 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11958 &image->exception);
11960 if (logging != MagickFalse)
11961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11962 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11963 (double) jpeg_image->rows);
11965 if (jng_color_type == 8 || jng_color_type == 12)
11966 jpeg_image_info->type=GrayscaleType;
11968 jpeg_image_info->quality=jng_quality % 1000;
11969 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11970 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11972 if (logging != MagickFalse)
11973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11974 " Creating blob.");
11976 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11978 if (logging != MagickFalse)
11980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11981 " Successfully read jpeg_image into a blob, length=%.20g.",
11984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11985 " Write JDAT chunk, length=%.20g.",(double) length);
11988 /* Write JDAT chunk(s) */
11989 (void) WriteBlobMSBULong(image,(size_t) length);
11990 PNGType(chunk,mng_JDAT);
11991 LogPNGChunk(logging,mng_JDAT,length);
11992 (void) WriteBlob(image,4,chunk);
11993 (void) WriteBlob(image,length,blob);
11994 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11996 jpeg_image=DestroyImage(jpeg_image);
11997 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11998 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11999 blob=(unsigned char *) RelinquishMagickMemory(blob);
12001 /* Write any JNG-chunk-e profiles */
12002 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12004 /* Write IEND chunk */
12005 (void) WriteBlobMSBULong(image,0L);
12006 PNGType(chunk,mng_IEND);
12007 LogPNGChunk(logging,mng_IEND,0);
12008 (void) WriteBlob(image,4,chunk);
12009 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12011 if (logging != MagickFalse)
12012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12013 " exit WriteOneJNGImage()");
12020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12024 % W r i t e J N G I m a g e %
12028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12030 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12032 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12034 % The format of the WriteJNGImage method is:
12036 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12038 % A description of each parameter follows:
12040 % o image_info: the image info.
12042 % o image: The image.
12044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12046 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12049 have_mng_structure,
12059 assert(image_info != (const ImageInfo *) NULL);
12060 assert(image_info->signature == MagickSignature);
12061 assert(image != (Image *) NULL);
12062 assert(image->signature == MagickSignature);
12063 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12064 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12065 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12066 if (status == MagickFalse)
12070 Allocate a MngInfo structure.
12072 have_mng_structure=MagickFalse;
12073 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12074 if (mng_info == (MngInfo *) NULL)
12075 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12077 Initialize members of the MngInfo structure.
12079 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12080 mng_info->image=image;
12081 have_mng_structure=MagickTrue;
12083 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12085 status=WriteOneJNGImage(mng_info,image_info,image);
12086 (void) CloseBlob(image);
12088 (void) CatchImageException(image);
12089 MngInfoFreeStruct(mng_info,&have_mng_structure);
12090 if (logging != MagickFalse)
12091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12098 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12107 have_mng_structure,
12110 volatile MagickBooleanType
12122 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12123 defined(PNG_MNG_FEATURES_SUPPORTED)
12126 all_images_are_gray,
12136 volatile unsigned int
12147 #if (PNG_LIBPNG_VER < 10200)
12148 if (image_info->verbose)
12149 printf("Your PNG library (libpng-%s) is rather old.\n",
12150 PNG_LIBPNG_VER_STRING);
12156 assert(image_info != (const ImageInfo *) NULL);
12157 assert(image_info->signature == MagickSignature);
12158 assert(image != (Image *) NULL);
12159 assert(image->signature == MagickSignature);
12160 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12161 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12162 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12163 if (status == MagickFalse)
12167 Allocate a MngInfo structure.
12169 have_mng_structure=MagickFalse;
12170 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12171 if (mng_info == (MngInfo *) NULL)
12172 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12174 Initialize members of the MngInfo structure.
12176 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12177 mng_info->image=image;
12178 have_mng_structure=MagickTrue;
12179 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12182 * See if user has requested a specific PNG subformat to be used
12183 * for all of the PNGs in the MNG being written, e.g.,
12185 * convert *.png png8:animation.mng
12187 * To do: check -define png:bit_depth and png:color_type as well,
12188 * or perhaps use mng:bit_depth and mng:color_type instead for
12192 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12193 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12194 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12196 write_jng=MagickFalse;
12197 if (image_info->compression == JPEGCompression)
12198 write_jng=MagickTrue;
12200 mng_info->adjoin=image_info->adjoin &&
12201 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12203 if (logging != MagickFalse)
12205 /* Log some info about the input */
12209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210 " Checking input image(s)");
12212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12213 " Image_info depth: %.20g",(double) image_info->depth);
12215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12216 " Type: %d",image_info->type);
12219 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12222 " Scene: %.20g",(double) scene++);
12224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12225 " Image depth: %.20g",(double) p->depth);
12228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12235 if (p->storage_class == PseudoClass)
12236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12237 " Storage class: PseudoClass");
12240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12241 " Storage class: DirectClass");
12244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12245 " Number of colors: %.20g",(double) p->colors);
12248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12249 " Number of colors: unspecified");
12251 if (mng_info->adjoin == MagickFalse)
12256 use_global_plte=MagickFalse;
12257 all_images_are_gray=MagickFalse;
12258 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12259 need_local_plte=MagickTrue;
12261 need_defi=MagickFalse;
12262 need_matte=MagickFalse;
12263 mng_info->framing_mode=1;
12264 mng_info->old_framing_mode=1;
12267 if (image_info->page != (char *) NULL)
12270 Determine image bounding box.
12272 SetGeometry(image,&mng_info->page);
12273 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12274 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12286 mng_info->page=image->page;
12287 need_geom=MagickTrue;
12288 if (mng_info->page.width || mng_info->page.height)
12289 need_geom=MagickFalse;
12291 Check all the scenes.
12293 initial_delay=image->delay;
12294 need_iterations=MagickFalse;
12295 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12296 mng_info->equal_physs=MagickTrue,
12297 mng_info->equal_gammas=MagickTrue;
12298 mng_info->equal_srgbs=MagickTrue;
12299 mng_info->equal_backgrounds=MagickTrue;
12301 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12302 defined(PNG_MNG_FEATURES_SUPPORTED)
12303 all_images_are_gray=MagickTrue;
12304 mng_info->equal_palettes=MagickFalse;
12305 need_local_plte=MagickFalse;
12307 for (next_image=image; next_image != (Image *) NULL; )
12311 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12312 mng_info->page.width=next_image->columns+next_image->page.x;
12314 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12315 mng_info->page.height=next_image->rows+next_image->page.y;
12318 if (next_image->page.x || next_image->page.y)
12319 need_defi=MagickTrue;
12321 if (next_image->matte)
12322 need_matte=MagickTrue;
12324 if ((int) next_image->dispose >= BackgroundDispose)
12325 if (next_image->matte || next_image->page.x || next_image->page.y ||
12326 ((next_image->columns < mng_info->page.width) &&
12327 (next_image->rows < mng_info->page.height)))
12328 mng_info->need_fram=MagickTrue;
12330 if (next_image->iterations)
12331 need_iterations=MagickTrue;
12333 final_delay=next_image->delay;
12335 if (final_delay != initial_delay || final_delay > 1UL*
12336 next_image->ticks_per_second)
12337 mng_info->need_fram=1;
12339 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12340 defined(PNG_MNG_FEATURES_SUPPORTED)
12342 check for global palette possibility.
12344 if (image->matte != MagickFalse)
12345 need_local_plte=MagickTrue;
12347 if (need_local_plte == 0)
12349 if (ImageIsGray(image) == MagickFalse)
12350 all_images_are_gray=MagickFalse;
12351 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12352 if (use_global_plte == 0)
12353 use_global_plte=mng_info->equal_palettes;
12354 need_local_plte=!mng_info->equal_palettes;
12357 if (GetNextImageInList(next_image) != (Image *) NULL)
12359 if (next_image->background_color.red !=
12360 next_image->next->background_color.red ||
12361 next_image->background_color.green !=
12362 next_image->next->background_color.green ||
12363 next_image->background_color.blue !=
12364 next_image->next->background_color.blue)
12365 mng_info->equal_backgrounds=MagickFalse;
12367 if (next_image->gamma != next_image->next->gamma)
12368 mng_info->equal_gammas=MagickFalse;
12370 if (next_image->rendering_intent !=
12371 next_image->next->rendering_intent)
12372 mng_info->equal_srgbs=MagickFalse;
12374 if ((next_image->units != next_image->next->units) ||
12375 (next_image->x_resolution != next_image->next->x_resolution) ||
12376 (next_image->y_resolution != next_image->next->y_resolution))
12377 mng_info->equal_physs=MagickFalse;
12379 if (mng_info->equal_chrms)
12381 if (next_image->chromaticity.red_primary.x !=
12382 next_image->next->chromaticity.red_primary.x ||
12383 next_image->chromaticity.red_primary.y !=
12384 next_image->next->chromaticity.red_primary.y ||
12385 next_image->chromaticity.green_primary.x !=
12386 next_image->next->chromaticity.green_primary.x ||
12387 next_image->chromaticity.green_primary.y !=
12388 next_image->next->chromaticity.green_primary.y ||
12389 next_image->chromaticity.blue_primary.x !=
12390 next_image->next->chromaticity.blue_primary.x ||
12391 next_image->chromaticity.blue_primary.y !=
12392 next_image->next->chromaticity.blue_primary.y ||
12393 next_image->chromaticity.white_point.x !=
12394 next_image->next->chromaticity.white_point.x ||
12395 next_image->chromaticity.white_point.y !=
12396 next_image->next->chromaticity.white_point.y)
12397 mng_info->equal_chrms=MagickFalse;
12401 next_image=GetNextImageInList(next_image);
12403 if (image_count < 2)
12405 mng_info->equal_backgrounds=MagickFalse;
12406 mng_info->equal_chrms=MagickFalse;
12407 mng_info->equal_gammas=MagickFalse;
12408 mng_info->equal_srgbs=MagickFalse;
12409 mng_info->equal_physs=MagickFalse;
12410 use_global_plte=MagickFalse;
12411 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12412 need_local_plte=MagickTrue;
12414 need_iterations=MagickFalse;
12417 if (mng_info->need_fram == MagickFalse)
12420 Only certain framing rates 100/n are exactly representable without
12421 the FRAM chunk but we'll allow some slop in VLC files
12423 if (final_delay == 0)
12425 if (need_iterations != MagickFalse)
12428 It's probably a GIF with loop; don't run it *too* fast.
12430 if (mng_info->adjoin)
12433 (void) ThrowMagickException(&image->exception,
12434 GetMagickModule(),CoderWarning,
12435 "input has zero delay between all frames; assuming",
12440 mng_info->ticks_per_second=0;
12442 if (final_delay != 0)
12443 mng_info->ticks_per_second=(png_uint_32)
12444 (image->ticks_per_second/final_delay);
12445 if (final_delay > 50)
12446 mng_info->ticks_per_second=2;
12448 if (final_delay > 75)
12449 mng_info->ticks_per_second=1;
12451 if (final_delay > 125)
12452 mng_info->need_fram=MagickTrue;
12454 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12455 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12456 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12457 1UL*image->ticks_per_second))
12458 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12461 if (mng_info->need_fram != MagickFalse)
12462 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12464 If pseudocolor, we should also check to see if all the
12465 palettes are identical and write a global PLTE if they are.
12469 Write the MNG version 1.0 signature and MHDR chunk.
12471 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12472 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12473 PNGType(chunk,mng_MHDR);
12474 LogPNGChunk(logging,mng_MHDR,28L);
12475 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12476 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12477 PNGLong(chunk+12,mng_info->ticks_per_second);
12478 PNGLong(chunk+16,0L); /* layer count=unknown */
12479 PNGLong(chunk+20,0L); /* frame count=unknown */
12480 PNGLong(chunk+24,0L); /* play time=unknown */
12485 if (need_defi || mng_info->need_fram || use_global_plte)
12486 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12489 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12494 if (need_defi || mng_info->need_fram || use_global_plte)
12495 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12498 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12506 if (need_defi || mng_info->need_fram || use_global_plte)
12507 PNGLong(chunk+28,11L); /* simplicity=LC */
12510 PNGLong(chunk+28,9L); /* simplicity=VLC */
12515 if (need_defi || mng_info->need_fram || use_global_plte)
12516 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12519 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12522 (void) WriteBlob(image,32,chunk);
12523 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12524 option=GetImageOption(image_info,"mng:need-cacheoff");
12525 if (option != (const char *) NULL)
12531 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12533 PNGType(chunk,mng_nEED);
12534 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12535 (void) WriteBlobMSBULong(image,(size_t) length);
12536 LogPNGChunk(logging,mng_nEED,(size_t) length);
12538 (void) WriteBlob(image,length,chunk);
12539 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12541 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12542 (GetNextImageInList(image) != (Image *) NULL) &&
12543 (image->iterations != 1))
12546 Write MNG TERM chunk
12548 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12549 PNGType(chunk,mng_TERM);
12550 LogPNGChunk(logging,mng_TERM,10L);
12551 chunk[4]=3; /* repeat animation */
12552 chunk[5]=0; /* show last frame when done */
12553 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12554 final_delay/MagickMax(image->ticks_per_second,1)));
12556 if (image->iterations == 0)
12557 PNGLong(chunk+10,PNG_UINT_31_MAX);
12560 PNGLong(chunk+10,(png_uint_32) image->iterations);
12562 if (logging != MagickFalse)
12564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12565 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12566 final_delay/MagickMax(image->ticks_per_second,1)));
12568 if (image->iterations == 0)
12569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12570 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12574 " Image iterations: %.20g",(double) image->iterations);
12576 (void) WriteBlob(image,14,chunk);
12577 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12580 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12582 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12583 mng_info->equal_srgbs)
12586 Write MNG sRGB chunk
12588 (void) WriteBlobMSBULong(image,1L);
12589 PNGType(chunk,mng_sRGB);
12590 LogPNGChunk(logging,mng_sRGB,1L);
12592 if (image->rendering_intent != UndefinedIntent)
12593 chunk[4]=(unsigned char)
12594 Magick_RenderingIntent_to_PNG_RenderingIntent(
12595 (image->rendering_intent));
12598 chunk[4]=(unsigned char)
12599 Magick_RenderingIntent_to_PNG_RenderingIntent(
12600 (PerceptualIntent));
12602 (void) WriteBlob(image,5,chunk);
12603 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12604 mng_info->have_write_global_srgb=MagickTrue;
12609 if (image->gamma && mng_info->equal_gammas)
12612 Write MNG gAMA chunk
12614 (void) WriteBlobMSBULong(image,4L);
12615 PNGType(chunk,mng_gAMA);
12616 LogPNGChunk(logging,mng_gAMA,4L);
12617 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12618 (void) WriteBlob(image,8,chunk);
12619 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12620 mng_info->have_write_global_gama=MagickTrue;
12622 if (mng_info->equal_chrms)
12628 Write MNG cHRM chunk
12630 (void) WriteBlobMSBULong(image,32L);
12631 PNGType(chunk,mng_cHRM);
12632 LogPNGChunk(logging,mng_cHRM,32L);
12633 primary=image->chromaticity.white_point;
12634 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12635 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12636 primary=image->chromaticity.red_primary;
12637 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12638 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12639 primary=image->chromaticity.green_primary;
12640 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12641 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12642 primary=image->chromaticity.blue_primary;
12643 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12644 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12645 (void) WriteBlob(image,36,chunk);
12646 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12647 mng_info->have_write_global_chrm=MagickTrue;
12650 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12653 Write MNG pHYs chunk
12655 (void) WriteBlobMSBULong(image,9L);
12656 PNGType(chunk,mng_pHYs);
12657 LogPNGChunk(logging,mng_pHYs,9L);
12659 if (image->units == PixelsPerInchResolution)
12661 PNGLong(chunk+4,(png_uint_32)
12662 (image->x_resolution*100.0/2.54+0.5));
12664 PNGLong(chunk+8,(png_uint_32)
12665 (image->y_resolution*100.0/2.54+0.5));
12672 if (image->units == PixelsPerCentimeterResolution)
12674 PNGLong(chunk+4,(png_uint_32)
12675 (image->x_resolution*100.0+0.5));
12677 PNGLong(chunk+8,(png_uint_32)
12678 (image->y_resolution*100.0+0.5));
12685 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12686 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
12690 (void) WriteBlob(image,13,chunk);
12691 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12694 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12695 or does not cover the entire frame.
12697 if (write_mng && (image->matte || image->page.x > 0 ||
12698 image->page.y > 0 || (image->page.width &&
12699 (image->page.width+image->page.x < mng_info->page.width))
12700 || (image->page.height && (image->page.height+image->page.y
12701 < mng_info->page.height))))
12703 (void) WriteBlobMSBULong(image,6L);
12704 PNGType(chunk,mng_BACK);
12705 LogPNGChunk(logging,mng_BACK,6L);
12706 red=ScaleQuantumToShort(image->background_color.red);
12707 green=ScaleQuantumToShort(image->background_color.green);
12708 blue=ScaleQuantumToShort(image->background_color.blue);
12709 PNGShort(chunk+4,red);
12710 PNGShort(chunk+6,green);
12711 PNGShort(chunk+8,blue);
12712 (void) WriteBlob(image,10,chunk);
12713 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12714 if (mng_info->equal_backgrounds)
12716 (void) WriteBlobMSBULong(image,6L);
12717 PNGType(chunk,mng_bKGD);
12718 LogPNGChunk(logging,mng_bKGD,6L);
12719 (void) WriteBlob(image,10,chunk);
12720 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12724 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12725 if ((need_local_plte == MagickFalse) &&
12726 (image->storage_class == PseudoClass) &&
12727 (all_images_are_gray == MagickFalse))
12733 Write MNG PLTE chunk
12735 data_length=3*image->colors;
12736 (void) WriteBlobMSBULong(image,data_length);
12737 PNGType(chunk,mng_PLTE);
12738 LogPNGChunk(logging,mng_PLTE,data_length);
12740 for (i=0; i < (ssize_t) image->colors; i++)
12742 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12743 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12744 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12747 (void) WriteBlob(image,data_length+4,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12749 mng_info->have_write_global_plte=MagickTrue;
12755 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12756 defined(PNG_MNG_FEATURES_SUPPORTED)
12757 mng_info->equal_palettes=MagickFalse;
12761 if (mng_info->adjoin)
12763 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12764 defined(PNG_MNG_FEATURES_SUPPORTED)
12766 If we aren't using a global palette for the entire MNG, check to
12767 see if we can use one for two or more consecutive images.
12769 if (need_local_plte && use_global_plte && !all_images_are_gray)
12771 if (mng_info->IsPalette)
12774 When equal_palettes is true, this image has the same palette
12775 as the previous PseudoClass image
12777 mng_info->have_write_global_plte=mng_info->equal_palettes;
12778 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12779 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12782 Write MNG PLTE chunk
12787 data_length=3*image->colors;
12788 (void) WriteBlobMSBULong(image,data_length);
12789 PNGType(chunk,mng_PLTE);
12790 LogPNGChunk(logging,mng_PLTE,data_length);
12792 for (i=0; i < (ssize_t) image->colors; i++)
12794 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12795 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12796 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12799 (void) WriteBlob(image,data_length+4,chunk);
12800 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12801 (uInt) (data_length+4)));
12802 mng_info->have_write_global_plte=MagickTrue;
12806 mng_info->have_write_global_plte=MagickFalse;
12817 previous_x=mng_info->page.x;
12818 previous_y=mng_info->page.y;
12825 mng_info->page=image->page;
12826 if ((mng_info->page.x != previous_x) ||
12827 (mng_info->page.y != previous_y))
12829 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12830 PNGType(chunk,mng_DEFI);
12831 LogPNGChunk(logging,mng_DEFI,12L);
12832 chunk[4]=0; /* object 0 MSB */
12833 chunk[5]=0; /* object 0 LSB */
12834 chunk[6]=0; /* visible */
12835 chunk[7]=0; /* abstract */
12836 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12837 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12838 (void) WriteBlob(image,16,chunk);
12839 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12844 mng_info->write_mng=write_mng;
12846 if ((int) image->dispose >= 3)
12847 mng_info->framing_mode=3;
12849 if (mng_info->need_fram && mng_info->adjoin &&
12850 ((image->delay != mng_info->delay) ||
12851 (mng_info->framing_mode != mng_info->old_framing_mode)))
12853 if (image->delay == mng_info->delay)
12856 Write a MNG FRAM chunk with the new framing mode.
12858 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12859 PNGType(chunk,mng_FRAM);
12860 LogPNGChunk(logging,mng_FRAM,1L);
12861 chunk[4]=(unsigned char) mng_info->framing_mode;
12862 (void) WriteBlob(image,5,chunk);
12863 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12868 Write a MNG FRAM chunk with the delay.
12870 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12871 PNGType(chunk,mng_FRAM);
12872 LogPNGChunk(logging,mng_FRAM,10L);
12873 chunk[4]=(unsigned char) mng_info->framing_mode;
12874 chunk[5]=0; /* frame name separator (no name) */
12875 chunk[6]=2; /* flag for changing default delay */
12876 chunk[7]=0; /* flag for changing frame timeout */
12877 chunk[8]=0; /* flag for changing frame clipping */
12878 chunk[9]=0; /* flag for changing frame sync_id */
12879 PNGLong(chunk+10,(png_uint_32)
12880 ((mng_info->ticks_per_second*
12881 image->delay)/MagickMax(image->ticks_per_second,1)));
12882 (void) WriteBlob(image,14,chunk);
12883 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12884 mng_info->delay=(png_uint_32) image->delay;
12886 mng_info->old_framing_mode=mng_info->framing_mode;
12889 #if defined(JNG_SUPPORTED)
12890 if (image_info->compression == JPEGCompression)
12895 if (logging != MagickFalse)
12896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12897 " Writing JNG object.");
12898 /* To do: specify the desired alpha compression method. */
12899 write_info=CloneImageInfo(image_info);
12900 write_info->compression=UndefinedCompression;
12901 status=WriteOneJNGImage(mng_info,write_info,image);
12902 write_info=DestroyImageInfo(write_info);
12907 if (logging != MagickFalse)
12908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12909 " Writing PNG object.");
12911 mng_info->need_blob = MagickFalse;
12912 mng_info->ping_preserve_colormap = MagickFalse;
12914 /* We don't want any ancillary chunks written */
12915 mng_info->ping_exclude_bKGD=MagickTrue;
12916 mng_info->ping_exclude_cHRM=MagickTrue;
12917 mng_info->ping_exclude_date=MagickTrue;
12918 mng_info->ping_exclude_EXIF=MagickTrue;
12919 mng_info->ping_exclude_gAMA=MagickTrue;
12920 mng_info->ping_exclude_iCCP=MagickTrue;
12921 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12922 mng_info->ping_exclude_oFFs=MagickTrue;
12923 mng_info->ping_exclude_pHYs=MagickTrue;
12924 mng_info->ping_exclude_sRGB=MagickTrue;
12925 mng_info->ping_exclude_tEXt=MagickTrue;
12926 mng_info->ping_exclude_tRNS=MagickTrue;
12927 mng_info->ping_exclude_vpAg=MagickTrue;
12928 mng_info->ping_exclude_zCCP=MagickTrue;
12929 mng_info->ping_exclude_zTXt=MagickTrue;
12931 status=WriteOnePNGImage(mng_info,image_info,image);
12934 if (status == MagickFalse)
12936 MngInfoFreeStruct(mng_info,&have_mng_structure);
12937 (void) CloseBlob(image);
12938 return(MagickFalse);
12940 (void) CatchImageException(image);
12941 if (GetNextImageInList(image) == (Image *) NULL)
12943 image=SyncNextImageInList(image);
12944 status=SetImageProgress(image,SaveImagesTag,scene++,
12945 GetImageListLength(image));
12947 if (status == MagickFalse)
12950 } while (mng_info->adjoin);
12954 while (GetPreviousImageInList(image) != (Image *) NULL)
12955 image=GetPreviousImageInList(image);
12957 Write the MEND chunk.
12959 (void) WriteBlobMSBULong(image,0x00000000L);
12960 PNGType(chunk,mng_MEND);
12961 LogPNGChunk(logging,mng_MEND,0L);
12962 (void) WriteBlob(image,4,chunk);
12963 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12966 Relinquish resources.
12968 (void) CloseBlob(image);
12969 MngInfoFreeStruct(mng_info,&have_mng_structure);
12971 if (logging != MagickFalse)
12972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12974 return(MagickTrue);
12976 #else /* PNG_LIBPNG_VER > 10011 */
12978 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12981 printf("Your PNG library is too old: You have libpng-%s\n",
12982 PNG_LIBPNG_VER_STRING);
12984 ThrowBinaryException(CoderError,"PNG library is too old",
12985 image_info->filename);
12988 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12990 return(WritePNGImage(image_info,image));
12992 #endif /* PNG_LIBPNG_VER > 10011 */