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***PixelComponent() */
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***PixelComponent() */
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)
8135 /* Worst case is black-and-white; we are looking at every
8139 if (ping_have_color == MagickFalse)
8142 for (x=0; x < (ssize_t) image->columns; x++)
8144 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8145 GetPixelRed(image,s) != GetPixelBlue(image,s))
8147 ping_have_color=MagickTrue;
8148 ping_have_non_bw=MagickTrue;
8151 s+=GetPixelChannels(image);
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;
8165 s+=GetPixelChannels(image);
8172 if (image_colors < 257)
8178 * Initialize image colormap.
8181 if (logging != MagickFalse)
8182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8183 " Sort the new colormap");
8185 /* Sort palette, transparent first */;
8189 for (i=0; i<number_transparent; i++)
8190 colormap[n++] = transparent[i];
8192 for (i=0; i<number_semitransparent; i++)
8193 colormap[n++] = semitransparent[i];
8195 for (i=0; i<number_opaque; i++)
8196 colormap[n++] = opaque[i];
8198 ping_background.index +=
8199 (number_transparent + number_semitransparent);
8201 /* image_colors < 257; search the colormap instead of the pixels
8202 * to get ping_have_color and ping_have_non_bw
8206 if (ping_have_color == MagickFalse)
8208 if (colormap[i].red != colormap[i].green ||
8209 colormap[i].red != colormap[i].blue)
8211 ping_have_color=MagickTrue;
8212 ping_have_non_bw=MagickTrue;
8217 if (ping_have_non_bw == MagickFalse)
8219 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8220 ping_have_non_bw=MagickTrue;
8224 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8225 (number_transparent == 0 && number_semitransparent == 0)) &&
8226 (((mng_info->write_png_colortype-1) ==
8227 PNG_COLOR_TYPE_PALETTE) ||
8228 (mng_info->write_png_colortype == 0)))
8230 if (logging != MagickFalse)
8232 if (n != (ssize_t) image_colors)
8233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8234 " image_colors (%d) and n (%d) don't match",
8237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8238 " AcquireImageColormap");
8241 image->colors = image_colors;
8243 if (AcquireImageColormap(image,image_colors) ==
8245 ThrowWriterException(ResourceLimitError,
8246 "MemoryAllocationFailed");
8248 for (i=0; i< (ssize_t) image_colors; i++)
8249 image->colormap[i] = colormap[i];
8251 if (logging != MagickFalse)
8253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8254 " image->colors=%d (%d)",
8255 (int) image->colors, image_colors);
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " Update the pixel indexes");
8261 /* Sync the pixel indices with the new colormap */
8263 for (y=0; y < (ssize_t) image->rows; y++)
8265 q=GetAuthenticPixels(image,0,y,image->columns,1,
8268 if (q == (const Quantum *) NULL)
8272 for (x=0; x < (ssize_t) image->columns; x++)
8274 for (i=0; i< (ssize_t) image_colors; i++)
8276 if ((image->matte == MagickFalse ||
8277 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8278 image->colormap[i].red == GetPixelRed(image,q) &&
8279 image->colormap[i].green == GetPixelGreen(image,q) &&
8280 image->colormap[i].blue == GetPixelBlue(image,q))
8282 SetPixelIndex(image,i,q);
8286 q+=GetPixelChannels(image);
8289 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8295 if (logging != MagickFalse)
8297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8298 " image->colors=%d", (int) image->colors);
8300 if (image->colormap != NULL)
8302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8303 " i (red,green,blue,alpha)");
8305 for (i=0; i < (ssize_t) image->colors; i++)
8307 if (i < 300 || i >= (ssize_t) image->colors - 10)
8309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8310 " %d (%d,%d,%d,%d)",
8312 (int) image->colormap[i].red,
8313 (int) image->colormap[i].green,
8314 (int) image->colormap[i].blue,
8315 (int) image->colormap[i].alpha);
8320 if (number_transparent < 257)
8321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8322 " number_transparent = %d",
8323 number_transparent);
8326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8327 " number_transparent > 256");
8329 if (number_opaque < 257)
8330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8331 " number_opaque = %d",
8335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8336 " number_opaque > 256");
8338 if (number_semitransparent < 257)
8339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8340 " number_semitransparent = %d",
8341 number_semitransparent);
8344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " number_semitransparent > 256");
8347 if (ping_have_non_bw == MagickFalse)
8348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8349 " All pixels and the background are black or white");
8351 else if (ping_have_color == MagickFalse)
8352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8353 " All pixels and the background are gray");
8356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8357 " At least one pixel or the background is non-gray");
8359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8360 " Exit BUILD_PALETTE:");
8363 if (mng_info->write_png8 == MagickFalse)
8366 /* Make any reductions necessary for the PNG8 format */
8367 if (image_colors <= 256 &&
8368 image_colors != 0 && image->colormap != NULL &&
8369 number_semitransparent == 0 &&
8370 number_transparent <= 1)
8373 /* PNG8 can't have semitransparent colors so we threshold the
8374 * alpha to 0 or OpaqueAlpha
8376 if (number_semitransparent != 0)
8378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " Thresholding the alpha channel to binary");
8381 for (y=0; y < (ssize_t) image->rows; y++)
8383 r=GetAuthenticPixels(image,0,y,image->columns,1,
8386 if (r == (Quantum *) NULL)
8389 for (x=0; x < (ssize_t) image->columns; x++)
8391 if (GetPixelAlpha(image,r) > TransparentAlpha/2)
8393 SetPixelPacket(image,&image->background_color,r);
8394 SetPixelAlpha(image,TransparentAlpha,r);
8397 SetPixelAlpha(image,OpaqueAlpha,r);
8398 r+=GetPixelChannels(image);
8401 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8404 if (image_colors != 0 && image_colors <= 256 &&
8405 image->colormap != NULL)
8406 for (i=0; i<image_colors; i++)
8407 image->colormap[i].alpha =
8408 (image->colormap[i].alpha > TransparentAlpha/2 ?
8409 TransparentAlpha : OpaqueAlpha);
8414 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8415 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8416 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8419 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8421 if (logging != MagickFalse)
8422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8423 " Quantizing the background color to 4-4-4");
8425 tried_444 = MagickTrue;
8427 LBR04PacketRGB(image->background_color);
8429 if (logging != MagickFalse)
8430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8431 " Quantizing the pixel colors to 4-4-4");
8433 if (image->colormap == NULL)
8435 for (y=0; y < (ssize_t) image->rows; y++)
8437 r=GetAuthenticPixels(image,0,y,image->columns,1,
8440 if (r == (Quantum *) NULL)
8443 for (x=0; x < (ssize_t) image->columns; x++)
8445 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8450 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8455 else /* Should not reach this; colormap already exists and
8458 if (logging != MagickFalse)
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " Quantizing the colormap to 4-4-4");
8462 for (i=0; i<image_colors; i++)
8464 LBR04PacketRGB(image->colormap[i]);
8470 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8472 if (logging != MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " Quantizing the background color to 3-3-3");
8476 tried_333 = MagickTrue;
8478 LBR03PacketRGB(image->background_color);
8480 if (logging != MagickFalse)
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " Quantizing the pixel colors to 3-3-3-1");
8484 if (image->colormap == NULL)
8486 for (y=0; y < (ssize_t) image->rows; y++)
8488 r=GetAuthenticPixels(image,0,y,image->columns,1,
8491 if (r == (Quantum *) NULL)
8494 for (x=0; x < (ssize_t) image->columns; x++)
8496 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8501 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8506 else /* Should not reach this; colormap already exists and
8509 if (logging != MagickFalse)
8510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8511 " Quantizing the colormap to 3-3-3-1");
8512 for (i=0; i<image_colors; i++)
8514 LBR03PacketRGB(image->colormap[i]);
8520 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8522 if (logging != MagickFalse)
8523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8524 " Quantizing the background color to 3-3-2");
8526 tried_332 = MagickTrue;
8528 /* Red and green were already done so we only quantize the blue
8532 LBR02PacketBlue(image->background_color);
8534 if (logging != MagickFalse)
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " Quantizing the pixel colors to 3-3-2-1");
8538 if (image->colormap == NULL)
8540 for (y=0; y < (ssize_t) image->rows; y++)
8542 r=GetAuthenticPixels(image,0,y,image->columns,1,
8545 if (r == (Quantum *) NULL)
8548 for (x=0; x < (ssize_t) image->columns; x++)
8550 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8555 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8560 else /* Should not reach this; colormap already exists and
8563 if (logging != MagickFalse)
8564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8565 " Quantizing the colormap to 3-3-2-1");
8566 for (i=0; i<image_colors; i++)
8568 LBR02PacketBlue(image->colormap[i]);
8575 if (image_colors == 0 || image_colors > 256)
8577 /* Take care of special case with 256 colors + 1 transparent
8578 * color. We don't need to quantize to 2-3-2-1; we only need to
8579 * eliminate one color, so we'll merge the two darkest red
8580 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8582 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8583 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8584 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8586 image->background_color.red=ScaleCharToQuantum(0x24);
8589 if (image->colormap == NULL)
8591 for (y=0; y < (ssize_t) image->rows; y++)
8593 r=GetAuthenticPixels(image,0,y,image->columns,1,
8596 if (r == (Quantum *) NULL)
8599 for (x=0; x < (ssize_t) image->columns; x++)
8601 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8602 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8603 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8604 GetPixelAlpha(image,r) == OpaqueAlpha)
8606 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8608 r+=GetPixelChannels(image);
8611 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8619 for (i=0; i<image_colors; i++)
8621 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8622 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8623 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8625 image->colormap[i].red=ScaleCharToQuantum(0x24);
8631 /* END OF BUILD_PALETTE */
8633 /* If we are excluding the tRNS chunk and there is transparency,
8634 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8637 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8638 (number_transparent != 0 || number_semitransparent != 0))
8640 unsigned int colortype=mng_info->write_png_colortype;
8642 if (ping_have_color == MagickFalse)
8643 mng_info->write_png_colortype = 5;
8646 mng_info->write_png_colortype = 7;
8648 if (colortype != 0 &&
8649 mng_info->write_png_colortype != colortype)
8650 ping_need_colortype_warning=MagickTrue;
8654 /* See if cheap transparency is possible. It is only possible
8655 * when there is a single transparent color, no semitransparent
8656 * color, and no opaque color that has the same RGB components
8657 * as the transparent color. We only need this information if
8658 * we are writing a PNG with colortype 0 or 2, and we have not
8659 * excluded the tRNS chunk.
8661 if (number_transparent == 1 &&
8662 mng_info->write_png_colortype < 4)
8664 ping_have_cheap_transparency = MagickTrue;
8666 if (number_semitransparent != 0)
8667 ping_have_cheap_transparency = MagickFalse;
8669 else if (image_colors == 0 || image_colors > 256 ||
8670 image->colormap == NULL)
8675 register const Quantum
8678 exception=(&image->exception);
8680 for (y=0; y < (ssize_t) image->rows; y++)
8682 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8684 if (q == (const Quantum *) NULL)
8687 for (x=0; x < (ssize_t) image->columns; x++)
8689 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8690 (unsigned short) GetPixelRed(image,q) ==
8691 ping_trans_color.red &&
8692 (unsigned short) GetPixelGreen(image,q) ==
8693 ping_trans_color.green &&
8694 (unsigned short) GetPixelBlue(image,q) ==
8695 ping_trans_color.blue)
8697 ping_have_cheap_transparency = MagickFalse;
8701 q+=GetPixelChannels(image);
8704 if (ping_have_cheap_transparency == MagickFalse)
8710 /* Assuming that image->colormap[0] is the one transparent color
8711 * and that all others are opaque.
8713 if (image_colors > 1)
8714 for (i=1; i<image_colors; i++)
8715 if (image->colormap[i].red == image->colormap[0].red &&
8716 image->colormap[i].green == image->colormap[0].green &&
8717 image->colormap[i].blue == image->colormap[0].blue)
8719 ping_have_cheap_transparency = MagickFalse;
8724 if (logging != MagickFalse)
8726 if (ping_have_cheap_transparency == MagickFalse)
8727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8728 " Cheap transparency is not possible.");
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " Cheap transparency is possible.");
8736 ping_have_cheap_transparency = MagickFalse;
8738 image_depth=image->depth;
8740 quantum_info = (QuantumInfo *) NULL;
8742 image_colors=(int) image->colors;
8743 image_matte=image->matte;
8745 mng_info->IsPalette=image->storage_class == PseudoClass &&
8746 image_colors <= 256 && image->colormap != NULL;
8748 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8749 (image->colors == 0 || image->colormap == NULL))
8751 image_info=DestroyImageInfo(image_info);
8752 image=DestroyImage(image);
8753 (void) ThrowMagickException(&IMimage->exception,
8754 GetMagickModule(),CoderError,
8755 "Cannot write PNG8 or color-type 3; colormap is NULL",
8756 "`%s'",IMimage->filename);
8757 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8758 UnlockSemaphoreInfo(ping_semaphore);
8760 return(MagickFalse);
8764 Allocate the PNG structures
8766 #ifdef PNG_USER_MEM_SUPPORTED
8767 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8768 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8769 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8772 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8773 MagickPNGErrorHandler,MagickPNGWarningHandler);
8776 if (ping == (png_struct *) NULL)
8777 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8779 ping_info=png_create_info_struct(ping);
8781 if (ping_info == (png_info *) NULL)
8783 png_destroy_write_struct(&ping,(png_info **) NULL);
8784 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8787 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8788 ping_pixels=(unsigned char *) NULL;
8790 if (setjmp(png_jmpbuf(ping)))
8796 if (image_info->verbose)
8797 (void) printf("PNG write has failed.\n");
8799 png_destroy_write_struct(&ping,&ping_info);
8800 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8801 UnlockSemaphoreInfo(ping_semaphore);
8803 if (ping_have_blob != MagickFalse)
8804 (void) CloseBlob(image);
8805 image_info=DestroyImageInfo(image_info);
8806 image=DestroyImage(image);
8807 return(MagickFalse);
8810 Prepare PNG for writing.
8812 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8813 if (mng_info->write_mng)
8814 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8817 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8818 if (mng_info->write_mng)
8819 png_permit_empty_plte(ping,MagickTrue);
8826 ping_width=(png_uint_32) image->columns;
8827 ping_height=(png_uint_32) image->rows;
8829 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8832 if (mng_info->write_png_depth != 0)
8833 image_depth=mng_info->write_png_depth;
8835 /* Adjust requested depth to next higher valid depth if necessary */
8836 if (image_depth > 8)
8839 if ((image_depth > 4) && (image_depth < 8))
8842 if (image_depth == 3)
8845 if (logging != MagickFalse)
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " width=%.20g",(double) ping_width);
8849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8850 " height=%.20g",(double) ping_height);
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " image_matte=%.20g",(double) image->matte);
8853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854 " image->depth=%.20g",(double) image->depth);
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8859 save_image_depth=image_depth;
8860 ping_bit_depth=(png_byte) save_image_depth;
8863 #if defined(PNG_pHYs_SUPPORTED)
8864 if (ping_exclude_pHYs == MagickFalse)
8866 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8867 (!mng_info->write_mng || !mng_info->equal_physs))
8869 if (logging != MagickFalse)
8870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8871 " Setting up pHYs chunk");
8873 if (image->units == PixelsPerInchResolution)
8875 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8876 ping_pHYs_x_resolution=
8877 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8878 ping_pHYs_y_resolution=
8879 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8882 else if (image->units == PixelsPerCentimeterResolution)
8884 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8885 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8886 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8891 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8892 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8893 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8896 if (logging != MagickFalse)
8897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8898 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8899 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8900 (int) ping_pHYs_unit_type);
8901 ping_have_pHYs = MagickTrue;
8906 if (ping_exclude_bKGD == MagickFalse)
8908 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8914 if (ping_bit_depth == 8)
8917 if (ping_bit_depth == 4)
8920 if (ping_bit_depth == 2)
8923 if (ping_bit_depth == 1)
8926 ping_background.red=(png_uint_16)
8927 (ScaleQuantumToShort(image->background_color.red) & mask);
8929 ping_background.green=(png_uint_16)
8930 (ScaleQuantumToShort(image->background_color.green) & mask);
8932 ping_background.blue=(png_uint_16)
8933 (ScaleQuantumToShort(image->background_color.blue) & mask);
8935 ping_background.gray=(png_uint_16) ping_background.green;
8938 if (logging != MagickFalse)
8940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8941 " Setting up bKGD chunk (1)");
8942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8943 " background_color index is %d",
8944 (int) ping_background.index);
8946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8947 " ping_bit_depth=%d",ping_bit_depth);
8950 ping_have_bKGD = MagickTrue;
8954 Select the color type.
8959 if (mng_info->IsPalette && mng_info->write_png8)
8962 /* To do: make this a function cause it's used twice, except
8963 for reducing the sample depth from 8. */
8965 number_colors=image_colors;
8967 ping_have_tRNS=MagickFalse;
8972 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8974 if (logging != MagickFalse)
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " Setting up PLTE chunk with %d colors (%d)",
8977 number_colors, image_colors);
8979 for (i=0; i < (ssize_t) number_colors; i++)
8981 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8982 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8983 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8984 if (logging != MagickFalse)
8985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8986 #if MAGICKCORE_QUANTUM_DEPTH == 8
8987 " %3ld (%3d,%3d,%3d)",
8989 " %5ld (%5d,%5d,%5d)",
8991 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8995 ping_have_PLTE=MagickTrue;
8996 image_depth=ping_bit_depth;
8999 if (matte != MagickFalse)
9002 Identify which colormap entry is transparent.
9004 assert(number_colors <= 256);
9005 assert(image->colormap != NULL);
9007 for (i=0; i < (ssize_t) number_transparent; i++)
9008 ping_trans_alpha[i]=0;
9011 ping_num_trans=(unsigned short) (number_transparent +
9012 number_semitransparent);
9014 if (ping_num_trans == 0)
9015 ping_have_tRNS=MagickFalse;
9018 ping_have_tRNS=MagickTrue;
9021 if (ping_exclude_bKGD == MagickFalse)
9024 * Identify which colormap entry is the background color.
9027 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9028 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9031 ping_background.index=(png_byte) i;
9033 if (logging != MagickFalse)
9035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9036 " background_color index is %d",
9037 (int) ping_background.index);
9040 } /* end of write_png8 */
9042 else if (mng_info->write_png24)
9044 image_matte=MagickFalse;
9045 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9048 else if (mng_info->write_png32)
9050 image_matte=MagickTrue;
9051 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9054 else /* mng_info->write_pngNN not specified */
9056 image_depth=ping_bit_depth;
9058 if (mng_info->write_png_colortype != 0)
9060 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9062 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9063 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9064 image_matte=MagickTrue;
9067 image_matte=MagickFalse;
9069 if (logging != MagickFalse)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " PNG colortype %d was specified:",(int) ping_color_type);
9074 else /* write_png_colortype not specified */
9076 if (logging != MagickFalse)
9077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9078 " Selecting PNG colortype:");
9080 ping_color_type=(png_byte) ((matte != MagickFalse)?
9081 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9083 if (image_info->type == TrueColorType)
9085 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9086 image_matte=MagickFalse;
9089 if (image_info->type == TrueColorMatteType)
9091 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9092 image_matte=MagickTrue;
9095 if (image_info->type == PaletteType ||
9096 image_info->type == PaletteMatteType)
9097 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9099 if (mng_info->write_png_colortype == 0 &&
9100 (image_info->type == UndefinedType ||
9101 image_info->type == OptimizeType))
9103 if (ping_have_color == MagickFalse)
9105 if (image_matte == MagickFalse)
9107 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9108 image_matte=MagickFalse;
9113 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9114 image_matte=MagickTrue;
9119 if (image_matte == MagickFalse)
9121 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9122 image_matte=MagickFalse;
9127 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9128 image_matte=MagickTrue;
9135 if (logging != MagickFalse)
9136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137 " Selected PNG colortype=%d",ping_color_type);
9139 if (ping_bit_depth < 8)
9141 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9142 ping_color_type == PNG_COLOR_TYPE_RGB ||
9143 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9147 old_bit_depth=ping_bit_depth;
9149 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9151 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9155 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9160 if (image->colors == 0)
9163 (void) ThrowMagickException(&image->exception,
9164 GetMagickModule(),CoderError,
9165 "image has 0 colors", "`%s'","");
9168 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9169 ping_bit_depth <<= 1;
9172 if (logging != MagickFalse)
9174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9175 " Number of colors: %.20g",(double) image_colors);
9177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9178 " Tentative PNG bit depth: %d",ping_bit_depth);
9181 if (ping_bit_depth < (int) mng_info->write_png_depth)
9182 ping_bit_depth = mng_info->write_png_depth;
9185 image_depth=ping_bit_depth;
9187 if (logging != MagickFalse)
9189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9190 " Tentative PNG color type: %.20g",(double) ping_color_type);
9192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9193 " image_info->type: %.20g",(double) image_info->type);
9195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9196 " image_depth: %.20g",(double) image_depth);
9198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200 " image->depth: %.20g",(double) image->depth);
9202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9203 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9206 if (matte != MagickFalse)
9208 if (mng_info->IsPalette)
9210 if (mng_info->write_png_colortype == 0)
9212 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9214 if (ping_have_color != MagickFalse)
9215 ping_color_type=PNG_COLOR_TYPE_RGBA;
9219 * Determine if there is any transparent color.
9221 if (number_transparent + number_semitransparent == 0)
9224 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9227 image_matte=MagickFalse;
9229 if (mng_info->write_png_colortype == 0)
9230 ping_color_type&=0x03;
9240 if (ping_bit_depth == 8)
9243 if (ping_bit_depth == 4)
9246 if (ping_bit_depth == 2)
9249 if (ping_bit_depth == 1)
9252 ping_trans_color.red=(png_uint_16)
9253 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9255 ping_trans_color.green=(png_uint_16)
9256 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9258 ping_trans_color.blue=(png_uint_16)
9259 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9261 ping_trans_color.gray=(png_uint_16)
9262 (ScaleQuantumToShort(GetPixelPacketIntensity(
9263 image->colormap)) & mask);
9265 ping_trans_color.index=(png_byte) 0;
9267 ping_have_tRNS=MagickTrue;
9270 if (ping_have_tRNS != MagickFalse)
9273 * Determine if there is one and only one transparent color
9274 * and if so if it is fully transparent.
9276 if (ping_have_cheap_transparency == MagickFalse)
9277 ping_have_tRNS=MagickFalse;
9280 if (ping_have_tRNS != MagickFalse)
9282 if (mng_info->write_png_colortype == 0)
9283 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9285 if (image_depth == 8)
9287 ping_trans_color.red&=0xff;
9288 ping_trans_color.green&=0xff;
9289 ping_trans_color.blue&=0xff;
9290 ping_trans_color.gray&=0xff;
9296 if (image_depth == 8)
9298 ping_trans_color.red&=0xff;
9299 ping_trans_color.green&=0xff;
9300 ping_trans_color.blue&=0xff;
9301 ping_trans_color.gray&=0xff;
9308 if (ping_have_tRNS != MagickFalse)
9309 image_matte=MagickFalse;
9311 if ((mng_info->IsPalette) &&
9312 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9313 ping_have_color == MagickFalse &&
9314 (image_matte == MagickFalse || image_depth >= 8))
9318 if (image_matte != MagickFalse)
9319 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9321 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9323 ping_color_type=PNG_COLOR_TYPE_GRAY;
9325 if (save_image_depth == 16 && image_depth == 8)
9327 if (logging != MagickFalse)
9329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9330 " Scaling ping_trans_color (0)");
9332 ping_trans_color.gray*=0x0101;
9336 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9337 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9339 if ((image_colors == 0) ||
9340 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9341 image_colors=(int) (one << image_depth);
9343 if (image_depth > 8)
9349 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9351 if(!mng_info->write_png_depth)
9355 while ((int) (one << ping_bit_depth)
9356 < (ssize_t) image_colors)
9357 ping_bit_depth <<= 1;
9361 else if (ping_color_type ==
9362 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9363 mng_info->IsPalette)
9365 /* Check if grayscale is reducible */
9368 depth_4_ok=MagickTrue,
9369 depth_2_ok=MagickTrue,
9370 depth_1_ok=MagickTrue;
9372 for (i=0; i < (ssize_t) image_colors; i++)
9377 intensity=ScaleQuantumToChar(image->colormap[i].red);
9379 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9380 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9381 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9382 depth_2_ok=depth_1_ok=MagickFalse;
9383 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9384 depth_1_ok=MagickFalse;
9387 if (depth_1_ok && mng_info->write_png_depth <= 1)
9390 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9393 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9398 image_depth=ping_bit_depth;
9403 if (mng_info->IsPalette)
9405 number_colors=image_colors;
9407 if (image_depth <= 8)
9412 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9414 if (mng_info->have_write_global_plte && matte == MagickFalse)
9416 png_set_PLTE(ping,ping_info,NULL,0);
9418 if (logging != MagickFalse)
9419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9420 " Setting up empty PLTE chunk");
9425 for (i=0; i < (ssize_t) number_colors; i++)
9427 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9428 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9429 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9432 if (logging != MagickFalse)
9433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9434 " Setting up PLTE chunk with %d colors",
9437 ping_have_PLTE=MagickTrue;
9440 /* color_type is PNG_COLOR_TYPE_PALETTE */
9441 if (mng_info->write_png_depth == 0)
9449 while ((one << ping_bit_depth) < (ssize_t) number_colors)
9450 ping_bit_depth <<= 1;
9455 if (matte != MagickFalse)
9458 * Set up trans_colors array.
9460 assert(number_colors <= 256);
9462 ping_num_trans=(unsigned short) (number_transparent +
9463 number_semitransparent);
9465 if (ping_num_trans == 0)
9466 ping_have_tRNS=MagickFalse;
9470 if (logging != MagickFalse)
9472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9473 " Scaling ping_trans_color (1)");
9475 ping_have_tRNS=MagickTrue;
9477 for (i=0; i < ping_num_trans; i++)
9479 ping_trans_alpha[i]= (png_byte)
9480 ScaleQuantumToChar(image->colormap[i].alpha);
9490 if (image_depth < 8)
9493 if ((save_image_depth == 16) && (image_depth == 8))
9495 if (logging != MagickFalse)
9497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9498 " Scaling ping_trans_color from (%d,%d,%d)",
9499 (int) ping_trans_color.red,
9500 (int) ping_trans_color.green,
9501 (int) ping_trans_color.blue);
9504 ping_trans_color.red*=0x0101;
9505 ping_trans_color.green*=0x0101;
9506 ping_trans_color.blue*=0x0101;
9507 ping_trans_color.gray*=0x0101;
9509 if (logging != MagickFalse)
9511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9513 (int) ping_trans_color.red,
9514 (int) ping_trans_color.green,
9515 (int) ping_trans_color.blue);
9520 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9521 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
9524 Adjust background and transparency samples in sub-8-bit grayscale files.
9526 if (ping_bit_depth < 8 && ping_color_type ==
9527 PNG_COLOR_TYPE_GRAY)
9535 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9537 if (ping_exclude_bKGD == MagickFalse)
9540 ping_background.gray=(png_uint_16)
9541 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9544 if (logging != MagickFalse)
9545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9546 " Setting up bKGD chunk (2)");
9547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9548 " background_color index is %d",
9549 (int) ping_background.index);
9551 ping_have_bKGD = MagickTrue;
9554 if (logging != MagickFalse)
9555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556 " Scaling ping_trans_color.gray from %d",
9557 (int)ping_trans_color.gray);
9559 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9560 ping_trans_color.gray)+.5);
9562 if (logging != MagickFalse)
9563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9564 " to %d", (int)ping_trans_color.gray);
9567 if (ping_exclude_bKGD == MagickFalse)
9569 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9572 Identify which colormap entry is the background color.
9575 number_colors=image_colors;
9577 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9578 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9581 ping_background.index=(png_byte) i;
9583 if (logging != MagickFalse)
9585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9586 " Setting up bKGD chunk with index=%d",(int) i);
9589 if (i < (ssize_t) number_colors)
9591 ping_have_bKGD = MagickTrue;
9593 if (logging != MagickFalse)
9595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9596 " background =(%d,%d,%d)",
9597 (int) ping_background.red,
9598 (int) ping_background.green,
9599 (int) ping_background.blue);
9603 else /* Can't happen */
9605 if (logging != MagickFalse)
9606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9607 " No room in PLTE to add bKGD color");
9608 ping_have_bKGD = MagickFalse;
9613 if (logging != MagickFalse)
9614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9615 " PNG color type: %d",ping_color_type);
9617 Initialize compression level and filtering.
9619 if (logging != MagickFalse)
9621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9622 " Setting up deflate compression");
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625 " Compression buffer size: 32768");
9628 png_set_compression_buffer_size(ping,32768L);
9630 if (logging != MagickFalse)
9631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9632 " Compression mem level: 9");
9634 /* Untangle the "-quality" setting:
9636 Undefined is 0; the default is used.
9641 0: Use Z_HUFFMAN_ONLY strategy with the
9642 zlib default compression level
9644 1-9: the zlib compression level
9648 0-4: the PNG filter method
9650 5: libpng adaptive filtering if compression level > 5
9651 libpng filter type "none" if compression level <= 5
9652 or if image is grayscale or palette
9654 6: libpng adaptive filtering
9656 7: "LOCO" filtering (intrapixel differing) if writing
9657 a MNG, othewise "none". Did not work in IM-6.7.0-9
9658 and earlier because of a missing "else".
9660 8: Z_RLE strategy, all filters
9661 Unused prior to IM-6.7.0-10, was same as 6
9663 9: Z_RLE strategy, no PNG filters
9664 Unused prior to IM-6.7.0-10, was same as 6
9666 Note that using the -quality option, not all combinations of
9667 PNG filter type, zlib compression level, and zlib compression
9668 strategy are possible. This will be addressed soon in a
9669 release that accomodates "-define PNG:compression-strategy",
9674 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9679 if (mng_info->write_png_compression_strategy == 0)
9680 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9683 else if (mng_info->write_png_compression_level == 0)
9688 level=(int) MagickMin((ssize_t) quality/10,9);
9690 mng_info->write_png_compression_level = level+1;
9693 if (mng_info->write_png_compression_strategy == 0)
9695 if ((quality %10) == 8 || (quality %10) == 9)
9696 mng_info->write_png_compression_strategy=Z_RLE;
9699 if (mng_info->write_png_compression_filter == 0)
9700 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9702 if (logging != MagickFalse)
9704 if (mng_info->write_png_compression_level)
9705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9706 " Compression level: %d",
9707 (int) mng_info->write_png_compression_level-1);
9709 if (mng_info->write_png_compression_strategy)
9710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9711 " Compression strategy: %d",
9712 (int) mng_info->write_png_compression_strategy-1);
9714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9715 " Setting up filtering");
9717 if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
9718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9719 " Base filter method: ADAPTIVE");
9720 else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
9721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9722 " Base filter method: NONE");
9724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9725 " Base filter method: %d",
9726 (int) mng_info->write_png_compression_filter-1);
9729 if (mng_info->write_png_compression_level != 0)
9730 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9732 if (mng_info->write_png_compression_filter == 6)
9734 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9735 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9737 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9739 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9742 if (mng_info->write_png_compression_filter == 7 ||
9743 mng_info->write_png_compression_filter == 10)
9744 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9746 else if (mng_info->write_png_compression_filter == 8)
9748 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9749 if (mng_info->write_mng)
9751 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9752 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9753 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9756 png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9759 else if (mng_info->write_png_compression_filter == 9)
9760 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9762 else if (mng_info->write_png_compression_filter != 0)
9763 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9764 mng_info->write_png_compression_filter-1);
9766 if (mng_info->write_png_compression_strategy != 0)
9767 png_set_compression_strategy(ping,
9768 mng_info->write_png_compression_strategy-1);
9771 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9772 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9774 ResetImageProfileIterator(image);
9775 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9777 profile=GetImageProfile(image,name);
9779 if (profile != (StringInfo *) NULL)
9781 #ifdef PNG_WRITE_iCCP_SUPPORTED
9782 if ((LocaleCompare(name,"ICC") == 0) ||
9783 (LocaleCompare(name,"ICM") == 0))
9786 if (ping_exclude_iCCP == MagickFalse)
9788 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9789 #if (PNG_LIBPNG_VER < 10500)
9790 (png_charp) GetStringInfoDatum(profile),
9792 (png_const_bytep) GetStringInfoDatum(profile),
9794 (png_uint_32) GetStringInfoLength(profile));
9800 if (ping_exclude_zCCP == MagickFalse)
9802 Magick_png_write_raw_profile(image_info,ping,ping_info,
9803 (unsigned char *) name,(unsigned char *) name,
9804 GetStringInfoDatum(profile),
9805 (png_uint_32) GetStringInfoLength(profile));
9809 if (logging != MagickFalse)
9810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9811 " Setting up text chunk with %s profile",name);
9813 name=GetNextImageProfile(image);
9817 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9818 if ((mng_info->have_write_global_srgb == 0) &&
9819 ((image->rendering_intent != UndefinedIntent) ||
9820 (image->colorspace == sRGBColorspace)))
9822 if (ping_exclude_sRGB == MagickFalse)
9825 Note image rendering intent.
9827 if (logging != MagickFalse)
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Setting up sRGB chunk");
9831 (void) png_set_sRGB(ping,ping_info,(
9832 Magick_RenderingIntent_to_PNG_RenderingIntent(
9833 image->rendering_intent)));
9835 if (ping_exclude_gAMA == MagickFalse)
9836 png_set_gAMA(ping,ping_info,0.45455);
9840 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9843 if (ping_exclude_gAMA == MagickFalse &&
9844 (ping_exclude_sRGB == MagickFalse ||
9845 (image->gamma < .45 || image->gamma > .46)))
9847 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9851 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9853 if (logging != MagickFalse)
9854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9855 " Setting up gAMA chunk");
9857 png_set_gAMA(ping,ping_info,image->gamma);
9861 if (ping_exclude_cHRM == MagickFalse)
9863 if ((mng_info->have_write_global_chrm == 0) &&
9864 (image->chromaticity.red_primary.x != 0.0))
9867 Note image chromaticity.
9868 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9876 wp=image->chromaticity.white_point;
9877 rp=image->chromaticity.red_primary;
9878 gp=image->chromaticity.green_primary;
9879 bp=image->chromaticity.blue_primary;
9881 if (logging != MagickFalse)
9882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9883 " Setting up cHRM chunk");
9885 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9891 ping_interlace_method=image_info->interlace != NoInterlace;
9893 if (mng_info->write_mng)
9894 png_set_sig_bytes(ping,8);
9896 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9898 if (mng_info->write_png_colortype != 0)
9900 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9901 if (ping_have_color != MagickFalse)
9903 ping_color_type = PNG_COLOR_TYPE_RGB;
9905 if (ping_bit_depth < 8)
9909 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9910 if (ping_have_color != MagickFalse)
9911 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9914 if (ping_need_colortype_warning != MagickFalse ||
9915 ((mng_info->write_png_depth &&
9916 (int) mng_info->write_png_depth != ping_bit_depth) ||
9917 (mng_info->write_png_colortype &&
9918 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9919 mng_info->write_png_colortype != 7 &&
9920 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9922 if (logging != MagickFalse)
9924 if (ping_need_colortype_warning != MagickFalse)
9926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9927 " Image has transparency but tRNS chunk was excluded");
9930 if (mng_info->write_png_depth)
9932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9933 " Defined PNG:bit-depth=%u, Computed depth=%u",
9934 mng_info->write_png_depth,
9938 if (mng_info->write_png_colortype)
9940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9941 " Defined PNG:color-type=%u, Computed color type=%u",
9942 mng_info->write_png_colortype-1,
9948 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9951 if (image_matte != MagickFalse && image->matte == MagickFalse)
9953 /* Add an opaque matte channel */
9954 image->matte = MagickTrue;
9955 (void) SetImageOpacity(image,0);
9957 if (logging != MagickFalse)
9958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9959 " Added an opaque matte channel");
9962 if (number_transparent != 0 || number_semitransparent != 0)
9964 if (ping_color_type < 4)
9966 ping_have_tRNS=MagickTrue;
9967 if (logging != MagickFalse)
9968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9969 " Setting ping_have_tRNS=MagickTrue.");
9973 if (logging != MagickFalse)
9974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9975 " Writing PNG header chunks");
9977 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9978 ping_bit_depth,ping_color_type,
9979 ping_interlace_method,ping_compression_method,
9980 ping_filter_method);
9982 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9984 png_set_PLTE(ping,ping_info,palette,number_colors);
9986 if (logging != MagickFalse)
9988 for (i=0; i< (ssize_t) number_colors; i++)
9990 if (i < ping_num_trans)
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9994 (int) palette[i].red,
9995 (int) palette[i].green,
9996 (int) palette[i].blue,
9998 (int) ping_trans_alpha[i]);
10000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10001 " PLTE[%d] = (%d,%d,%d)",
10003 (int) palette[i].red,
10004 (int) palette[i].green,
10005 (int) palette[i].blue);
10010 if (ping_exclude_bKGD == MagickFalse)
10012 if (ping_have_bKGD != MagickFalse)
10014 png_set_bKGD(ping,ping_info,&ping_background);
10017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10018 " Setting up bKGD chunk");
10019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10020 " background color = (%d,%d,%d)",
10021 (int) ping_background.red,
10022 (int) ping_background.green,
10023 (int) ping_background.blue);
10024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10025 " index = %d, gray=%d",
10026 (int) ping_background.index,
10027 (int) ping_background.gray);
10032 if (ping_exclude_pHYs == MagickFalse)
10034 if (ping_have_pHYs != MagickFalse)
10036 png_set_pHYs(ping,ping_info,
10037 ping_pHYs_x_resolution,
10038 ping_pHYs_y_resolution,
10039 ping_pHYs_unit_type);
10043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044 " Setting up pHYs chunk");
10045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10046 " x_resolution=%lu",
10047 (unsigned long) ping_pHYs_x_resolution);
10048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10049 " y_resolution=%lu",
10050 (unsigned long) ping_pHYs_y_resolution);
10051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10053 (unsigned long) ping_pHYs_unit_type);
10058 #if defined(PNG_oFFs_SUPPORTED)
10059 if (ping_exclude_oFFs == MagickFalse)
10061 if (image->page.x || image->page.y)
10063 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10064 (png_int_32) image->page.y, 0);
10066 if (logging != MagickFalse)
10067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10068 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10069 (int) image->page.x, (int) image->page.y);
10074 if (mng_info->need_blob != MagickFalse)
10076 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10078 png_error(ping,"WriteBlob Failed");
10080 ping_have_blob=MagickTrue;
10083 png_write_info_before_PLTE(ping, ping_info);
10085 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10087 if (logging != MagickFalse)
10089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10090 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10093 if (ping_color_type == 3)
10094 (void) png_set_tRNS(ping, ping_info,
10101 (void) png_set_tRNS(ping, ping_info,
10104 &ping_trans_color);
10106 if (logging != MagickFalse)
10108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10109 " tRNS color =(%d,%d,%d)",
10110 (int) ping_trans_color.red,
10111 (int) ping_trans_color.green,
10112 (int) ping_trans_color.blue);
10117 /* write any png-chunk-b profiles */
10118 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10120 png_write_info(ping,ping_info);
10122 /* write any PNG-chunk-m profiles */
10123 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10125 if (ping_exclude_vpAg == MagickFalse)
10127 if ((image->page.width != 0 && image->page.width != image->columns) ||
10128 (image->page.height != 0 && image->page.height != image->rows))
10133 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10134 PNGType(chunk,mng_vpAg);
10135 LogPNGChunk(logging,mng_vpAg,9L);
10136 PNGLong(chunk+4,(png_uint_32) image->page.width);
10137 PNGLong(chunk+8,(png_uint_32) image->page.height);
10138 chunk[12]=0; /* unit = pixels */
10139 (void) WriteBlob(image,13,chunk);
10140 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10144 #if (PNG_LIBPNG_VER == 10206)
10145 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10146 #define PNG_HAVE_IDAT 0x04
10147 ping->mode |= PNG_HAVE_IDAT;
10148 #undef PNG_HAVE_IDAT
10151 png_set_packing(ping);
10155 rowbytes=image->columns;
10156 if (image_depth > 8)
10158 switch (ping_color_type)
10160 case PNG_COLOR_TYPE_RGB:
10164 case PNG_COLOR_TYPE_GRAY_ALPHA:
10168 case PNG_COLOR_TYPE_RGBA:
10176 if (logging != MagickFalse)
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179 " Writing PNG image data");
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10184 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10185 sizeof(*ping_pixels));
10187 if (ping_pixels == (unsigned char *) NULL)
10188 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10191 Initialize image scanlines.
10193 if (setjmp(png_jmpbuf(ping)))
10199 if (image_info->verbose)
10200 (void) printf("PNG write has failed.\n");
10202 png_destroy_write_struct(&ping,&ping_info);
10203 if (quantum_info != (QuantumInfo *) NULL)
10204 quantum_info=DestroyQuantumInfo(quantum_info);
10205 if (ping_pixels != (unsigned char *) NULL)
10206 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10207 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10208 UnlockSemaphoreInfo(ping_semaphore);
10210 if (ping_have_blob != MagickFalse)
10211 (void) CloseBlob(image);
10212 image_info=DestroyImageInfo(image_info);
10213 image=DestroyImage(image);
10214 return(MagickFalse);
10216 quantum_info=AcquireQuantumInfo(image_info,image);
10217 if (quantum_info == (QuantumInfo *) NULL)
10218 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10219 quantum_info->format=UndefinedQuantumFormat;
10220 quantum_info->depth=image_depth;
10221 num_passes=png_set_interlace_handling(ping);
10223 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10224 !mng_info->write_png32) &&
10225 (mng_info->IsPalette ||
10226 (image_info->type == BilevelType)) &&
10227 image_matte == MagickFalse &&
10228 ping_have_non_bw == MagickFalse)
10230 /* Palette, Bilevel, or Opaque Monochrome */
10231 register const Quantum
10234 quantum_info->depth=8;
10235 for (pass=0; pass < num_passes; pass++)
10238 Convert PseudoClass image to a PNG monochrome image.
10240 for (y=0; y < (ssize_t) image->rows; y++)
10242 if (logging != MagickFalse && y == 0)
10243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10244 " Writing row of pixels (0)");
10246 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10248 if (p == (const Quantum *) NULL)
10251 if (mng_info->IsPalette)
10253 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10254 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10255 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10256 mng_info->write_png_depth &&
10257 mng_info->write_png_depth != old_bit_depth)
10259 /* Undo pixel scaling */
10260 for (i=0; i < (ssize_t) image->columns; i++)
10261 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10262 >> (8-old_bit_depth));
10268 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10269 quantum_info,RedQuantum,ping_pixels,&image->exception);
10272 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10273 for (i=0; i < (ssize_t) image->columns; i++)
10274 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10277 if (logging != MagickFalse && y == 0)
10278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10279 " Writing row of pixels (1)");
10281 png_write_row(ping,ping_pixels);
10283 if (image->previous == (Image *) NULL)
10285 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10286 if (status == MagickFalse)
10292 else /* Not Palette, Bilevel, or Opaque Monochrome */
10294 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10295 !mng_info->write_png32) &&
10296 (image_matte != MagickFalse ||
10297 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10298 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10300 register const Quantum
10303 for (pass=0; pass < num_passes; pass++)
10306 for (y=0; y < (ssize_t) image->rows; y++)
10308 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10310 if (p == (const Quantum *) NULL)
10313 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10315 if (mng_info->IsPalette)
10316 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10317 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10320 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10321 quantum_info,RedQuantum,ping_pixels,&image->exception);
10323 if (logging != MagickFalse && y == 0)
10324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10325 " Writing GRAY PNG pixels (2)");
10328 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10330 if (logging != MagickFalse && y == 0)
10331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10332 " Writing GRAY_ALPHA PNG pixels (2)");
10334 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10335 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
10338 if (logging != MagickFalse && y == 0)
10339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340 " Writing row of pixels (2)");
10342 png_write_row(ping,ping_pixels);
10345 if (image->previous == (Image *) NULL)
10347 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10348 if (status == MagickFalse)
10356 register const Quantum
10359 for (pass=0; pass < num_passes; pass++)
10361 if ((image_depth > 8) || (mng_info->write_png24 ||
10362 mng_info->write_png32 ||
10363 (!mng_info->write_png8 && !mng_info->IsPalette)))
10365 for (y=0; y < (ssize_t) image->rows; y++)
10367 p=GetVirtualPixels(image,0,y,image->columns,1,
10368 &image->exception);
10370 if (p == (const Quantum *) NULL)
10373 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10375 if (image->storage_class == DirectClass)
10376 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10377 quantum_info,RedQuantum,ping_pixels,&image->exception);
10380 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10381 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10384 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10386 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10387 quantum_info,GrayAlphaQuantum,ping_pixels,
10388 &image->exception);
10390 if (logging != MagickFalse && y == 0)
10391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392 " Writing GRAY_ALPHA PNG pixels (3)");
10395 else if (image_matte != MagickFalse)
10396 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10397 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
10400 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10401 quantum_info,RGBQuantum,ping_pixels,&image->exception);
10403 if (logging != MagickFalse && y == 0)
10404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10405 " Writing row of pixels (3)");
10407 png_write_row(ping,ping_pixels);
10412 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10413 mng_info->write_png32 ||
10414 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10416 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10417 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10419 if (logging != MagickFalse)
10420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10421 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10423 quantum_info->depth=8;
10427 for (y=0; y < (ssize_t) image->rows; y++)
10429 if (logging != MagickFalse && y == 0)
10430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10431 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10433 p=GetVirtualPixels(image,0,y,image->columns,1,
10434 &image->exception);
10436 if (p == (const Quantum *) NULL)
10439 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10441 quantum_info->depth=image->depth;
10443 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10444 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10447 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10449 if (logging != MagickFalse && y == 0)
10450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10451 " Writing GRAY_ALPHA PNG pixels (4)");
10453 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10454 quantum_info,GrayAlphaQuantum,ping_pixels,
10455 &image->exception);
10460 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10461 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10463 if (logging != MagickFalse && y <= 2)
10465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10466 " Writing row of non-gray pixels (4)");
10468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10469 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10470 (int)ping_pixels[0],(int)ping_pixels[1]);
10473 png_write_row(ping,ping_pixels);
10477 if (image->previous == (Image *) NULL)
10479 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10480 if (status == MagickFalse)
10487 if (quantum_info != (QuantumInfo *) NULL)
10488 quantum_info=DestroyQuantumInfo(quantum_info);
10490 if (logging != MagickFalse)
10492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10493 " Wrote PNG image data");
10495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10496 " Width: %.20g",(double) ping_width);
10498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10499 " Height: %.20g",(double) ping_height);
10501 if (mng_info->write_png_depth)
10503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10504 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " PNG bit-depth written: %d",ping_bit_depth);
10510 if (mng_info->write_png_colortype)
10512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10513 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10517 " PNG color-type written: %d",ping_color_type);
10519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520 " PNG Interlace method: %d",ping_interlace_method);
10523 Generate text chunks after IDAT.
10525 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10527 ResetImagePropertyIterator(image);
10528 property=GetNextImageProperty(image);
10529 while (property != (const char *) NULL)
10534 value=GetImageProperty(image,property);
10536 /* Don't write any "png:" properties; those are just for "identify" */
10537 if (LocaleNCompare(property,"png:",4) != 0 &&
10539 /* Suppress density and units if we wrote a pHYs chunk */
10540 (ping_exclude_pHYs != MagickFalse ||
10541 LocaleCompare(property,"density") != 0 ||
10542 LocaleCompare(property,"units") != 0) &&
10544 /* Suppress the IM-generated Date:create and Date:modify */
10545 (ping_exclude_date == MagickFalse ||
10546 LocaleNCompare(property, "Date:",5) != 0))
10548 if (value != (const char *) NULL)
10550 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10551 text[0].key=(char *) property;
10552 text[0].text=(char *) value;
10553 text[0].text_length=strlen(value);
10555 if (ping_exclude_tEXt != MagickFalse)
10556 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10558 else if (ping_exclude_zTXt != MagickFalse)
10559 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10563 text[0].compression=image_info->compression == NoCompression ||
10564 (image_info->compression == UndefinedCompression &&
10565 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10566 PNG_TEXT_COMPRESSION_zTXt ;
10569 if (logging != MagickFalse)
10571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10572 " Setting up text chunk");
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " keyword: %s",text[0].key);
10578 png_set_text(ping,ping_info,text,1);
10579 png_free(ping,text);
10582 property=GetNextImageProperty(image);
10586 /* write any PNG-chunk-e profiles */
10587 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10589 if (logging != MagickFalse)
10590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10591 " Writing PNG end info");
10593 png_write_end(ping,ping_info);
10595 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10597 if (mng_info->page.x || mng_info->page.y ||
10598 (ping_width != mng_info->page.width) ||
10599 (ping_height != mng_info->page.height))
10605 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10607 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10608 PNGType(chunk,mng_FRAM);
10609 LogPNGChunk(logging,mng_FRAM,27L);
10611 chunk[5]=0; /* frame name separator (no name) */
10612 chunk[6]=1; /* flag for changing delay, for next frame only */
10613 chunk[7]=0; /* flag for changing frame timeout */
10614 chunk[8]=1; /* flag for changing frame clipping for next frame */
10615 chunk[9]=0; /* flag for changing frame sync_id */
10616 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10617 chunk[14]=0; /* clipping boundaries delta type */
10618 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10620 (png_uint_32) (mng_info->page.x + ping_width));
10621 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10623 (png_uint_32) (mng_info->page.y + ping_height));
10624 (void) WriteBlob(image,31,chunk);
10625 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10626 mng_info->old_framing_mode=4;
10627 mng_info->framing_mode=1;
10631 mng_info->framing_mode=3;
10633 if (mng_info->write_mng && !mng_info->need_fram &&
10634 ((int) image->dispose == 3))
10635 (void) ThrowMagickException(&image->exception,GetMagickModule(),
10636 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10637 "`%s'",image->filename);
10640 Free PNG resources.
10643 png_destroy_write_struct(&ping,&ping_info);
10645 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10647 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10648 UnlockSemaphoreInfo(ping_semaphore);
10651 if (ping_have_blob != MagickFalse)
10652 (void) CloseBlob(image);
10654 image_info=DestroyImageInfo(image_info);
10655 image=DestroyImage(image);
10657 /* Store bit depth actually written */
10658 s[0]=(char) ping_bit_depth;
10661 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10663 if (logging != MagickFalse)
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " exit WriteOnePNGImage()");
10667 return(MagickTrue);
10668 /* End write one PNG image */
10672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10676 % W r i t e P N G I m a g e %
10680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10682 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10683 % Multiple-image Network Graphics (MNG) image file.
10685 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10687 % The format of the WritePNGImage method is:
10689 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10691 % A description of each parameter follows:
10693 % o image_info: the image info.
10695 % o image: The image.
10697 % Returns MagickTrue on success, MagickFalse on failure.
10699 % Communicating with the PNG encoder:
10701 % While the datastream written is always in PNG format and normally would
10702 % be given the "png" file extension, this method also writes the following
10703 % pseudo-formats which are subsets of PNG:
10705 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10706 % a depth greater than 8, the depth is reduced. If transparency
10707 % is present, the tRNS chunk must only have values 0 and 255
10708 % (i.e., transparency is binary: fully opaque or fully
10709 % transparent). If other values are present they will be
10710 % 50%-thresholded to binary transparency. If more than 256
10711 % colors are present, they will be quantized to the 4-4-4-1,
10712 % 3-3-3-1, or 3-3-2-1 palette.
10714 % If you want better quantization or dithering of the colors
10715 % or alpha than that, you need to do it before calling the
10716 % PNG encoder. The pixels contain 8-bit indices even if
10717 % they could be represented with 1, 2, or 4 bits. Grayscale
10718 % images will be written as indexed PNG files even though the
10719 % PNG grayscale type might be slightly more efficient. Please
10720 % note that writing to the PNG8 format may result in loss
10721 % of color and alpha data.
10723 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10724 % chunk can be present to convey binary transparency by naming
10725 % one of the colors as transparent. The only loss incurred
10726 % is reduction of sample depth to 8. If the image has more
10727 % than one transparent color, has semitransparent pixels, or
10728 % has an opaque pixel with the same RGB components as the
10729 % transparent color, an image is not written.
10731 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10732 % transparency is permitted, i.e., the alpha sample for
10733 % each pixel can have any value from 0 to 255. The alpha
10734 % channel is present even if the image is fully opaque.
10735 % The only loss in data is the reduction of the sample depth
10738 % o -define: For more precise control of the PNG output, you can use the
10739 % Image options "png:bit-depth" and "png:color-type". These
10740 % can be set from the commandline with "-define" and also
10741 % from the application programming interfaces. The options
10742 % are case-independent and are converted to lowercase before
10743 % being passed to this encoder.
10745 % png:color-type can be 0, 2, 3, 4, or 6.
10747 % When png:color-type is 0 (Grayscale), png:bit-depth can
10748 % be 1, 2, 4, 8, or 16.
10750 % When png:color-type is 2 (RGB), png:bit-depth can
10753 % When png:color-type is 3 (Indexed), png:bit-depth can
10754 % be 1, 2, 4, or 8. This refers to the number of bits
10755 % used to store the index. The color samples always have
10756 % bit-depth 8 in indexed PNG files.
10758 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10759 % png:bit-depth can be 8 or 16.
10761 % If the image cannot be written without loss with the requested bit-depth
10762 % and color-type, a PNG file will not be written, and the encoder will
10763 % return MagickFalse.
10765 % Since image encoders should not be responsible for the "heavy lifting",
10766 % the user should make sure that ImageMagick has already reduced the
10767 % image depth and number of colors and limit transparency to binary
10768 % transparency prior to attempting to write the image with depth, color,
10769 % or transparency limitations.
10771 % Note that another definition, "png:bit-depth-written" exists, but it
10772 % is not intended for external use. It is only used internally by the
10773 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10775 % It is possible to request that the PNG encoder write previously-formatted
10776 % ancillary chunks in the output PNG file, using the "-profile" commandline
10777 % option as shown below or by setting the profile via a programming
10780 % -profile PNG-chunk-x:<file>
10782 % where x is a location flag and <file> is a file containing the chunk
10783 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10784 % This encoder will compute the chunk length and CRC, so those must not
10785 % be included in the file.
10787 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10788 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10789 % of the same type, then add a short unique string after the "x" to prevent
10790 % subsequent profiles from overwriting the preceding ones, e.g.,
10792 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10794 % As of version 6.6.6 the following optimizations are always done:
10796 % o 32-bit depth is reduced to 16.
10797 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10798 % high byte and low byte are identical.
10799 % o Palette is sorted to remove unused entries and to put a
10800 % transparent color first, if BUILD_PNG_PALETTE is defined.
10801 % o Opaque matte channel is removed when writing an indexed PNG.
10802 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10803 % this can be done without loss and a larger bit depth N was not
10804 % requested via the "-define PNG:bit-depth=N" option.
10805 % o If matte channel is present but only one transparent color is
10806 % present, RGB+tRNS is written instead of RGBA
10807 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10808 % was requested when converting an opaque image).
10810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10812 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10818 have_mng_structure,
10834 assert(image_info != (const ImageInfo *) NULL);
10835 assert(image_info->signature == MagickSignature);
10836 assert(image != (Image *) NULL);
10837 assert(image->signature == MagickSignature);
10838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10839 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10841 Allocate a MngInfo structure.
10843 have_mng_structure=MagickFalse;
10844 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10846 if (mng_info == (MngInfo *) NULL)
10847 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10850 Initialize members of the MngInfo structure.
10852 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10853 mng_info->image=image;
10854 mng_info->equal_backgrounds=MagickTrue;
10855 have_mng_structure=MagickTrue;
10857 /* See if user has requested a specific PNG subformat */
10859 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10860 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10861 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10863 if (mng_info->write_png8)
10865 mng_info->write_png_colortype = /* 3 */ 4;
10866 mng_info->write_png_depth = 8;
10870 if (mng_info->write_png24)
10872 mng_info->write_png_colortype = /* 2 */ 3;
10873 mng_info->write_png_depth = 8;
10876 if (image->matte == MagickTrue)
10877 (void) SetImageType(image,TrueColorMatteType);
10880 (void) SetImageType(image,TrueColorType);
10882 (void) SyncImage(image);
10885 if (mng_info->write_png32)
10887 mng_info->write_png_colortype = /* 6 */ 7;
10888 mng_info->write_png_depth = 8;
10891 if (image->matte == MagickTrue)
10892 (void) SetImageType(image,TrueColorMatteType);
10895 (void) SetImageType(image,TrueColorType);
10897 (void) SyncImage(image);
10900 value=GetImageOption(image_info,"png:bit-depth");
10902 if (value != (char *) NULL)
10904 if (LocaleCompare(value,"1") == 0)
10905 mng_info->write_png_depth = 1;
10907 else if (LocaleCompare(value,"2") == 0)
10908 mng_info->write_png_depth = 2;
10910 else if (LocaleCompare(value,"4") == 0)
10911 mng_info->write_png_depth = 4;
10913 else if (LocaleCompare(value,"8") == 0)
10914 mng_info->write_png_depth = 8;
10916 else if (LocaleCompare(value,"16") == 0)
10917 mng_info->write_png_depth = 16;
10920 (void) ThrowMagickException(&image->exception,
10921 GetMagickModule(),CoderWarning,
10922 "ignoring invalid defined png:bit-depth",
10925 if (logging != MagickFalse)
10926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10927 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10930 value=GetImageOption(image_info,"png:color-type");
10932 if (value != (char *) NULL)
10934 /* We must store colortype+1 because 0 is a valid colortype */
10935 if (LocaleCompare(value,"0") == 0)
10936 mng_info->write_png_colortype = 1;
10938 else if (LocaleCompare(value,"2") == 0)
10939 mng_info->write_png_colortype = 3;
10941 else if (LocaleCompare(value,"3") == 0)
10942 mng_info->write_png_colortype = 4;
10944 else if (LocaleCompare(value,"4") == 0)
10945 mng_info->write_png_colortype = 5;
10947 else if (LocaleCompare(value,"6") == 0)
10948 mng_info->write_png_colortype = 7;
10951 (void) ThrowMagickException(&image->exception,
10952 GetMagickModule(),CoderWarning,
10953 "ignoring invalid defined png:color-type",
10956 if (logging != MagickFalse)
10957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10958 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10961 /* Check for chunks to be excluded:
10963 * The default is to not exclude any known chunks except for any
10964 * listed in the "unused_chunks" array, above.
10966 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10967 * define (in the image properties or in the image artifacts)
10968 * or via a mng_info member. For convenience, in addition
10969 * to or instead of a comma-separated list of chunks, the
10970 * "exclude-chunk" string can be simply "all" or "none".
10972 * The exclude-chunk define takes priority over the mng_info.
10974 * A "PNG:include-chunk" define takes priority over both the
10975 * mng_info and the "PNG:exclude-chunk" define. Like the
10976 * "exclude-chunk" string, it can define "all" or "none" as
10977 * well as a comma-separated list. Chunks that are unknown to
10978 * ImageMagick are always excluded, regardless of their "copy-safe"
10979 * status according to the PNG specification, and even if they
10980 * appear in the "include-chunk" list.
10982 * Finally, all chunks listed in the "unused_chunks" array are
10983 * automatically excluded, regardless of the other instructions
10986 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10987 * will not be written and the gAMA chunk will only be written if it
10988 * is not between .45 and .46, or approximately (1.0/2.2).
10990 * If you exclude tRNS and the image has transparency, the colortype
10991 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10993 * The -strip option causes StripImage() to set the png:include-chunk
10994 * artifact to "none,gama".
10997 mng_info->ping_exclude_bKGD=MagickFalse;
10998 mng_info->ping_exclude_cHRM=MagickFalse;
10999 mng_info->ping_exclude_date=MagickFalse;
11000 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11001 mng_info->ping_exclude_gAMA=MagickFalse;
11002 mng_info->ping_exclude_iCCP=MagickFalse;
11003 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11004 mng_info->ping_exclude_oFFs=MagickFalse;
11005 mng_info->ping_exclude_pHYs=MagickFalse;
11006 mng_info->ping_exclude_sRGB=MagickFalse;
11007 mng_info->ping_exclude_tEXt=MagickFalse;
11008 mng_info->ping_exclude_tRNS=MagickFalse;
11009 mng_info->ping_exclude_vpAg=MagickFalse;
11010 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11011 mng_info->ping_exclude_zTXt=MagickFalse;
11013 mng_info->ping_preserve_colormap=MagickFalse;
11015 value=GetImageArtifact(image,"png:preserve-colormap");
11017 value=GetImageOption(image_info,"png:preserve-colormap");
11019 mng_info->ping_preserve_colormap=MagickTrue;
11021 /* Thes compression-level, compression-strategy, and compression-filter
11022 * defines take precedence over values from the -quality option.
11024 value=GetImageArtifact(image,"png:compression-level");
11026 value=GetImageOption(image_info,"png:compression-level");
11029 /* We have to add 1 to everything because 0 is a valid input,
11030 * and we want to use 0 (the default) to mean undefined.
11032 if (LocaleCompare(value,"0") == 0)
11033 mng_info->write_png_compression_level = 1;
11035 if (LocaleCompare(value,"1") == 0)
11036 mng_info->write_png_compression_level = 2;
11038 else if (LocaleCompare(value,"2") == 0)
11039 mng_info->write_png_compression_level = 3;
11041 else if (LocaleCompare(value,"3") == 0)
11042 mng_info->write_png_compression_level = 4;
11044 else if (LocaleCompare(value,"4") == 0)
11045 mng_info->write_png_compression_level = 5;
11047 else if (LocaleCompare(value,"5") == 0)
11048 mng_info->write_png_compression_level = 6;
11050 else if (LocaleCompare(value,"6") == 0)
11051 mng_info->write_png_compression_level = 7;
11053 else if (LocaleCompare(value,"7") == 0)
11054 mng_info->write_png_compression_level = 8;
11056 else if (LocaleCompare(value,"8") == 0)
11057 mng_info->write_png_compression_level = 9;
11059 else if (LocaleCompare(value,"9") == 0)
11060 mng_info->write_png_compression_level = 10;
11063 (void) ThrowMagickException(&image->exception,
11064 GetMagickModule(),CoderWarning,
11065 "ignoring invalid defined png:compression-level",
11069 value=GetImageArtifact(image,"png:compression-strategy");
11071 value=GetImageOption(image_info,"png:compression-strategy");
11075 if (LocaleCompare(value,"0") == 0)
11076 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11078 else if (LocaleCompare(value,"1") == 0)
11079 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11081 else if (LocaleCompare(value,"2") == 0)
11082 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11084 else if (LocaleCompare(value,"3") == 0)
11085 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11086 mng_info->write_png_compression_strategy = Z_RLE+1;
11088 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11091 else if (LocaleCompare(value,"4") == 0)
11092 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11093 mng_info->write_png_compression_strategy = Z_FIXED+1;
11095 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11099 (void) ThrowMagickException(&image->exception,
11100 GetMagickModule(),CoderWarning,
11101 "ignoring invalid defined png:compression-strategy",
11105 value=GetImageArtifact(image,"png:compression-filter");
11107 value=GetImageOption(image_info,"png:compression-filter");
11111 /* To do: combinations of filters allowed by libpng
11112 * masks 0x08 through 0xf8
11114 * Implement this as a comma-separated list of 0,1,2,3,4,5
11115 * where 5 is a special case meaning PNG_ALL_FILTERS.
11118 if (LocaleCompare(value,"0") == 0)
11119 mng_info->write_png_compression_filter = 1;
11121 if (LocaleCompare(value,"1") == 0)
11122 mng_info->write_png_compression_filter = 2;
11124 else if (LocaleCompare(value,"2") == 0)
11125 mng_info->write_png_compression_filter = 3;
11127 else if (LocaleCompare(value,"3") == 0)
11128 mng_info->write_png_compression_filter = 4;
11130 else if (LocaleCompare(value,"4") == 0)
11131 mng_info->write_png_compression_filter = 5;
11133 else if (LocaleCompare(value,"5") == 0)
11134 mng_info->write_png_compression_filter = 6;
11136 else if (LocaleCompare(value,"6") == 0)
11137 mng_info->write_png_compression_filter = 7;
11139 else if (LocaleCompare(value,"7") == 0)
11140 mng_info->write_png_compression_filter = 8;
11142 else if (LocaleCompare(value,"8") == 0)
11143 mng_info->write_png_compression_filter = 9;
11145 else if (LocaleCompare(value,"9") == 0)
11146 mng_info->write_png_compression_filter = 10;
11149 (void) ThrowMagickException(&image->exception,
11150 GetMagickModule(),CoderWarning,
11151 "ignoring invalid defined png:compression-filter",
11155 excluding=MagickFalse;
11157 for (source=0; source<1; source++)
11161 value=GetImageArtifact(image,"png:exclude-chunk");
11164 value=GetImageArtifact(image,"png:exclude-chunks");
11168 value=GetImageOption(image_info,"png:exclude-chunk");
11171 value=GetImageOption(image_info,"png:exclude-chunks");
11180 excluding=MagickTrue;
11182 if (logging != MagickFalse)
11185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11186 " png:exclude-chunk=%s found in image artifacts.\n", value);
11188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11189 " png:exclude-chunk=%s found in image properties.\n", value);
11192 last=strlen(value);
11194 for (i=0; i<(int) last; i+=5)
11197 if (LocaleNCompare(value+i,"all",3) == 0)
11199 mng_info->ping_exclude_bKGD=MagickTrue;
11200 mng_info->ping_exclude_cHRM=MagickTrue;
11201 mng_info->ping_exclude_date=MagickTrue;
11202 mng_info->ping_exclude_EXIF=MagickTrue;
11203 mng_info->ping_exclude_gAMA=MagickTrue;
11204 mng_info->ping_exclude_iCCP=MagickTrue;
11205 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11206 mng_info->ping_exclude_oFFs=MagickTrue;
11207 mng_info->ping_exclude_pHYs=MagickTrue;
11208 mng_info->ping_exclude_sRGB=MagickTrue;
11209 mng_info->ping_exclude_tEXt=MagickTrue;
11210 mng_info->ping_exclude_tRNS=MagickTrue;
11211 mng_info->ping_exclude_vpAg=MagickTrue;
11212 mng_info->ping_exclude_zCCP=MagickTrue;
11213 mng_info->ping_exclude_zTXt=MagickTrue;
11217 if (LocaleNCompare(value+i,"none",4) == 0)
11219 mng_info->ping_exclude_bKGD=MagickFalse;
11220 mng_info->ping_exclude_cHRM=MagickFalse;
11221 mng_info->ping_exclude_date=MagickFalse;
11222 mng_info->ping_exclude_EXIF=MagickFalse;
11223 mng_info->ping_exclude_gAMA=MagickFalse;
11224 mng_info->ping_exclude_iCCP=MagickFalse;
11225 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11226 mng_info->ping_exclude_oFFs=MagickFalse;
11227 mng_info->ping_exclude_pHYs=MagickFalse;
11228 mng_info->ping_exclude_sRGB=MagickFalse;
11229 mng_info->ping_exclude_tEXt=MagickFalse;
11230 mng_info->ping_exclude_tRNS=MagickFalse;
11231 mng_info->ping_exclude_vpAg=MagickFalse;
11232 mng_info->ping_exclude_zCCP=MagickFalse;
11233 mng_info->ping_exclude_zTXt=MagickFalse;
11236 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11237 mng_info->ping_exclude_bKGD=MagickTrue;
11239 if (LocaleNCompare(value+i,"chrm",4) == 0)
11240 mng_info->ping_exclude_cHRM=MagickTrue;
11242 if (LocaleNCompare(value+i,"date",4) == 0)
11243 mng_info->ping_exclude_date=MagickTrue;
11245 if (LocaleNCompare(value+i,"exif",4) == 0)
11246 mng_info->ping_exclude_EXIF=MagickTrue;
11248 if (LocaleNCompare(value+i,"gama",4) == 0)
11249 mng_info->ping_exclude_gAMA=MagickTrue;
11251 if (LocaleNCompare(value+i,"iccp",4) == 0)
11252 mng_info->ping_exclude_iCCP=MagickTrue;
11255 if (LocaleNCompare(value+i,"itxt",4) == 0)
11256 mng_info->ping_exclude_iTXt=MagickTrue;
11259 if (LocaleNCompare(value+i,"gama",4) == 0)
11260 mng_info->ping_exclude_gAMA=MagickTrue;
11262 if (LocaleNCompare(value+i,"offs",4) == 0)
11263 mng_info->ping_exclude_oFFs=MagickTrue;
11265 if (LocaleNCompare(value+i,"phys",4) == 0)
11266 mng_info->ping_exclude_pHYs=MagickTrue;
11268 if (LocaleNCompare(value+i,"srgb",4) == 0)
11269 mng_info->ping_exclude_sRGB=MagickTrue;
11271 if (LocaleNCompare(value+i,"text",4) == 0)
11272 mng_info->ping_exclude_tEXt=MagickTrue;
11274 if (LocaleNCompare(value+i,"trns",4) == 0)
11275 mng_info->ping_exclude_tRNS=MagickTrue;
11277 if (LocaleNCompare(value+i,"vpag",4) == 0)
11278 mng_info->ping_exclude_vpAg=MagickTrue;
11280 if (LocaleNCompare(value+i,"zccp",4) == 0)
11281 mng_info->ping_exclude_zCCP=MagickTrue;
11283 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11284 mng_info->ping_exclude_zTXt=MagickTrue;
11290 for (source=0; source<1; source++)
11294 value=GetImageArtifact(image,"png:include-chunk");
11297 value=GetImageArtifact(image,"png:include-chunks");
11301 value=GetImageOption(image_info,"png:include-chunk");
11304 value=GetImageOption(image_info,"png:include-chunks");
11312 excluding=MagickTrue;
11314 if (logging != MagickFalse)
11317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11318 " png:include-chunk=%s found in image artifacts.\n", value);
11320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11321 " png:include-chunk=%s found in image properties.\n", value);
11324 last=strlen(value);
11326 for (i=0; i<(int) last; i+=5)
11328 if (LocaleNCompare(value+i,"all",3) == 0)
11330 mng_info->ping_exclude_bKGD=MagickFalse;
11331 mng_info->ping_exclude_cHRM=MagickFalse;
11332 mng_info->ping_exclude_date=MagickFalse;
11333 mng_info->ping_exclude_EXIF=MagickFalse;
11334 mng_info->ping_exclude_gAMA=MagickFalse;
11335 mng_info->ping_exclude_iCCP=MagickFalse;
11336 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11337 mng_info->ping_exclude_oFFs=MagickFalse;
11338 mng_info->ping_exclude_pHYs=MagickFalse;
11339 mng_info->ping_exclude_sRGB=MagickFalse;
11340 mng_info->ping_exclude_tEXt=MagickFalse;
11341 mng_info->ping_exclude_tRNS=MagickFalse;
11342 mng_info->ping_exclude_vpAg=MagickFalse;
11343 mng_info->ping_exclude_zCCP=MagickFalse;
11344 mng_info->ping_exclude_zTXt=MagickFalse;
11348 if (LocaleNCompare(value+i,"none",4) == 0)
11350 mng_info->ping_exclude_bKGD=MagickTrue;
11351 mng_info->ping_exclude_cHRM=MagickTrue;
11352 mng_info->ping_exclude_date=MagickTrue;
11353 mng_info->ping_exclude_EXIF=MagickTrue;
11354 mng_info->ping_exclude_gAMA=MagickTrue;
11355 mng_info->ping_exclude_iCCP=MagickTrue;
11356 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11357 mng_info->ping_exclude_oFFs=MagickTrue;
11358 mng_info->ping_exclude_pHYs=MagickTrue;
11359 mng_info->ping_exclude_sRGB=MagickTrue;
11360 mng_info->ping_exclude_tEXt=MagickTrue;
11361 mng_info->ping_exclude_tRNS=MagickTrue;
11362 mng_info->ping_exclude_vpAg=MagickTrue;
11363 mng_info->ping_exclude_zCCP=MagickTrue;
11364 mng_info->ping_exclude_zTXt=MagickTrue;
11367 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11368 mng_info->ping_exclude_bKGD=MagickFalse;
11370 if (LocaleNCompare(value+i,"chrm",4) == 0)
11371 mng_info->ping_exclude_cHRM=MagickFalse;
11373 if (LocaleNCompare(value+i,"date",4) == 0)
11374 mng_info->ping_exclude_date=MagickFalse;
11376 if (LocaleNCompare(value+i,"exif",4) == 0)
11377 mng_info->ping_exclude_EXIF=MagickFalse;
11379 if (LocaleNCompare(value+i,"gama",4) == 0)
11380 mng_info->ping_exclude_gAMA=MagickFalse;
11382 if (LocaleNCompare(value+i,"iccp",4) == 0)
11383 mng_info->ping_exclude_iCCP=MagickFalse;
11386 if (LocaleNCompare(value+i,"itxt",4) == 0)
11387 mng_info->ping_exclude_iTXt=MagickFalse;
11390 if (LocaleNCompare(value+i,"gama",4) == 0)
11391 mng_info->ping_exclude_gAMA=MagickFalse;
11393 if (LocaleNCompare(value+i,"offs",4) == 0)
11394 mng_info->ping_exclude_oFFs=MagickFalse;
11396 if (LocaleNCompare(value+i,"phys",4) == 0)
11397 mng_info->ping_exclude_pHYs=MagickFalse;
11399 if (LocaleNCompare(value+i,"srgb",4) == 0)
11400 mng_info->ping_exclude_sRGB=MagickFalse;
11402 if (LocaleNCompare(value+i,"text",4) == 0)
11403 mng_info->ping_exclude_tEXt=MagickFalse;
11405 if (LocaleNCompare(value+i,"trns",4) == 0)
11406 mng_info->ping_exclude_tRNS=MagickFalse;
11408 if (LocaleNCompare(value+i,"vpag",4) == 0)
11409 mng_info->ping_exclude_vpAg=MagickFalse;
11411 if (LocaleNCompare(value+i,"zccp",4) == 0)
11412 mng_info->ping_exclude_zCCP=MagickFalse;
11414 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11415 mng_info->ping_exclude_zTXt=MagickFalse;
11421 if (excluding != MagickFalse && logging != MagickFalse)
11423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11424 " Chunks to be excluded from the output PNG:");
11425 if (mng_info->ping_exclude_bKGD != MagickFalse)
11426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11428 if (mng_info->ping_exclude_cHRM != MagickFalse)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11431 if (mng_info->ping_exclude_date != MagickFalse)
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11434 if (mng_info->ping_exclude_EXIF != MagickFalse)
11435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 if (mng_info->ping_exclude_gAMA != MagickFalse)
11438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11440 if (mng_info->ping_exclude_iCCP != MagickFalse)
11441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11444 if (mng_info->ping_exclude_iTXt != MagickFalse)
11445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11448 if (mng_info->ping_exclude_oFFs != MagickFalse)
11449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11451 if (mng_info->ping_exclude_pHYs != MagickFalse)
11452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11454 if (mng_info->ping_exclude_sRGB != MagickFalse)
11455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11457 if (mng_info->ping_exclude_tEXt != MagickFalse)
11458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11460 if (mng_info->ping_exclude_tRNS != MagickFalse)
11461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11463 if (mng_info->ping_exclude_vpAg != MagickFalse)
11464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466 if (mng_info->ping_exclude_zCCP != MagickFalse)
11467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11469 if (mng_info->ping_exclude_zTXt != MagickFalse)
11470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11474 mng_info->need_blob = MagickTrue;
11476 status=WriteOnePNGImage(mng_info,image_info,image);
11478 MngInfoFreeStruct(mng_info,&have_mng_structure);
11480 if (logging != MagickFalse)
11481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11486 #if defined(JNG_SUPPORTED)
11488 /* Write one JNG image */
11489 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11490 const ImageInfo *image_info,Image *image)
11511 jng_alpha_compression_method,
11512 jng_alpha_sample_depth,
11519 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11520 " Enter WriteOneJNGImage()");
11522 blob=(unsigned char *) NULL;
11523 jpeg_image=(Image *) NULL;
11524 jpeg_image_info=(ImageInfo *) NULL;
11527 transparent=image_info->type==GrayscaleMatteType ||
11528 image_info->type==TrueColorMatteType;
11530 jng_alpha_sample_depth=0;
11531 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11532 jng_alpha_compression_method=0;
11534 if (image->matte != MagickFalse)
11536 /* if any pixels are transparent */
11537 transparent=MagickTrue;
11538 if (image_info->compression==JPEGCompression)
11539 jng_alpha_compression_method=8;
11546 /* Create JPEG blob, image, and image_info */
11547 if (logging != MagickFalse)
11548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11549 " Creating jpeg_image_info for alpha.");
11551 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11553 if (jpeg_image_info == (ImageInfo *) NULL)
11554 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11556 if (logging != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " Creating jpeg_image.");
11560 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11562 if (jpeg_image == (Image *) NULL)
11563 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11565 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11566 status=SeparateImageChannel(jpeg_image,AlphaChannel);
11567 jpeg_image->matte=MagickFalse;
11569 if (jng_quality >= 1000)
11570 jpeg_image_info->quality=jng_quality/1000;
11573 jpeg_image_info->quality=jng_quality;
11575 jpeg_image_info->type=GrayscaleType;
11576 (void) SetImageType(jpeg_image,GrayscaleType);
11577 (void) AcquireUniqueFilename(jpeg_image->filename);
11578 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11579 "%s",jpeg_image->filename);
11582 /* To do: check bit depth of PNG alpha channel */
11584 /* Check if image is grayscale. */
11585 if (image_info->type != TrueColorMatteType && image_info->type !=
11586 TrueColorType && ImageIsGray(image))
11591 if (jng_alpha_compression_method==0)
11596 /* Encode alpha as a grayscale PNG blob */
11597 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11598 &image->exception);
11599 if (logging != MagickFalse)
11600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11601 " Creating PNG blob.");
11604 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11605 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11606 jpeg_image_info->interlace=NoInterlace;
11608 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11609 &image->exception);
11611 /* Retrieve sample depth used */
11612 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11613 if (value != (char *) NULL)
11614 jng_alpha_sample_depth= (unsigned int) value[0];
11618 /* Encode alpha as a grayscale JPEG blob */
11620 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11621 &image->exception);
11623 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11624 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11625 jpeg_image_info->interlace=NoInterlace;
11626 if (logging != MagickFalse)
11627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11628 " Creating blob.");
11629 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11630 &image->exception);
11631 jng_alpha_sample_depth=8;
11633 if (logging != MagickFalse)
11634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11635 " Successfully read jpeg_image into a blob, length=%.20g.",
11639 /* Destroy JPEG image and image_info */
11640 jpeg_image=DestroyImage(jpeg_image);
11641 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11642 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11645 /* Write JHDR chunk */
11646 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11647 PNGType(chunk,mng_JHDR);
11648 LogPNGChunk(logging,mng_JHDR,16L);
11649 PNGLong(chunk+4,(png_uint_32) image->columns);
11650 PNGLong(chunk+8,(png_uint_32) image->rows);
11651 chunk[12]=jng_color_type;
11652 chunk[13]=8; /* sample depth */
11653 chunk[14]=8; /*jng_image_compression_method */
11654 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11655 chunk[16]=jng_alpha_sample_depth;
11656 chunk[17]=jng_alpha_compression_method;
11657 chunk[18]=0; /*jng_alpha_filter_method */
11658 chunk[19]=0; /*jng_alpha_interlace_method */
11659 (void) WriteBlob(image,20,chunk);
11660 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11661 if (logging != MagickFalse)
11663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11664 " JNG width:%15lu",(unsigned long) image->columns);
11666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11667 " JNG height:%14lu",(unsigned long) image->rows);
11669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670 " JNG color type:%10d",jng_color_type);
11672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673 " JNG sample depth:%8d",8);
11675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11676 " JNG compression:%9d",8);
11678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11679 " JNG interlace:%11d",0);
11681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11682 " JNG alpha depth:%9d",jng_alpha_sample_depth);
11684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11685 " JNG alpha compression:%3d",jng_alpha_compression_method);
11687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11688 " JNG alpha filter:%8d",0);
11690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11691 " JNG alpha interlace:%5d",0);
11694 /* Write any JNG-chunk-b profiles */
11695 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11698 Write leading ancillary chunks
11704 Write JNG bKGD chunk
11715 if (jng_color_type == 8 || jng_color_type == 12)
11719 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11720 PNGType(chunk,mng_bKGD);
11721 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11722 red=ScaleQuantumToChar(image->background_color.red);
11723 green=ScaleQuantumToChar(image->background_color.green);
11724 blue=ScaleQuantumToChar(image->background_color.blue);
11731 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11732 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11735 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11738 Write JNG sRGB chunk
11740 (void) WriteBlobMSBULong(image,1L);
11741 PNGType(chunk,mng_sRGB);
11742 LogPNGChunk(logging,mng_sRGB,1L);
11744 if (image->rendering_intent != UndefinedIntent)
11745 chunk[4]=(unsigned char)
11746 Magick_RenderingIntent_to_PNG_RenderingIntent(
11747 (image->rendering_intent));
11750 chunk[4]=(unsigned char)
11751 Magick_RenderingIntent_to_PNG_RenderingIntent(
11752 (PerceptualIntent));
11754 (void) WriteBlob(image,5,chunk);
11755 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11759 if (image->gamma != 0.0)
11762 Write JNG gAMA chunk
11764 (void) WriteBlobMSBULong(image,4L);
11765 PNGType(chunk,mng_gAMA);
11766 LogPNGChunk(logging,mng_gAMA,4L);
11767 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11768 (void) WriteBlob(image,8,chunk);
11769 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11772 if ((mng_info->equal_chrms == MagickFalse) &&
11773 (image->chromaticity.red_primary.x != 0.0))
11779 Write JNG cHRM chunk
11781 (void) WriteBlobMSBULong(image,32L);
11782 PNGType(chunk,mng_cHRM);
11783 LogPNGChunk(logging,mng_cHRM,32L);
11784 primary=image->chromaticity.white_point;
11785 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11786 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11787 primary=image->chromaticity.red_primary;
11788 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11789 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11790 primary=image->chromaticity.green_primary;
11791 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11792 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11793 primary=image->chromaticity.blue_primary;
11794 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11795 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11796 (void) WriteBlob(image,36,chunk);
11797 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11801 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11804 Write JNG pHYs chunk
11806 (void) WriteBlobMSBULong(image,9L);
11807 PNGType(chunk,mng_pHYs);
11808 LogPNGChunk(logging,mng_pHYs,9L);
11809 if (image->units == PixelsPerInchResolution)
11811 PNGLong(chunk+4,(png_uint_32)
11812 (image->x_resolution*100.0/2.54+0.5));
11814 PNGLong(chunk+8,(png_uint_32)
11815 (image->y_resolution*100.0/2.54+0.5));
11822 if (image->units == PixelsPerCentimeterResolution)
11824 PNGLong(chunk+4,(png_uint_32)
11825 (image->x_resolution*100.0+0.5));
11827 PNGLong(chunk+8,(png_uint_32)
11828 (image->y_resolution*100.0+0.5));
11835 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11836 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11840 (void) WriteBlob(image,13,chunk);
11841 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11844 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11847 Write JNG oFFs chunk
11849 (void) WriteBlobMSBULong(image,9L);
11850 PNGType(chunk,mng_oFFs);
11851 LogPNGChunk(logging,mng_oFFs,9L);
11852 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11853 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11855 (void) WriteBlob(image,13,chunk);
11856 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11858 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11860 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11861 PNGType(chunk,mng_vpAg);
11862 LogPNGChunk(logging,mng_vpAg,9L);
11863 PNGLong(chunk+4,(png_uint_32) image->page.width);
11864 PNGLong(chunk+8,(png_uint_32) image->page.height);
11865 chunk[12]=0; /* unit = pixels */
11866 (void) WriteBlob(image,13,chunk);
11867 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11873 if (jng_alpha_compression_method==0)
11881 /* Write IDAT chunk header */
11882 if (logging != MagickFalse)
11883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11884 " Write IDAT chunks from blob, length=%.20g.",(double)
11887 /* Copy IDAT chunks */
11890 for (i=8; i<(ssize_t) length; i+=len+12)
11892 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11895 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11897 /* Found an IDAT chunk. */
11898 (void) WriteBlobMSBULong(image,(size_t) len);
11899 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11900 (void) WriteBlob(image,(size_t) len+4,p);
11901 (void) WriteBlobMSBULong(image,
11902 crc32(0,p,(uInt) len+4));
11907 if (logging != MagickFalse)
11908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11909 " Skipping %c%c%c%c chunk, length=%.20g.",
11910 *(p),*(p+1),*(p+2),*(p+3),(double) len);
11917 /* Write JDAA chunk header */
11918 if (logging != MagickFalse)
11919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11920 " Write JDAA chunk, length=%.20g.",(double) length);
11921 (void) WriteBlobMSBULong(image,(size_t) length);
11922 PNGType(chunk,mng_JDAA);
11923 LogPNGChunk(logging,mng_JDAA,length);
11924 /* Write JDAT chunk(s) data */
11925 (void) WriteBlob(image,4,chunk);
11926 (void) WriteBlob(image,length,blob);
11927 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11930 blob=(unsigned char *) RelinquishMagickMemory(blob);
11933 /* Encode image as a JPEG blob */
11934 if (logging != MagickFalse)
11935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11936 " Creating jpeg_image_info.");
11937 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11938 if (jpeg_image_info == (ImageInfo *) NULL)
11939 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11941 if (logging != MagickFalse)
11942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11943 " Creating jpeg_image.");
11945 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11946 if (jpeg_image == (Image *) NULL)
11947 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11948 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11950 (void) AcquireUniqueFilename(jpeg_image->filename);
11951 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
11952 jpeg_image->filename);
11954 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11955 &image->exception);
11957 if (logging != MagickFalse)
11958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11959 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11960 (double) jpeg_image->rows);
11962 if (jng_color_type == 8 || jng_color_type == 12)
11963 jpeg_image_info->type=GrayscaleType;
11965 jpeg_image_info->quality=jng_quality % 1000;
11966 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11967 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11969 if (logging != MagickFalse)
11970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11971 " Creating blob.");
11973 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11975 if (logging != MagickFalse)
11977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11978 " Successfully read jpeg_image into a blob, length=%.20g.",
11981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11982 " Write JDAT chunk, length=%.20g.",(double) length);
11985 /* Write JDAT chunk(s) */
11986 (void) WriteBlobMSBULong(image,(size_t) length);
11987 PNGType(chunk,mng_JDAT);
11988 LogPNGChunk(logging,mng_JDAT,length);
11989 (void) WriteBlob(image,4,chunk);
11990 (void) WriteBlob(image,length,blob);
11991 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11993 jpeg_image=DestroyImage(jpeg_image);
11994 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11995 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11996 blob=(unsigned char *) RelinquishMagickMemory(blob);
11998 /* Write any JNG-chunk-e profiles */
11999 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12001 /* Write IEND chunk */
12002 (void) WriteBlobMSBULong(image,0L);
12003 PNGType(chunk,mng_IEND);
12004 LogPNGChunk(logging,mng_IEND,0);
12005 (void) WriteBlob(image,4,chunk);
12006 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12008 if (logging != MagickFalse)
12009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12010 " exit WriteOneJNGImage()");
12017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12021 % W r i t e J N G I m a g e %
12025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12027 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12029 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12031 % The format of the WriteJNGImage method is:
12033 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12035 % A description of each parameter follows:
12037 % o image_info: the image info.
12039 % o image: The image.
12041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12043 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12046 have_mng_structure,
12056 assert(image_info != (const ImageInfo *) NULL);
12057 assert(image_info->signature == MagickSignature);
12058 assert(image != (Image *) NULL);
12059 assert(image->signature == MagickSignature);
12060 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12061 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12062 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12063 if (status == MagickFalse)
12067 Allocate a MngInfo structure.
12069 have_mng_structure=MagickFalse;
12070 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12071 if (mng_info == (MngInfo *) NULL)
12072 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12074 Initialize members of the MngInfo structure.
12076 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12077 mng_info->image=image;
12078 have_mng_structure=MagickTrue;
12080 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12082 status=WriteOneJNGImage(mng_info,image_info,image);
12083 (void) CloseBlob(image);
12085 (void) CatchImageException(image);
12086 MngInfoFreeStruct(mng_info,&have_mng_structure);
12087 if (logging != MagickFalse)
12088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12095 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12104 have_mng_structure,
12107 volatile MagickBooleanType
12119 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12120 defined(PNG_MNG_FEATURES_SUPPORTED)
12123 all_images_are_gray,
12133 volatile unsigned int
12144 #if (PNG_LIBPNG_VER < 10200)
12145 if (image_info->verbose)
12146 printf("Your PNG library (libpng-%s) is rather old.\n",
12147 PNG_LIBPNG_VER_STRING);
12153 assert(image_info != (const ImageInfo *) NULL);
12154 assert(image_info->signature == MagickSignature);
12155 assert(image != (Image *) NULL);
12156 assert(image->signature == MagickSignature);
12157 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12158 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12159 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12160 if (status == MagickFalse)
12164 Allocate a MngInfo structure.
12166 have_mng_structure=MagickFalse;
12167 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12168 if (mng_info == (MngInfo *) NULL)
12169 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12171 Initialize members of the MngInfo structure.
12173 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12174 mng_info->image=image;
12175 have_mng_structure=MagickTrue;
12176 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12179 * See if user has requested a specific PNG subformat to be used
12180 * for all of the PNGs in the MNG being written, e.g.,
12182 * convert *.png png8:animation.mng
12184 * To do: check -define png:bit_depth and png:color_type as well,
12185 * or perhaps use mng:bit_depth and mng:color_type instead for
12189 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12190 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12191 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12193 write_jng=MagickFalse;
12194 if (image_info->compression == JPEGCompression)
12195 write_jng=MagickTrue;
12197 mng_info->adjoin=image_info->adjoin &&
12198 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12200 if (logging != MagickFalse)
12202 /* Log some info about the input */
12206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12207 " Checking input image(s)");
12209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210 " Image_info depth: %.20g",(double) image_info->depth);
12212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12213 " Type: %d",image_info->type);
12216 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12219 " Scene: %.20g",(double) scene++);
12221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12222 " Image depth: %.20g",(double) p->depth);
12225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12232 if (p->storage_class == PseudoClass)
12233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12234 " Storage class: PseudoClass");
12237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12238 " Storage class: DirectClass");
12241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12242 " Number of colors: %.20g",(double) p->colors);
12245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12246 " Number of colors: unspecified");
12248 if (mng_info->adjoin == MagickFalse)
12253 use_global_plte=MagickFalse;
12254 all_images_are_gray=MagickFalse;
12255 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12256 need_local_plte=MagickTrue;
12258 need_defi=MagickFalse;
12259 need_matte=MagickFalse;
12260 mng_info->framing_mode=1;
12261 mng_info->old_framing_mode=1;
12264 if (image_info->page != (char *) NULL)
12267 Determine image bounding box.
12269 SetGeometry(image,&mng_info->page);
12270 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12271 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12283 mng_info->page=image->page;
12284 need_geom=MagickTrue;
12285 if (mng_info->page.width || mng_info->page.height)
12286 need_geom=MagickFalse;
12288 Check all the scenes.
12290 initial_delay=image->delay;
12291 need_iterations=MagickFalse;
12292 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12293 mng_info->equal_physs=MagickTrue,
12294 mng_info->equal_gammas=MagickTrue;
12295 mng_info->equal_srgbs=MagickTrue;
12296 mng_info->equal_backgrounds=MagickTrue;
12298 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12299 defined(PNG_MNG_FEATURES_SUPPORTED)
12300 all_images_are_gray=MagickTrue;
12301 mng_info->equal_palettes=MagickFalse;
12302 need_local_plte=MagickFalse;
12304 for (next_image=image; next_image != (Image *) NULL; )
12308 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12309 mng_info->page.width=next_image->columns+next_image->page.x;
12311 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12312 mng_info->page.height=next_image->rows+next_image->page.y;
12315 if (next_image->page.x || next_image->page.y)
12316 need_defi=MagickTrue;
12318 if (next_image->matte)
12319 need_matte=MagickTrue;
12321 if ((int) next_image->dispose >= BackgroundDispose)
12322 if (next_image->matte || next_image->page.x || next_image->page.y ||
12323 ((next_image->columns < mng_info->page.width) &&
12324 (next_image->rows < mng_info->page.height)))
12325 mng_info->need_fram=MagickTrue;
12327 if (next_image->iterations)
12328 need_iterations=MagickTrue;
12330 final_delay=next_image->delay;
12332 if (final_delay != initial_delay || final_delay > 1UL*
12333 next_image->ticks_per_second)
12334 mng_info->need_fram=1;
12336 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12337 defined(PNG_MNG_FEATURES_SUPPORTED)
12339 check for global palette possibility.
12341 if (image->matte != MagickFalse)
12342 need_local_plte=MagickTrue;
12344 if (need_local_plte == 0)
12346 if (ImageIsGray(image) == MagickFalse)
12347 all_images_are_gray=MagickFalse;
12348 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12349 if (use_global_plte == 0)
12350 use_global_plte=mng_info->equal_palettes;
12351 need_local_plte=!mng_info->equal_palettes;
12354 if (GetNextImageInList(next_image) != (Image *) NULL)
12356 if (next_image->background_color.red !=
12357 next_image->next->background_color.red ||
12358 next_image->background_color.green !=
12359 next_image->next->background_color.green ||
12360 next_image->background_color.blue !=
12361 next_image->next->background_color.blue)
12362 mng_info->equal_backgrounds=MagickFalse;
12364 if (next_image->gamma != next_image->next->gamma)
12365 mng_info->equal_gammas=MagickFalse;
12367 if (next_image->rendering_intent !=
12368 next_image->next->rendering_intent)
12369 mng_info->equal_srgbs=MagickFalse;
12371 if ((next_image->units != next_image->next->units) ||
12372 (next_image->x_resolution != next_image->next->x_resolution) ||
12373 (next_image->y_resolution != next_image->next->y_resolution))
12374 mng_info->equal_physs=MagickFalse;
12376 if (mng_info->equal_chrms)
12378 if (next_image->chromaticity.red_primary.x !=
12379 next_image->next->chromaticity.red_primary.x ||
12380 next_image->chromaticity.red_primary.y !=
12381 next_image->next->chromaticity.red_primary.y ||
12382 next_image->chromaticity.green_primary.x !=
12383 next_image->next->chromaticity.green_primary.x ||
12384 next_image->chromaticity.green_primary.y !=
12385 next_image->next->chromaticity.green_primary.y ||
12386 next_image->chromaticity.blue_primary.x !=
12387 next_image->next->chromaticity.blue_primary.x ||
12388 next_image->chromaticity.blue_primary.y !=
12389 next_image->next->chromaticity.blue_primary.y ||
12390 next_image->chromaticity.white_point.x !=
12391 next_image->next->chromaticity.white_point.x ||
12392 next_image->chromaticity.white_point.y !=
12393 next_image->next->chromaticity.white_point.y)
12394 mng_info->equal_chrms=MagickFalse;
12398 next_image=GetNextImageInList(next_image);
12400 if (image_count < 2)
12402 mng_info->equal_backgrounds=MagickFalse;
12403 mng_info->equal_chrms=MagickFalse;
12404 mng_info->equal_gammas=MagickFalse;
12405 mng_info->equal_srgbs=MagickFalse;
12406 mng_info->equal_physs=MagickFalse;
12407 use_global_plte=MagickFalse;
12408 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12409 need_local_plte=MagickTrue;
12411 need_iterations=MagickFalse;
12414 if (mng_info->need_fram == MagickFalse)
12417 Only certain framing rates 100/n are exactly representable without
12418 the FRAM chunk but we'll allow some slop in VLC files
12420 if (final_delay == 0)
12422 if (need_iterations != MagickFalse)
12425 It's probably a GIF with loop; don't run it *too* fast.
12427 if (mng_info->adjoin)
12430 (void) ThrowMagickException(&image->exception,
12431 GetMagickModule(),CoderWarning,
12432 "input has zero delay between all frames; assuming",
12437 mng_info->ticks_per_second=0;
12439 if (final_delay != 0)
12440 mng_info->ticks_per_second=(png_uint_32)
12441 (image->ticks_per_second/final_delay);
12442 if (final_delay > 50)
12443 mng_info->ticks_per_second=2;
12445 if (final_delay > 75)
12446 mng_info->ticks_per_second=1;
12448 if (final_delay > 125)
12449 mng_info->need_fram=MagickTrue;
12451 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12452 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12453 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12454 1UL*image->ticks_per_second))
12455 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12458 if (mng_info->need_fram != MagickFalse)
12459 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12461 If pseudocolor, we should also check to see if all the
12462 palettes are identical and write a global PLTE if they are.
12466 Write the MNG version 1.0 signature and MHDR chunk.
12468 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12469 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12470 PNGType(chunk,mng_MHDR);
12471 LogPNGChunk(logging,mng_MHDR,28L);
12472 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12473 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12474 PNGLong(chunk+12,mng_info->ticks_per_second);
12475 PNGLong(chunk+16,0L); /* layer count=unknown */
12476 PNGLong(chunk+20,0L); /* frame count=unknown */
12477 PNGLong(chunk+24,0L); /* play time=unknown */
12482 if (need_defi || mng_info->need_fram || use_global_plte)
12483 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
12486 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12491 if (need_defi || mng_info->need_fram || use_global_plte)
12492 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
12495 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12503 if (need_defi || mng_info->need_fram || use_global_plte)
12504 PNGLong(chunk+28,11L); /* simplicity=LC */
12507 PNGLong(chunk+28,9L); /* simplicity=VLC */
12512 if (need_defi || mng_info->need_fram || use_global_plte)
12513 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
12516 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12519 (void) WriteBlob(image,32,chunk);
12520 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12521 option=GetImageOption(image_info,"mng:need-cacheoff");
12522 if (option != (const char *) NULL)
12528 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12530 PNGType(chunk,mng_nEED);
12531 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12532 (void) WriteBlobMSBULong(image,(size_t) length);
12533 LogPNGChunk(logging,mng_nEED,(size_t) length);
12535 (void) WriteBlob(image,length,chunk);
12536 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12538 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12539 (GetNextImageInList(image) != (Image *) NULL) &&
12540 (image->iterations != 1))
12543 Write MNG TERM chunk
12545 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12546 PNGType(chunk,mng_TERM);
12547 LogPNGChunk(logging,mng_TERM,10L);
12548 chunk[4]=3; /* repeat animation */
12549 chunk[5]=0; /* show last frame when done */
12550 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12551 final_delay/MagickMax(image->ticks_per_second,1)));
12553 if (image->iterations == 0)
12554 PNGLong(chunk+10,PNG_UINT_31_MAX);
12557 PNGLong(chunk+10,(png_uint_32) image->iterations);
12559 if (logging != MagickFalse)
12561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12563 final_delay/MagickMax(image->ticks_per_second,1)));
12565 if (image->iterations == 0)
12566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12567 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12571 " Image iterations: %.20g",(double) image->iterations);
12573 (void) WriteBlob(image,14,chunk);
12574 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12577 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12579 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12580 mng_info->equal_srgbs)
12583 Write MNG sRGB chunk
12585 (void) WriteBlobMSBULong(image,1L);
12586 PNGType(chunk,mng_sRGB);
12587 LogPNGChunk(logging,mng_sRGB,1L);
12589 if (image->rendering_intent != UndefinedIntent)
12590 chunk[4]=(unsigned char)
12591 Magick_RenderingIntent_to_PNG_RenderingIntent(
12592 (image->rendering_intent));
12595 chunk[4]=(unsigned char)
12596 Magick_RenderingIntent_to_PNG_RenderingIntent(
12597 (PerceptualIntent));
12599 (void) WriteBlob(image,5,chunk);
12600 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12601 mng_info->have_write_global_srgb=MagickTrue;
12606 if (image->gamma && mng_info->equal_gammas)
12609 Write MNG gAMA chunk
12611 (void) WriteBlobMSBULong(image,4L);
12612 PNGType(chunk,mng_gAMA);
12613 LogPNGChunk(logging,mng_gAMA,4L);
12614 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12615 (void) WriteBlob(image,8,chunk);
12616 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12617 mng_info->have_write_global_gama=MagickTrue;
12619 if (mng_info->equal_chrms)
12625 Write MNG cHRM chunk
12627 (void) WriteBlobMSBULong(image,32L);
12628 PNGType(chunk,mng_cHRM);
12629 LogPNGChunk(logging,mng_cHRM,32L);
12630 primary=image->chromaticity.white_point;
12631 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12632 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12633 primary=image->chromaticity.red_primary;
12634 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12635 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12636 primary=image->chromaticity.green_primary;
12637 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12638 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12639 primary=image->chromaticity.blue_primary;
12640 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12641 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12642 (void) WriteBlob(image,36,chunk);
12643 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12644 mng_info->have_write_global_chrm=MagickTrue;
12647 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12650 Write MNG pHYs chunk
12652 (void) WriteBlobMSBULong(image,9L);
12653 PNGType(chunk,mng_pHYs);
12654 LogPNGChunk(logging,mng_pHYs,9L);
12656 if (image->units == PixelsPerInchResolution)
12658 PNGLong(chunk+4,(png_uint_32)
12659 (image->x_resolution*100.0/2.54+0.5));
12661 PNGLong(chunk+8,(png_uint_32)
12662 (image->y_resolution*100.0/2.54+0.5));
12669 if (image->units == PixelsPerCentimeterResolution)
12671 PNGLong(chunk+4,(png_uint_32)
12672 (image->x_resolution*100.0+0.5));
12674 PNGLong(chunk+8,(png_uint_32)
12675 (image->y_resolution*100.0+0.5));
12682 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12683 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
12687 (void) WriteBlob(image,13,chunk);
12688 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12691 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12692 or does not cover the entire frame.
12694 if (write_mng && (image->matte || image->page.x > 0 ||
12695 image->page.y > 0 || (image->page.width &&
12696 (image->page.width+image->page.x < mng_info->page.width))
12697 || (image->page.height && (image->page.height+image->page.y
12698 < mng_info->page.height))))
12700 (void) WriteBlobMSBULong(image,6L);
12701 PNGType(chunk,mng_BACK);
12702 LogPNGChunk(logging,mng_BACK,6L);
12703 red=ScaleQuantumToShort(image->background_color.red);
12704 green=ScaleQuantumToShort(image->background_color.green);
12705 blue=ScaleQuantumToShort(image->background_color.blue);
12706 PNGShort(chunk+4,red);
12707 PNGShort(chunk+6,green);
12708 PNGShort(chunk+8,blue);
12709 (void) WriteBlob(image,10,chunk);
12710 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12711 if (mng_info->equal_backgrounds)
12713 (void) WriteBlobMSBULong(image,6L);
12714 PNGType(chunk,mng_bKGD);
12715 LogPNGChunk(logging,mng_bKGD,6L);
12716 (void) WriteBlob(image,10,chunk);
12717 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12721 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12722 if ((need_local_plte == MagickFalse) &&
12723 (image->storage_class == PseudoClass) &&
12724 (all_images_are_gray == MagickFalse))
12730 Write MNG PLTE chunk
12732 data_length=3*image->colors;
12733 (void) WriteBlobMSBULong(image,data_length);
12734 PNGType(chunk,mng_PLTE);
12735 LogPNGChunk(logging,mng_PLTE,data_length);
12737 for (i=0; i < (ssize_t) image->colors; i++)
12739 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12740 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12741 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12744 (void) WriteBlob(image,data_length+4,chunk);
12745 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12746 mng_info->have_write_global_plte=MagickTrue;
12752 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12753 defined(PNG_MNG_FEATURES_SUPPORTED)
12754 mng_info->equal_palettes=MagickFalse;
12758 if (mng_info->adjoin)
12760 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12761 defined(PNG_MNG_FEATURES_SUPPORTED)
12763 If we aren't using a global palette for the entire MNG, check to
12764 see if we can use one for two or more consecutive images.
12766 if (need_local_plte && use_global_plte && !all_images_are_gray)
12768 if (mng_info->IsPalette)
12771 When equal_palettes is true, this image has the same palette
12772 as the previous PseudoClass image
12774 mng_info->have_write_global_plte=mng_info->equal_palettes;
12775 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12776 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12779 Write MNG PLTE chunk
12784 data_length=3*image->colors;
12785 (void) WriteBlobMSBULong(image,data_length);
12786 PNGType(chunk,mng_PLTE);
12787 LogPNGChunk(logging,mng_PLTE,data_length);
12789 for (i=0; i < (ssize_t) image->colors; i++)
12791 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12792 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12793 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12796 (void) WriteBlob(image,data_length+4,chunk);
12797 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12798 (uInt) (data_length+4)));
12799 mng_info->have_write_global_plte=MagickTrue;
12803 mng_info->have_write_global_plte=MagickFalse;
12814 previous_x=mng_info->page.x;
12815 previous_y=mng_info->page.y;
12822 mng_info->page=image->page;
12823 if ((mng_info->page.x != previous_x) ||
12824 (mng_info->page.y != previous_y))
12826 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12827 PNGType(chunk,mng_DEFI);
12828 LogPNGChunk(logging,mng_DEFI,12L);
12829 chunk[4]=0; /* object 0 MSB */
12830 chunk[5]=0; /* object 0 LSB */
12831 chunk[6]=0; /* visible */
12832 chunk[7]=0; /* abstract */
12833 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12834 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12835 (void) WriteBlob(image,16,chunk);
12836 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12841 mng_info->write_mng=write_mng;
12843 if ((int) image->dispose >= 3)
12844 mng_info->framing_mode=3;
12846 if (mng_info->need_fram && mng_info->adjoin &&
12847 ((image->delay != mng_info->delay) ||
12848 (mng_info->framing_mode != mng_info->old_framing_mode)))
12850 if (image->delay == mng_info->delay)
12853 Write a MNG FRAM chunk with the new framing mode.
12855 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12856 PNGType(chunk,mng_FRAM);
12857 LogPNGChunk(logging,mng_FRAM,1L);
12858 chunk[4]=(unsigned char) mng_info->framing_mode;
12859 (void) WriteBlob(image,5,chunk);
12860 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12865 Write a MNG FRAM chunk with the delay.
12867 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12868 PNGType(chunk,mng_FRAM);
12869 LogPNGChunk(logging,mng_FRAM,10L);
12870 chunk[4]=(unsigned char) mng_info->framing_mode;
12871 chunk[5]=0; /* frame name separator (no name) */
12872 chunk[6]=2; /* flag for changing default delay */
12873 chunk[7]=0; /* flag for changing frame timeout */
12874 chunk[8]=0; /* flag for changing frame clipping */
12875 chunk[9]=0; /* flag for changing frame sync_id */
12876 PNGLong(chunk+10,(png_uint_32)
12877 ((mng_info->ticks_per_second*
12878 image->delay)/MagickMax(image->ticks_per_second,1)));
12879 (void) WriteBlob(image,14,chunk);
12880 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12881 mng_info->delay=(png_uint_32) image->delay;
12883 mng_info->old_framing_mode=mng_info->framing_mode;
12886 #if defined(JNG_SUPPORTED)
12887 if (image_info->compression == JPEGCompression)
12892 if (logging != MagickFalse)
12893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12894 " Writing JNG object.");
12895 /* To do: specify the desired alpha compression method. */
12896 write_info=CloneImageInfo(image_info);
12897 write_info->compression=UndefinedCompression;
12898 status=WriteOneJNGImage(mng_info,write_info,image);
12899 write_info=DestroyImageInfo(write_info);
12904 if (logging != MagickFalse)
12905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12906 " Writing PNG object.");
12908 mng_info->need_blob = MagickFalse;
12909 mng_info->ping_preserve_colormap = MagickFalse;
12911 /* We don't want any ancillary chunks written */
12912 mng_info->ping_exclude_bKGD=MagickTrue;
12913 mng_info->ping_exclude_cHRM=MagickTrue;
12914 mng_info->ping_exclude_date=MagickTrue;
12915 mng_info->ping_exclude_EXIF=MagickTrue;
12916 mng_info->ping_exclude_gAMA=MagickTrue;
12917 mng_info->ping_exclude_iCCP=MagickTrue;
12918 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12919 mng_info->ping_exclude_oFFs=MagickTrue;
12920 mng_info->ping_exclude_pHYs=MagickTrue;
12921 mng_info->ping_exclude_sRGB=MagickTrue;
12922 mng_info->ping_exclude_tEXt=MagickTrue;
12923 mng_info->ping_exclude_tRNS=MagickTrue;
12924 mng_info->ping_exclude_vpAg=MagickTrue;
12925 mng_info->ping_exclude_zCCP=MagickTrue;
12926 mng_info->ping_exclude_zTXt=MagickTrue;
12928 status=WriteOnePNGImage(mng_info,image_info,image);
12931 if (status == MagickFalse)
12933 MngInfoFreeStruct(mng_info,&have_mng_structure);
12934 (void) CloseBlob(image);
12935 return(MagickFalse);
12937 (void) CatchImageException(image);
12938 if (GetNextImageInList(image) == (Image *) NULL)
12940 image=SyncNextImageInList(image);
12941 status=SetImageProgress(image,SaveImagesTag,scene++,
12942 GetImageListLength(image));
12944 if (status == MagickFalse)
12947 } while (mng_info->adjoin);
12951 while (GetPreviousImageInList(image) != (Image *) NULL)
12952 image=GetPreviousImageInList(image);
12954 Write the MEND chunk.
12956 (void) WriteBlobMSBULong(image,0x00000000L);
12957 PNGType(chunk,mng_MEND);
12958 LogPNGChunk(logging,mng_MEND,0L);
12959 (void) WriteBlob(image,4,chunk);
12960 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12963 Relinquish resources.
12965 (void) CloseBlob(image);
12966 MngInfoFreeStruct(mng_info,&have_mng_structure);
12968 if (logging != MagickFalse)
12969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12971 return(MagickTrue);
12973 #else /* PNG_LIBPNG_VER > 10011 */
12975 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12978 printf("Your PNG library is too old: You have libpng-%s\n",
12979 PNG_LIBPNG_VER_STRING);
12981 ThrowBinaryException(CoderError,"PNG library is too old",
12982 image_info->filename);
12985 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12987 return(WritePNGImage(image_info,image));
12989 #endif /* PNG_LIBPNG_VER > 10011 */