2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2017 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 % https://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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/layer.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/MagickCore.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/module.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/profile.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/semaphore.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/static.h"
84 #include "MagickCore/statistic.h"
85 #include "MagickCore/string_.h"
86 #include "MagickCore/string-private.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/utility.h"
89 #if defined(MAGICKCORE_PNG_DELEGATE)
91 /* Suppress libpng pedantic warnings that were added in
92 * libpng-1.2.41 and libpng-1.4.0. If you are working on
93 * migration to libpng-1.5, remove these defines and then
94 * fix any code that generates warnings.
96 /* #define PNG_DEPRECATED Use of this function is deprecated */
97 /* #define PNG_USE_RESULT The result of this function must be checked */
98 /* #define PNG_NORETURN This function does not return */
99 /* #define PNG_ALLOCATED The result of the function is new memory */
100 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
102 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
103 #define PNG_PTR_NORETURN
108 /* ImageMagick differences */
109 #define first_scene scene
111 #if PNG_LIBPNG_VER > 10011
113 Optional declarations. Define or undefine them as you like.
115 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
118 Features under construction. Define these to work on them.
120 #undef MNG_OBJECT_BUFFERS
121 #undef MNG_BASI_SUPPORTED
122 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
123 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
124 #if defined(MAGICKCORE_JPEG_DELEGATE)
125 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
127 #if !defined(RGBColorMatchExact)
128 #define IsPNGColorEqual(color,target) \
129 (((color).red == (target).red) && \
130 ((color).green == (target).green) && \
131 ((color).blue == (target).blue))
134 /* Table of recognized sRGB ICC profiles */
135 struct sRGB_info_struct
142 const struct sRGB_info_struct sRGB_info[] =
144 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
145 { 3048, 0x3b8772b9UL, 0},
147 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
148 { 3052, 0x427ebb21UL, 1},
150 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
151 {60988, 0x306fd8aeUL, 0},
153 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
154 {60960, 0xbbef7812UL, 0},
156 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
157 { 3024, 0x5d5129ceUL, 1},
159 /* HP-Microsoft sRGB v2 perceptual */
160 { 3144, 0x182ea552UL, 0},
162 /* HP-Microsoft sRGB v2 media-relative */
163 { 3144, 0xf29e526dUL, 1},
165 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
166 { 524, 0xd4938c39UL, 0},
168 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
169 { 3212, 0x034af5a1UL, 0},
172 { 0, 0x00000000UL, 0},
175 /* Macros for left-bit-replication to ensure that pixels
176 * and PixelInfos all have the same image->depth, and for use
177 * in PNG8 quantization.
180 /* LBR01: Replicate top bit */
182 #define LBR01PacketRed(pixelpacket) \
183 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
186 #define LBR01PacketGreen(pixelpacket) \
187 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
190 #define LBR01PacketBlue(pixelpacket) \
191 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
194 #define LBR01PacketAlpha(pixelpacket) \
195 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
198 #define LBR01PacketRGB(pixelpacket) \
200 LBR01PacketRed((pixelpacket)); \
201 LBR01PacketGreen((pixelpacket)); \
202 LBR01PacketBlue((pixelpacket)); \
205 #define LBR01PacketRGBO(pixelpacket) \
207 LBR01PacketRGB((pixelpacket)); \
208 LBR01PacketAlpha((pixelpacket)); \
211 #define LBR01PixelRed(pixel) \
212 (SetPixelRed(image, \
213 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
214 0 : QuantumRange,(pixel)));
216 #define LBR01PixelGreen(pixel) \
217 (SetPixelGreen(image, \
218 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
219 0 : QuantumRange,(pixel)));
221 #define LBR01PixelBlue(pixel) \
222 (SetPixelBlue(image, \
223 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
224 0 : QuantumRange,(pixel)));
226 #define LBR01PixelAlpha(pixel) \
227 (SetPixelAlpha(image, \
228 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
229 0 : QuantumRange,(pixel)));
231 #define LBR01PixelRGB(pixel) \
233 LBR01PixelRed((pixel)); \
234 LBR01PixelGreen((pixel)); \
235 LBR01PixelBlue((pixel)); \
238 #define LBR01PixelRGBA(pixel) \
240 LBR01PixelRGB((pixel)); \
241 LBR01PixelAlpha((pixel)); \
244 /* LBR02: Replicate top 2 bits */
246 #define LBR02PacketRed(pixelpacket) \
248 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
249 (pixelpacket).red=ScaleCharToQuantum( \
250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
252 #define LBR02PacketGreen(pixelpacket) \
254 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
255 (pixelpacket).green=ScaleCharToQuantum( \
256 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
258 #define LBR02PacketBlue(pixelpacket) \
260 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
261 (pixelpacket).blue=ScaleCharToQuantum( \
262 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
264 #define LBR02PacketAlpha(pixelpacket) \
266 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
267 (pixelpacket).alpha=ScaleCharToQuantum( \
268 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
271 #define LBR02PacketRGB(pixelpacket) \
273 LBR02PacketRed((pixelpacket)); \
274 LBR02PacketGreen((pixelpacket)); \
275 LBR02PacketBlue((pixelpacket)); \
278 #define LBR02PacketRGBO(pixelpacket) \
280 LBR02PacketRGB((pixelpacket)); \
281 LBR02PacketAlpha((pixelpacket)); \
284 #define LBR02PixelRed(pixel) \
286 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
288 SetPixelRed(image, ScaleCharToQuantum( \
289 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
292 #define LBR02PixelGreen(pixel) \
294 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
296 SetPixelGreen(image, ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
300 #define LBR02PixelBlue(pixel) \
302 unsigned char lbr_bits= \
303 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
304 SetPixelBlue(image, ScaleCharToQuantum( \
305 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
308 #define LBR02PixelAlpha(pixel) \
310 unsigned char lbr_bits= \
311 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
312 SetPixelAlpha(image, ScaleCharToQuantum( \
313 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
317 #define LBR02PixelRGB(pixel) \
319 LBR02PixelRed((pixel)); \
320 LBR02PixelGreen((pixel)); \
321 LBR02PixelBlue((pixel)); \
324 #define LBR02PixelRGBA(pixel) \
326 LBR02PixelRGB((pixel)); \
327 LBR02PixelAlpha((pixel)); \
330 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
331 PNG8 quantization) */
333 #define LBR03PacketRed(pixelpacket) \
335 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
336 (pixelpacket).red=ScaleCharToQuantum( \
337 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
339 #define LBR03PacketGreen(pixelpacket) \
341 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
342 (pixelpacket).green=ScaleCharToQuantum( \
343 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
345 #define LBR03PacketBlue(pixelpacket) \
347 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
348 (pixelpacket).blue=ScaleCharToQuantum( \
349 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
352 #define LBR03PacketRGB(pixelpacket) \
354 LBR03PacketRed((pixelpacket)); \
355 LBR03PacketGreen((pixelpacket)); \
356 LBR03PacketBlue((pixelpacket)); \
359 #define LBR03PixelRed(pixel) \
361 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
363 SetPixelRed(image, ScaleCharToQuantum( \
364 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
366 #define LBR03Green(pixel) \
368 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
370 SetPixelGreen(image, ScaleCharToQuantum( \
371 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
373 #define LBR03Blue(pixel) \
375 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
377 SetPixelBlue(image, ScaleCharToQuantum( \
378 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
381 #define LBR03RGB(pixel) \
383 LBR03PixelRed((pixel)); \
384 LBR03Green((pixel)); \
385 LBR03Blue((pixel)); \
388 /* LBR04: Replicate top 4 bits */
390 #define LBR04PacketRed(pixelpacket) \
392 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
393 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
395 #define LBR04PacketGreen(pixelpacket) \
397 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
398 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
400 #define LBR04PacketBlue(pixelpacket) \
402 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
403 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
405 #define LBR04PacketAlpha(pixelpacket) \
407 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
408 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
411 #define LBR04PacketRGB(pixelpacket) \
413 LBR04PacketRed((pixelpacket)); \
414 LBR04PacketGreen((pixelpacket)); \
415 LBR04PacketBlue((pixelpacket)); \
418 #define LBR04PacketRGBO(pixelpacket) \
420 LBR04PacketRGB((pixelpacket)); \
421 LBR04PacketAlpha((pixelpacket)); \
424 #define LBR04PixelRed(pixel) \
426 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
429 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
431 #define LBR04PixelGreen(pixel) \
433 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
435 SetPixelGreen(image,\
436 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
438 #define LBR04PixelBlue(pixel) \
440 unsigned char lbr_bits= \
441 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
443 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
445 #define LBR04PixelAlpha(pixel) \
447 unsigned char lbr_bits= \
448 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
449 SetPixelAlpha(image,\
450 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
453 #define LBR04PixelRGB(pixel) \
455 LBR04PixelRed((pixel)); \
456 LBR04PixelGreen((pixel)); \
457 LBR04PixelBlue((pixel)); \
460 #define LBR04PixelRGBA(pixel) \
462 LBR04PixelRGB((pixel)); \
463 LBR04PixelAlpha((pixel)); \
467 Establish thread safety.
468 setjmp/longjmp is claimed to be safe on these platforms:
469 setjmp/longjmp is alleged to be unsafe on these platforms:
471 #ifdef PNG_SETJMP_SUPPORTED
472 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
473 # define IMPNG_SETJMP_NOT_THREAD_SAFE
476 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
478 *ping_semaphore = (SemaphoreInfo *) NULL;
483 This temporary until I set up malloc'ed object attributes array.
484 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
487 #define MNG_MAX_OBJECTS 256
490 If this not defined, spec is interpreted strictly. If it is
491 defined, an attempt will be made to recover from some errors,
493 o global PLTE too short
498 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
499 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
500 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
501 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
502 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
503 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
504 will be enabled by default in libpng-1.2.0.
506 #ifdef PNG_MNG_FEATURES_SUPPORTED
507 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
508 # define PNG_READ_EMPTY_PLTE_SUPPORTED
510 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
511 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
516 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
517 This macro is only defined in libpng-1.0.3 and later.
518 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
520 #ifndef PNG_UINT_31_MAX
521 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
525 Constant strings for known chunk types. If you need to add a chunk,
526 add a string holding the name here. To make the code more
527 portable, we use ASCII numbers like this, not characters.
530 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
531 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
532 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
533 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
534 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
535 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
536 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
537 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
538 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
539 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
540 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
541 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
542 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
543 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
544 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
545 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
546 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
547 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
548 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
549 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
550 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
551 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
552 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
553 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
554 static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
555 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
556 static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
557 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
558 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
559 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
560 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
561 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
562 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
563 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
564 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
565 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
567 #if defined(JNG_SUPPORTED)
568 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
569 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
570 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
571 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
572 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
573 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
577 /* Other known chunks that are not yet supported by ImageMagick: */
578 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
579 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
580 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
581 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
582 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
583 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
584 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
587 typedef struct _MngBox
596 typedef struct _MngPair
603 #ifdef MNG_OBJECT_BUFFERS
604 typedef struct _MngBuffer
636 typedef struct _MngInfo
639 #ifdef MNG_OBJECT_BUFFERS
641 *ob[MNG_MAX_OBJECTS];
652 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
653 bytes_in_read_buffer,
659 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
660 defined(PNG_MNG_FEATURES_SUPPORTED)
672 have_saved_bkgd_index,
673 have_write_global_chrm,
674 have_write_global_gama,
675 have_write_global_plte,
676 have_write_global_srgb,
690 x_off[MNG_MAX_OBJECTS],
691 y_off[MNG_MAX_OBJECTS];
697 object_clip[MNG_MAX_OBJECTS];
700 /* These flags could be combined into one byte */
701 exists[MNG_MAX_OBJECTS],
702 frozen[MNG_MAX_OBJECTS],
704 invisible[MNG_MAX_OBJECTS],
705 viewable[MNG_MAX_OBJECTS];
717 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
735 global_x_pixels_per_unit,
736 global_y_pixels_per_unit,
746 global_phys_unit_type,
761 write_png_compression_level,
762 write_png_compression_strategy,
763 write_png_compression_filter,
770 #ifdef MNG_BASI_SUPPORTED
778 basi_compression_method,
780 basi_interlace_method,
803 /* Added at version 6.6.6-7 */
812 /* ping_exclude_iTXt, */
820 ping_exclude_zCCP, /* hex-encoded iCCP */
822 ping_preserve_colormap,
823 /* Added at version 6.8.5-7 */
825 /* Added at version 6.8.9-9 */
832 Forward declarations.
834 static MagickBooleanType
835 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
837 static MagickBooleanType
838 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
840 #if defined(JNG_SUPPORTED)
841 static MagickBooleanType
842 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
845 #if PNG_LIBPNG_VER > 10011
848 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
849 static MagickBooleanType
850 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
852 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
854 * This is true if the high byte and the next highest byte of
855 * each sample of the image, the colormap, and the background color
856 * are equal to each other. We check this by seeing if the samples
857 * are unchanged when we scale them down to 8 and back up to Quantum.
859 * We don't use the method GetImageDepth() because it doesn't check
860 * background and doesn't handle PseudoClass specially.
863 #define QuantumToCharToQuantumEqQuantum(quantum) \
864 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
867 ok_to_reduce=MagickFalse;
869 if (image->depth >= 16)
876 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
877 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
878 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
879 MagickTrue : MagickFalse;
881 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
885 for (indx=0; indx < (ssize_t) image->colors; indx++)
888 QuantumToCharToQuantumEqQuantum(
889 image->colormap[indx].red) &&
890 QuantumToCharToQuantumEqQuantum(
891 image->colormap[indx].green) &&
892 QuantumToCharToQuantumEqQuantum(
893 image->colormap[indx].blue)) ?
894 MagickTrue : MagickFalse;
896 if (ok_to_reduce == MagickFalse)
901 if ((ok_to_reduce != MagickFalse) &&
902 (image->storage_class != PseudoClass))
910 for (y=0; y < (ssize_t) image->rows; y++)
912 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
914 if (p == (const Quantum *) NULL)
916 ok_to_reduce = MagickFalse;
920 for (x=(ssize_t) image->columns-1; x >= 0; x--)
923 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
924 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
925 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
926 MagickTrue : MagickFalse;
928 if (ok_to_reduce == MagickFalse)
931 p+=GetPixelChannels(image);
938 if (ok_to_reduce != MagickFalse)
940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
941 " OK to reduce PNG bit depth to 8 without loss of info");
945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
946 " Not OK to reduce PNG bit depth to 8 without losing info");
952 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
954 static const char* PngColorTypeToString(const unsigned int color_type)
961 case PNG_COLOR_TYPE_GRAY:
964 case PNG_COLOR_TYPE_GRAY_ALPHA:
965 result = "Gray+Alpha";
967 case PNG_COLOR_TYPE_PALETTE:
970 case PNG_COLOR_TYPE_RGB:
973 case PNG_COLOR_TYPE_RGB_ALPHA:
974 result = "RGB+Alpha";
982 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
986 case PerceptualIntent:
992 case SaturationIntent:
1003 static RenderingIntent
1004 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1006 switch (ping_intent)
1009 return PerceptualIntent;
1012 return RelativeIntent;
1015 return SaturationIntent;
1018 return AbsoluteIntent;
1021 return UndefinedIntent;
1026 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1028 switch (ping_intent)
1031 return "Perceptual Intent";
1034 return "Relative Intent";
1037 return "Saturation Intent";
1040 return "Absolute Intent";
1043 return "Undefined Intent";
1048 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1050 switch (ping_colortype)
1068 return "UndefinedColorType";
1072 #endif /* PNG_LIBPNG_VER > 10011 */
1073 #endif /* MAGICKCORE_PNG_DELEGATE */
1076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1086 % IsMNG() returns MagickTrue if the image format type, identified by the
1087 % magick string, is MNG.
1089 % The format of the IsMNG method is:
1091 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1093 % A description of each parameter follows:
1095 % o magick: compare image format pattern against these bytes.
1097 % o length: Specifies the length of the magick string.
1101 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1104 return(MagickFalse);
1106 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1109 return(MagickFalse);
1113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123 % IsJNG() returns MagickTrue if the image format type, identified by the
1124 % magick string, is JNG.
1126 % The format of the IsJNG method is:
1128 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1130 % A description of each parameter follows:
1132 % o magick: compare image format pattern against these bytes.
1134 % o length: Specifies the length of the magick string.
1138 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1141 return(MagickFalse);
1143 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1146 return(MagickFalse);
1150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1160 % IsPNG() returns MagickTrue if the image format type, identified by the
1161 % magick string, is PNG.
1163 % The format of the IsPNG method is:
1165 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1167 % A description of each parameter follows:
1169 % o magick: compare image format pattern against these bytes.
1171 % o length: Specifies the length of the magick string.
1174 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1177 return(MagickFalse);
1179 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1182 return(MagickFalse);
1185 #if defined(MAGICKCORE_PNG_DELEGATE)
1186 #if defined(__cplusplus) || defined(c_plusplus)
1190 #if (PNG_LIBPNG_VER > 10011)
1191 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1196 assert(image != (Image *) NULL);
1197 assert(image->signature == MagickCoreSignature);
1198 buffer[0]=(unsigned char) (value >> 24);
1199 buffer[1]=(unsigned char) (value >> 16);
1200 buffer[2]=(unsigned char) (value >> 8);
1201 buffer[3]=(unsigned char) value;
1202 return((size_t) WriteBlob(image,4,buffer));
1205 static void PNGLong(png_bytep p,png_uint_32 value)
1207 *p++=(png_byte) ((value >> 24) & 0xff);
1208 *p++=(png_byte) ((value >> 16) & 0xff);
1209 *p++=(png_byte) ((value >> 8) & 0xff);
1210 *p++=(png_byte) (value & 0xff);
1213 static void PNGsLong(png_bytep p,png_int_32 value)
1215 *p++=(png_byte) ((value >> 24) & 0xff);
1216 *p++=(png_byte) ((value >> 16) & 0xff);
1217 *p++=(png_byte) ((value >> 8) & 0xff);
1218 *p++=(png_byte) (value & 0xff);
1221 static void PNGShort(png_bytep p,png_uint_16 value)
1223 *p++=(png_byte) ((value >> 8) & 0xff);
1224 *p++=(png_byte) (value & 0xff);
1227 static void PNGType(png_bytep p,const png_byte *type)
1229 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1232 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1235 if (logging != MagickFalse)
1236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1237 " Writing %c%c%c%c chunk, length: %.20g",
1238 type[0],type[1],type[2],type[3],(double) length);
1240 #endif /* PNG_LIBPNG_VER > 10011 */
1242 #if defined(__cplusplus) || defined(c_plusplus)
1246 #if PNG_LIBPNG_VER > 10011
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252 % R e a d P N G I m a g e %
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1259 % Multiple-image Network Graphics (MNG) image file and returns it. It
1260 % allocates the memory necessary for the new Image structure and returns a
1261 % pointer to the new image or set of images.
1263 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1265 % The format of the ReadPNGImage method is:
1267 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1269 % A description of each parameter follows:
1271 % o image_info: the image info.
1273 % o exception: return any errors or warnings in this structure.
1275 % To do, more or less in chronological order (as of version 5.5.2,
1276 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1278 % Get 16-bit cheap transparency working.
1280 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1282 % Preserve all unknown and not-yet-handled known chunks found in input
1283 % PNG file and copy them into output PNG files according to the PNG
1286 % (At this point, PNG encoding should be in full MNG compliance)
1288 % Provide options for choice of background to use when the MNG BACK
1289 % chunk is not present or is not mandatory (i.e., leave transparent,
1290 % user specified, MNG BACK, PNG bKGD)
1292 % Implement LOOP/ENDL [done, but could do discretionary loops more
1293 % efficiently by linking in the duplicate frames.].
1295 % Decode and act on the MHDR simplicity profile (offer option to reject
1296 % files or attempt to process them anyway when the profile isn't LC or VLC).
1298 % Upgrade to full MNG without Delta-PNG.
1300 % o BACK [done a while ago except for background image ID]
1301 % o MOVE [done 15 May 1999]
1302 % o CLIP [done 15 May 1999]
1303 % o DISC [done 19 May 1999]
1304 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1305 % o SEEK [partially done 19 May 1999 (discard function only)]
1309 % o MNG-level tEXt/iTXt/zTXt
1314 % o iTXt (wait for libpng implementation).
1316 % Use the scene signature to discover when an identical scene is
1317 % being reused, and just point to the original image->exception instead
1318 % of storing another set of pixels. This not specific to MNG
1319 % but could be applied generally.
1321 % Upgrade to full MNG with Delta-PNG.
1323 % JNG tEXt/iTXt/zTXt
1325 % We will not attempt to read files containing the CgBI chunk.
1326 % They are really Xcode files meant for display on the iPhone.
1327 % These are not valid PNG files and it is impossible to recover
1328 % the original PNG from files that have been converted to Xcode-PNG,
1329 % since irretrievable loss of color data has occurred due to the
1330 % use of premultiplied alpha.
1333 #if defined(__cplusplus) || defined(c_plusplus)
1338 This the function that does the actual reading of data. It is
1339 the same as the one supplied in libpng, except that it receives the
1340 datastream from the ReadBlob() function instead of standard input.
1342 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1347 image=(Image *) png_get_io_ptr(png_ptr);
1353 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1354 if (check != length)
1357 msg[MagickPathExtent];
1359 (void) FormatLocaleString(msg,MagickPathExtent,
1360 "Expected %.20g bytes; found %.20g bytes",(double) length,
1362 png_warning(png_ptr,msg);
1363 png_error(png_ptr,"Read Exception");
1368 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1369 !defined(PNG_MNG_FEATURES_SUPPORTED)
1370 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1371 * older than libpng-1.0.3a, which was the first to allow the empty
1372 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1373 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1374 * encountered after an empty PLTE, so we have to look ahead for bKGD
1375 * chunks and remove them from the datastream that is passed to libpng,
1376 * and store their contents for later use.
1378 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1393 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1394 image=(Image *) mng_info->image;
1395 while (mng_info->bytes_in_read_buffer && length)
1397 data[i]=mng_info->read_buffer[i];
1398 mng_info->bytes_in_read_buffer--;
1404 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1406 if (check != length)
1407 png_error(png_ptr,"Read Exception");
1411 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1414 check=(png_size_t) ReadBlob(image,(size_t) length,
1415 (char *) mng_info->read_buffer);
1416 mng_info->read_buffer[4]=0;
1417 mng_info->bytes_in_read_buffer=4;
1418 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1419 mng_info->found_empty_plte=MagickTrue;
1420 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1422 mng_info->found_empty_plte=MagickFalse;
1423 mng_info->have_saved_bkgd_index=MagickFalse;
1427 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1430 check=(png_size_t) ReadBlob(image,(size_t) length,
1431 (char *) mng_info->read_buffer);
1432 mng_info->read_buffer[4]=0;
1433 mng_info->bytes_in_read_buffer=4;
1434 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1435 if (mng_info->found_empty_plte)
1438 Skip the bKGD data byte and CRC.
1441 ReadBlob(image,5,(char *) mng_info->read_buffer);
1442 check=(png_size_t) ReadBlob(image,(size_t) length,
1443 (char *) mng_info->read_buffer);
1444 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1445 mng_info->have_saved_bkgd_index=MagickTrue;
1446 mng_info->bytes_in_read_buffer=0;
1454 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1459 image=(Image *) png_get_io_ptr(png_ptr);
1465 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1467 if (check != length)
1468 png_error(png_ptr,"WriteBlob Failed");
1472 static void png_flush_data(png_structp png_ptr)
1477 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1478 static int PalettesAreEqual(Image *a,Image *b)
1483 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1484 return((int) MagickFalse);
1486 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1487 return((int) MagickFalse);
1489 if (a->colors != b->colors)
1490 return((int) MagickFalse);
1492 for (i=0; i < (ssize_t) a->colors; i++)
1494 if ((a->colormap[i].red != b->colormap[i].red) ||
1495 (a->colormap[i].green != b->colormap[i].green) ||
1496 (a->colormap[i].blue != b->colormap[i].blue))
1497 return((int) MagickFalse);
1500 return((int) MagickTrue);
1504 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1506 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1507 mng_info->exists[i] && !mng_info->frozen[i])
1509 #ifdef MNG_OBJECT_BUFFERS
1510 if (mng_info->ob[i] != (MngBuffer *) NULL)
1512 if (mng_info->ob[i]->reference_count > 0)
1513 mng_info->ob[i]->reference_count--;
1515 if (mng_info->ob[i]->reference_count == 0)
1517 if (mng_info->ob[i]->image != (Image *) NULL)
1518 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1520 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1523 mng_info->ob[i]=(MngBuffer *) NULL;
1525 mng_info->exists[i]=MagickFalse;
1526 mng_info->invisible[i]=MagickFalse;
1527 mng_info->viewable[i]=MagickFalse;
1528 mng_info->frozen[i]=MagickFalse;
1529 mng_info->x_off[i]=0;
1530 mng_info->y_off[i]=0;
1531 mng_info->object_clip[i].left=0;
1532 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1533 mng_info->object_clip[i].top=0;
1534 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1538 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1543 if (mng_info == (MngInfo *) NULL)
1544 return((MngInfo *) NULL);
1546 for (i=1; i < MNG_MAX_OBJECTS; i++)
1547 MngInfoDiscardObject(mng_info,i);
1549 mng_info->global_plte=(png_colorp)
1550 RelinquishMagickMemory(mng_info->global_plte);
1552 return((MngInfo *) RelinquishMagickMemory(mng_info));
1555 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1561 if (box.left < box2.left)
1564 if (box.top < box2.top)
1567 if (box.right > box2.right)
1568 box.right=box2.right;
1570 if (box.bottom > box2.bottom)
1571 box.bottom=box2.bottom;
1576 static long mng_get_long(unsigned char *p)
1578 return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1579 ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1582 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1589 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1591 box.left=mng_get_long(p);
1592 box.right=mng_get_long(&p[4]);
1593 box.top=mng_get_long(&p[8]);
1594 box.bottom=mng_get_long(&p[12]);
1595 if (delta_type != 0)
1597 box.left+=previous_box.left;
1598 box.right+=previous_box.right;
1599 box.top+=previous_box.top;
1600 box.bottom+=previous_box.bottom;
1606 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1613 Read two ssize_t's from CLON, MOVE or PAST chunk
1615 pair.a=mng_get_long(p);
1616 pair.b=mng_get_long(&p[4]);
1617 if (delta_type != 0)
1619 pair.a+=previous_pair.a;
1620 pair.b+=previous_pair.b;
1626 typedef struct _PNGErrorInfo
1635 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1646 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1647 image=error_info->image;
1648 exception=error_info->exception;
1650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1651 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1653 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1654 "`%s'",image->filename);
1656 #if (PNG_LIBPNG_VER < 10500)
1657 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1658 * are building with libpng-1.4.x and can be ignored.
1660 longjmp(ping->jmpbuf,1);
1662 png_longjmp(ping,1);
1666 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1677 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1678 png_error(ping, message);
1680 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1681 image=error_info->image;
1682 exception=error_info->exception;
1683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1684 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1686 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1687 message,"`%s'",image->filename);
1690 #ifdef PNG_USER_MEM_SUPPORTED
1691 #if PNG_LIBPNG_VER >= 10400
1692 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1694 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1698 return((png_voidp) AcquireMagickMemory((size_t) size));
1702 Free a pointer. It is removed from the list at the same time.
1704 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1707 ptr=RelinquishMagickMemory(ptr);
1708 return((png_free_ptr) NULL);
1712 #if defined(__cplusplus) || defined(c_plusplus)
1717 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1718 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1723 register unsigned char
1737 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1739 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1740 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1741 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1745 /* look for newline */
1749 /* look for length */
1750 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1753 length=(png_uint_32) StringToLong(sp);
1755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1756 " length: %lu",(unsigned long) length);
1758 while (*sp != ' ' && *sp != '\n')
1761 /* allocate space */
1764 png_warning(ping,"invalid profile length");
1765 return(MagickFalse);
1768 profile=BlobToStringInfo((const void *) NULL,length);
1770 if (profile == (StringInfo *) NULL)
1772 png_warning(ping, "unable to copy profile");
1773 return(MagickFalse);
1776 /* copy profile, skipping white space and column 1 "=" signs */
1777 dp=GetStringInfoDatum(profile);
1780 for (i=0; i < (ssize_t) nibbles; i++)
1782 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1786 png_warning(ping, "ran out of profile data");
1787 profile=DestroyStringInfo(profile);
1788 return(MagickFalse);
1794 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1797 (*dp++)+=unhex[(int) *sp++];
1800 We have already read "Raw profile type.
1802 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1803 profile=DestroyStringInfo(profile);
1805 if (image_info->verbose)
1806 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1811 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1813 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1819 /* The unknown chunk structure contains the chunk data:
1824 Note that libpng has already taken care of the CRC handling.
1826 Returns one of the following:
1827 return(-n); chunk had an error
1828 return(0); did not recognize
1832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1833 " read_user_chunk: found %c%c%c%c chunk",
1834 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1836 if (chunk->name[0] == 101 &&
1837 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
1838 chunk->name[2] == 73 &&
1839 chunk-> name[3] == 102)
1841 /* process eXIf or exIf chunk */
1858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1859 " recognized eXIf chunk");
1861 image=(Image *) png_get_user_chunk_ptr(ping);
1863 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1865 profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1867 if (profile == (StringInfo *) NULL)
1869 (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1870 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1874 p=GetStringInfoDatum(profile);
1878 /* Initialize profile with "Exif\0\0" if it is not
1879 already present by accident
1890 if (p[1] != 'x' || p[2] != 'i' || p[3] != 'f' ||
1891 p[4] != '\0' || p[5] != '\0')
1893 /* Chunk is malformed */
1894 profile=DestroyStringInfo(profile);
1899 /* copy chunk->data to profile */
1901 for (i=0; i<chunk->size; i++)
1904 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1905 (void) SetImageProfile(image,"exif",profile,
1906 error_info->exception);
1908 profile=DestroyStringInfo(profile);
1913 /* vpAg (deprecated, replaced by caNv) */
1914 if (chunk->name[0] == 118 &&
1915 chunk->name[1] == 112 &&
1916 chunk->name[2] == 65 &&
1917 chunk->name[3] == 103)
1919 /* recognized vpAg */
1921 if (chunk->size != 9)
1922 return(-1); /* Error return */
1924 if (chunk->data[8] != 0)
1925 return(0); /* ImageMagick requires pixel units */
1927 image=(Image *) png_get_user_chunk_ptr(ping);
1929 image->page.width=(size_t)mng_get_long(chunk->data);
1930 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
1936 if (chunk->name[0] == 99 &&
1937 chunk->name[1] == 97 &&
1938 chunk->name[2] == 78 &&
1939 chunk->name[3] == 118)
1941 /* recognized caNv */
1943 if (chunk->size != 16)
1944 return(-1); /* Error return */
1946 image=(Image *) png_get_user_chunk_ptr(ping);
1948 image->page.width=(size_t)mng_get_long(chunk->data);
1949 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
1950 image->page.x=(size_t)mng_get_long(&chunk->data[8]);
1951 image->page.y=(size_t)mng_get_long(&chunk->data[12]);
1956 return(0); /* Did not recognize */
1958 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
1960 #if defined(PNG_tIME_SUPPORTED)
1961 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1962 ExceptionInfo *exception)
1967 if (png_get_tIME(ping,info,&time))
1972 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1973 time->year,time->month,time->day,time->hour,time->minute,time->second);
1974 SetImageProperty(image,"png:tIME",timestamp,exception);
1980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1984 % R e a d O n e P N G I m a g e %
1988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1990 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1991 % (minus the 8-byte signature) and returns it. It allocates the memory
1992 % necessary for the new Image structure and returns a pointer to the new
1995 % The format of the ReadOnePNGImage method is:
1997 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1998 % ExceptionInfo *exception)
2000 % A description of each parameter follows:
2002 % o mng_info: Specifies a pointer to a MngInfo structure.
2004 % o image_info: the image info.
2006 % o exception: return any errors or warnings in this structure.
2009 static Image *ReadOnePNGImage(MngInfo *mng_info,
2010 const ImageInfo *image_info, ExceptionInfo *exception)
2012 /* Read one PNG image */
2014 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2027 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2037 ping_interlace_method,
2038 ping_compression_method,
2052 ping_found_sRGB_cHRM,
2057 *volatile pixel_info;
2089 *volatile quantum_info;
2092 *volatile quantum_scanline;
2098 register unsigned char
2118 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2119 png_byte unused_chunks[]=
2121 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2122 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2123 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2124 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2125 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2126 #if !defined(PNG_tIME_SUPPORTED)
2127 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2129 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2130 /* ignore the APNG chunks */
2131 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2132 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2133 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2138 /* Define these outside of the following "if logging()" block so they will
2139 * show in debuggers.
2142 (void) ConcatenateMagickString(im_vers,
2143 MagickLibVersionText,32);
2144 (void) ConcatenateMagickString(im_vers,
2145 MagickLibAddendum,32);
2148 (void) ConcatenateMagickString(libpng_vers,
2149 PNG_LIBPNG_VER_STRING,32);
2151 (void) ConcatenateMagickString(libpng_runv,
2152 png_get_libpng_ver(NULL),32);
2155 (void) ConcatenateMagickString(zlib_vers,
2158 (void) ConcatenateMagickString(zlib_runv,
2161 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2162 " Enter ReadOnePNGImage()\n"
2163 " IM version = %s\n"
2164 " Libpng version = %s",
2165 im_vers, libpng_vers);
2167 if (logging != MagickFalse)
2169 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2171 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2174 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2176 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2178 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2183 #if (PNG_LIBPNG_VER < 10200)
2184 if (image_info->verbose)
2185 printf("Your PNG library (libpng-%s) is rather old.\n",
2186 PNG_LIBPNG_VER_STRING);
2189 #if (PNG_LIBPNG_VER >= 10400)
2190 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2191 if (image_info->verbose)
2193 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2194 PNG_LIBPNG_VER_STRING);
2195 printf("Please update it.\n");
2201 quantum_info = (QuantumInfo *) NULL;
2202 image=mng_info->image;
2204 if (logging != MagickFalse)
2206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2207 " Before reading:\n"
2208 " image->alpha_trait=%d\n"
2209 " image->rendering_intent=%d\n"
2210 " image->colorspace=%d\n"
2212 (int) image->alpha_trait, (int) image->rendering_intent,
2213 (int) image->colorspace, image->gamma);
2216 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2218 /* Set to an out-of-range color unless tRNS chunk is present */
2219 transparent_color.red=65537;
2220 transparent_color.green=65537;
2221 transparent_color.blue=65537;
2222 transparent_color.alpha=65537;
2227 num_raw_profiles = 0;
2229 ping_found_cHRM = MagickFalse;
2230 ping_found_gAMA = MagickFalse;
2231 ping_found_iCCP = MagickFalse;
2232 ping_found_sRGB = MagickFalse;
2233 ping_found_sRGB_cHRM = MagickFalse;
2234 ping_preserve_iCCP = MagickFalse;
2238 Allocate the PNG structures
2240 #ifdef PNG_USER_MEM_SUPPORTED
2241 error_info.image=image;
2242 error_info.exception=exception;
2243 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2244 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2245 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2247 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2248 MagickPNGErrorHandler,MagickPNGWarningHandler);
2250 if (ping == (png_struct *) NULL)
2251 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2253 ping_info=png_create_info_struct(ping);
2255 if (ping_info == (png_info *) NULL)
2257 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2258 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2261 end_info=png_create_info_struct(ping);
2263 if (end_info == (png_info *) NULL)
2265 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2266 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2269 pixel_info=(MemoryInfo *) NULL;
2270 quantum_scanline = (Quantum *) NULL;
2271 quantum_info = (QuantumInfo *) NULL;
2273 if (setjmp(png_jmpbuf(ping)))
2276 PNG image is corrupt.
2278 png_destroy_read_struct(&ping,&ping_info,&end_info);
2280 if (pixel_info != (MemoryInfo *) NULL)
2281 pixel_info=RelinquishVirtualMemory(pixel_info);
2283 if (quantum_info != (QuantumInfo *) NULL)
2284 quantum_info=DestroyQuantumInfo(quantum_info);
2286 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2288 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2289 UnlockSemaphoreInfo(ping_semaphore);
2292 if (logging != MagickFalse)
2293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2294 " exit ReadOnePNGImage() with error.");
2296 return(GetFirstImageInList(image));
2299 /* { For navigation to end of SETJMP-protected block. Within this
2300 * block, use png_error() instead of Throwing an Exception, to ensure
2301 * that libpng is able to clean up, and that the semaphore is unlocked.
2304 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2305 LockSemaphoreInfo(ping_semaphore);
2308 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2309 /* Allow benign errors */
2310 png_set_benign_errors(ping, 1);
2313 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2314 /* Reject images with too many rows or columns */
2315 png_set_user_limits(ping,
2316 (png_uint_32) MagickMin(0x7fffffffL,
2317 GetMagickResourceLimit(WidthResource)),
2318 (png_uint_32) MagickMin(0x7fffffffL,
2319 GetMagickResourceLimit(HeightResource)));
2320 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2323 Prepare PNG for reading.
2326 mng_info->image_found++;
2327 png_set_sig_bytes(ping,8);
2329 if (LocaleCompare(image_info->magick,"MNG") == 0)
2331 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2332 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2333 png_set_read_fn(ping,image,png_get_data);
2335 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2336 png_permit_empty_plte(ping,MagickTrue);
2337 png_set_read_fn(ping,image,png_get_data);
2339 mng_info->image=image;
2340 mng_info->bytes_in_read_buffer=0;
2341 mng_info->found_empty_plte=MagickFalse;
2342 mng_info->have_saved_bkgd_index=MagickFalse;
2343 png_set_read_fn(ping,mng_info,mng_get_data);
2349 png_set_read_fn(ping,image,png_get_data);
2355 value=GetImageOption(image_info,"profile:skip");
2357 if (IsOptionMember("ICC",value) == MagickFalse)
2360 value=GetImageOption(image_info,"png:preserve-iCCP");
2363 value=GetImageArtifact(image,"png:preserve-iCCP");
2366 ping_preserve_iCCP=MagickTrue;
2368 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2369 /* Don't let libpng check for ICC/sRGB profile because we're going
2370 * to do that anyway. This feature was added at libpng-1.6.12.
2371 * If logging, go ahead and check and issue a warning as appropriate.
2373 if (logging == MagickFalse)
2374 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2377 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2380 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2384 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2385 /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2386 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2387 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2389 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2391 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2392 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2393 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2394 (int)sizeof(unused_chunks)/5);
2395 /* Callback for other unknown chunks */
2396 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2399 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2400 # if (PNG_LIBPNG_VER >= 10400)
2401 /* Limit the size of the chunk storage cache used for sPLT, text,
2402 * and unknown chunks.
2404 png_set_chunk_cache_max(ping, 32767);
2408 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2409 /* Disable new libpng-1.5.10 feature */
2410 png_set_check_for_invalid_index (ping, 0);
2413 #if (PNG_LIBPNG_VER < 10400)
2414 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2415 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2416 /* Disable thread-unsafe features of pnggccrd */
2417 if (png_access_version_number() >= 10200)
2419 png_uint_32 mmx_disable_mask=0;
2420 png_uint_32 asm_flags;
2422 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2423 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2424 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2425 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2426 asm_flags=png_get_asm_flags(ping);
2427 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2432 png_read_info(ping,ping_info);
2434 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2435 &ping_bit_depth,&ping_color_type,
2436 &ping_interlace_method,&ping_compression_method,
2437 &ping_filter_method);
2439 ping_file_depth = ping_bit_depth;
2441 /* Swap bytes if requested */
2442 if (ping_file_depth == 16)
2447 value=GetImageOption(image_info,"png:swap-bytes");
2450 value=GetImageArtifact(image,"png:swap-bytes");
2456 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2459 msg[MagickPathExtent];
2461 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2462 (int) ping_color_type);
2463 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2465 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2466 (int) ping_bit_depth);
2467 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2470 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2473 (void) png_get_bKGD(ping, ping_info, &ping_background);
2475 if (ping_bit_depth < 8)
2477 png_set_packing(ping);
2481 image->depth=ping_bit_depth;
2482 image->depth=GetImageQuantumDepth(image,MagickFalse);
2483 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2485 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2486 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2488 image->rendering_intent=UndefinedIntent;
2489 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2490 (void) ResetMagickMemory(&image->chromaticity,0,
2491 sizeof(image->chromaticity));
2494 if (logging != MagickFalse)
2496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2497 " PNG width: %.20g, height: %.20g\n"
2498 " PNG color_type: %d, bit_depth: %d\n"
2499 " PNG compression_method: %d\n"
2500 " PNG interlace_method: %d, filter_method: %d",
2501 (double) ping_width, (double) ping_height,
2502 ping_color_type, ping_bit_depth,
2503 ping_compression_method,
2504 ping_interlace_method,ping_filter_method);
2508 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2510 ping_found_iCCP=MagickTrue;
2511 if (logging != MagickFalse)
2512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2513 " Found PNG iCCP chunk.");
2516 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2518 ping_found_gAMA=MagickTrue;
2519 if (logging != MagickFalse)
2520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2521 " Found PNG gAMA chunk.");
2524 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2526 ping_found_cHRM=MagickTrue;
2527 if (logging != MagickFalse)
2528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2529 " Found PNG cHRM chunk.");
2532 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2535 ping_found_sRGB=MagickTrue;
2536 if (logging != MagickFalse)
2537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538 " Found PNG sRGB chunk.");
2541 #ifdef PNG_READ_iCCP_SUPPORTED
2542 if (ping_found_iCCP !=MagickTrue &&
2543 ping_found_sRGB != MagickTrue &&
2544 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2546 ping_found_iCCP=MagickTrue;
2547 if (logging != MagickFalse)
2548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2549 " Found PNG iCCP chunk.");
2552 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2557 #if (PNG_LIBPNG_VER < 10500)
2571 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2574 if (profile_length != 0)
2579 if (logging != MagickFalse)
2580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2581 " Reading PNG iCCP chunk.");
2583 profile=BlobToStringInfo(info,profile_length);
2585 if (profile == (StringInfo *) NULL)
2587 png_warning(ping, "ICC profile is NULL");
2588 profile=DestroyStringInfo(profile);
2592 if (ping_preserve_iCCP == MagickFalse)
2606 length=(png_uint_32) GetStringInfoLength(profile);
2608 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2610 if (length == sRGB_info[icheck].len)
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 " Got a %lu-byte ICC profile (potentially sRGB)",
2616 (unsigned long) length);
2618 data=GetStringInfoDatum(profile);
2619 profile_crc=crc32(0,data,length);
2621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2622 " with crc=%8x",(unsigned int) profile_crc);
2626 if (profile_crc == sRGB_info[icheck].crc)
2628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2629 " It is sRGB with rendering intent = %s",
2630 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2631 sRGB_info[icheck].intent));
2632 if (image->rendering_intent==UndefinedIntent)
2634 image->rendering_intent=
2635 Magick_RenderingIntent_from_PNG_RenderingIntent(
2636 sRGB_info[icheck].intent);
2642 if (sRGB_info[icheck].len == 0)
2644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2645 " Got %lu-byte ICC profile not recognized as sRGB",
2646 (unsigned long) length);
2647 (void) SetImageProfile(image,"icc",profile,exception);
2650 else /* Preserve-iCCP */
2652 (void) SetImageProfile(image,"icc",profile,exception);
2655 profile=DestroyStringInfo(profile);
2661 #if defined(PNG_READ_sRGB_SUPPORTED)
2663 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2666 if (png_get_sRGB(ping,ping_info,&intent))
2668 if (image->rendering_intent == UndefinedIntent)
2669 image->rendering_intent=
2670 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2672 if (logging != MagickFalse)
2673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2674 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2678 else if (mng_info->have_global_srgb)
2680 if (image->rendering_intent == UndefinedIntent)
2681 image->rendering_intent=
2682 Magick_RenderingIntent_from_PNG_RenderingIntent
2683 (mng_info->global_srgb_intent);
2690 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2691 if (mng_info->have_global_gama)
2692 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2694 if (png_get_gAMA(ping,ping_info,&file_gamma))
2696 image->gamma=(float) file_gamma;
2697 if (logging != MagickFalse)
2698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2699 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2703 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2705 if (mng_info->have_global_chrm != MagickFalse)
2707 (void) png_set_cHRM(ping,ping_info,
2708 mng_info->global_chrm.white_point.x,
2709 mng_info->global_chrm.white_point.y,
2710 mng_info->global_chrm.red_primary.x,
2711 mng_info->global_chrm.red_primary.y,
2712 mng_info->global_chrm.green_primary.x,
2713 mng_info->global_chrm.green_primary.y,
2714 mng_info->global_chrm.blue_primary.x,
2715 mng_info->global_chrm.blue_primary.y);
2719 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2721 (void) png_get_cHRM(ping,ping_info,
2722 &image->chromaticity.white_point.x,
2723 &image->chromaticity.white_point.y,
2724 &image->chromaticity.red_primary.x,
2725 &image->chromaticity.red_primary.y,
2726 &image->chromaticity.green_primary.x,
2727 &image->chromaticity.green_primary.y,
2728 &image->chromaticity.blue_primary.x,
2729 &image->chromaticity.blue_primary.y);
2731 ping_found_cHRM=MagickTrue;
2733 if (image->chromaticity.red_primary.x>0.6399f &&
2734 image->chromaticity.red_primary.x<0.6401f &&
2735 image->chromaticity.red_primary.y>0.3299f &&
2736 image->chromaticity.red_primary.y<0.3301f &&
2737 image->chromaticity.green_primary.x>0.2999f &&
2738 image->chromaticity.green_primary.x<0.3001f &&
2739 image->chromaticity.green_primary.y>0.5999f &&
2740 image->chromaticity.green_primary.y<0.6001f &&
2741 image->chromaticity.blue_primary.x>0.1499f &&
2742 image->chromaticity.blue_primary.x<0.1501f &&
2743 image->chromaticity.blue_primary.y>0.0599f &&
2744 image->chromaticity.blue_primary.y<0.0601f &&
2745 image->chromaticity.white_point.x>0.3126f &&
2746 image->chromaticity.white_point.x<0.3128f &&
2747 image->chromaticity.white_point.y>0.3289f &&
2748 image->chromaticity.white_point.y<0.3291f)
2749 ping_found_sRGB_cHRM=MagickTrue;
2752 if (image->rendering_intent != UndefinedIntent)
2754 if (ping_found_sRGB != MagickTrue &&
2755 (ping_found_gAMA != MagickTrue ||
2756 (image->gamma > .45 && image->gamma < .46)) &&
2757 (ping_found_cHRM != MagickTrue ||
2758 ping_found_sRGB_cHRM != MagickFalse) &&
2759 ping_found_iCCP != MagickTrue)
2761 png_set_sRGB(ping,ping_info,
2762 Magick_RenderingIntent_to_PNG_RenderingIntent
2763 (image->rendering_intent));
2764 file_gamma=1.000f/2.200f;
2765 ping_found_sRGB=MagickTrue;
2766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2767 " Setting sRGB as if in input");
2771 #if defined(PNG_oFFs_SUPPORTED)
2772 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2774 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2775 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2777 if (logging != MagickFalse)
2778 if (image->page.x || image->page.y)
2779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2780 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2781 image->page.x,(double) image->page.y);
2784 #if defined(PNG_pHYs_SUPPORTED)
2785 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2787 if (mng_info->have_global_phys)
2789 png_set_pHYs(ping,ping_info,
2790 mng_info->global_x_pixels_per_unit,
2791 mng_info->global_y_pixels_per_unit,
2792 mng_info->global_phys_unit_type);
2799 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2802 Set image resolution.
2804 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2806 image->resolution.x=(double) x_resolution;
2807 image->resolution.y=(double) y_resolution;
2809 if (unit_type == PNG_RESOLUTION_METER)
2811 image->units=PixelsPerCentimeterResolution;
2812 image->resolution.x=(double) x_resolution/100.0;
2813 image->resolution.y=(double) y_resolution/100.0;
2816 if (logging != MagickFalse)
2817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2818 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2819 (double) x_resolution,(double) y_resolution,unit_type);
2823 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2828 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2830 if ((number_colors == 0) &&
2831 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2833 if (mng_info->global_plte_length)
2835 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2836 (int) mng_info->global_plte_length);
2838 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2840 if (mng_info->global_trns_length)
2843 "global tRNS has more entries than global PLTE");
2847 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2848 (int) mng_info->global_trns_length,NULL);
2851 #ifdef PNG_READ_bKGD_SUPPORTED
2853 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2854 mng_info->have_saved_bkgd_index ||
2856 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2861 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2862 if (mng_info->have_saved_bkgd_index)
2863 background.index=mng_info->saved_bkgd_index;
2865 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2866 background.index=ping_background->index;
2868 background.red=(png_uint_16)
2869 mng_info->global_plte[background.index].red;
2871 background.green=(png_uint_16)
2872 mng_info->global_plte[background.index].green;
2874 background.blue=(png_uint_16)
2875 mng_info->global_plte[background.index].blue;
2877 background.gray=(png_uint_16)
2878 mng_info->global_plte[background.index].green;
2880 png_set_bKGD(ping,ping_info,&background);
2885 png_error(ping,"No global PLTE in file");
2889 #ifdef PNG_READ_bKGD_SUPPORTED
2890 if (mng_info->have_global_bkgd &&
2891 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2892 image->background_color=mng_info->mng_global_bkgd;
2894 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2899 /* Set image background color.
2900 * Scale background components to 16-bit, then scale
2906 if (ping_file_depth == 1)
2909 else if (ping_file_depth == 2)
2912 else if (ping_file_depth == 4)
2915 if (ping_file_depth <= 8)
2918 ping_background->red *= bkgd_scale;
2919 ping_background->green *= bkgd_scale;
2920 ping_background->blue *= bkgd_scale;
2922 if (logging != MagickFalse)
2924 if (logging != MagickFalse)
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
2927 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
2928 ping_background->red,ping_background->green,
2929 ping_background->blue,
2930 bkgd_scale,ping_background->red,
2931 ping_background->green,ping_background->blue);
2934 image->background_color.red=
2935 ScaleShortToQuantum(ping_background->red);
2937 image->background_color.green=
2938 ScaleShortToQuantum(ping_background->green);
2940 image->background_color.blue=
2941 ScaleShortToQuantum(ping_background->blue);
2943 image->background_color.alpha=OpaqueAlpha;
2945 if (logging != MagickFalse)
2946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2947 " image->background_color=(%.20g,%.20g,%.20g).",
2948 (double) image->background_color.red,
2949 (double) image->background_color.green,
2950 (double) image->background_color.blue);
2952 #endif /* PNG_READ_bKGD_SUPPORTED */
2954 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2957 Image has a tRNS chunk.
2965 if (logging != MagickFalse)
2966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2967 " Reading PNG tRNS chunk.");
2969 max_sample = (int) ((one << ping_file_depth) - 1);
2971 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2972 (int)ping_trans_color->gray > max_sample) ||
2973 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2974 ((int)ping_trans_color->red > max_sample ||
2975 (int)ping_trans_color->green > max_sample ||
2976 (int)ping_trans_color->blue > max_sample)))
2978 if (logging != MagickFalse)
2979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2980 " Ignoring PNG tRNS chunk with out-of-range sample.");
2981 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2982 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2983 image->alpha_trait=UndefinedPixelTrait;
2990 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2992 /* Scale transparent_color to short */
2993 transparent_color.red= scale_to_short*ping_trans_color->red;
2994 transparent_color.green= scale_to_short*ping_trans_color->green;
2995 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2996 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2998 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3000 if (logging != MagickFalse)
3002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3003 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3004 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3007 transparent_color.red=transparent_color.alpha;
3008 transparent_color.green=transparent_color.alpha;
3009 transparent_color.blue=transparent_color.alpha;
3013 #if defined(PNG_READ_sBIT_SUPPORTED)
3014 if (mng_info->have_global_sbit)
3016 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3017 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3020 num_passes=png_set_interlace_handling(ping);
3022 png_read_update_info(ping,ping_info);
3024 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3027 Initialize image structure.
3029 mng_info->image_box.left=0;
3030 mng_info->image_box.right=(ssize_t) ping_width;
3031 mng_info->image_box.top=0;
3032 mng_info->image_box.bottom=(ssize_t) ping_height;
3033 if (mng_info->mng_type == 0)
3035 mng_info->mng_width=ping_width;
3036 mng_info->mng_height=ping_height;
3037 mng_info->frame=mng_info->image_box;
3038 mng_info->clip=mng_info->image_box;
3043 image->page.y=mng_info->y_off[mng_info->object_id];
3046 image->compression=ZipCompression;
3047 image->columns=ping_width;
3048 image->rows=ping_height;
3050 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3051 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3054 image_gamma = image->gamma;
3056 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3057 " image->gamma=%f",(float) image_gamma);
3059 if (image_gamma > 0.75)
3061 /* Set image->rendering_intent to Undefined,
3062 * image->colorspace to GRAY, and reset image->chromaticity.
3064 image->intensity = Rec709LuminancePixelIntensityMethod;
3065 SetImageColorspace(image,GRAYColorspace,exception);
3070 save_rendering_intent = image->rendering_intent;
3072 save_chromaticity = image->chromaticity;
3074 SetImageColorspace(image,GRAYColorspace,exception);
3075 image->rendering_intent = save_rendering_intent;
3076 image->chromaticity = save_chromaticity;
3079 image->gamma = image_gamma;
3082 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3083 " image->colorspace=%d",(int) image->colorspace);
3085 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3086 ((int) ping_bit_depth < 16 &&
3087 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3092 image->storage_class=PseudoClass;
3094 image->colors=one << ping_file_depth;
3095 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3096 if (image->colors > 256)
3099 if (image->colors > 65536L)
3100 image->colors=65536L;
3102 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3107 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3108 image->colors=(size_t) number_colors;
3110 if (logging != MagickFalse)
3111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3112 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3116 if (image->storage_class == PseudoClass)
3119 Initialize image colormap.
3121 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3122 png_error(ping,"Memory allocation failed");
3124 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3129 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3131 for (i=0; i < (ssize_t) number_colors; i++)
3133 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3134 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3135 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3138 for ( ; i < (ssize_t) image->colors; i++)
3140 image->colormap[i].red=0;
3141 image->colormap[i].green=0;
3142 image->colormap[i].blue=0;
3151 scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3153 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3154 scale = ScaleShortToQuantum(scale);
3157 for (i=0; i < (ssize_t) image->colors; i++)
3159 image->colormap[i].red=(Quantum) (i*scale);
3160 image->colormap[i].green=(Quantum) (i*scale);
3161 image->colormap[i].blue=(Quantum) (i*scale);
3166 /* Set some properties for reporting by "identify" */
3169 msg[MagickPathExtent];
3171 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3172 ping_interlace_method in value */
3174 (void) FormatLocaleString(msg,MagickPathExtent,
3175 "%d, %d",(int) ping_width, (int) ping_height);
3176 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3178 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3179 (int) ping_file_depth);
3180 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3182 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3183 (int) ping_color_type,
3184 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3185 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3187 if (ping_interlace_method == 0)
3189 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3190 (int) ping_interlace_method);
3192 else if (ping_interlace_method == 1)
3194 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3195 (int) ping_interlace_method);
3199 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3200 (int) ping_interlace_method);
3202 (void) SetImageProperty(image,"png:IHDR.interlace_method",
3205 if (number_colors != 0)
3207 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3208 (int) number_colors);
3209 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3213 #if defined(PNG_tIME_SUPPORTED)
3214 read_tIME_chunk(image,ping,ping_info,exception);
3219 Read image scanlines.
3221 if (image->delay != 0)
3222 mng_info->scenes_found++;
3224 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3225 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3226 (image_info->first_scene+image_info->number_scenes))))
3228 /* This happens later in non-ping decodes */
3229 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3230 image->storage_class=DirectClass;
3232 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3233 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3234 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3235 BlendPixelTrait : UndefinedPixelTrait;
3237 if (logging != MagickFalse)
3238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239 " Skipping PNG image data for scene %.20g",(double)
3240 mng_info->scenes_found-1);
3241 png_destroy_read_struct(&ping,&ping_info,&end_info);
3243 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3244 UnlockSemaphoreInfo(ping_semaphore);
3247 if (logging != MagickFalse)
3248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249 " exit ReadOnePNGImage().");
3254 if (logging != MagickFalse)
3255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3256 " Reading PNG IDAT chunk(s)");
3258 status=SetImageExtent(image,image->columns,image->rows,exception);
3259 if (status == MagickFalse)
3261 png_destroy_read_struct(&ping,&ping_info,&end_info);
3262 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3263 UnlockSemaphoreInfo(ping_semaphore);
3265 return(DestroyImageList(image));
3269 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3270 sizeof(*ping_pixels));
3272 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3274 if (pixel_info == (MemoryInfo *) NULL)
3275 png_error(ping,"Memory allocation failed");
3276 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3278 if (logging != MagickFalse)
3279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3280 " Converting PNG pixels to pixel packets");
3282 Convert PNG pixels to pixel packets.
3284 quantum_info=AcquireQuantumInfo(image_info,image);
3286 if (quantum_info == (QuantumInfo *) NULL)
3287 png_error(ping,"Failed to allocate quantum_info");
3289 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3294 found_transparent_pixel;
3296 found_transparent_pixel=MagickFalse;
3298 if (image->storage_class == DirectClass)
3300 for (pass=0; pass < num_passes; pass++)
3303 Convert image to DirectClass pixel packets.
3306 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3307 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3308 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3309 BlendPixelTrait : UndefinedPixelTrait;
3311 for (y=0; y < (ssize_t) image->rows; y++)
3314 row_offset=ping_rowbytes*y;
3319 png_read_row(ping,ping_pixels+row_offset,NULL);
3321 if (pass < num_passes-1)
3324 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3326 if (q == (Quantum *) NULL)
3329 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3330 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3331 GrayQuantum,ping_pixels+row_offset,exception);
3333 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3334 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3335 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3337 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3338 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3339 RGBAQuantum,ping_pixels+row_offset,exception);
3341 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3342 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3343 IndexQuantum,ping_pixels+row_offset,exception);
3345 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3346 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3347 RGBQuantum,ping_pixels+row_offset,exception);
3349 if (found_transparent_pixel == MagickFalse)
3351 /* Is there a transparent pixel in the row? */
3352 if (y== 0 && logging != MagickFalse)
3353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3354 " Looking for cheap transparent pixel");
3356 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3358 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3359 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3360 (GetPixelAlpha(image,q) != OpaqueAlpha))
3362 if (logging != MagickFalse)
3363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3366 found_transparent_pixel = MagickTrue;
3369 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3370 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3371 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3372 transparent_color.red &&
3373 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3374 transparent_color.green &&
3375 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3376 transparent_color.blue))
3378 if (logging != MagickFalse)
3379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3381 found_transparent_pixel = MagickTrue;
3384 q+=GetPixelChannels(image);
3388 if (num_passes == 1)
3390 status=SetImageProgress(image,LoadImageTag,
3391 (MagickOffsetType) y, image->rows);
3393 if (status == MagickFalse)
3396 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3400 if (num_passes != 1)
3402 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3403 if (status == MagickFalse)
3409 else /* image->storage_class != DirectClass */
3411 for (pass=0; pass < num_passes; pass++)
3417 Convert grayscale image to PseudoClass pixel packets.
3419 if (logging != MagickFalse)
3420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3421 " Converting grayscale pixels to pixel packets");
3423 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3424 BlendPixelTrait : UndefinedPixelTrait;
3426 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3427 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3428 sizeof(*quantum_scanline));
3430 if (quantum_scanline == (Quantum *) NULL)
3431 png_error(ping,"Memory allocation failed");
3433 for (y=0; y < (ssize_t) image->rows; y++)
3439 row_offset=ping_rowbytes*y;
3444 png_read_row(ping,ping_pixels+row_offset,NULL);
3446 if (pass < num_passes-1)
3449 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3451 if (q == (Quantum *) NULL)
3454 p=ping_pixels+row_offset;
3457 switch (ping_bit_depth)
3462 if (ping_color_type == 4)
3463 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3467 alpha=ScaleCharToQuantum((unsigned char)*p++);
3469 SetPixelAlpha(image,alpha,q);
3471 if (alpha != OpaqueAlpha)
3472 found_transparent_pixel = MagickTrue;
3474 q+=GetPixelChannels(image);
3478 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3486 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3488 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3492 if (image->colors > 256)
3493 quantum=(((unsigned int) *p++) << 8);
3499 *r=ScaleShortToQuantum(quantum);
3502 if (ping_color_type == 4)
3504 if (image->colors > 256)
3505 quantum=(((unsigned int) *p++) << 8);
3511 alpha=ScaleShortToQuantum(quantum);
3512 SetPixelAlpha(image,alpha,q);
3514 if (alpha != OpaqueAlpha)
3515 found_transparent_pixel = MagickTrue;
3517 q+=GetPixelChannels(image);
3520 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3522 p++; /* strip low byte */
3524 if (ping_color_type == 4)
3526 SetPixelAlpha(image,*p++,q);
3528 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3529 found_transparent_pixel = MagickTrue;
3532 q+=GetPixelChannels(image);
3545 Transfer image scanline.
3549 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3551 if (q == (Quantum *) NULL)
3553 for (x=0; x < (ssize_t) image->columns; x++)
3555 SetPixelIndex(image,*r++,q);
3556 q+=GetPixelChannels(image);
3559 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3562 if (num_passes == 1)
3564 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3567 if (status == MagickFalse)
3572 if (num_passes != 1)
3574 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3576 if (status == MagickFalse)
3580 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3583 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3584 UndefinedPixelTrait;
3586 if (logging != MagickFalse)
3588 if (found_transparent_pixel != MagickFalse)
3589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3590 " Found transparent pixel");
3593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3594 " No transparent pixel was found");
3596 ping_color_type&=0x03;
3601 quantum_info=DestroyQuantumInfo(quantum_info);
3603 if (image->storage_class == PseudoClass)
3608 alpha_trait=image->alpha_trait;
3609 image->alpha_trait=UndefinedPixelTrait;
3610 (void) SyncImage(image,exception);
3611 image->alpha_trait=alpha_trait;
3614 png_read_end(ping,end_info);
3616 if (logging != MagickFalse)
3618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3619 " image->storage_class=%d\n",(int) image->storage_class);
3622 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3623 (ssize_t) image_info->first_scene && image->delay != 0)
3625 png_destroy_read_struct(&ping,&ping_info,&end_info);
3626 pixel_info=RelinquishVirtualMemory(pixel_info);
3628 (void) SetImageBackgroundColor(image,exception);
3629 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3630 UnlockSemaphoreInfo(ping_semaphore);
3632 if (logging != MagickFalse)
3633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3634 " exit ReadOnePNGImage() early.");
3638 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3644 Image has a transparent background.
3646 storage_class=image->storage_class;
3647 image->alpha_trait=BlendPixelTrait;
3649 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3651 if (storage_class == PseudoClass)
3653 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3655 for (x=0; x < ping_num_trans; x++)
3657 image->colormap[x].alpha_trait=BlendPixelTrait;
3658 image->colormap[x].alpha =
3659 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3663 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3665 for (x=0; x < (int) image->colors; x++)
3667 if (ScaleQuantumToShort(image->colormap[x].red) ==
3668 transparent_color.alpha)
3670 image->colormap[x].alpha_trait=BlendPixelTrait;
3671 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3675 (void) SyncImage(image,exception);
3678 #if 1 /* Should have already been done above, but glennrp problem P10
3683 for (y=0; y < (ssize_t) image->rows; y++)
3685 image->storage_class=storage_class;
3686 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3688 if (q == (Quantum *) NULL)
3692 /* Caution: on a Q8 build, this does not distinguish between
3693 * 16-bit colors that differ only in the low byte
3695 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3697 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3698 transparent_color.red &&
3699 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3700 transparent_color.green &&
3701 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3702 transparent_color.blue)
3704 SetPixelAlpha(image,TransparentAlpha,q);
3707 #if 0 /* I have not found a case where this is needed. */
3710 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3714 q+=GetPixelChannels(image);
3717 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3723 image->storage_class=DirectClass;
3726 for (j = 0; j < 2; j++)
3729 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3730 MagickTrue : MagickFalse;
3732 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3733 MagickTrue : MagickFalse;
3735 if (status != MagickFalse)
3736 for (i=0; i < (ssize_t) num_text; i++)
3738 /* Check for a profile */
3740 if (logging != MagickFalse)
3741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742 " Reading PNG text chunk");
3744 if (strlen(text[i].key) > 16 &&
3745 memcmp(text[i].key, "Raw profile type ",17) == 0)
3750 value=GetImageOption(image_info,"profile:skip");
3752 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3754 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3757 if (logging != MagickFalse)
3758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3759 " Read raw profile %s",text[i].key+17);
3763 if (logging != MagickFalse)
3764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3765 " Skipping raw profile %s",text[i].key+17);
3774 length=text[i].text_length;
3775 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3777 if (value == (char *) NULL)
3779 png_error(ping,"Memory allocation failed");
3783 (void) ConcatenateMagickString(value,text[i].text,length+2);
3785 /* Don't save "density" or "units" property if we have a pHYs
3788 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3789 (LocaleCompare(text[i].key,"density") != 0 &&
3790 LocaleCompare(text[i].key,"units") != 0))
3791 (void) SetImageProperty(image,text[i].key,value,exception);
3793 if (logging != MagickFalse)
3795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3798 (unsigned long) length,
3802 value=DestroyString(value);
3805 num_text_total += num_text;
3808 #ifdef MNG_OBJECT_BUFFERS
3810 Store the object if necessary.
3812 if (object_id && !mng_info->frozen[object_id])
3814 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3817 create a new object buffer.
3819 mng_info->ob[object_id]=(MngBuffer *)
3820 AcquireMagickMemory(sizeof(MngBuffer));
3822 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3824 mng_info->ob[object_id]->image=(Image *) NULL;
3825 mng_info->ob[object_id]->reference_count=1;
3829 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3830 mng_info->ob[object_id]->frozen)
3832 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3833 png_error(ping,"Memory allocation failed");
3835 if (mng_info->ob[object_id]->frozen)
3836 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3842 if (mng_info->ob[object_id]->image != (Image *) NULL)
3843 mng_info->ob[object_id]->image=DestroyImage
3844 (mng_info->ob[object_id]->image);
3846 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3849 if (mng_info->ob[object_id]->image != (Image *) NULL)
3850 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3853 png_error(ping, "Cloning image for object buffer failed");
3855 if (ping_width > 250000L || ping_height > 250000L)
3856 png_error(ping,"PNG Image dimensions are too large.");
3858 mng_info->ob[object_id]->width=ping_width;
3859 mng_info->ob[object_id]->height=ping_height;
3860 mng_info->ob[object_id]->color_type=ping_color_type;
3861 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3862 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3863 mng_info->ob[object_id]->compression_method=
3864 ping_compression_method;
3865 mng_info->ob[object_id]->filter_method=ping_filter_method;
3867 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3873 Copy the PLTE to the object buffer.
3875 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3876 mng_info->ob[object_id]->plte_length=number_colors;
3878 for (i=0; i < number_colors; i++)
3880 mng_info->ob[object_id]->plte[i]=plte[i];
3885 mng_info->ob[object_id]->plte_length=0;
3890 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3891 * alpha or if a valid tRNS chunk is present, no matter whether there
3892 * is actual transparency present.
3894 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3895 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3896 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3897 BlendPixelTrait : UndefinedPixelTrait;
3899 #if 0 /* I'm not sure what's wrong here but it does not work. */
3900 if (image->alpha_trait != UndefinedPixelTrait)
3902 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3903 (void) SetImageType(image,GrayscaleAlphaType,exception);
3905 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3906 (void) SetImageType(image,PaletteAlphaType,exception);
3909 (void) SetImageType(image,TrueColorAlphaType,exception);
3914 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3915 (void) SetImageType(image,GrayscaleType,exception);
3917 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3918 (void) SetImageType(image,PaletteType,exception);
3921 (void) SetImageType(image,TrueColorType,exception);
3925 /* Set more properties for identify to retrieve */
3928 msg[MagickPathExtent];
3930 if (num_text_total != 0)
3932 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3933 (void) FormatLocaleString(msg,MagickPathExtent,
3934 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3935 (void) SetImageProperty(image,"png:text",msg,
3939 if (num_raw_profiles != 0)
3941 (void) FormatLocaleString(msg,MagickPathExtent,
3942 "%d were found", num_raw_profiles);
3943 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3948 if (ping_found_cHRM != MagickFalse)
3950 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3951 "chunk was found (see Chromaticity, above)");
3952 (void) SetImageProperty(image,"png:cHRM",msg,
3957 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3959 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3960 "chunk was found (see Background color, above)");
3961 (void) SetImageProperty(image,"png:bKGD",msg,
3965 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3968 #if defined(PNG_iCCP_SUPPORTED)
3970 if (ping_found_iCCP != MagickFalse)
3971 (void) SetImageProperty(image,"png:iCCP",msg,
3975 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3976 (void) SetImageProperty(image,"png:tRNS",msg,
3979 #if defined(PNG_sRGB_SUPPORTED)
3981 if (ping_found_sRGB != MagickFalse)
3983 (void) FormatLocaleString(msg,MagickPathExtent,
3986 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3987 (void) SetImageProperty(image,"png:sRGB",msg,
3993 if (ping_found_gAMA != MagickFalse)
3995 (void) FormatLocaleString(msg,MagickPathExtent,
3996 "gamma=%.8g (See Gamma, above)",
3998 (void) SetImageProperty(image,"png:gAMA",msg,
4002 #if defined(PNG_pHYs_SUPPORTED)
4004 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4006 (void) FormatLocaleString(msg,MagickPathExtent,
4007 "x_res=%.10g, y_res=%.10g, units=%d",
4008 (double) x_resolution,(double) y_resolution, unit_type);
4009 (void) SetImageProperty(image,"png:pHYs",msg,
4014 #if defined(PNG_oFFs_SUPPORTED)
4016 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4018 (void) FormatLocaleString(msg,MagickPathExtent,
4019 "x_off=%.20g, y_off=%.20g",
4020 (double) image->page.x,(double) image->page.y);
4021 (void) SetImageProperty(image,"png:oFFs",msg,
4026 #if defined(PNG_tIME_SUPPORTED)
4027 read_tIME_chunk(image,ping,end_info,exception);
4031 if ((image->page.width != 0 && image->page.width != image->columns) ||
4032 (image->page.height != 0 && image->page.height != image->rows) ||
4033 (image->page.x != 0 || image->page.y != 0))
4035 (void) FormatLocaleString(msg,MagickPathExtent,
4036 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4037 (double) image->page.width,(double) image->page.height,
4038 (double) image->page.x,(double) image->page.y);
4039 (void) SetImageProperty(image,"png:caNv",msg,
4044 if ((image->page.width != 0 && image->page.width != image->columns) ||
4045 (image->page.height != 0 && image->page.height != image->rows))
4047 (void) FormatLocaleString(msg,MagickPathExtent,
4048 "width=%.20g, height=%.20g",
4049 (double) image->page.width,(double) image->page.height);
4050 (void) SetImageProperty(image,"png:vpAg",msg,
4056 Relinquish resources.
4058 png_destroy_read_struct(&ping,&ping_info,&end_info);
4060 pixel_info=RelinquishVirtualMemory(pixel_info);
4062 if (logging != MagickFalse)
4063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4064 " exit ReadOnePNGImage()");
4066 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4067 UnlockSemaphoreInfo(ping_semaphore);
4070 /* } for navigation to beginning of SETJMP-protected block, revert to
4071 * Throwing an Exception when an error occurs.
4076 /* end of reading one PNG image */
4079 static Image *ReadPNGImage(const ImageInfo *image_info,
4080 ExceptionInfo *exception)
4093 magic_number[MagickPathExtent];
4101 assert(image_info != (const ImageInfo *) NULL);
4102 assert(image_info->signature == MagickCoreSignature);
4104 if (image_info->debug != MagickFalse)
4105 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4106 image_info->filename);
4108 assert(exception != (ExceptionInfo *) NULL);
4109 assert(exception->signature == MagickCoreSignature);
4110 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4111 image=AcquireImage(image_info,exception);
4112 mng_info=(MngInfo *) NULL;
4113 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4115 if (status == MagickFalse)
4116 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4119 Verify PNG signature.
4121 count=ReadBlob(image,8,(unsigned char *) magic_number);
4123 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4124 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4127 Verify that file size large enough to contain a PNG datastream.
4129 if (GetBlobSize(image) < 61)
4130 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4133 Allocate a MngInfo structure.
4135 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4137 if (mng_info == (MngInfo *) NULL)
4138 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4141 Initialize members of the MngInfo structure.
4143 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4144 mng_info->image=image;
4146 image=ReadOnePNGImage(mng_info,image_info,exception);
4147 mng_info=MngInfoFreeStruct(mng_info);
4149 if (image == (Image *) NULL)
4151 if (logging != MagickFalse)
4152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4153 "exit ReadPNGImage() with error");
4155 return((Image *) NULL);
4158 (void) CloseBlob(image);
4160 if ((image->columns == 0) || (image->rows == 0))
4162 if (logging != MagickFalse)
4163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164 "exit ReadPNGImage() with error.");
4166 ThrowReaderException(CorruptImageError,"CorruptImage");
4169 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4170 ((image->gamma < .45) || (image->gamma > .46)) &&
4171 !(image->chromaticity.red_primary.x>0.6399f &&
4172 image->chromaticity.red_primary.x<0.6401f &&
4173 image->chromaticity.red_primary.y>0.3299f &&
4174 image->chromaticity.red_primary.y<0.3301f &&
4175 image->chromaticity.green_primary.x>0.2999f &&
4176 image->chromaticity.green_primary.x<0.3001f &&
4177 image->chromaticity.green_primary.y>0.5999f &&
4178 image->chromaticity.green_primary.y<0.6001f &&
4179 image->chromaticity.blue_primary.x>0.1499f &&
4180 image->chromaticity.blue_primary.x<0.1501f &&
4181 image->chromaticity.blue_primary.y>0.0599f &&
4182 image->chromaticity.blue_primary.y<0.0601f &&
4183 image->chromaticity.white_point.x>0.3126f &&
4184 image->chromaticity.white_point.x<0.3128f &&
4185 image->chromaticity.white_point.y>0.3289f &&
4186 image->chromaticity.white_point.y<0.3291f))
4188 SetImageColorspace(image,RGBColorspace,exception);
4191 if (logging != MagickFalse)
4193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4194 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4195 (double) image->page.width,(double) image->page.height,
4196 (double) image->page.x,(double) image->page.y);
4197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4198 " image->colorspace: %d", (int) image->colorspace);
4201 if (logging != MagickFalse)
4202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4209 #if defined(JNG_SUPPORTED)
4211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4215 % R e a d O n e J N G I m a g e %
4219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4221 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4222 % (minus the 8-byte signature) and returns it. It allocates the memory
4223 % necessary for the new Image structure and returns a pointer to the new
4226 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4228 % The format of the ReadOneJNGImage method is:
4230 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4231 % ExceptionInfo *exception)
4233 % A description of each parameter follows:
4235 % o mng_info: Specifies a pointer to a MngInfo structure.
4237 % o image_info: the image info.
4239 % o exception: return any errors or warnings in this structure.
4243 DestroyJNG(unsigned char *chunk,Image **color_image,
4244 ImageInfo **color_image_info,
4245 Image **alpha_image,ImageInfo **alpha_image_info)
4247 (void) RelinquishMagickMemory(chunk);
4248 if (*color_image_info)
4250 DestroyImageInfo(*color_image_info);
4251 *color_image_info = (ImageInfo *)NULL;
4253 if (*alpha_image_info)
4255 DestroyImageInfo(*alpha_image_info);
4256 *alpha_image_info = (ImageInfo *)NULL;
4260 DestroyImage(*color_image);
4261 *color_image = (Image *)NULL;
4265 DestroyImage(*alpha_image);
4266 *alpha_image = (Image *)NULL;
4269 static Image *ReadOneJNGImage(MngInfo *mng_info,
4270 const ImageInfo *image_info, ExceptionInfo *exception)
4297 jng_image_sample_depth,
4298 jng_image_compression_method,
4299 jng_image_interlace_method,
4300 jng_alpha_sample_depth,
4301 jng_alpha_compression_method,
4302 jng_alpha_filter_method,
4303 jng_alpha_interlace_method;
4305 register const Quantum
4315 register unsigned char
4325 jng_alpha_compression_method=0;
4326 jng_alpha_sample_depth=8;
4330 alpha_image=(Image *) NULL;
4331 color_image=(Image *) NULL;
4332 alpha_image_info=(ImageInfo *) NULL;
4333 color_image_info=(ImageInfo *) NULL;
4335 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4336 " Enter ReadOneJNGImage()");
4338 image=mng_info->image;
4340 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4343 Allocate next image structure.
4345 if (logging != MagickFalse)
4346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4347 " AcquireNextImage()");
4349 AcquireNextImage(image_info,image,exception);
4351 if (GetNextImageInList(image) == (Image *) NULL)
4352 return(DestroyImageList(image));
4354 image=SyncNextImageInList(image);
4356 mng_info->image=image;
4359 Signature bytes have already been read.
4362 read_JSEP=MagickFalse;
4363 reading_idat=MagickFalse;
4367 type[MagickPathExtent];
4376 Read a new JNG chunk.
4378 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4379 2*GetBlobSize(image));
4381 if (status == MagickFalse)
4385 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4386 length=ReadBlobMSBLong(image);
4387 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4389 if (logging != MagickFalse)
4390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4391 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4392 type[0],type[1],type[2],type[3],(double) length);
4394 if (length > PNG_UINT_31_MAX || count == 0)
4396 DestroyJNG(NULL,&color_image,&color_image_info,
4397 &alpha_image,&alpha_image_info);
4398 ThrowReaderException(CorruptImageError,"CorruptImage");
4402 chunk=(unsigned char *) NULL;
4406 if (length > GetBlobSize(image))
4408 DestroyJNG(NULL,&color_image,&color_image_info,NULL,NULL);
4409 ThrowReaderException(CorruptImageError,
4410 "InsufficientImageDataInFile");
4412 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4414 if (chunk == (unsigned char *) NULL)
4415 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4417 for (i=0; i < (ssize_t) length; i++)
4422 c=ReadBlobByte(image);
4425 chunk[i]=(unsigned char) c;
4431 (void) ReadBlobMSBLong(image); /* read crc word */
4433 if (memcmp(type,mng_JHDR,4) == 0)
4437 jng_width=(png_uint_32)mng_get_long(p);
4438 jng_height=(png_uint_32)mng_get_long(&p[4]);
4439 if ((jng_width == 0) || (jng_height == 0))
4440 ThrowReaderException(CorruptImageError,
4441 "NegativeOrZeroImageSize");
4442 jng_color_type=p[8];
4443 jng_image_sample_depth=p[9];
4444 jng_image_compression_method=p[10];
4445 jng_image_interlace_method=p[11];
4447 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4450 jng_alpha_sample_depth=p[12];
4451 jng_alpha_compression_method=p[13];
4452 jng_alpha_filter_method=p[14];
4453 jng_alpha_interlace_method=p[15];
4455 if (logging != MagickFalse)
4457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4458 " jng_width: %16lu, jng_height: %16lu\n"
4459 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4460 " jng_image_compression_method:%3d",
4461 (unsigned long) jng_width, (unsigned long) jng_height,
4462 jng_color_type, jng_image_sample_depth,
4463 jng_image_compression_method);
4465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4466 " jng_image_interlace_method: %3d"
4467 " jng_alpha_sample_depth: %3d",
4468 jng_image_interlace_method,
4469 jng_alpha_sample_depth);
4471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4472 " jng_alpha_compression_method:%3d\n"
4473 " jng_alpha_filter_method: %3d\n"
4474 " jng_alpha_interlace_method: %3d",
4475 jng_alpha_compression_method,
4476 jng_alpha_filter_method,
4477 jng_alpha_interlace_method);
4481 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4483 if (jng_width > 65535 || jng_height > 65535 ||
4484 (long) jng_width > GetMagickResourceLimit(WidthResource) ||
4485 (long) jng_height > GetMagickResourceLimit(HeightResource))
4487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4488 " JNG width or height too large: (%lu x %lu)",
4489 (long) jng_width, (long) jng_height);
4490 DestroyJNG(chunk,&color_image,&color_image_info,
4491 &alpha_image,&alpha_image_info);
4492 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4499 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4500 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4501 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4504 o create color_image
4505 o open color_blob, attached to color_image
4506 o if (color type has alpha)
4507 open alpha_blob, attached to alpha_image
4510 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4512 if (color_image_info == (ImageInfo *) NULL)
4513 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4515 GetImageInfo(color_image_info);
4516 color_image=AcquireImage(color_image_info,exception);
4518 if (color_image == (Image *) NULL)
4519 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4521 if (logging != MagickFalse)
4522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4523 " Creating color_blob.");
4525 (void) AcquireUniqueFilename(color_image->filename);
4526 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4529 if (status == MagickFalse)
4531 color_image=DestroyImage(color_image);
4532 return(DestroyImageList(image));
4535 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4537 alpha_image_info=(ImageInfo *)
4538 AcquireMagickMemory(sizeof(ImageInfo));
4540 if (alpha_image_info == (ImageInfo *) NULL)
4542 color_image=DestroyImage(color_image);
4543 ThrowReaderException(ResourceLimitError,
4544 "MemoryAllocationFailed");
4547 GetImageInfo(alpha_image_info);
4548 alpha_image=AcquireImage(alpha_image_info,exception);
4550 if (alpha_image == (Image *) NULL)
4552 alpha_image_info=DestroyImageInfo(alpha_image_info);
4553 color_image=DestroyImage(color_image);
4554 ThrowReaderException(ResourceLimitError,
4555 "MemoryAllocationFailed");
4558 if (logging != MagickFalse)
4559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4560 " Creating alpha_blob.");
4562 (void) AcquireUniqueFilename(alpha_image->filename);
4563 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4566 if (status == MagickFalse)
4568 alpha_image=DestroyImage(alpha_image);
4569 alpha_image_info=DestroyImageInfo(alpha_image_info);
4570 color_image=DestroyImage(color_image);
4571 return(DestroyImageList(image));
4574 if (jng_alpha_compression_method == 0)
4579 if (logging != MagickFalse)
4580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4581 " Writing IHDR chunk to alpha_blob.");
4583 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4584 "\211PNG\r\n\032\n");
4586 (void) WriteBlobMSBULong(alpha_image,13L);
4587 PNGType(data,mng_IHDR);
4588 LogPNGChunk(logging,mng_IHDR,13L);
4589 PNGLong(data+4,jng_width);
4590 PNGLong(data+8,jng_height);
4591 data[12]=jng_alpha_sample_depth;
4592 data[13]=0; /* color_type gray */
4593 data[14]=0; /* compression method 0 */
4594 data[15]=0; /* filter_method 0 */
4595 data[16]=0; /* interlace_method 0 */
4596 (void) WriteBlob(alpha_image,17,data);
4597 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4600 reading_idat=MagickTrue;
4603 if (memcmp(type,mng_JDAT,4) == 0)
4605 /* Copy chunk to color_image->blob */
4607 if (logging != MagickFalse)
4608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4609 " Copying JDAT chunk data to color_blob.");
4613 (void) WriteBlob(color_image,length,chunk);
4614 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4620 if (memcmp(type,mng_IDAT,4) == 0)
4625 /* Copy IDAT header and chunk data to alpha_image->blob */
4627 if (alpha_image != NULL && image_info->ping == MagickFalse)
4629 if (logging != MagickFalse)
4630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4631 " Copying IDAT chunk data to alpha_blob.");
4633 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4634 PNGType(data,mng_IDAT);
4635 LogPNGChunk(logging,mng_IDAT,length);
4636 (void) WriteBlob(alpha_image,4,data);
4637 (void) WriteBlob(alpha_image,length,chunk);
4638 (void) WriteBlobMSBULong(alpha_image,
4639 crc32(crc32(0,data,4),chunk,(uInt) length));
4642 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4647 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4649 /* Copy chunk data to alpha_image->blob */
4651 if (alpha_image != NULL && image_info->ping == MagickFalse)
4653 if (logging != MagickFalse)
4654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4655 " Copying JDAA chunk data to alpha_blob.");
4657 (void) WriteBlob(alpha_image,length,chunk);
4660 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4665 if (memcmp(type,mng_JSEP,4) == 0)
4667 read_JSEP=MagickTrue;
4669 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4674 if (memcmp(type,mng_bKGD,4) == 0)
4678 image->background_color.red=ScaleCharToQuantum(p[1]);
4679 image->background_color.green=image->background_color.red;
4680 image->background_color.blue=image->background_color.red;
4685 image->background_color.red=ScaleCharToQuantum(p[1]);
4686 image->background_color.green=ScaleCharToQuantum(p[3]);
4687 image->background_color.blue=ScaleCharToQuantum(p[5]);
4690 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4694 if (memcmp(type,mng_gAMA,4) == 0)
4697 image->gamma=((float) mng_get_long(p))*0.00001;
4699 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4703 if (memcmp(type,mng_cHRM,4) == 0)
4707 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4708 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4709 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4710 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4711 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4712 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4713 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4714 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4717 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4721 if (memcmp(type,mng_sRGB,4) == 0)
4725 image->rendering_intent=
4726 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4727 image->gamma=1.000f/2.200f;
4728 image->chromaticity.red_primary.x=0.6400f;
4729 image->chromaticity.red_primary.y=0.3300f;
4730 image->chromaticity.green_primary.x=0.3000f;
4731 image->chromaticity.green_primary.y=0.6000f;
4732 image->chromaticity.blue_primary.x=0.1500f;
4733 image->chromaticity.blue_primary.y=0.0600f;
4734 image->chromaticity.white_point.x=0.3127f;
4735 image->chromaticity.white_point.y=0.3290f;
4738 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4742 if (memcmp(type,mng_oFFs,4) == 0)
4746 image->page.x=(ssize_t) mng_get_long(p);
4747 image->page.y=(ssize_t) mng_get_long(&p[4]);
4749 if ((int) p[8] != 0)
4751 image->page.x/=10000;
4752 image->page.y/=10000;
4756 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4761 if (memcmp(type,mng_pHYs,4) == 0)
4765 image->resolution.x=(double) mng_get_long(p);
4766 image->resolution.y=(double) mng_get_long(&p[4]);
4767 if ((int) p[8] == PNG_RESOLUTION_METER)
4769 image->units=PixelsPerCentimeterResolution;
4770 image->resolution.x=image->resolution.x/100.0f;
4771 image->resolution.y=image->resolution.y/100.0f;
4775 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4780 if (memcmp(type,mng_iCCP,4) == 0)
4783 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4789 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4791 if (memcmp(type,mng_IEND,4))
4801 Finish up reading image data:
4803 o read main image from color_blob.
4807 o if (color_type has alpha)
4808 if alpha_encoding is PNG
4809 read secondary image from alpha_blob via ReadPNG
4810 if alpha_encoding is JPEG
4811 read secondary image from alpha_blob via ReadJPEG
4815 o copy intensity of secondary image into
4816 alpha samples of main image.
4818 o destroy the secondary image.
4821 if (color_image_info == (ImageInfo *) NULL)
4823 assert(color_image == (Image *) NULL);
4824 assert(alpha_image == (Image *) NULL);
4825 return(DestroyImageList(image));
4828 if (color_image == (Image *) NULL)
4830 assert(alpha_image == (Image *) NULL);
4831 return(DestroyImageList(image));
4834 (void) SeekBlob(color_image,0,SEEK_SET);
4836 if (logging != MagickFalse)
4837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4838 " Reading jng_image from color_blob.");
4840 assert(color_image_info != (ImageInfo *) NULL);
4841 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4842 color_image->filename);
4844 color_image_info->ping=MagickFalse; /* To do: avoid this */
4845 jng_image=ReadImage(color_image_info,exception);
4847 (void) RelinquishUniqueFileResource(color_image->filename);
4848 color_image=DestroyImage(color_image);
4849 color_image_info=DestroyImageInfo(color_image_info);
4851 if (jng_image == (Image *) NULL)
4852 return(DestroyImageList(image));
4854 if (logging != MagickFalse)
4855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4856 " Copying jng_image pixels to main image.");
4858 image->rows=jng_height;
4859 image->columns=jng_width;
4861 status=SetImageExtent(image,image->columns,image->rows,exception);
4862 if (status == MagickFalse)
4863 return(DestroyImageList(image));
4865 for (y=0; y < (ssize_t) image->rows; y++)
4867 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4868 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4869 for (x=(ssize_t) image->columns; x != 0; x--)
4871 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4872 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4873 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4874 q+=GetPixelChannels(image);
4875 s+=GetPixelChannels(jng_image);
4878 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4882 jng_image=DestroyImage(jng_image);
4884 if (image_info->ping == MagickFalse)
4886 if (jng_color_type >= 12)
4888 if (jng_alpha_compression_method == 0)
4892 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4893 PNGType(data,mng_IEND);
4894 LogPNGChunk(logging,mng_IEND,0L);
4895 (void) WriteBlob(alpha_image,4,data);
4896 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4899 (void) CloseBlob(alpha_image);
4901 if (logging != MagickFalse)
4902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4903 " Reading alpha from alpha_blob.");
4905 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
4906 "%s",alpha_image->filename);
4908 jng_image=ReadImage(alpha_image_info,exception);
4910 if (jng_image != (Image *) NULL)
4911 for (y=0; y < (ssize_t) image->rows; y++)
4913 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4915 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4917 if (image->alpha_trait != UndefinedPixelTrait)
4918 for (x=(ssize_t) image->columns; x != 0; x--)
4920 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4921 q+=GetPixelChannels(image);
4922 s+=GetPixelChannels(jng_image);
4926 for (x=(ssize_t) image->columns; x != 0; x--)
4928 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4929 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4930 image->alpha_trait=BlendPixelTrait;
4931 q+=GetPixelChannels(image);
4932 s+=GetPixelChannels(jng_image);
4935 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4938 (void) RelinquishUniqueFileResource(alpha_image->filename);
4939 alpha_image=DestroyImage(alpha_image);
4940 alpha_image_info=DestroyImageInfo(alpha_image_info);
4941 if (jng_image != (Image *) NULL)
4942 jng_image=DestroyImage(jng_image);
4946 /* Read the JNG image. */
4948 if (mng_info->mng_type == 0)
4950 mng_info->mng_width=jng_width;
4951 mng_info->mng_height=jng_height;
4954 if (image->page.width == 0 && image->page.height == 0)
4956 image->page.width=jng_width;
4957 image->page.height=jng_height;
4960 if (image->page.x == 0 && image->page.y == 0)
4962 image->page.x=mng_info->x_off[mng_info->object_id];
4963 image->page.y=mng_info->y_off[mng_info->object_id];
4968 image->page.y=mng_info->y_off[mng_info->object_id];
4971 mng_info->image_found++;
4972 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4973 2*GetBlobSize(image));
4975 if (status == MagickFalse)
4976 return(DestroyImageList(image));
4978 if (logging != MagickFalse)
4979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4980 " exit ReadOneJNGImage()");
4986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4990 % R e a d J N G I m a g e %
4994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4996 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4997 % (including the 8-byte signature) and returns it. It allocates the memory
4998 % necessary for the new Image structure and returns a pointer to the new
5001 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5003 % The format of the ReadJNGImage method is:
5005 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5008 % A description of each parameter follows:
5010 % o image_info: the image info.
5012 % o exception: return any errors or warnings in this structure.
5016 static Image *ReadJNGImage(const ImageInfo *image_info,
5017 ExceptionInfo *exception)
5030 magic_number[MagickPathExtent];
5038 assert(image_info != (const ImageInfo *) NULL);
5039 assert(image_info->signature == MagickCoreSignature);
5040 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5041 image_info->filename);
5042 assert(exception != (ExceptionInfo *) NULL);
5043 assert(exception->signature == MagickCoreSignature);
5044 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5045 image=AcquireImage(image_info,exception);
5046 mng_info=(MngInfo *) NULL;
5047 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5049 if (status == MagickFalse)
5050 return((Image *) NULL);
5052 if (LocaleCompare(image_info->magick,"JNG") != 0)
5053 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5055 /* Verify JNG signature. */
5057 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5059 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5060 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5063 Verify that file size large enough to contain a JNG datastream.
5065 if (GetBlobSize(image) < 147)
5066 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5068 /* Allocate a MngInfo structure. */
5070 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5072 if (mng_info == (MngInfo *) NULL)
5073 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5075 /* Initialize members of the MngInfo structure. */
5077 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5079 mng_info->image=image;
5080 image=ReadOneJNGImage(mng_info,image_info,exception);
5081 mng_info=MngInfoFreeStruct(mng_info);
5083 if (image == (Image *) NULL)
5085 if (logging != MagickFalse)
5086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5087 "exit ReadJNGImage() with error");
5089 return((Image *) NULL);
5091 (void) CloseBlob(image);
5093 if (image->columns == 0 || image->rows == 0)
5095 if (logging != MagickFalse)
5096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5097 "exit ReadJNGImage() with error");
5099 ThrowReaderException(CorruptImageError,"CorruptImage");
5102 if (logging != MagickFalse)
5103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5109 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5110 ExceptionInfo *exception)
5113 page_geometry[MagickPathExtent];
5141 #if defined(MNG_INSERT_LAYERS)
5143 mng_background_color;
5146 register unsigned char
5161 #if defined(MNG_INSERT_LAYERS)
5166 volatile unsigned int
5167 #ifdef MNG_OBJECT_BUFFERS
5168 mng_background_object=0,
5170 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5173 default_frame_timeout,
5175 #if defined(MNG_INSERT_LAYERS)
5181 /* These delays are all measured in image ticks_per_second,
5182 * not in MNG ticks_per_second
5185 default_frame_delay,
5189 #if defined(MNG_INSERT_LAYERS)
5198 previous_fb.bottom=0;
5200 previous_fb.right=0;
5202 default_fb.bottom=0;
5206 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5207 " Enter ReadOneMNGImage()");
5209 image=mng_info->image;
5211 if (LocaleCompare(image_info->magick,"MNG") == 0)
5214 magic_number[MagickPathExtent];
5216 /* Verify MNG signature. */
5217 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5218 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5219 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5221 /* Initialize some nonzero members of the MngInfo structure. */
5222 for (i=0; i < MNG_MAX_OBJECTS; i++)
5224 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5225 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5227 mng_info->exists[0]=MagickTrue;
5231 first_mng_object=MagickTrue;
5233 #if defined(MNG_INSERT_LAYERS)
5234 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5236 default_frame_delay=0;
5237 default_frame_timeout=0;
5240 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5242 skip_to_iend=MagickFalse;
5243 term_chunk_found=MagickFalse;
5244 mng_info->framing_mode=1;
5245 #if defined(MNG_INSERT_LAYERS)
5246 mandatory_back=MagickFalse;
5248 #if defined(MNG_INSERT_LAYERS)
5249 mng_background_color=image->background_color;
5251 default_fb=mng_info->frame;
5252 previous_fb=mng_info->frame;
5256 type[MagickPathExtent];
5258 if (LocaleCompare(image_info->magick,"MNG") == 0)
5267 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5268 length=ReadBlobMSBLong(image);
5269 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5271 if (logging != MagickFalse)
5272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5273 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5274 type[0],type[1],type[2],type[3],(double) length);
5276 if (length > PNG_UINT_31_MAX)
5283 ThrowReaderException(CorruptImageError,"CorruptImage");
5286 chunk=(unsigned char *) NULL;
5290 if (length > GetBlobSize(image))
5291 ThrowReaderException(CorruptImageError,
5292 "InsufficientImageDataInFile");
5293 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5295 if (chunk == (unsigned char *) NULL)
5296 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5298 for (i=0; i < (ssize_t) length; i++)
5303 c=ReadBlobByte(image);
5306 chunk[i]=(unsigned char) c;
5312 (void) ReadBlobMSBLong(image); /* read crc word */
5314 #if !defined(JNG_SUPPORTED)
5315 if (memcmp(type,mng_JHDR,4) == 0)
5317 skip_to_iend=MagickTrue;
5319 if (mng_info->jhdr_warning == 0)
5320 (void) ThrowMagickException(exception,GetMagickModule(),
5321 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5323 mng_info->jhdr_warning++;
5326 if (memcmp(type,mng_DHDR,4) == 0)
5328 skip_to_iend=MagickTrue;
5330 if (mng_info->dhdr_warning == 0)
5331 (void) ThrowMagickException(exception,GetMagickModule(),
5332 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5334 mng_info->dhdr_warning++;
5336 if (memcmp(type,mng_MEND,4) == 0)
5338 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5344 if (memcmp(type,mng_IEND,4) == 0)
5345 skip_to_iend=MagickFalse;
5347 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5349 if (logging != MagickFalse)
5350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5356 if (memcmp(type,mng_MHDR,4) == 0)
5360 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5361 ThrowReaderException(CorruptImageError,"CorruptImage");
5364 mng_info->mng_width=(unsigned long)mng_get_long(p);
5365 mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5367 if (logging != MagickFalse)
5369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5370 " MNG width: %.20g",(double) mng_info->mng_width);
5371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5372 " MNG height: %.20g",(double) mng_info->mng_height);
5376 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5378 if (mng_info->ticks_per_second == 0)
5379 default_frame_delay=0;
5382 default_frame_delay=1UL*image->ticks_per_second/
5383 mng_info->ticks_per_second;
5385 frame_delay=default_frame_delay;
5389 simplicity=(size_t) mng_get_long(p);
5391 mng_type=1; /* Full MNG */
5393 if ((simplicity != 0) && ((simplicity | 11) == 11))
5394 mng_type=2; /* LC */
5396 if ((simplicity != 0) && ((simplicity | 9) == 9))
5397 mng_type=3; /* VLC */
5399 #if defined(MNG_INSERT_LAYERS)
5401 insert_layers=MagickTrue;
5403 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5405 /* Allocate next image structure. */
5406 AcquireNextImage(image_info,image,exception);
5408 if (GetNextImageInList(image) == (Image *) NULL)
5409 return((Image *) NULL);
5411 image=SyncNextImageInList(image);
5412 mng_info->image=image;
5415 if ((mng_info->mng_width > 65535L) ||
5416 (mng_info->mng_height > 65535L))
5418 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5419 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5422 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5423 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5424 mng_info->mng_height);
5426 mng_info->frame.left=0;
5427 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5428 mng_info->frame.top=0;
5429 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5430 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5432 for (i=0; i < MNG_MAX_OBJECTS; i++)
5433 mng_info->object_clip[i]=mng_info->frame;
5435 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5439 if (memcmp(type,mng_TERM,4) == 0)
5447 if (repeat == 3 && length > 8)
5449 final_delay=(png_uint_32) mng_get_long(&p[2]);
5450 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5452 if (mng_iterations == PNG_UINT_31_MAX)
5455 image->iterations=mng_iterations;
5456 term_chunk_found=MagickTrue;
5459 if (logging != MagickFalse)
5461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5462 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5463 repeat,(double) final_delay, (double) image->iterations);
5466 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5469 if (memcmp(type,mng_DEFI,4) == 0)
5472 (void) ThrowMagickException(exception,GetMagickModule(),
5473 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5478 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5479 ThrowReaderException(CorruptImageError,"CorruptImage");
5482 object_id=(p[0] << 8) | p[1];
5484 if (mng_type == 2 && object_id != 0)
5485 (void) ThrowMagickException(exception,GetMagickModule(),
5486 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5489 if (object_id > MNG_MAX_OBJECTS)
5492 Instead of using a warning we should allocate a larger
5493 MngInfo structure and continue.
5495 (void) ThrowMagickException(exception,GetMagickModule(),
5496 CoderError,"object id too large","`%s'",image->filename);
5497 object_id=MNG_MAX_OBJECTS;
5500 if (mng_info->exists[object_id])
5501 if (mng_info->frozen[object_id])
5503 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5504 (void) ThrowMagickException(exception,
5505 GetMagickModule(),CoderError,
5506 "DEFI cannot redefine a frozen MNG object","`%s'",
5511 mng_info->exists[object_id]=MagickTrue;
5514 mng_info->invisible[object_id]=p[2];
5517 Extract object offset info.
5521 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5522 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5523 if (logging != MagickFalse)
5525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5526 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5527 object_id,(double) mng_info->x_off[object_id],
5528 object_id,(double) mng_info->y_off[object_id]);
5533 Extract object clipping info.
5536 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5539 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5542 if (memcmp(type,mng_bKGD,4) == 0)
5544 mng_info->have_global_bkgd=MagickFalse;
5548 mng_info->mng_global_bkgd.red=
5549 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5551 mng_info->mng_global_bkgd.green=
5552 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5554 mng_info->mng_global_bkgd.blue=
5555 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5557 mng_info->have_global_bkgd=MagickTrue;
5560 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5563 if (memcmp(type,mng_BACK,4) == 0)
5565 #if defined(MNG_INSERT_LAYERS)
5567 mandatory_back=p[6];
5572 if (mandatory_back && length > 5)
5574 mng_background_color.red=
5575 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5577 mng_background_color.green=
5578 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5580 mng_background_color.blue=
5581 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5583 mng_background_color.alpha=OpaqueAlpha;
5586 #ifdef MNG_OBJECT_BUFFERS
5588 mng_background_object=(p[7] << 8) | p[8];
5591 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5595 if (memcmp(type,mng_PLTE,4) == 0)
5597 /* Read global PLTE. */
5599 if (length && (length < 769))
5601 if (mng_info->global_plte == (png_colorp) NULL)
5602 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5603 sizeof(*mng_info->global_plte));
5605 for (i=0; i < (ssize_t) (length/3); i++)
5607 mng_info->global_plte[i].red=p[3*i];
5608 mng_info->global_plte[i].green=p[3*i+1];
5609 mng_info->global_plte[i].blue=p[3*i+2];
5612 mng_info->global_plte_length=(unsigned int) (length/3);
5615 for ( ; i < 256; i++)
5617 mng_info->global_plte[i].red=i;
5618 mng_info->global_plte[i].green=i;
5619 mng_info->global_plte[i].blue=i;
5623 mng_info->global_plte_length=256;
5626 mng_info->global_plte_length=0;
5628 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5632 if (memcmp(type,mng_tRNS,4) == 0)
5634 /* read global tRNS */
5636 if (length > 0 && length < 257)
5637 for (i=0; i < (ssize_t) length; i++)
5638 mng_info->global_trns[i]=p[i];
5641 for ( ; i < 256; i++)
5642 mng_info->global_trns[i]=255;
5644 mng_info->global_trns_length=(unsigned int) length;
5645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5648 if (memcmp(type,mng_gAMA,4) == 0)
5655 igamma=mng_get_long(p);
5656 mng_info->global_gamma=((float) igamma)*0.00001;
5657 mng_info->have_global_gama=MagickTrue;
5661 mng_info->have_global_gama=MagickFalse;
5663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5667 if (memcmp(type,mng_cHRM,4) == 0)
5669 /* Read global cHRM */
5673 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5674 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5675 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5676 mng_info->global_chrm.red_primary.y=0.00001*
5677 mng_get_long(&p[12]);
5678 mng_info->global_chrm.green_primary.x=0.00001*
5679 mng_get_long(&p[16]);
5680 mng_info->global_chrm.green_primary.y=0.00001*
5681 mng_get_long(&p[20]);
5682 mng_info->global_chrm.blue_primary.x=0.00001*
5683 mng_get_long(&p[24]);
5684 mng_info->global_chrm.blue_primary.y=0.00001*
5685 mng_get_long(&p[28]);
5686 mng_info->have_global_chrm=MagickTrue;
5689 mng_info->have_global_chrm=MagickFalse;
5691 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5695 if (memcmp(type,mng_sRGB,4) == 0)
5702 mng_info->global_srgb_intent=
5703 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5704 mng_info->have_global_srgb=MagickTrue;
5707 mng_info->have_global_srgb=MagickFalse;
5709 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5713 if (memcmp(type,mng_iCCP,4) == 0)
5720 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5725 if (memcmp(type,mng_FRAM,4) == 0)
5728 (void) ThrowMagickException(exception,GetMagickModule(),
5729 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5732 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5733 image->delay=frame_delay;
5735 frame_delay=default_frame_delay;
5736 frame_timeout=default_frame_timeout;
5741 mng_info->framing_mode=p[0];
5743 if (logging != MagickFalse)
5744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5745 " Framing_mode=%d",mng_info->framing_mode);
5749 /* Note the delay and frame clipping boundaries. */
5751 p++; /* framing mode */
5753 while (*p && ((p-chunk) < (ssize_t) length))
5754 p++; /* frame name */
5756 p++; /* frame name terminator */
5758 if ((p-chunk) < (ssize_t) (length-4))
5765 change_delay=(*p++);
5766 change_timeout=(*p++);
5767 change_clipping=(*p++);
5768 p++; /* change_sync */
5770 if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5772 frame_delay=1UL*image->ticks_per_second*
5775 if (mng_info->ticks_per_second != 0)
5776 frame_delay/=mng_info->ticks_per_second;
5779 frame_delay=PNG_UINT_31_MAX;
5781 if (change_delay == 2)
5782 default_frame_delay=frame_delay;
5786 if (logging != MagickFalse)
5787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5788 " Framing_delay=%.20g",(double) frame_delay);
5791 if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5793 frame_timeout=1UL*image->ticks_per_second*
5796 if (mng_info->ticks_per_second != 0)
5797 frame_timeout/=mng_info->ticks_per_second;
5800 frame_timeout=PNG_UINT_31_MAX;
5802 if (change_timeout == 2)
5803 default_frame_timeout=frame_timeout;
5807 if (logging != MagickFalse)
5808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5809 " Framing_timeout=%.20g",(double) frame_timeout);
5812 if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
5814 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5818 if (logging != MagickFalse)
5819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5820 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5821 (double) fb.left,(double) fb.right,(double) fb.top,
5822 (double) fb.bottom);
5824 if (change_clipping == 2)
5830 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5832 subframe_width=(size_t) (mng_info->clip.right
5833 -mng_info->clip.left);
5835 subframe_height=(size_t) (mng_info->clip.bottom
5836 -mng_info->clip.top);
5838 Insert a background layer behind the frame if framing_mode is 4.
5840 #if defined(MNG_INSERT_LAYERS)
5841 if (logging != MagickFalse)
5842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843 " subframe_width=%.20g, subframe_height=%.20g",(double)
5844 subframe_width,(double) subframe_height);
5846 if (insert_layers && (mng_info->framing_mode == 4) &&
5847 (subframe_width) && (subframe_height))
5849 /* Allocate next image structure. */
5850 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5852 AcquireNextImage(image_info,image,exception);
5854 if (GetNextImageInList(image) == (Image *) NULL)
5855 return(DestroyImageList(image));
5857 image=SyncNextImageInList(image);
5860 mng_info->image=image;
5862 if (term_chunk_found)
5864 image->start_loop=MagickTrue;
5865 image->iterations=mng_iterations;
5866 term_chunk_found=MagickFalse;
5870 image->start_loop=MagickFalse;
5872 image->columns=subframe_width;
5873 image->rows=subframe_height;
5874 image->page.width=subframe_width;
5875 image->page.height=subframe_height;
5876 image->page.x=mng_info->clip.left;
5877 image->page.y=mng_info->clip.top;
5878 image->background_color=mng_background_color;
5879 image->alpha_trait=UndefinedPixelTrait;
5881 (void) SetImageBackgroundColor(image,exception);
5883 if (logging != MagickFalse)
5884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5885 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5886 (double) mng_info->clip.left,
5887 (double) mng_info->clip.right,
5888 (double) mng_info->clip.top,
5889 (double) mng_info->clip.bottom);
5892 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5896 if (memcmp(type,mng_CLIP,4) == 0)
5907 first_object=(p[0] << 8) | p[1];
5908 last_object=(p[2] << 8) | p[3];
5911 for (i=(int) first_object; i <= (int) last_object; i++)
5913 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
5916 if (mng_info->exists[i] && !mng_info->frozen[i])
5921 box=mng_info->object_clip[i];
5922 if ((p-chunk) < (ssize_t) (length-17))
5923 mng_info->object_clip[i]=
5924 mng_read_box(box,(char) p[0],&p[1]);
5929 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5933 if (memcmp(type,mng_SAVE,4) == 0)
5935 for (i=1; i < MNG_MAX_OBJECTS; i++)
5936 if (mng_info->exists[i])
5938 mng_info->frozen[i]=MagickTrue;
5939 #ifdef MNG_OBJECT_BUFFERS
5940 if (mng_info->ob[i] != (MngBuffer *) NULL)
5941 mng_info->ob[i]->frozen=MagickTrue;
5945 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5950 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5952 /* Read DISC or SEEK. */
5954 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5956 for (i=1; i < MNG_MAX_OBJECTS; i++)
5957 MngInfoDiscardObject(mng_info,i);
5965 for (j=1; j < (ssize_t) length; j+=2)
5967 i=p[j-1] << 8 | p[j];
5968 MngInfoDiscardObject(mng_info,i);
5972 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5977 if (memcmp(type,mng_MOVE,4) == 0)
5987 first_object=(p[0] << 8) | p[1];
5988 last_object=(p[2] << 8) | p[3];
5991 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5993 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
5996 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5997 (p-chunk) < (ssize_t) (length-8))
6005 old_pair.a=mng_info->x_off[i];
6006 old_pair.b=mng_info->y_off[i];
6007 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6008 mng_info->x_off[i]=new_pair.a;
6009 mng_info->y_off[i]=new_pair.b;
6014 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6018 if (memcmp(type,mng_LOOP,4) == 0)
6020 ssize_t loop_iters=1;
6023 loop_level=chunk[0];
6024 mng_info->loop_active[loop_level]=1; /* mark loop active */
6026 /* Record starting point. */
6027 loop_iters=mng_get_long(&chunk[1]);
6029 if (logging != MagickFalse)
6030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6031 " LOOP level %.20g has %.20g iterations ",
6032 (double) loop_level, (double) loop_iters);
6034 if (loop_iters == 0)
6035 skipping_loop=loop_level;
6039 mng_info->loop_jump[loop_level]=TellBlob(image);
6040 mng_info->loop_count[loop_level]=loop_iters;
6043 mng_info->loop_iteration[loop_level]=0;
6045 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6049 if (memcmp(type,mng_ENDL,4) == 0)
6053 loop_level=chunk[0];
6055 if (skipping_loop > 0)
6057 if (skipping_loop == loop_level)
6060 Found end of zero-iteration loop.
6063 mng_info->loop_active[loop_level]=0;
6069 if (mng_info->loop_active[loop_level] == 1)
6071 mng_info->loop_count[loop_level]--;
6072 mng_info->loop_iteration[loop_level]++;
6074 if (logging != MagickFalse)
6075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6076 " ENDL: LOOP level %.20g has %.20g remaining iters",
6077 (double) loop_level,(double)
6078 mng_info->loop_count[loop_level]);
6080 if (mng_info->loop_count[loop_level] != 0)
6083 SeekBlob(image,mng_info->loop_jump[loop_level],
6088 chunk=(unsigned char *) RelinquishMagickMemory(
6090 ThrowReaderException(CorruptImageError,
6091 "ImproperImageHeader");
6103 mng_info->loop_active[loop_level]=0;
6105 for (i=0; i < loop_level; i++)
6106 if (mng_info->loop_active[i] == 1)
6107 last_level=(short) i;
6108 loop_level=last_level;
6114 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6118 if (memcmp(type,mng_CLON,4) == 0)
6120 if (mng_info->clon_warning == 0)
6121 (void) ThrowMagickException(exception,GetMagickModule(),
6122 CoderError,"CLON is not implemented yet","`%s'",
6125 mng_info->clon_warning++;
6128 if (memcmp(type,mng_MAGN,4) == 0)
6143 magn_first=(p[0] << 8) | p[1];
6149 magn_last=(p[2] << 8) | p[3];
6152 magn_last=magn_first;
6153 #ifndef MNG_OBJECT_BUFFERS
6154 if (magn_first || magn_last)
6155 if (mng_info->magn_warning == 0)
6157 (void) ThrowMagickException(exception,
6158 GetMagickModule(),CoderError,
6159 "MAGN is not implemented yet for nonzero objects",
6160 "`%s'",image->filename);
6162 mng_info->magn_warning++;
6172 magn_mx=(p[5] << 8) | p[6];
6181 magn_my=(p[7] << 8) | p[8];
6190 magn_ml=(p[9] << 8) | p[10];
6199 magn_mr=(p[11] << 8) | p[12];
6208 magn_mt=(p[13] << 8) | p[14];
6217 magn_mb=(p[15] << 8) | p[16];
6229 magn_methy=magn_methx;
6232 if (magn_methx > 5 || magn_methy > 5)
6233 if (mng_info->magn_warning == 0)
6235 (void) ThrowMagickException(exception,
6236 GetMagickModule(),CoderError,
6237 "Unknown MAGN method in MNG datastream","`%s'",
6240 mng_info->magn_warning++;
6242 #ifdef MNG_OBJECT_BUFFERS
6243 /* Magnify existing objects in the range magn_first to magn_last */
6245 if (magn_first == 0 || magn_last == 0)
6247 /* Save the magnification factors for object 0 */
6248 mng_info->magn_mb=magn_mb;
6249 mng_info->magn_ml=magn_ml;
6250 mng_info->magn_mr=magn_mr;
6251 mng_info->magn_mt=magn_mt;
6252 mng_info->magn_mx=magn_mx;
6253 mng_info->magn_my=magn_my;
6254 mng_info->magn_methx=magn_methx;
6255 mng_info->magn_methy=magn_methy;
6259 if (memcmp(type,mng_PAST,4) == 0)
6261 if (mng_info->past_warning == 0)
6262 (void) ThrowMagickException(exception,GetMagickModule(),
6263 CoderError,"PAST is not implemented yet","`%s'",
6266 mng_info->past_warning++;
6269 if (memcmp(type,mng_SHOW,4) == 0)
6271 if (mng_info->show_warning == 0)
6272 (void) ThrowMagickException(exception,GetMagickModule(),
6273 CoderError,"SHOW is not implemented yet","`%s'",
6276 mng_info->show_warning++;
6279 if (memcmp(type,mng_sBIT,4) == 0)
6282 mng_info->have_global_sbit=MagickFalse;
6286 mng_info->global_sbit.gray=p[0];
6287 mng_info->global_sbit.red=p[0];
6288 mng_info->global_sbit.green=p[1];
6289 mng_info->global_sbit.blue=p[2];
6290 mng_info->global_sbit.alpha=p[3];
6291 mng_info->have_global_sbit=MagickTrue;
6294 if (memcmp(type,mng_pHYs,4) == 0)
6298 mng_info->global_x_pixels_per_unit=
6299 (size_t) mng_get_long(p);
6300 mng_info->global_y_pixels_per_unit=
6301 (size_t) mng_get_long(&p[4]);
6302 mng_info->global_phys_unit_type=p[8];
6303 mng_info->have_global_phys=MagickTrue;
6307 mng_info->have_global_phys=MagickFalse;
6309 if (memcmp(type,mng_pHYg,4) == 0)
6311 if (mng_info->phyg_warning == 0)
6312 (void) ThrowMagickException(exception,GetMagickModule(),
6313 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6315 mng_info->phyg_warning++;
6317 if (memcmp(type,mng_BASI,4) == 0)
6319 skip_to_iend=MagickTrue;
6321 if (mng_info->basi_warning == 0)
6322 (void) ThrowMagickException(exception,GetMagickModule(),
6323 CoderError,"BASI is not implemented yet","`%s'",
6326 mng_info->basi_warning++;
6327 #ifdef MNG_BASI_SUPPORTED
6328 basi_width=(unsigned long) mng_get_long(p);
6329 basi_width=(unsigned long) mng_get_long(&p[4]);
6330 basi_color_type=p[8];
6331 basi_compression_method=p[9];
6332 basi_filter_type=p[10];
6333 basi_interlace_method=p[11];
6335 basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6341 basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6347 basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6353 basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6357 if (basi_sample_depth == 16)
6364 basi_viewable=p[20];
6370 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6374 if (memcmp(type,mng_IHDR,4)
6375 #if defined(JNG_SUPPORTED)
6376 && memcmp(type,mng_JHDR,4)
6380 /* Not an IHDR or JHDR chunk */
6381 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6386 if (logging != MagickFalse)
6387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6388 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6390 mng_info->exists[object_id]=MagickTrue;
6391 mng_info->viewable[object_id]=MagickTrue;
6393 if (mng_info->invisible[object_id])
6395 if (logging != MagickFalse)
6396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6397 " Skipping invisible object");
6399 skip_to_iend=MagickTrue;
6400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6403 #if defined(MNG_INSERT_LAYERS)
6406 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6407 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6410 image_width=(size_t) mng_get_long(p);
6411 image_height=(size_t) mng_get_long(&p[4]);
6413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6416 Insert a transparent background layer behind the entire animation
6417 if it is not full screen.
6419 #if defined(MNG_INSERT_LAYERS)
6420 if (insert_layers && mng_type && first_mng_object)
6422 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6423 (image_width < mng_info->mng_width) ||
6424 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6425 (image_height < mng_info->mng_height) ||
6426 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6428 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6431 Allocate next image structure.
6433 AcquireNextImage(image_info,image,exception);
6435 if (GetNextImageInList(image) == (Image *) NULL)
6436 return(DestroyImageList(image));
6438 image=SyncNextImageInList(image);
6440 mng_info->image=image;
6442 if (term_chunk_found)
6444 image->start_loop=MagickTrue;
6445 image->iterations=mng_iterations;
6446 term_chunk_found=MagickFalse;
6450 image->start_loop=MagickFalse;
6452 /* Make a background rectangle. */
6455 image->columns=mng_info->mng_width;
6456 image->rows=mng_info->mng_height;
6457 image->page.width=mng_info->mng_width;
6458 image->page.height=mng_info->mng_height;
6461 image->background_color=mng_background_color;
6462 (void) SetImageBackgroundColor(image,exception);
6463 if (logging != MagickFalse)
6464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6465 " Inserted transparent background layer, W=%.20g, H=%.20g",
6466 (double) mng_info->mng_width,(double) mng_info->mng_height);
6470 Insert a background layer behind the upcoming image if
6471 framing_mode is 3, and we haven't already inserted one.
6473 if (insert_layers && (mng_info->framing_mode == 3) &&
6474 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6475 (simplicity & 0x08)))
6477 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6480 Allocate next image structure.
6482 AcquireNextImage(image_info,image,exception);
6484 if (GetNextImageInList(image) == (Image *) NULL)
6485 return(DestroyImageList(image));
6487 image=SyncNextImageInList(image);
6490 mng_info->image=image;
6492 if (term_chunk_found)
6494 image->start_loop=MagickTrue;
6495 image->iterations=mng_iterations;
6496 term_chunk_found=MagickFalse;
6500 image->start_loop=MagickFalse;
6503 image->columns=subframe_width;
6504 image->rows=subframe_height;
6505 image->page.width=subframe_width;
6506 image->page.height=subframe_height;
6507 image->page.x=mng_info->clip.left;
6508 image->page.y=mng_info->clip.top;
6509 image->background_color=mng_background_color;
6510 image->alpha_trait=UndefinedPixelTrait;
6511 (void) SetImageBackgroundColor(image,exception);
6513 if (logging != MagickFalse)
6514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6515 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6516 (double) mng_info->clip.left,(double) mng_info->clip.right,
6517 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6519 #endif /* MNG_INSERT_LAYERS */
6520 first_mng_object=MagickFalse;
6522 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6525 Allocate next image structure.
6527 AcquireNextImage(image_info,image,exception);
6529 if (GetNextImageInList(image) == (Image *) NULL)
6530 return(DestroyImageList(image));
6532 image=SyncNextImageInList(image);
6534 mng_info->image=image;
6535 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6536 GetBlobSize(image));
6538 if (status == MagickFalse)
6541 if (term_chunk_found)
6543 image->start_loop=MagickTrue;
6544 term_chunk_found=MagickFalse;
6548 image->start_loop=MagickFalse;
6550 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6552 image->delay=frame_delay;
6553 frame_delay=default_frame_delay;
6559 image->page.width=mng_info->mng_width;
6560 image->page.height=mng_info->mng_height;
6561 image->page.x=mng_info->x_off[object_id];
6562 image->page.y=mng_info->y_off[object_id];
6563 image->iterations=mng_iterations;
6566 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6569 if (logging != MagickFalse)
6570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6571 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6574 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6577 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6580 mng_info->image=image;
6581 mng_info->mng_type=mng_type;
6582 mng_info->object_id=object_id;
6584 if (memcmp(type,mng_IHDR,4) == 0)
6585 image=ReadOnePNGImage(mng_info,image_info,exception);
6587 #if defined(JNG_SUPPORTED)
6589 image=ReadOneJNGImage(mng_info,image_info,exception);
6592 if (image == (Image *) NULL)
6594 if (logging != MagickFalse)
6595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6596 "exit ReadJNGImage() with error");
6598 return((Image *) NULL);
6601 if (image->columns == 0 || image->rows == 0)
6603 (void) CloseBlob(image);
6604 return(DestroyImageList(image));
6607 mng_info->image=image;
6614 if (mng_info->magn_methx || mng_info->magn_methy)
6620 if (logging != MagickFalse)
6621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6622 " Processing MNG MAGN chunk");
6624 if (mng_info->magn_methx == 1)
6626 magnified_width=mng_info->magn_ml;
6628 if (image->columns > 1)
6629 magnified_width += mng_info->magn_mr;
6631 if (image->columns > 2)
6632 magnified_width += (png_uint_32)
6633 ((image->columns-2)*(mng_info->magn_mx));
6638 magnified_width=(png_uint_32) image->columns;
6640 if (image->columns > 1)
6641 magnified_width += mng_info->magn_ml-1;
6643 if (image->columns > 2)
6644 magnified_width += mng_info->magn_mr-1;
6646 if (image->columns > 3)
6647 magnified_width += (png_uint_32)
6648 ((image->columns-3)*(mng_info->magn_mx-1));
6651 if (mng_info->magn_methy == 1)
6653 magnified_height=mng_info->magn_mt;
6655 if (image->rows > 1)
6656 magnified_height += mng_info->magn_mb;
6658 if (image->rows > 2)
6659 magnified_height += (png_uint_32)
6660 ((image->rows-2)*(mng_info->magn_my));
6665 magnified_height=(png_uint_32) image->rows;
6667 if (image->rows > 1)
6668 magnified_height += mng_info->magn_mt-1;
6670 if (image->rows > 2)
6671 magnified_height += mng_info->magn_mb-1;
6673 if (image->rows > 3)
6674 magnified_height += (png_uint_32)
6675 ((image->rows-3)*(mng_info->magn_my-1));
6678 if (magnified_height > image->rows ||
6679 magnified_width > image->columns)
6706 /* Allocate next image structure. */
6708 if (logging != MagickFalse)
6709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6710 " Allocate magnified image");
6712 AcquireNextImage(image_info,image,exception);
6714 if (GetNextImageInList(image) == (Image *) NULL)
6715 return(DestroyImageList(image));
6717 large_image=SyncNextImageInList(image);
6719 large_image->columns=magnified_width;
6720 large_image->rows=magnified_height;
6722 magn_methx=mng_info->magn_methx;
6723 magn_methy=mng_info->magn_methy;
6725 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6726 #define QM unsigned short
6727 if (magn_methx != 1 || magn_methy != 1)
6730 Scale pixels to unsigned shorts to prevent
6731 overflow of intermediate values of interpolations
6733 for (y=0; y < (ssize_t) image->rows; y++)
6735 q=GetAuthenticPixels(image,0,y,image->columns,1,
6738 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6740 SetPixelRed(image,ScaleQuantumToShort(
6741 GetPixelRed(image,q)),q);
6742 SetPixelGreen(image,ScaleQuantumToShort(
6743 GetPixelGreen(image,q)),q);
6744 SetPixelBlue(image,ScaleQuantumToShort(
6745 GetPixelBlue(image,q)),q);
6746 SetPixelAlpha(image,ScaleQuantumToShort(
6747 GetPixelAlpha(image,q)),q);
6748 q+=GetPixelChannels(image);
6751 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6759 if (image->alpha_trait != UndefinedPixelTrait)
6760 (void) SetImageBackgroundColor(large_image,exception);
6764 large_image->background_color.alpha=OpaqueAlpha;
6765 (void) SetImageBackgroundColor(large_image,exception);
6767 if (magn_methx == 4)
6770 if (magn_methx == 5)
6773 if (magn_methy == 4)
6776 if (magn_methy == 5)
6780 /* magnify the rows into the right side of the large image */
6782 if (logging != MagickFalse)
6783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6784 " Magnify the rows to %.20g",
6785 (double) large_image->rows);
6786 m=(ssize_t) mng_info->magn_mt;
6788 length=(size_t) GetPixelChannels(image)*image->columns;
6789 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6790 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6792 if ((prev == (Quantum *) NULL) ||
6793 (next == (Quantum *) NULL))
6795 image=DestroyImageList(image);
6796 ThrowReaderException(ResourceLimitError,
6797 "MemoryAllocationFailed");
6800 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6801 (void) CopyMagickMemory(next,n,length);
6803 for (y=0; y < (ssize_t) image->rows; y++)
6806 m=(ssize_t) mng_info->magn_mt;
6808 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6809 m=(ssize_t) mng_info->magn_mb;
6811 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6812 m=(ssize_t) mng_info->magn_mb;
6814 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6818 m=(ssize_t) mng_info->magn_my;
6824 if (y < (ssize_t) image->rows-1)
6826 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6828 (void) CopyMagickMemory(next,n,length);
6831 for (i=0; i < m; i++, yy++)
6836 assert(yy < (ssize_t) large_image->rows);
6839 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6841 q+=(large_image->columns-image->columns)*
6842 GetPixelChannels(large_image);
6844 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6846 /* To do: get color as function of indexes[x] */
6848 if (image->storage_class == PseudoClass)
6853 if (magn_methy <= 1)
6855 /* replicate previous */
6856 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6857 SetPixelGreen(large_image,GetPixelGreen(image,
6859 SetPixelBlue(large_image,GetPixelBlue(image,
6861 SetPixelAlpha(large_image,GetPixelAlpha(image,
6865 else if (magn_methy == 2 || magn_methy == 4)
6869 SetPixelRed(large_image,GetPixelRed(image,
6871 SetPixelGreen(large_image,GetPixelGreen(image,
6873 SetPixelBlue(large_image,GetPixelBlue(image,
6875 SetPixelAlpha(large_image,GetPixelAlpha(image,
6882 SetPixelRed(large_image,((QM) (((ssize_t)
6883 (2*i*(GetPixelRed(image,n)
6884 -GetPixelRed(image,pixels)+m))/
6886 +GetPixelRed(image,pixels)))),q);
6887 SetPixelGreen(large_image,((QM) (((ssize_t)
6888 (2*i*(GetPixelGreen(image,n)
6889 -GetPixelGreen(image,pixels)+m))/
6891 +GetPixelGreen(image,pixels)))),q);
6892 SetPixelBlue(large_image,((QM) (((ssize_t)
6893 (2*i*(GetPixelBlue(image,n)
6894 -GetPixelBlue(image,pixels)+m))/
6896 +GetPixelBlue(image,pixels)))),q);
6898 if (image->alpha_trait != UndefinedPixelTrait)
6899 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6900 (2*i*(GetPixelAlpha(image,n)
6901 -GetPixelAlpha(image,pixels)+m))
6903 GetPixelAlpha(image,pixels)))),q);
6906 if (magn_methy == 4)
6908 /* Replicate nearest */
6909 if (i <= ((m+1) << 1))
6910 SetPixelAlpha(large_image,GetPixelAlpha(image,
6913 SetPixelAlpha(large_image,GetPixelAlpha(image,
6918 else /* if (magn_methy == 3 || magn_methy == 5) */
6920 /* Replicate nearest */
6921 if (i <= ((m+1) << 1))
6923 SetPixelRed(large_image,GetPixelRed(image,
6925 SetPixelGreen(large_image,GetPixelGreen(image,
6927 SetPixelBlue(large_image,GetPixelBlue(image,
6929 SetPixelAlpha(large_image,GetPixelAlpha(image,
6935 SetPixelRed(large_image,GetPixelRed(image,n),q);
6936 SetPixelGreen(large_image,GetPixelGreen(image,n),
6938 SetPixelBlue(large_image,GetPixelBlue(image,n),
6940 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6944 if (magn_methy == 5)
6946 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6947 (GetPixelAlpha(image,n)
6948 -GetPixelAlpha(image,pixels))
6949 +m))/((ssize_t) (m*2))
6950 +GetPixelAlpha(image,pixels)),q);
6953 n+=GetPixelChannels(image);
6954 q+=GetPixelChannels(large_image);
6955 pixels+=GetPixelChannels(image);
6958 if (SyncAuthenticPixels(large_image,exception) == 0)
6964 prev=(Quantum *) RelinquishMagickMemory(prev);
6965 next=(Quantum *) RelinquishMagickMemory(next);
6967 length=image->columns;
6969 if (logging != MagickFalse)
6970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6971 " Delete original image");
6973 DeleteImageFromList(&image);
6977 mng_info->image=image;
6979 /* magnify the columns */
6980 if (logging != MagickFalse)
6981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6982 " Magnify the columns to %.20g",
6983 (double) image->columns);
6985 for (y=0; y < (ssize_t) image->rows; y++)
6990 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6991 pixels=q+(image->columns-length)*GetPixelChannels(image);
6992 n=pixels+GetPixelChannels(image);
6994 for (x=(ssize_t) (image->columns-length);
6995 x < (ssize_t) image->columns; x++)
6997 /* To do: Rewrite using Get/Set***PixelChannel() */
6999 if (x == (ssize_t) (image->columns-length))
7000 m=(ssize_t) mng_info->magn_ml;
7002 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7003 m=(ssize_t) mng_info->magn_mr;
7005 else if (magn_methx <= 1 &&
7006 x == (ssize_t) image->columns-1)
7007 m=(ssize_t) mng_info->magn_mr;
7009 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7013 m=(ssize_t) mng_info->magn_mx;
7015 for (i=0; i < m; i++)
7017 if (magn_methx <= 1)
7019 /* replicate previous */
7020 SetPixelRed(image,GetPixelRed(image,pixels),q);
7021 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7022 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7023 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7026 else if (magn_methx == 2 || magn_methx == 4)
7030 SetPixelRed(image,GetPixelRed(image,pixels),q);
7031 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7032 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7033 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7036 /* To do: Rewrite using Get/Set***PixelChannel() */
7040 SetPixelRed(image,(QM) ((2*i*(
7041 GetPixelRed(image,n)
7042 -GetPixelRed(image,pixels))+m)
7044 GetPixelRed(image,pixels)),q);
7046 SetPixelGreen(image,(QM) ((2*i*(
7047 GetPixelGreen(image,n)
7048 -GetPixelGreen(image,pixels))+m)
7050 GetPixelGreen(image,pixels)),q);
7052 SetPixelBlue(image,(QM) ((2*i*(
7053 GetPixelBlue(image,n)
7054 -GetPixelBlue(image,pixels))+m)
7056 GetPixelBlue(image,pixels)),q);
7057 if (image->alpha_trait != UndefinedPixelTrait)
7058 SetPixelAlpha(image,(QM) ((2*i*(
7059 GetPixelAlpha(image,n)
7060 -GetPixelAlpha(image,pixels))+m)
7062 GetPixelAlpha(image,pixels)),q);
7065 if (magn_methx == 4)
7067 /* Replicate nearest */
7068 if (i <= ((m+1) << 1))
7070 SetPixelAlpha(image,
7071 GetPixelAlpha(image,pixels)+0,q);
7075 SetPixelAlpha(image,
7076 GetPixelAlpha(image,n)+0,q);
7081 else /* if (magn_methx == 3 || magn_methx == 5) */
7083 /* Replicate nearest */
7084 if (i <= ((m+1) << 1))
7086 SetPixelRed(image,GetPixelRed(image,pixels),q);
7087 SetPixelGreen(image,GetPixelGreen(image,
7089 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7090 SetPixelAlpha(image,GetPixelAlpha(image,
7096 SetPixelRed(image,GetPixelRed(image,n),q);
7097 SetPixelGreen(image,GetPixelGreen(image,n),q);
7098 SetPixelBlue(image,GetPixelBlue(image,n),q);
7099 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7102 if (magn_methx == 5)
7105 SetPixelAlpha(image,
7106 (QM) ((2*i*( GetPixelAlpha(image,n)
7107 -GetPixelAlpha(image,pixels))+m)/
7109 +GetPixelAlpha(image,pixels)),q);
7112 q+=GetPixelChannels(image);
7114 n+=GetPixelChannels(image);
7117 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7120 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7121 if (magn_methx != 1 || magn_methy != 1)
7124 Rescale pixels to Quantum
7126 for (y=0; y < (ssize_t) image->rows; y++)
7128 q=GetAuthenticPixels(image,0,y,image->columns,1,
7131 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7133 SetPixelRed(image,ScaleShortToQuantum(
7134 GetPixelRed(image,q)),q);
7135 SetPixelGreen(image,ScaleShortToQuantum(
7136 GetPixelGreen(image,q)),q);
7137 SetPixelBlue(image,ScaleShortToQuantum(
7138 GetPixelBlue(image,q)),q);
7139 SetPixelAlpha(image,ScaleShortToQuantum(
7140 GetPixelAlpha(image,q)),q);
7141 q+=GetPixelChannels(image);
7144 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7149 if (logging != MagickFalse)
7150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7151 " Finished MAGN processing");
7156 Crop_box is with respect to the upper left corner of the MNG.
7158 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7159 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7160 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7161 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7162 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7163 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7164 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7165 if ((crop_box.left != (mng_info->image_box.left
7166 +mng_info->x_off[object_id])) ||
7167 (crop_box.right != (mng_info->image_box.right
7168 +mng_info->x_off[object_id])) ||
7169 (crop_box.top != (mng_info->image_box.top
7170 +mng_info->y_off[object_id])) ||
7171 (crop_box.bottom != (mng_info->image_box.bottom
7172 +mng_info->y_off[object_id])))
7174 if (logging != MagickFalse)
7175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7176 " Crop the PNG image");
7178 if ((crop_box.left < crop_box.right) &&
7179 (crop_box.top < crop_box.bottom))
7188 Crop_info is with respect to the upper left corner of
7191 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7192 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7193 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7194 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7195 image->page.width=image->columns;
7196 image->page.height=image->rows;
7199 im=CropImage(image,&crop_info,exception);
7201 if (im != (Image *) NULL)
7203 image->columns=im->columns;
7204 image->rows=im->rows;
7205 im=DestroyImage(im);
7206 image->page.width=image->columns;
7207 image->page.height=image->rows;
7208 image->page.x=crop_box.left;
7209 image->page.y=crop_box.top;
7216 No pixels in crop area. The MNG spec still requires
7217 a layer, though, so make a single transparent pixel in
7218 the top left corner.
7223 (void) SetImageBackgroundColor(image,exception);
7224 image->page.width=1;
7225 image->page.height=1;
7230 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7231 image=mng_info->image;
7235 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7236 /* PNG does not handle depths greater than 16 so reduce it even
7239 if (image->depth > 16)
7243 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7244 if (image->depth > 8)
7246 /* To do: fill low byte properly */
7250 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7254 if (image_info->number_scenes != 0)
7256 if (mng_info->scenes_found >
7257 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7261 if (logging != MagickFalse)
7262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263 " Finished reading image datastream.");
7265 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7267 (void) CloseBlob(image);
7269 if (logging != MagickFalse)
7270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7271 " Finished reading all image datastreams.");
7273 #if defined(MNG_INSERT_LAYERS)
7274 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7275 (mng_info->mng_height))
7278 Insert a background layer if nothing else was found.
7280 if (logging != MagickFalse)
7281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7282 " No images found. Inserting a background layer.");
7284 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7287 Allocate next image structure.
7289 AcquireNextImage(image_info,image,exception);
7290 if (GetNextImageInList(image) == (Image *) NULL)
7292 if (logging != MagickFalse)
7293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7294 " Allocation failed, returning NULL.");
7296 return(DestroyImageList(image));;
7298 image=SyncNextImageInList(image);
7300 image->columns=mng_info->mng_width;
7301 image->rows=mng_info->mng_height;
7302 image->page.width=mng_info->mng_width;
7303 image->page.height=mng_info->mng_height;
7306 image->background_color=mng_background_color;
7307 image->alpha_trait=UndefinedPixelTrait;
7309 if (image_info->ping == MagickFalse)
7310 (void) SetImageBackgroundColor(image,exception);
7312 mng_info->image_found++;
7315 image->iterations=mng_iterations;
7317 if (mng_iterations == 1)
7318 image->start_loop=MagickTrue;
7320 while (GetPreviousImageInList(image) != (Image *) NULL)
7323 if (image_count > 10*mng_info->image_found)
7325 if (logging != MagickFalse)
7326 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7328 (void) ThrowMagickException(exception,GetMagickModule(),
7329 CoderError,"Linked list is corrupted, beginning of list not found",
7330 "`%s'",image_info->filename);
7332 return(DestroyImageList(image));
7335 image=GetPreviousImageInList(image);
7337 if (GetNextImageInList(image) == (Image *) NULL)
7339 if (logging != MagickFalse)
7340 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7342 (void) ThrowMagickException(exception,GetMagickModule(),
7343 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7344 image_info->filename);
7348 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7349 GetNextImageInList(image) ==
7352 if (logging != MagickFalse)
7353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7354 " First image null");
7356 (void) ThrowMagickException(exception,GetMagickModule(),
7357 CoderError,"image->next for first image is NULL but shouldn't be.",
7358 "`%s'",image_info->filename);
7361 if (mng_info->image_found == 0)
7363 if (logging != MagickFalse)
7364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7365 " No visible images found.");
7367 (void) ThrowMagickException(exception,GetMagickModule(),
7368 CoderError,"No visible images in file","`%s'",image_info->filename);
7370 return(DestroyImageList(image));
7373 if (mng_info->ticks_per_second)
7374 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7375 final_delay/mng_info->ticks_per_second;
7378 image->start_loop=MagickTrue;
7380 /* Find final nonzero image delay */
7381 final_image_delay=0;
7383 while (GetNextImageInList(image) != (Image *) NULL)
7386 final_image_delay=image->delay;
7388 image=GetNextImageInList(image);
7391 if (final_delay < final_image_delay)
7392 final_delay=final_image_delay;
7394 image->delay=final_delay;
7396 if (logging != MagickFalse)
7397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7398 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7399 (double) final_delay);
7401 if (logging != MagickFalse)
7407 image=GetFirstImageInList(image);
7409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7410 " Before coalesce:");
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7413 " scene 0 delay=%.20g",(double) image->delay);
7415 while (GetNextImageInList(image) != (Image *) NULL)
7417 image=GetNextImageInList(image);
7418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7419 " scene %.20g delay=%.20g",(double) scene++,
7420 (double) image->delay);
7424 image=GetFirstImageInList(image);
7425 #ifdef MNG_COALESCE_LAYERS
7435 if (logging != MagickFalse)
7436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7437 " Coalesce Images");
7440 next_image=CoalesceImages(image,exception);
7442 if (next_image == (Image *) NULL)
7443 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7445 image=DestroyImageList(image);
7448 for (next=image; next != (Image *) NULL; next=next_image)
7450 next->page.width=mng_info->mng_width;
7451 next->page.height=mng_info->mng_height;
7454 next->scene=scene++;
7455 next_image=GetNextImageInList(next);
7457 if (next_image == (Image *) NULL)
7460 if (next->delay == 0)
7463 next_image->previous=GetPreviousImageInList(next);
7464 if (GetPreviousImageInList(next) == (Image *) NULL)
7467 next->previous->next=next_image;
7468 next=DestroyImage(next);
7474 while (GetNextImageInList(image) != (Image *) NULL)
7475 image=GetNextImageInList(image);
7477 image->dispose=BackgroundDispose;
7479 if (logging != MagickFalse)
7485 image=GetFirstImageInList(image);
7487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7488 " After coalesce:");
7490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7491 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7492 (double) image->dispose);
7494 while (GetNextImageInList(image) != (Image *) NULL)
7496 image=GetNextImageInList(image);
7498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7499 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7500 (double) image->delay,(double) image->dispose);
7504 if (logging != MagickFalse)
7505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7506 " exit ReadOneMNGImage();");
7511 static Image *ReadMNGImage(const ImageInfo *image_info,
7512 ExceptionInfo *exception)
7524 /* Open image file. */
7526 assert(image_info != (const ImageInfo *) NULL);
7527 assert(image_info->signature == MagickCoreSignature);
7528 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7529 image_info->filename);
7530 assert(exception != (ExceptionInfo *) NULL);
7531 assert(exception->signature == MagickCoreSignature);
7532 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7533 image=AcquireImage(image_info,exception);
7534 mng_info=(MngInfo *) NULL;
7535 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7537 if (status == MagickFalse)
7538 return((Image *) NULL);
7540 /* Allocate a MngInfo structure. */
7542 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7544 if (mng_info == (MngInfo *) NULL)
7545 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7547 /* Initialize members of the MngInfo structure. */
7549 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7550 mng_info->image=image;
7551 image=ReadOneMNGImage(mng_info,image_info,exception);
7552 mng_info=MngInfoFreeStruct(mng_info);
7554 if (image == (Image *) NULL)
7556 if (logging != MagickFalse)
7557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7558 "exit ReadMNGImage() with error");
7560 return((Image *) NULL);
7562 (void) CloseBlob(image);
7564 if (logging != MagickFalse)
7565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7567 return(GetFirstImageInList(image));
7569 #else /* PNG_LIBPNG_VER > 10011 */
7570 static Image *ReadPNGImage(const ImageInfo *image_info,
7571 ExceptionInfo *exception)
7573 printf("Your PNG library is too old: You have libpng-%s\n",
7574 PNG_LIBPNG_VER_STRING);
7576 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7577 "PNG library is too old","`%s'",image_info->filename);
7579 return(Image *) NULL;
7582 static Image *ReadMNGImage(const ImageInfo *image_info,
7583 ExceptionInfo *exception)
7585 return(ReadPNGImage(image_info,exception));
7587 #endif /* PNG_LIBPNG_VER > 10011 */
7591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7595 % R e g i s t e r P N G I m a g e %
7599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7601 % RegisterPNGImage() adds properties for the PNG image format to
7602 % the list of supported formats. The properties include the image format
7603 % tag, a method to read and/or write the format, whether the format
7604 % supports the saving of more than one frame to the same file or blob,
7605 % whether the format supports native in-memory I/O, and a brief
7606 % description of the format.
7608 % The format of the RegisterPNGImage method is:
7610 % size_t RegisterPNGImage(void)
7613 ModuleExport size_t RegisterPNGImage(void)
7616 version[MagickPathExtent];
7624 "See http://www.libpng.org/ for details about the PNG format."
7629 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7635 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7641 #if defined(PNG_LIBPNG_VER_STRING)
7642 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7643 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7646 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7648 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7649 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7654 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7655 entry->flags|=CoderDecoderSeekableStreamFlag;
7657 #if defined(MAGICKCORE_PNG_DELEGATE)
7658 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7659 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7662 entry->magick=(IsImageFormatHandler *) IsMNG;
7664 if (*version != '\0')
7665 entry->version=ConstantString(version);
7667 entry->mime_type=ConstantString("video/x-mng");
7668 entry->note=ConstantString(MNGNote);
7669 (void) RegisterMagickInfo(entry);
7671 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7673 #if defined(MAGICKCORE_PNG_DELEGATE)
7674 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7675 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7678 entry->magick=(IsImageFormatHandler *) IsPNG;
7679 entry->flags|=CoderDecoderSeekableStreamFlag;
7680 entry->flags^=CoderAdjoinFlag;
7681 entry->mime_type=ConstantString("image/png");
7683 if (*version != '\0')
7684 entry->version=ConstantString(version);
7686 entry->note=ConstantString(PNGNote);
7687 (void) RegisterMagickInfo(entry);
7689 entry=AcquireMagickInfo("PNG","PNG8",
7690 "8-bit indexed with optional binary transparency");
7692 #if defined(MAGICKCORE_PNG_DELEGATE)
7693 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7694 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7697 entry->magick=(IsImageFormatHandler *) IsPNG;
7698 entry->flags|=CoderDecoderSeekableStreamFlag;
7699 entry->flags^=CoderAdjoinFlag;
7700 entry->mime_type=ConstantString("image/png");
7701 (void) RegisterMagickInfo(entry);
7703 entry=AcquireMagickInfo("PNG","PNG24",
7704 "opaque or binary transparent 24-bit RGB");
7707 #if defined(ZLIB_VERSION)
7708 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7709 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7711 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7713 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7714 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7718 if (*version != '\0')
7719 entry->version=ConstantString(version);
7721 #if defined(MAGICKCORE_PNG_DELEGATE)
7722 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7723 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7726 entry->magick=(IsImageFormatHandler *) IsPNG;
7727 entry->flags|=CoderDecoderSeekableStreamFlag;
7728 entry->flags^=CoderAdjoinFlag;
7729 entry->mime_type=ConstantString("image/png");
7730 (void) RegisterMagickInfo(entry);
7732 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7734 #if defined(MAGICKCORE_PNG_DELEGATE)
7735 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7736 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7739 entry->magick=(IsImageFormatHandler *) IsPNG;
7740 entry->flags|=CoderDecoderSeekableStreamFlag;
7741 entry->flags^=CoderAdjoinFlag;
7742 entry->mime_type=ConstantString("image/png");
7743 (void) RegisterMagickInfo(entry);
7745 entry=AcquireMagickInfo("PNG","PNG48",
7746 "opaque or binary transparent 48-bit RGB");
7748 #if defined(MAGICKCORE_PNG_DELEGATE)
7749 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7750 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7753 entry->magick=(IsImageFormatHandler *) IsPNG;
7754 entry->flags|=CoderDecoderSeekableStreamFlag;
7755 entry->flags^=CoderAdjoinFlag;
7756 entry->mime_type=ConstantString("image/png");
7757 (void) RegisterMagickInfo(entry);
7759 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7761 #if defined(MAGICKCORE_PNG_DELEGATE)
7762 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7763 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7766 entry->magick=(IsImageFormatHandler *) IsPNG;
7767 entry->flags|=CoderDecoderSeekableStreamFlag;
7768 entry->flags^=CoderAdjoinFlag;
7769 entry->mime_type=ConstantString("image/png");
7770 (void) RegisterMagickInfo(entry);
7772 entry=AcquireMagickInfo("PNG","PNG00",
7773 "PNG inheriting bit-depth, color-type from original, if possible");
7775 #if defined(MAGICKCORE_PNG_DELEGATE)
7776 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7777 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7780 entry->magick=(IsImageFormatHandler *) IsPNG;
7781 entry->flags|=CoderDecoderSeekableStreamFlag;
7782 entry->flags^=CoderAdjoinFlag;
7783 entry->mime_type=ConstantString("image/png");
7784 (void) RegisterMagickInfo(entry);
7786 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7788 #if defined(JNG_SUPPORTED)
7789 #if defined(MAGICKCORE_PNG_DELEGATE)
7790 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7791 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7795 entry->magick=(IsImageFormatHandler *) IsJNG;
7796 entry->flags|=CoderDecoderSeekableStreamFlag;
7797 entry->flags^=CoderAdjoinFlag;
7798 entry->mime_type=ConstantString("image/x-jng");
7799 entry->note=ConstantString(JNGNote);
7800 (void) RegisterMagickInfo(entry);
7802 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7803 ping_semaphore=AcquireSemaphoreInfo();
7806 return(MagickImageCoderSignature);
7810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7814 % U n r e g i s t e r P N G I m a g e %
7818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7820 % UnregisterPNGImage() removes format registrations made by the
7821 % PNG module from the list of supported formats.
7823 % The format of the UnregisterPNGImage method is:
7825 % UnregisterPNGImage(void)
7828 ModuleExport void UnregisterPNGImage(void)
7830 (void) UnregisterMagickInfo("MNG");
7831 (void) UnregisterMagickInfo("PNG");
7832 (void) UnregisterMagickInfo("PNG8");
7833 (void) UnregisterMagickInfo("PNG24");
7834 (void) UnregisterMagickInfo("PNG32");
7835 (void) UnregisterMagickInfo("PNG48");
7836 (void) UnregisterMagickInfo("PNG64");
7837 (void) UnregisterMagickInfo("PNG00");
7838 (void) UnregisterMagickInfo("JNG");
7840 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7841 if (ping_semaphore != (SemaphoreInfo *) NULL)
7842 RelinquishSemaphoreInfo(&ping_semaphore);
7846 #if defined(MAGICKCORE_PNG_DELEGATE)
7847 #if PNG_LIBPNG_VER > 10011
7849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7853 % W r i t e M N G I m a g e %
7857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7859 % WriteMNGImage() writes an image in the Portable Network Graphics
7860 % Group's "Multiple-image Network Graphics" encoded image format.
7862 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7864 % The format of the WriteMNGImage method is:
7866 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7867 % Image *image,ExceptionInfo *exception)
7869 % A description of each parameter follows.
7871 % o image_info: the image info.
7873 % o image: The image.
7875 % o exception: return any errors or warnings in this structure.
7877 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7878 % "To do" under ReadPNGImage):
7880 % Preserve all unknown and not-yet-handled known chunks found in input
7881 % PNG file and copy them into output PNG files according to the PNG
7884 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7886 % Improve selection of color type (use indexed-colour or indexed-colour
7887 % with tRNS when 256 or fewer unique RGBA values are present).
7889 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7890 % This will be complicated if we limit ourselves to generating MNG-LC
7891 % files. For now we ignore disposal method 3 and simply overlay the next
7894 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7895 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7896 % [mostly done 15 June 1999 but still need to take care of tRNS]
7898 % Check for identical sRGB and replace with a global sRGB (and remove
7899 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7900 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7901 % local gAMA/cHRM with local sRGB if appropriate).
7903 % Check for identical sBIT chunks and write global ones.
7905 % Provide option to skip writing the signature tEXt chunks.
7907 % Use signatures to detect identical objects and reuse the first
7908 % instance of such objects instead of writing duplicate objects.
7910 % Use a smaller-than-32k value of compression window size when
7913 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7914 % ancillary text chunks and save profiles.
7916 % Provide an option to force LC files (to ensure exact framing rate)
7919 % Provide an option to force VLC files instead of LC, even when offsets
7920 % are present. This will involve expanding the embedded images with a
7921 % transparent region at the top and/or left.
7925 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7926 png_info *ping_info, unsigned char *profile_type, unsigned char
7927 *profile_description, unsigned char *profile_data, png_uint_32 length)
7946 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7948 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7951 if (image_info->verbose)
7953 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7954 (char *) profile_type, (double) length);
7957 #if PNG_LIBPNG_VER >= 10400
7958 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7960 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7962 description_length=(png_uint_32) strlen((const char *) profile_description);
7963 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7964 + description_length);
7965 #if PNG_LIBPNG_VER >= 10400
7966 text[0].text=(png_charp) png_malloc(ping,
7967 (png_alloc_size_t) allocated_length);
7968 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7970 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7971 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7973 text[0].key[0]='\0';
7974 (void) ConcatenateMagickString(text[0].key,
7975 "Raw profile type ",MagickPathExtent);
7976 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7980 (void) CopyMagickString(dp,(const char *) profile_description,
7982 dp+=description_length;
7984 (void) FormatLocaleString(dp,allocated_length-
7985 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7988 for (i=0; i < (ssize_t) length; i++)
7992 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7993 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7998 text[0].text_length=(png_size_t) (dp-text[0].text);
7999 text[0].compression=image_info->compression == NoCompression ||
8000 (image_info->compression == UndefinedCompression &&
8001 text[0].text_length < 128) ? -1 : 0;
8003 if (text[0].text_length <= allocated_length)
8004 png_set_text(ping,ping_info,text,1);
8006 png_free(ping,text[0].text);
8007 png_free(ping,text[0].key);
8008 png_free(ping,text);
8011 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8012 const Quantum *p, const PixelInfo *q)
8017 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8018 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8019 return(MagickFalse);
8020 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8021 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8022 return(MagickFalse);
8023 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8024 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8025 return(MagickFalse);
8030 #if defined(PNG_tIME_SUPPORTED)
8031 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8032 const char *date,ExceptionInfo *exception)
8048 if (date != (const char *) NULL)
8050 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8053 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8054 "Invalid date format specified for png:tIME","`%s'",
8058 ptime.year=(png_uint_16) year;
8059 ptime.month=(png_byte) month;
8060 ptime.day=(png_byte) day;
8061 ptime.hour=(png_byte) hour;
8062 ptime.minute=(png_byte) minute;
8063 ptime.second=(png_byte) second;
8068 png_convert_from_time_t(&ptime,ttime);
8070 png_set_tIME(ping,info,&ptime);
8074 /* Write one PNG image */
8075 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8076 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8108 ping_trans_alpha[256];
8136 ping_have_cheap_transparency,
8150 /* ping_exclude_EXIF, */
8154 /* ping_exclude_iTXt, */
8160 /* ping_exclude_tRNS, */
8163 ping_exclude_zCCP, /* hex-encoded iCCP */
8166 ping_preserve_colormap,
8168 ping_need_colortype_warning,
8176 *volatile pixel_info;
8195 ping_interlace_method,
8196 ping_compression_method,
8213 number_semitransparent,
8215 ping_pHYs_unit_type;
8218 ping_pHYs_x_resolution,
8219 ping_pHYs_y_resolution;
8221 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8222 " Enter WriteOnePNGImage()");
8224 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8225 if (image == (Image *) NULL)
8226 return(MagickFalse);
8227 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8228 if (image_info == (ImageInfo *) NULL)
8229 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8231 /* Define these outside of the following "if logging()" block so they will
8232 * show in debuggers.
8235 (void) ConcatenateMagickString(im_vers,
8236 MagickLibVersionText,MagickPathExtent);
8237 (void) ConcatenateMagickString(im_vers,
8238 MagickLibAddendum,MagickPathExtent);
8241 (void) ConcatenateMagickString(libpng_vers,
8242 PNG_LIBPNG_VER_STRING,32);
8244 (void) ConcatenateMagickString(libpng_runv,
8245 png_get_libpng_ver(NULL),32);
8248 (void) ConcatenateMagickString(zlib_vers,
8251 (void) ConcatenateMagickString(zlib_runv,
8254 if (logging != MagickFalse)
8256 (void) LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8258 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8260 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8262 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8265 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8267 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8269 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8274 /* Initialize some stuff */
8277 ping_interlace_method=0,
8278 ping_compression_method=0,
8279 ping_filter_method=0,
8282 ping_background.red = 0;
8283 ping_background.green = 0;
8284 ping_background.blue = 0;
8285 ping_background.gray = 0;
8286 ping_background.index = 0;
8288 ping_trans_color.red=0;
8289 ping_trans_color.green=0;
8290 ping_trans_color.blue=0;
8291 ping_trans_color.gray=0;
8293 ping_pHYs_unit_type = 0;
8294 ping_pHYs_x_resolution = 0;
8295 ping_pHYs_y_resolution = 0;
8297 ping_have_blob=MagickFalse;
8298 ping_have_cheap_transparency=MagickFalse;
8299 ping_have_color=MagickTrue;
8300 ping_have_non_bw=MagickTrue;
8301 ping_have_PLTE=MagickFalse;
8302 ping_have_bKGD=MagickFalse;
8303 ping_have_eXIf=MagickTrue;
8304 ping_have_iCCP=MagickFalse;
8305 ping_have_pHYs=MagickFalse;
8306 ping_have_sRGB=MagickFalse;
8307 ping_have_tRNS=MagickFalse;
8309 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8310 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8311 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8312 ping_exclude_date=mng_info->ping_exclude_date;
8313 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8314 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8315 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8316 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8317 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8318 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8319 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8320 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8321 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8322 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8323 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8324 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8325 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8327 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8328 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8329 ping_need_colortype_warning = MagickFalse;
8331 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8332 * i.e., eliminate the ICC profile and set image->rendering_intent.
8333 * Note that this will not involve any changes to the actual pixels
8334 * but merely passes information to applications that read the resulting
8337 * To do: recognize other variants of the sRGB profile, using the CRC to
8338 * verify all recognized variants including the 7 already known.
8340 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8342 * Use something other than image->rendering_intent to record the fact
8343 * that the sRGB profile was found.
8345 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8346 * profile. Record the Blackpoint Compensation, if any.
8348 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8356 ResetImageProfileIterator(image);
8357 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8359 profile=GetImageProfile(image,name);
8361 if (profile != (StringInfo *) NULL)
8363 if ((LocaleCompare(name,"ICC") == 0) ||
8364 (LocaleCompare(name,"ICM") == 0))
8379 length=(png_uint_32) GetStringInfoLength(profile);
8381 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8383 if (length == sRGB_info[icheck].len)
8387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8388 " Got a %lu-byte ICC profile (potentially sRGB)",
8389 (unsigned long) length);
8391 data=GetStringInfoDatum(profile);
8392 profile_crc=crc32(0,data,length);
8394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8395 " with crc=%8x",(unsigned int) profile_crc);
8399 if (profile_crc == sRGB_info[icheck].crc)
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " It is sRGB with rendering intent = %s",
8403 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8404 sRGB_info[icheck].intent));
8405 if (image->rendering_intent==UndefinedIntent)
8407 image->rendering_intent=
8408 Magick_RenderingIntent_from_PNG_RenderingIntent(
8409 sRGB_info[icheck].intent);
8411 ping_exclude_iCCP = MagickTrue;
8412 ping_exclude_zCCP = MagickTrue;
8413 ping_have_sRGB = MagickTrue;
8418 if (sRGB_info[icheck].len == 0)
8419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8420 " Got %lu-byte ICC profile not recognized as sRGB",
8421 (unsigned long) length);
8424 name=GetNextImageProfile(image);
8429 number_semitransparent = 0;
8430 number_transparent = 0;
8432 if (logging != MagickFalse)
8434 if (image->storage_class == UndefinedClass)
8435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8436 " image->storage_class=UndefinedClass");
8437 if (image->storage_class == DirectClass)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " image->storage_class=DirectClass");
8440 if (image->storage_class == PseudoClass)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " image->storage_class=PseudoClass");
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8444 " image->taint=MagickTrue":
8445 " image->taint=MagickFalse");
8448 if (image->storage_class == PseudoClass &&
8449 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8450 mng_info->write_png48 || mng_info->write_png64 ||
8451 (mng_info->write_png_colortype != 1 &&
8452 mng_info->write_png_colortype != 5)))
8454 (void) SyncImage(image,exception);
8455 image->storage_class = DirectClass;
8458 if (ping_preserve_colormap == MagickFalse)
8460 if (image->storage_class != PseudoClass && image->colormap != NULL)
8462 /* Free the bogus colormap; it can cause trouble later */
8463 if (logging != MagickFalse)
8464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8465 " Freeing bogus colormap");
8466 (void) RelinquishMagickMemory(image->colormap);
8467 image->colormap=NULL;
8471 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8472 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8475 Sometimes we get PseudoClass images whose RGB values don't match
8476 the colors in the colormap. This code syncs the RGB values.
8478 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8479 (void) SyncImage(image,exception);
8481 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8482 if (image->depth > 8)
8484 if (logging != MagickFalse)
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8492 /* Respect the -depth option */
8493 if (image->depth < 4)
8498 if (image->depth > 2)
8500 /* Scale to 4-bit */
8501 LBR04PacketRGBO(image->background_color);
8503 for (y=0; y < (ssize_t) image->rows; y++)
8505 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8507 if (r == (Quantum *) NULL)
8510 for (x=0; x < (ssize_t) image->columns; x++)
8513 r+=GetPixelChannels(image);
8516 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8520 if (image->storage_class == PseudoClass && image->colormap != NULL)
8522 for (i=0; i < (ssize_t) image->colors; i++)
8524 LBR04PacketRGBO(image->colormap[i]);
8528 else if (image->depth > 1)
8530 /* Scale to 2-bit */
8531 LBR02PacketRGBO(image->background_color);
8533 for (y=0; y < (ssize_t) image->rows; y++)
8535 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8537 if (r == (Quantum *) NULL)
8540 for (x=0; x < (ssize_t) image->columns; x++)
8543 r+=GetPixelChannels(image);
8546 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8550 if (image->storage_class == PseudoClass && image->colormap != NULL)
8552 for (i=0; i < (ssize_t) image->colors; i++)
8554 LBR02PacketRGBO(image->colormap[i]);
8560 /* Scale to 1-bit */
8561 LBR01PacketRGBO(image->background_color);
8563 for (y=0; y < (ssize_t) image->rows; y++)
8565 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8567 if (r == (Quantum *) NULL)
8570 for (x=0; x < (ssize_t) image->columns; x++)
8573 r+=GetPixelChannels(image);
8576 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8580 if (image->storage_class == PseudoClass && image->colormap != NULL)
8582 for (i=0; i < (ssize_t) image->colors; i++)
8584 LBR01PacketRGBO(image->colormap[i]);
8590 /* To do: set to next higher multiple of 8 */
8591 if (image->depth < 8)
8594 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8595 /* PNG does not handle depths greater than 16 so reduce it even
8598 if (image->depth > 8)
8602 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8603 if (image->depth > 8)
8605 /* To do: fill low byte properly */
8609 if (image->depth == 16 && mng_info->write_png_depth != 16)
8610 if (mng_info->write_png8 ||
8611 LosslessReduceDepthOK(image,exception) != MagickFalse)
8615 image_colors = (int) image->colors;
8616 number_opaque = (int) image->colors;
8617 number_transparent = 0;
8618 number_semitransparent = 0;
8620 if (mng_info->write_png_colortype &&
8621 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8622 mng_info->write_png_colortype < 4 &&
8623 image->alpha_trait == UndefinedPixelTrait)))
8625 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8626 * are not going to need the result.
8628 if (mng_info->write_png_colortype == 1 ||
8629 mng_info->write_png_colortype == 5)
8630 ping_have_color=MagickFalse;
8632 if (image->alpha_trait != UndefinedPixelTrait)
8634 number_transparent = 2;
8635 number_semitransparent = 1;
8639 if (mng_info->write_png_colortype < 7)
8643 * Normally we run this just once, but in the case of writing PNG8
8644 * we reduce the transparency to binary and run again, then if there
8645 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8646 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8647 * palette. Then (To do) we take care of a final reduction that is only
8648 * needed if there are still 256 colors present and one of them has both
8649 * transparent and opaque instances.
8652 tried_332 = MagickFalse;
8653 tried_333 = MagickFalse;
8654 tried_444 = MagickFalse;
8659 * Sometimes we get DirectClass images that have 256 colors or fewer.
8660 * This code will build a colormap.
8662 * Also, sometimes we get PseudoClass images with an out-of-date
8663 * colormap. This code will replace the colormap with a new one.
8664 * Sometimes we get PseudoClass images that have more than 256 colors.
8665 * This code will delete the colormap and change the image to
8668 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8669 * even though it sometimes contains left-over non-opaque values.
8671 * Also we gather some information (number of opaque, transparent,
8672 * and semitransparent pixels, and whether the image has any non-gray
8673 * pixels or only black-and-white pixels) that we might need later.
8675 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8676 * we need to check for bogus non-opaque values, at least.
8684 semitransparent[260],
8687 register const Quantum
8694 if (logging != MagickFalse)
8695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8696 " Enter BUILD_PALETTE:");
8698 if (logging != MagickFalse)
8700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8701 " image->columns=%.20g",(double) image->columns);
8702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8703 " image->rows=%.20g",(double) image->rows);
8704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8705 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707 " image->depth=%.20g",(double) image->depth);
8709 if (image->storage_class == PseudoClass && image->colormap != NULL)
8711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8712 " Original colormap:");
8713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714 " i (red,green,blue,alpha)");
8716 for (i=0; i < 256; i++)
8718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8719 " %d (%d,%d,%d,%d)",
8721 (int) image->colormap[i].red,
8722 (int) image->colormap[i].green,
8723 (int) image->colormap[i].blue,
8724 (int) image->colormap[i].alpha);
8727 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " %d (%d,%d,%d,%d)",
8734 (int) image->colormap[i].red,
8735 (int) image->colormap[i].green,
8736 (int) image->colormap[i].blue,
8737 (int) image->colormap[i].alpha);
8742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8743 " image->colors=%d",(int) image->colors);
8745 if (image->colors == 0)
8746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747 " (zero means unknown)");
8749 if (ping_preserve_colormap == MagickFalse)
8750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8751 " Regenerate the colormap");
8756 number_semitransparent = 0;
8757 number_transparent = 0;
8759 for (y=0; y < (ssize_t) image->rows; y++)
8761 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8763 if (q == (Quantum *) NULL)
8766 for (x=0; x < (ssize_t) image->columns; x++)
8768 if (image->alpha_trait == UndefinedPixelTrait ||
8769 GetPixelAlpha(image,q) == OpaqueAlpha)
8771 if (number_opaque < 259)
8773 if (number_opaque == 0)
8775 GetPixelInfoPixel(image, q, opaque);
8776 opaque[0].alpha=OpaqueAlpha;
8780 for (i=0; i< (ssize_t) number_opaque; i++)
8782 if (Magick_png_color_equal(image,q,opaque+i))
8786 if (i == (ssize_t) number_opaque && number_opaque < 259)
8789 GetPixelInfoPixel(image, q, opaque+i);
8790 opaque[i].alpha=OpaqueAlpha;
8794 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8796 if (number_transparent < 259)
8798 if (number_transparent == 0)
8800 GetPixelInfoPixel(image, q, transparent);
8801 ping_trans_color.red=(unsigned short)
8802 GetPixelRed(image,q);
8803 ping_trans_color.green=(unsigned short)
8804 GetPixelGreen(image,q);
8805 ping_trans_color.blue=(unsigned short)
8806 GetPixelBlue(image,q);
8807 ping_trans_color.gray=(unsigned short)
8808 GetPixelGray(image,q);
8809 number_transparent = 1;
8812 for (i=0; i< (ssize_t) number_transparent; i++)
8814 if (Magick_png_color_equal(image,q,transparent+i))
8818 if (i == (ssize_t) number_transparent &&
8819 number_transparent < 259)
8821 number_transparent++;
8822 GetPixelInfoPixel(image,q,transparent+i);
8828 if (number_semitransparent < 259)
8830 if (number_semitransparent == 0)
8832 GetPixelInfoPixel(image,q,semitransparent);
8833 number_semitransparent = 1;
8836 for (i=0; i< (ssize_t) number_semitransparent; i++)
8838 if (Magick_png_color_equal(image,q,semitransparent+i)
8839 && GetPixelAlpha(image,q) ==
8840 semitransparent[i].alpha)
8844 if (i == (ssize_t) number_semitransparent &&
8845 number_semitransparent < 259)
8847 number_semitransparent++;
8848 GetPixelInfoPixel(image, q, semitransparent+i);
8852 q+=GetPixelChannels(image);
8856 if (mng_info->write_png8 == MagickFalse &&
8857 ping_exclude_bKGD == MagickFalse)
8859 /* Add the background color to the palette, if it
8860 * isn't already there.
8862 if (logging != MagickFalse)
8864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865 " Check colormap for background (%d,%d,%d)",
8866 (int) image->background_color.red,
8867 (int) image->background_color.green,
8868 (int) image->background_color.blue);
8870 for (i=0; i<number_opaque; i++)
8872 if (opaque[i].red == image->background_color.red &&
8873 opaque[i].green == image->background_color.green &&
8874 opaque[i].blue == image->background_color.blue)
8877 if (number_opaque < 259 && i == number_opaque)
8879 opaque[i] = image->background_color;
8880 ping_background.index = i;
8882 if (logging != MagickFalse)
8884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8885 " background_color index is %d",(int) i);
8889 else if (logging != MagickFalse)
8890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891 " No room in the colormap to add background color");
8894 image_colors=number_opaque+number_transparent+number_semitransparent;
8896 if (logging != MagickFalse)
8898 if (image_colors > 256)
8899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8900 " image has more than 256 colors");
8903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8904 " image has %d colors",image_colors);
8907 if (ping_preserve_colormap != MagickFalse)
8910 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8912 ping_have_color=MagickFalse;
8913 ping_have_non_bw=MagickFalse;
8915 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918 "incompatible colorspace");
8919 ping_have_color=MagickTrue;
8920 ping_have_non_bw=MagickTrue;
8923 if(image_colors > 256)
8925 for (y=0; y < (ssize_t) image->rows; y++)
8927 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8929 if (q == (Quantum *) NULL)
8933 for (x=0; x < (ssize_t) image->columns; x++)
8935 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8936 GetPixelRed(image,s) != GetPixelBlue(image,s))
8938 ping_have_color=MagickTrue;
8939 ping_have_non_bw=MagickTrue;
8942 s+=GetPixelChannels(image);
8945 if (ping_have_color != MagickFalse)
8948 /* Worst case is black-and-white; we are looking at every
8952 if (ping_have_non_bw == MagickFalse)
8955 for (x=0; x < (ssize_t) image->columns; x++)
8957 if (GetPixelRed(image,s) != 0 &&
8958 GetPixelRed(image,s) != QuantumRange)
8960 ping_have_non_bw=MagickTrue;
8963 s+=GetPixelChannels(image);
8970 if (image_colors < 257)
8976 * Initialize image colormap.
8979 if (logging != MagickFalse)
8980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8981 " Sort the new colormap");
8983 /* Sort palette, transparent first */;
8987 for (i=0; i<number_transparent; i++)
8988 colormap[n++] = transparent[i];
8990 for (i=0; i<number_semitransparent; i++)
8991 colormap[n++] = semitransparent[i];
8993 for (i=0; i<number_opaque; i++)
8994 colormap[n++] = opaque[i];
8996 ping_background.index +=
8997 (number_transparent + number_semitransparent);
8999 /* image_colors < 257; search the colormap instead of the pixels
9000 * to get ping_have_color and ping_have_non_bw
9004 if (ping_have_color == MagickFalse)
9006 if (colormap[i].red != colormap[i].green ||
9007 colormap[i].red != colormap[i].blue)
9009 ping_have_color=MagickTrue;
9010 ping_have_non_bw=MagickTrue;
9015 if (ping_have_non_bw == MagickFalse)
9017 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9018 ping_have_non_bw=MagickTrue;
9022 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9023 (number_transparent == 0 && number_semitransparent == 0)) &&
9024 (((mng_info->write_png_colortype-1) ==
9025 PNG_COLOR_TYPE_PALETTE) ||
9026 (mng_info->write_png_colortype == 0)))
9028 if (logging != MagickFalse)
9030 if (n != (ssize_t) image_colors)
9031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9032 " image_colors (%d) and n (%d) don't match",
9035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9036 " AcquireImageColormap");
9039 image->colors = image_colors;
9041 if (AcquireImageColormap(image,image_colors,exception) ==
9043 ThrowWriterException(ResourceLimitError,
9044 "MemoryAllocationFailed");
9046 for (i=0; i< (ssize_t) image_colors; i++)
9047 image->colormap[i] = colormap[i];
9049 if (logging != MagickFalse)
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " image->colors=%d (%d)",
9053 (int) image->colors, image_colors);
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " Update the pixel indexes");
9059 /* Sync the pixel indices with the new colormap */
9061 for (y=0; y < (ssize_t) image->rows; y++)
9063 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9065 if (q == (Quantum *) NULL)
9068 for (x=0; x < (ssize_t) image->columns; x++)
9070 for (i=0; i< (ssize_t) image_colors; i++)
9072 if ((image->alpha_trait == UndefinedPixelTrait ||
9073 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9074 image->colormap[i].red == GetPixelRed(image,q) &&
9075 image->colormap[i].green == GetPixelGreen(image,q) &&
9076 image->colormap[i].blue == GetPixelBlue(image,q))
9078 SetPixelIndex(image,i,q);
9082 q+=GetPixelChannels(image);
9085 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9091 if (logging != MagickFalse)
9093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9094 " image->colors=%d", (int) image->colors);
9096 if (image->colormap != NULL)
9098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9099 " i (red,green,blue,alpha)");
9101 for (i=0; i < (ssize_t) image->colors; i++)
9103 if (i < 300 || i >= (ssize_t) image->colors - 10)
9105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106 " %d (%d,%d,%d,%d)",
9108 (int) image->colormap[i].red,
9109 (int) image->colormap[i].green,
9110 (int) image->colormap[i].blue,
9111 (int) image->colormap[i].alpha);
9116 if (number_transparent < 257)
9117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9118 " number_transparent = %d",
9119 number_transparent);
9122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9123 " number_transparent > 256");
9125 if (number_opaque < 257)
9126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127 " number_opaque = %d",
9131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9132 " number_opaque > 256");
9134 if (number_semitransparent < 257)
9135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9136 " number_semitransparent = %d",
9137 number_semitransparent);
9140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141 " number_semitransparent > 256");
9143 if (ping_have_non_bw == MagickFalse)
9144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " All pixels and the background are black or white");
9147 else if (ping_have_color == MagickFalse)
9148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149 " All pixels and the background are gray");
9152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9153 " At least one pixel or the background is non-gray");
9155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9156 " Exit BUILD_PALETTE:");
9159 if (mng_info->write_png8 == MagickFalse)
9162 /* Make any reductions necessary for the PNG8 format */
9163 if (image_colors <= 256 &&
9164 image_colors != 0 && image->colormap != NULL &&
9165 number_semitransparent == 0 &&
9166 number_transparent <= 1)
9169 /* PNG8 can't have semitransparent colors so we threshold the
9170 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9171 * transparent color so if more than one is transparent we merge
9172 * them into image->background_color.
9174 if (number_semitransparent != 0 || number_transparent > 1)
9176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 " Thresholding the alpha channel to binary");
9179 for (y=0; y < (ssize_t) image->rows; y++)
9181 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9183 if (r == (Quantum *) NULL)
9186 for (x=0; x < (ssize_t) image->columns; x++)
9188 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9190 SetPixelViaPixelInfo(image,&image->background_color,r);
9191 SetPixelAlpha(image,TransparentAlpha,r);
9194 SetPixelAlpha(image,OpaqueAlpha,r);
9195 r+=GetPixelChannels(image);
9198 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9201 if (image_colors != 0 && image_colors <= 256 &&
9202 image->colormap != NULL)
9203 for (i=0; i<image_colors; i++)
9204 image->colormap[i].alpha =
9205 (image->colormap[i].alpha > TransparentAlpha/2 ?
9206 TransparentAlpha : OpaqueAlpha);
9211 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9212 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9213 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9216 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9218 if (logging != MagickFalse)
9219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9220 " Quantizing the background color to 4-4-4");
9222 tried_444 = MagickTrue;
9224 LBR04PacketRGB(image->background_color);
9226 if (logging != MagickFalse)
9227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9228 " Quantizing the pixel colors to 4-4-4");
9230 if (image->colormap == NULL)
9232 for (y=0; y < (ssize_t) image->rows; y++)
9234 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9236 if (r == (Quantum *) NULL)
9239 for (x=0; x < (ssize_t) image->columns; x++)
9241 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9243 r+=GetPixelChannels(image);
9246 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9251 else /* Should not reach this; colormap already exists and
9254 if (logging != MagickFalse)
9255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256 " Quantizing the colormap to 4-4-4");
9258 for (i=0; i<image_colors; i++)
9260 LBR04PacketRGB(image->colormap[i]);
9266 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9268 if (logging != MagickFalse)
9269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9270 " Quantizing the background color to 3-3-3");
9272 tried_333 = MagickTrue;
9274 LBR03PacketRGB(image->background_color);
9276 if (logging != MagickFalse)
9277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278 " Quantizing the pixel colors to 3-3-3-1");
9280 if (image->colormap == NULL)
9282 for (y=0; y < (ssize_t) image->rows; y++)
9284 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9286 if (r == (Quantum *) NULL)
9289 for (x=0; x < (ssize_t) image->columns; x++)
9291 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9293 r+=GetPixelChannels(image);
9296 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9301 else /* Should not reach this; colormap already exists and
9304 if (logging != MagickFalse)
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306 " Quantizing the colormap to 3-3-3-1");
9307 for (i=0; i<image_colors; i++)
9309 LBR03PacketRGB(image->colormap[i]);
9315 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9317 if (logging != MagickFalse)
9318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9319 " Quantizing the background color to 3-3-2");
9321 tried_332 = MagickTrue;
9323 /* Red and green were already done so we only quantize the blue
9327 LBR02PacketBlue(image->background_color);
9329 if (logging != MagickFalse)
9330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331 " Quantizing the pixel colors to 3-3-2-1");
9333 if (image->colormap == NULL)
9335 for (y=0; y < (ssize_t) image->rows; y++)
9337 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9339 if (r == (Quantum *) NULL)
9342 for (x=0; x < (ssize_t) image->columns; x++)
9344 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9346 r+=GetPixelChannels(image);
9349 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9354 else /* Should not reach this; colormap already exists and
9357 if (logging != MagickFalse)
9358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359 " Quantizing the colormap to 3-3-2-1");
9360 for (i=0; i<image_colors; i++)
9362 LBR02PacketBlue(image->colormap[i]);
9368 if (image_colors == 0 || image_colors > 256)
9370 /* Take care of special case with 256 opaque colors + 1 transparent
9371 * color. We don't need to quantize to 2-3-2-1; we only need to
9372 * eliminate one color, so we'll merge the two darkest red
9373 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9375 if (logging != MagickFalse)
9376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377 " Merging two dark red background colors to 3-3-2-1");
9379 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9380 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9381 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9383 image->background_color.red=ScaleCharToQuantum(0x24);
9386 if (logging != MagickFalse)
9387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9388 " Merging two dark red pixel colors to 3-3-2-1");
9390 if (image->colormap == NULL)
9392 for (y=0; y < (ssize_t) image->rows; y++)
9394 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9396 if (r == (Quantum *) NULL)
9399 for (x=0; x < (ssize_t) image->columns; x++)
9401 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9402 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9403 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9404 GetPixelAlpha(image,r) == OpaqueAlpha)
9406 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9408 r+=GetPixelChannels(image);
9411 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9419 for (i=0; i<image_colors; i++)
9421 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9422 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9423 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9425 image->colormap[i].red=ScaleCharToQuantum(0x24);
9432 /* END OF BUILD_PALETTE */
9434 /* If we are excluding the tRNS chunk and there is transparency,
9435 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9438 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9439 (number_transparent != 0 || number_semitransparent != 0))
9441 unsigned int colortype=mng_info->write_png_colortype;
9443 if (ping_have_color == MagickFalse)
9444 mng_info->write_png_colortype = 5;
9447 mng_info->write_png_colortype = 7;
9449 if (colortype != 0 &&
9450 mng_info->write_png_colortype != colortype)
9451 ping_need_colortype_warning=MagickTrue;
9455 /* See if cheap transparency is possible. It is only possible
9456 * when there is a single transparent color, no semitransparent
9457 * color, and no opaque color that has the same RGB components
9458 * as the transparent color. We only need this information if
9459 * we are writing a PNG with colortype 0 or 2, and we have not
9460 * excluded the tRNS chunk.
9462 if (number_transparent == 1 &&
9463 mng_info->write_png_colortype < 4)
9465 ping_have_cheap_transparency = MagickTrue;
9467 if (number_semitransparent != 0)
9468 ping_have_cheap_transparency = MagickFalse;
9470 else if (image_colors == 0 || image_colors > 256 ||
9471 image->colormap == NULL)
9473 register const Quantum
9476 for (y=0; y < (ssize_t) image->rows; y++)
9478 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9480 if (q == (Quantum *) NULL)
9483 for (x=0; x < (ssize_t) image->columns; x++)
9485 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9486 (unsigned short) GetPixelRed(image,q) ==
9487 ping_trans_color.red &&
9488 (unsigned short) GetPixelGreen(image,q) ==
9489 ping_trans_color.green &&
9490 (unsigned short) GetPixelBlue(image,q) ==
9491 ping_trans_color.blue)
9493 ping_have_cheap_transparency = MagickFalse;
9497 q+=GetPixelChannels(image);
9500 if (ping_have_cheap_transparency == MagickFalse)
9506 /* Assuming that image->colormap[0] is the one transparent color
9507 * and that all others are opaque.
9509 if (image_colors > 1)
9510 for (i=1; i<image_colors; i++)
9511 if (image->colormap[i].red == image->colormap[0].red &&
9512 image->colormap[i].green == image->colormap[0].green &&
9513 image->colormap[i].blue == image->colormap[0].blue)
9515 ping_have_cheap_transparency = MagickFalse;
9520 if (logging != MagickFalse)
9522 if (ping_have_cheap_transparency == MagickFalse)
9523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9524 " Cheap transparency is not possible.");
9527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9528 " Cheap transparency is possible.");
9532 ping_have_cheap_transparency = MagickFalse;
9534 image_depth=image->depth;
9536 quantum_info = (QuantumInfo *) NULL;
9538 image_colors=(int) image->colors;
9539 image_matte=image->alpha_trait !=
9540 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9542 if (mng_info->write_png_colortype < 5)
9543 mng_info->IsPalette=image->storage_class == PseudoClass &&
9544 image_colors <= 256 && image->colormap != NULL;
9546 mng_info->IsPalette = MagickFalse;
9548 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9549 (image->colors == 0 || image->colormap == NULL))
9551 image_info=DestroyImageInfo(image_info);
9552 image=DestroyImage(image);
9553 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9554 "Cannot write PNG8 or color-type 3; colormap is NULL",
9555 "`%s'",IMimage->filename);
9556 return(MagickFalse);
9560 Allocate the PNG structures
9562 #ifdef PNG_USER_MEM_SUPPORTED
9563 error_info.image=image;
9564 error_info.exception=exception;
9565 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9566 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9567 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9570 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9571 MagickPNGErrorHandler,MagickPNGWarningHandler);
9574 if (ping == (png_struct *) NULL)
9575 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9577 ping_info=png_create_info_struct(ping);
9579 if (ping_info == (png_info *) NULL)
9581 png_destroy_write_struct(&ping,(png_info **) NULL);
9582 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9585 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9586 pixel_info=(MemoryInfo *) NULL;
9588 if (setjmp(png_jmpbuf(ping)))
9594 if (image_info->verbose)
9595 (void) printf("PNG write has failed.\n");
9597 png_destroy_write_struct(&ping,&ping_info);
9598 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9599 UnlockSemaphoreInfo(ping_semaphore);
9602 if (pixel_info != (MemoryInfo *) NULL)
9603 pixel_info=RelinquishVirtualMemory(pixel_info);
9605 if (quantum_info != (QuantumInfo *) NULL)
9606 quantum_info=DestroyQuantumInfo(quantum_info);
9608 if (ping_have_blob != MagickFalse)
9609 (void) CloseBlob(image);
9610 image_info=DestroyImageInfo(image_info);
9611 image=DestroyImage(image);
9612 return(MagickFalse);
9615 /* { For navigation to end of SETJMP-protected block. Within this
9616 * block, use png_error() instead of Throwing an Exception, to ensure
9617 * that libpng is able to clean up, and that the semaphore is unlocked.
9620 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9621 LockSemaphoreInfo(ping_semaphore);
9624 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9625 /* Allow benign errors */
9626 png_set_benign_errors(ping, 1);
9629 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9630 /* Reject images with too many rows or columns */
9631 png_set_user_limits(ping,
9632 (png_uint_32) MagickMin(0x7fffffffL,
9633 GetMagickResourceLimit(WidthResource)),
9634 (png_uint_32) MagickMin(0x7fffffffL,
9635 GetMagickResourceLimit(HeightResource)));
9636 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9639 Prepare PNG for writing.
9642 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9643 if (mng_info->write_mng)
9645 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9646 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9647 /* Disable new libpng-1.5.10 feature when writing a MNG because
9648 * zero-length PLTE is OK
9650 png_set_check_for_invalid_index (ping, 0);
9655 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9656 if (mng_info->write_mng)
9657 png_permit_empty_plte(ping,MagickTrue);
9664 ping_width=(png_uint_32) image->columns;
9665 ping_height=(png_uint_32) image->rows;
9667 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9670 if (mng_info->write_png48 || mng_info->write_png64)
9673 if (mng_info->write_png_depth != 0)
9674 image_depth=mng_info->write_png_depth;
9676 /* Adjust requested depth to next higher valid depth if necessary */
9677 if (image_depth > 8)
9680 if ((image_depth > 4) && (image_depth < 8))
9683 if (image_depth == 3)
9686 if (logging != MagickFalse)
9688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9689 " width=%.20g",(double) ping_width);
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 " height=%.20g",(double) ping_height);
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693 " image_matte=%.20g",(double) image->alpha_trait);
9694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9695 " image->depth=%.20g",(double) image->depth);
9696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9697 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9700 save_image_depth=image_depth;
9701 ping_bit_depth=(png_byte) save_image_depth;
9704 #if defined(PNG_pHYs_SUPPORTED)
9705 if (ping_exclude_pHYs == MagickFalse)
9707 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9708 (!mng_info->write_mng || !mng_info->equal_physs))
9710 if (logging != MagickFalse)
9711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712 " Setting up pHYs chunk");
9714 if (image->units == PixelsPerInchResolution)
9716 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9717 ping_pHYs_x_resolution=
9718 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9719 ping_pHYs_y_resolution=
9720 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9723 else if (image->units == PixelsPerCentimeterResolution)
9725 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9726 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9727 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9732 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9733 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9734 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9737 if (logging != MagickFalse)
9738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9739 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9740 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9741 (int) ping_pHYs_unit_type);
9742 ping_have_pHYs = MagickTrue;
9747 if (ping_exclude_bKGD == MagickFalse)
9749 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9755 if (ping_bit_depth == 8)
9758 if (ping_bit_depth == 4)
9761 if (ping_bit_depth == 2)
9764 if (ping_bit_depth == 1)
9767 ping_background.red=(png_uint_16)
9768 (ScaleQuantumToShort(image->background_color.red) & mask);
9770 ping_background.green=(png_uint_16)
9771 (ScaleQuantumToShort(image->background_color.green) & mask);
9773 ping_background.blue=(png_uint_16)
9774 (ScaleQuantumToShort(image->background_color.blue) & mask);
9776 ping_background.gray=(png_uint_16) ping_background.green;
9779 if (logging != MagickFalse)
9781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9782 " Setting up bKGD chunk (1)");
9783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9784 " background_color index is %d",
9785 (int) ping_background.index);
9787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9788 " ping_bit_depth=%d",ping_bit_depth);
9791 ping_have_bKGD = MagickTrue;
9795 Select the color type.
9800 if (mng_info->IsPalette && mng_info->write_png8)
9802 /* To do: make this a function cause it's used twice, except
9803 for reducing the sample depth from 8. */
9805 number_colors=image_colors;
9807 ping_have_tRNS=MagickFalse;
9812 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9814 if (logging != MagickFalse)
9815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9816 " Setting up PLTE chunk with %d colors (%d)",
9817 number_colors, image_colors);
9819 for (i=0; i < (ssize_t) number_colors; i++)
9821 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9822 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9823 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9824 if (logging != MagickFalse)
9825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 #if MAGICKCORE_QUANTUM_DEPTH == 8
9827 " %3ld (%3d,%3d,%3d)",
9829 " %5ld (%5d,%5d,%5d)",
9831 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9835 ping_have_PLTE=MagickTrue;
9836 image_depth=ping_bit_depth;
9839 if (matte != MagickFalse)
9842 Identify which colormap entry is transparent.
9844 assert(number_colors <= 256);
9845 assert(image->colormap != NULL);
9847 for (i=0; i < (ssize_t) number_transparent; i++)
9848 ping_trans_alpha[i]=0;
9851 ping_num_trans=(unsigned short) (number_transparent +
9852 number_semitransparent);
9854 if (ping_num_trans == 0)
9855 ping_have_tRNS=MagickFalse;
9858 ping_have_tRNS=MagickTrue;
9861 if (ping_exclude_bKGD == MagickFalse)
9864 * Identify which colormap entry is the background color.
9867 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9868 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9871 ping_background.index=(png_byte) i;
9873 if (logging != MagickFalse)
9875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9876 " background_color index is %d",
9877 (int) ping_background.index);
9880 } /* end of write_png8 */
9882 else if (mng_info->write_png_colortype == 1)
9884 image_matte=MagickFalse;
9885 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9888 else if (mng_info->write_png24 || mng_info->write_png48 ||
9889 mng_info->write_png_colortype == 3)
9891 image_matte=MagickFalse;
9892 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9895 else if (mng_info->write_png32 || mng_info->write_png64 ||
9896 mng_info->write_png_colortype == 7)
9898 image_matte=MagickTrue;
9899 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9902 else /* mng_info->write_pngNN not specified */
9904 image_depth=ping_bit_depth;
9906 if (mng_info->write_png_colortype != 0)
9908 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9910 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9911 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9912 image_matte=MagickTrue;
9915 image_matte=MagickFalse;
9917 if (logging != MagickFalse)
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919 " PNG colortype %d was specified:",(int) ping_color_type);
9922 else /* write_png_colortype not specified */
9924 if (logging != MagickFalse)
9925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9926 " Selecting PNG colortype:");
9928 ping_color_type=(png_byte) ((matte != MagickFalse)?
9929 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9931 if (image_info->type == TrueColorType)
9933 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9934 image_matte=MagickFalse;
9937 if (image_info->type == TrueColorAlphaType)
9939 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9940 image_matte=MagickTrue;
9943 if (image_info->type == PaletteType ||
9944 image_info->type == PaletteAlphaType)
9945 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9947 if (mng_info->write_png_colortype == 0 &&
9948 image_info->type == UndefinedType)
9950 if (ping_have_color == MagickFalse)
9952 if (image_matte == MagickFalse)
9954 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9955 image_matte=MagickFalse;
9960 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9961 image_matte=MagickTrue;
9966 if (image_matte == MagickFalse)
9968 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9969 image_matte=MagickFalse;
9974 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9975 image_matte=MagickTrue;
9982 if (logging != MagickFalse)
9983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9984 " Selected PNG colortype=%d",ping_color_type);
9986 if (ping_bit_depth < 8)
9988 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9989 ping_color_type == PNG_COLOR_TYPE_RGB ||
9990 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9994 old_bit_depth=ping_bit_depth;
9996 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9998 if (image->alpha_trait == UndefinedPixelTrait &&
9999 ping_have_non_bw == MagickFalse)
10003 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10008 if (image->colors == 0)
10011 png_error(ping,"image has 0 colors");
10014 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10015 ping_bit_depth <<= 1;
10018 if (logging != MagickFalse)
10020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10021 " Number of colors: %.20g",(double) image_colors);
10023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10024 " Tentative PNG bit depth: %d",ping_bit_depth);
10027 if (ping_bit_depth < (int) mng_info->write_png_depth)
10028 ping_bit_depth = mng_info->write_png_depth;
10031 image_depth=ping_bit_depth;
10033 if (logging != MagickFalse)
10035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10036 " Tentative PNG color type: %s (%.20g)",
10037 PngColorTypeToString(ping_color_type),
10038 (double) ping_color_type);
10040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10041 " image_info->type: %.20g",(double) image_info->type);
10043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044 " image_depth: %.20g",(double) image_depth);
10046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10048 " image->depth: %.20g",(double) image->depth);
10050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10051 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10054 if (matte != MagickFalse)
10056 if (mng_info->IsPalette)
10058 if (mng_info->write_png_colortype == 0)
10060 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10062 if (ping_have_color != MagickFalse)
10063 ping_color_type=PNG_COLOR_TYPE_RGBA;
10067 * Determine if there is any transparent color.
10069 if (number_transparent + number_semitransparent == 0)
10072 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10075 image_matte=MagickFalse;
10077 if (mng_info->write_png_colortype == 0)
10078 ping_color_type&=0x03;
10088 if (ping_bit_depth == 8)
10091 if (ping_bit_depth == 4)
10094 if (ping_bit_depth == 2)
10097 if (ping_bit_depth == 1)
10100 ping_trans_color.red=(png_uint_16)
10101 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10103 ping_trans_color.green=(png_uint_16)
10104 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10106 ping_trans_color.blue=(png_uint_16)
10107 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10109 ping_trans_color.gray=(png_uint_16)
10110 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10111 image->colormap)) & mask);
10113 ping_trans_color.index=(png_byte) 0;
10115 ping_have_tRNS=MagickTrue;
10118 if (ping_have_tRNS != MagickFalse)
10121 * Determine if there is one and only one transparent color
10122 * and if so if it is fully transparent.
10124 if (ping_have_cheap_transparency == MagickFalse)
10125 ping_have_tRNS=MagickFalse;
10128 if (ping_have_tRNS != MagickFalse)
10130 if (mng_info->write_png_colortype == 0)
10131 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10133 if (image_depth == 8)
10135 ping_trans_color.red&=0xff;
10136 ping_trans_color.green&=0xff;
10137 ping_trans_color.blue&=0xff;
10138 ping_trans_color.gray&=0xff;
10144 if (image_depth == 8)
10146 ping_trans_color.red&=0xff;
10147 ping_trans_color.green&=0xff;
10148 ping_trans_color.blue&=0xff;
10149 ping_trans_color.gray&=0xff;
10156 if (ping_have_tRNS != MagickFalse)
10157 image_matte=MagickFalse;
10159 if ((mng_info->IsPalette) &&
10160 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10161 ping_have_color == MagickFalse &&
10162 (image_matte == MagickFalse || image_depth >= 8))
10166 if (image_matte != MagickFalse)
10167 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10169 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10171 ping_color_type=PNG_COLOR_TYPE_GRAY;
10173 if (save_image_depth == 16 && image_depth == 8)
10175 if (logging != MagickFalse)
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " Scaling ping_trans_color (0)");
10180 ping_trans_color.gray*=0x0101;
10184 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10185 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10187 if ((image_colors == 0) ||
10188 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10189 image_colors=(int) (one << image_depth);
10191 if (image_depth > 8)
10197 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10199 if(!mng_info->write_png_depth)
10203 while ((int) (one << ping_bit_depth)
10204 < (ssize_t) image_colors)
10205 ping_bit_depth <<= 1;
10209 else if (ping_color_type ==
10210 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10211 mng_info->IsPalette)
10213 /* Check if grayscale is reducible */
10216 depth_4_ok=MagickTrue,
10217 depth_2_ok=MagickTrue,
10218 depth_1_ok=MagickTrue;
10220 for (i=0; i < (ssize_t) image_colors; i++)
10225 intensity=ScaleQuantumToChar(image->colormap[i].red);
10227 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10228 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10229 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10230 depth_2_ok=depth_1_ok=MagickFalse;
10231 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10232 depth_1_ok=MagickFalse;
10235 if (depth_1_ok && mng_info->write_png_depth <= 1)
10238 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10241 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10246 image_depth=ping_bit_depth;
10251 if (mng_info->IsPalette)
10253 number_colors=image_colors;
10255 if (image_depth <= 8)
10260 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10262 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10264 for (i=0; i < (ssize_t) number_colors; i++)
10266 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10268 ScaleQuantumToChar(image->colormap[i].green);
10269 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10272 if (logging != MagickFalse)
10273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10274 " Setting up PLTE chunk with %d colors",
10277 ping_have_PLTE=MagickTrue;
10280 /* color_type is PNG_COLOR_TYPE_PALETTE */
10281 if (mng_info->write_png_depth == 0)
10289 while ((one << ping_bit_depth) < (size_t) number_colors)
10290 ping_bit_depth <<= 1;
10295 if (matte != MagickFalse)
10298 * Set up trans_colors array.
10300 assert(number_colors <= 256);
10302 ping_num_trans=(unsigned short) (number_transparent +
10303 number_semitransparent);
10305 if (ping_num_trans == 0)
10306 ping_have_tRNS=MagickFalse;
10310 if (logging != MagickFalse)
10312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10313 " Scaling ping_trans_color (1)");
10315 ping_have_tRNS=MagickTrue;
10317 for (i=0; i < ping_num_trans; i++)
10319 ping_trans_alpha[i]= (png_byte)
10320 ScaleQuantumToChar(image->colormap[i].alpha);
10330 if (image_depth < 8)
10333 if ((save_image_depth == 16) && (image_depth == 8))
10335 if (logging != MagickFalse)
10337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10338 " Scaling ping_trans_color from (%d,%d,%d)",
10339 (int) ping_trans_color.red,
10340 (int) ping_trans_color.green,
10341 (int) ping_trans_color.blue);
10344 ping_trans_color.red*=0x0101;
10345 ping_trans_color.green*=0x0101;
10346 ping_trans_color.blue*=0x0101;
10347 ping_trans_color.gray*=0x0101;
10349 if (logging != MagickFalse)
10351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10353 (int) ping_trans_color.red,
10354 (int) ping_trans_color.green,
10355 (int) ping_trans_color.blue);
10360 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10361 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10364 Adjust background and transparency samples in sub-8-bit grayscale files.
10366 if (ping_bit_depth < 8 && ping_color_type ==
10367 PNG_COLOR_TYPE_GRAY)
10375 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10377 if (ping_exclude_bKGD == MagickFalse)
10380 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10381 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10382 &image->background_color))) +.5)));
10384 if (logging != MagickFalse)
10385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10386 " Setting up bKGD chunk (2)");
10387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " background_color index is %d",
10389 (int) ping_background.index);
10391 ping_have_bKGD = MagickTrue;
10394 if (logging != MagickFalse)
10395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396 " Scaling ping_trans_color.gray from %d",
10397 (int)ping_trans_color.gray);
10399 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10400 ping_trans_color.gray)+.5);
10402 if (logging != MagickFalse)
10403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404 " to %d", (int)ping_trans_color.gray);
10407 if (ping_exclude_bKGD == MagickFalse)
10409 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10412 Identify which colormap entry is the background color.
10415 number_colors=image_colors;
10417 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10418 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10421 ping_background.index=(png_byte) i;
10423 if (logging != MagickFalse)
10425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426 " Setting up bKGD chunk with index=%d",(int) i);
10429 if (i < (ssize_t) number_colors)
10431 ping_have_bKGD = MagickTrue;
10433 if (logging != MagickFalse)
10435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 " background =(%d,%d,%d)",
10437 (int) ping_background.red,
10438 (int) ping_background.green,
10439 (int) ping_background.blue);
10443 else /* Can't happen */
10445 if (logging != MagickFalse)
10446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10447 " No room in PLTE to add bKGD color");
10448 ping_have_bKGD = MagickFalse;
10453 if (logging != MagickFalse)
10454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10458 Initialize compression level and filtering.
10460 if (logging != MagickFalse)
10462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10463 " Setting up deflate compression");
10465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10466 " Compression buffer size: 32768");
10469 png_set_compression_buffer_size(ping,32768L);
10471 if (logging != MagickFalse)
10472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10473 " Compression mem level: 9");
10475 png_set_compression_mem_level(ping, 9);
10477 /* Untangle the "-quality" setting:
10479 Undefined is 0; the default is used.
10484 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10485 zlib default compression level
10487 1-9: the zlib compression level
10491 0-4: the PNG filter method
10493 5: libpng adaptive filtering if compression level > 5
10494 libpng filter type "none" if compression level <= 5
10495 or if image is grayscale or palette
10497 6: libpng adaptive filtering
10499 7: "LOCO" filtering (intrapixel differing) if writing
10500 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10501 and earlier because of a missing "else".
10503 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10504 filtering. Unused prior to IM-6.7.0-10, was same as 6
10506 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10507 Unused prior to IM-6.7.0-10, was same as 6
10509 Note that using the -quality option, not all combinations of
10510 PNG filter type, zlib compression level, and zlib compression
10511 strategy are possible. This will be addressed soon in a
10512 release that accomodates "-define png:compression-strategy", etc.
10516 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10517 image_info->quality;
10521 if (mng_info->write_png_compression_strategy == 0)
10522 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10525 else if (mng_info->write_png_compression_level == 0)
10530 level=(int) MagickMin((ssize_t) quality/10,9);
10532 mng_info->write_png_compression_level = level+1;
10535 if (mng_info->write_png_compression_strategy == 0)
10537 if ((quality %10) == 8 || (quality %10) == 9)
10538 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10539 mng_info->write_png_compression_strategy=Z_RLE+1;
10541 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10545 if (mng_info->write_png_compression_filter == 0)
10546 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10548 if (logging != MagickFalse)
10550 if (mng_info->write_png_compression_level)
10551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10552 " Compression level: %d",
10553 (int) mng_info->write_png_compression_level-1);
10555 if (mng_info->write_png_compression_strategy)
10556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10557 " Compression strategy: %d",
10558 (int) mng_info->write_png_compression_strategy-1);
10560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10561 " Setting up filtering");
10563 if (mng_info->write_png_compression_filter == 6)
10564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10565 " Base filter method: ADAPTIVE");
10566 else if (mng_info->write_png_compression_filter == 0 ||
10567 mng_info->write_png_compression_filter == 1)
10568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569 " Base filter method: NONE");
10571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10572 " Base filter method: %d",
10573 (int) mng_info->write_png_compression_filter-1);
10576 if (mng_info->write_png_compression_level != 0)
10577 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10579 if (mng_info->write_png_compression_filter == 6)
10581 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10582 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10584 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10586 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10588 else if (mng_info->write_png_compression_filter == 7 ||
10589 mng_info->write_png_compression_filter == 10)
10590 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10592 else if (mng_info->write_png_compression_filter == 8)
10594 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10595 if (mng_info->write_mng)
10597 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10598 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10599 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10602 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10605 else if (mng_info->write_png_compression_filter == 9)
10606 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10608 else if (mng_info->write_png_compression_filter != 0)
10609 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10610 mng_info->write_png_compression_filter-1);
10612 if (mng_info->write_png_compression_strategy != 0)
10613 png_set_compression_strategy(ping,
10614 mng_info->write_png_compression_strategy-1);
10616 ping_interlace_method=image_info->interlace != NoInterlace;
10618 if (mng_info->write_mng)
10619 png_set_sig_bytes(ping,8);
10621 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10623 if (mng_info->write_png_colortype != 0)
10625 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10626 if (ping_have_color != MagickFalse)
10628 ping_color_type = PNG_COLOR_TYPE_RGB;
10630 if (ping_bit_depth < 8)
10634 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10635 if (ping_have_color != MagickFalse)
10636 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10639 if (ping_need_colortype_warning != MagickFalse ||
10640 ((mng_info->write_png_depth &&
10641 (int) mng_info->write_png_depth != ping_bit_depth) ||
10642 (mng_info->write_png_colortype &&
10643 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10644 mng_info->write_png_colortype != 7 &&
10645 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10647 if (logging != MagickFalse)
10649 if (ping_need_colortype_warning != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " Image has transparency but tRNS chunk was excluded");
10655 if (mng_info->write_png_depth)
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " Defined png:bit-depth=%u, Computed depth=%u",
10659 mng_info->write_png_depth,
10663 if (mng_info->write_png_colortype)
10665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10666 " Defined png:color-type=%u, Computed color type=%u",
10667 mng_info->write_png_colortype-1,
10673 "Cannot write image with defined png:bit-depth or png:color-type.");
10676 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10678 /* Add an opaque matte channel */
10679 image->alpha_trait = BlendPixelTrait;
10680 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10682 if (logging != MagickFalse)
10683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684 " Added an opaque matte channel");
10687 if (number_transparent != 0 || number_semitransparent != 0)
10689 if (ping_color_type < 4)
10691 ping_have_tRNS=MagickTrue;
10692 if (logging != MagickFalse)
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " Setting ping_have_tRNS=MagickTrue.");
10698 if (logging != MagickFalse)
10699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700 " Writing PNG header chunks");
10702 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10703 ping_bit_depth,ping_color_type,
10704 ping_interlace_method,ping_compression_method,
10705 ping_filter_method);
10707 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10709 png_set_PLTE(ping,ping_info,palette,number_colors);
10711 if (logging != MagickFalse)
10713 for (i=0; i< (ssize_t) number_colors; i++)
10715 if (i < ping_num_trans)
10716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10717 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10719 (int) palette[i].red,
10720 (int) palette[i].green,
10721 (int) palette[i].blue,
10723 (int) ping_trans_alpha[i]);
10725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10726 " PLTE[%d] = (%d,%d,%d)",
10728 (int) palette[i].red,
10729 (int) palette[i].green,
10730 (int) palette[i].blue);
10735 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10736 if (ping_exclude_sRGB != MagickFalse ||
10737 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10739 if ((ping_exclude_tEXt == MagickFalse ||
10740 ping_exclude_zTXt == MagickFalse) &&
10741 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10743 ResetImageProfileIterator(image);
10744 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10746 profile=GetImageProfile(image,name);
10748 if (profile != (StringInfo *) NULL)
10750 #ifdef PNG_WRITE_iCCP_SUPPORTED
10751 if ((LocaleCompare(name,"ICC") == 0) ||
10752 (LocaleCompare(name,"ICM") == 0))
10754 ping_have_iCCP = MagickTrue;
10755 if (ping_exclude_iCCP == MagickFalse)
10757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10758 " Setting up iCCP chunk");
10760 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10761 #if (PNG_LIBPNG_VER < 10500)
10762 (png_charp) GetStringInfoDatum(profile),
10764 (const png_byte *) GetStringInfoDatum(profile),
10766 (png_uint_32) GetStringInfoLength(profile));
10770 /* Do not write hex-encoded ICC chunk */
10771 name=GetNextImageProfile(image);
10775 #endif /* WRITE_iCCP */
10777 if (LocaleCompare(name,"exif") == 0)
10779 /* Do not write hex-encoded ICC chunk; we will
10780 write it later as an eXIf chunk */
10781 name=GetNextImageProfile(image);
10785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10786 " Setting up zTXt chunk with uuencoded %s profile",
10788 Magick_png_write_raw_profile(image_info,ping,ping_info,
10789 (unsigned char *) name,(unsigned char *) name,
10790 GetStringInfoDatum(profile),
10791 (png_uint_32) GetStringInfoLength(profile));
10793 name=GetNextImageProfile(image);
10798 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10799 if ((mng_info->have_write_global_srgb == 0) &&
10800 ping_have_iCCP != MagickTrue &&
10801 (ping_have_sRGB != MagickFalse ||
10802 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10804 if (ping_exclude_sRGB == MagickFalse)
10807 Note image rendering intent.
10809 if (logging != MagickFalse)
10810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10811 " Setting up sRGB chunk");
10813 (void) png_set_sRGB(ping,ping_info,(
10814 Magick_RenderingIntent_to_PNG_RenderingIntent(
10815 image->rendering_intent)));
10817 ping_have_sRGB = MagickTrue;
10821 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10824 if (ping_exclude_gAMA == MagickFalse &&
10825 ping_have_iCCP == MagickFalse &&
10826 ping_have_sRGB == MagickFalse &&
10827 (ping_exclude_sRGB == MagickFalse ||
10828 (image->gamma < .45 || image->gamma > .46)))
10830 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10834 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10836 if (logging != MagickFalse)
10837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10838 " Setting up gAMA chunk");
10840 png_set_gAMA(ping,ping_info,image->gamma);
10844 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10846 if ((mng_info->have_write_global_chrm == 0) &&
10847 (image->chromaticity.red_primary.x != 0.0))
10850 Note image chromaticity.
10851 Note: if cHRM+gAMA == sRGB write sRGB instead.
10859 wp=image->chromaticity.white_point;
10860 rp=image->chromaticity.red_primary;
10861 gp=image->chromaticity.green_primary;
10862 bp=image->chromaticity.blue_primary;
10864 if (logging != MagickFalse)
10865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10866 " Setting up cHRM chunk");
10868 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10874 if (ping_exclude_bKGD == MagickFalse)
10876 if (ping_have_bKGD != MagickFalse)
10878 png_set_bKGD(ping,ping_info,&ping_background);
10879 if (logging != MagickFalse)
10881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10882 " Setting up bKGD chunk");
10883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884 " background color = (%d,%d,%d)",
10885 (int) ping_background.red,
10886 (int) ping_background.green,
10887 (int) ping_background.blue);
10888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10889 " index = %d, gray=%d",
10890 (int) ping_background.index,
10891 (int) ping_background.gray);
10896 if (ping_exclude_pHYs == MagickFalse)
10898 if (ping_have_pHYs != MagickFalse)
10900 png_set_pHYs(ping,ping_info,
10901 ping_pHYs_x_resolution,
10902 ping_pHYs_y_resolution,
10903 ping_pHYs_unit_type);
10905 if (logging != MagickFalse)
10907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10908 " Setting up pHYs chunk");
10909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10910 " x_resolution=%lu",
10911 (unsigned long) ping_pHYs_x_resolution);
10912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913 " y_resolution=%lu",
10914 (unsigned long) ping_pHYs_y_resolution);
10915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10917 (unsigned long) ping_pHYs_unit_type);
10922 #if defined(PNG_tIME_SUPPORTED)
10923 if (ping_exclude_tIME == MagickFalse)
10928 if (image->taint == MagickFalse)
10930 timestamp=GetImageOption(image_info,"png:tIME");
10932 if (timestamp == (const char *) NULL)
10933 timestamp=GetImageProperty(image,"png:tIME",exception);
10938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939 " Reset tIME in tainted image");
10941 timestamp=GetImageProperty(image,"date:modify",exception);
10944 if (timestamp != (const char *) NULL)
10945 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10949 if (mng_info->need_blob != MagickFalse)
10951 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10953 png_error(ping,"WriteBlob Failed");
10955 ping_have_blob=MagickTrue;
10958 png_write_info_before_PLTE(ping, ping_info);
10960 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10962 if (logging != MagickFalse)
10964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10965 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10968 if (ping_color_type == 3)
10969 (void) png_set_tRNS(ping, ping_info,
10976 (void) png_set_tRNS(ping, ping_info,
10979 &ping_trans_color);
10981 if (logging != MagickFalse)
10983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10984 " tRNS color =(%d,%d,%d)",
10985 (int) ping_trans_color.red,
10986 (int) ping_trans_color.green,
10987 (int) ping_trans_color.blue);
10992 png_write_info(ping,ping_info);
10994 ping_wrote_caNv = MagickFalse;
10996 /* write caNv chunk */
10997 if (ping_exclude_caNv == MagickFalse)
10999 if ((image->page.width != 0 && image->page.width != image->columns) ||
11000 (image->page.height != 0 && image->page.height != image->rows) ||
11001 image->page.x != 0 || image->page.y != 0)
11006 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11007 PNGType(chunk,mng_caNv);
11008 LogPNGChunk(logging,mng_caNv,16L);
11009 PNGLong(chunk+4,(png_uint_32) image->page.width);
11010 PNGLong(chunk+8,(png_uint_32) image->page.height);
11011 PNGsLong(chunk+12,(png_int_32) image->page.x);
11012 PNGsLong(chunk+16,(png_int_32) image->page.y);
11013 (void) WriteBlob(image,20,chunk);
11014 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11015 ping_wrote_caNv = MagickTrue;
11019 #if defined(PNG_oFFs_SUPPORTED)
11020 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11022 if (image->page.x || image->page.y)
11024 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11025 (png_int_32) image->page.y, 0);
11027 if (logging != MagickFalse)
11028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11029 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11030 (int) image->page.x, (int) image->page.y);
11035 /* write vpAg chunk (deprecated, replaced by caNv) */
11036 if (ping_exclude_vpAg == MagickFalse && ping_wrote_caNv == MagickFalse)
11038 if ((image->page.width != 0 && image->page.width != image->columns) ||
11039 (image->page.height != 0 && image->page.height != image->rows))
11044 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11045 PNGType(chunk,mng_vpAg);
11046 LogPNGChunk(logging,mng_vpAg,9L);
11047 PNGLong(chunk+4,(png_uint_32) image->page.width);
11048 PNGLong(chunk+8,(png_uint_32) image->page.height);
11049 chunk[12]=0; /* unit = pixels */
11050 (void) WriteBlob(image,13,chunk);
11051 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11055 #if (PNG_LIBPNG_VER == 10206)
11056 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11057 #define PNG_HAVE_IDAT 0x04
11058 ping->mode |= PNG_HAVE_IDAT;
11059 #undef PNG_HAVE_IDAT
11062 png_set_packing(ping);
11066 rowbytes=image->columns;
11067 if (image_depth > 8)
11069 switch (ping_color_type)
11071 case PNG_COLOR_TYPE_RGB:
11075 case PNG_COLOR_TYPE_GRAY_ALPHA:
11079 case PNG_COLOR_TYPE_RGBA:
11087 if (logging != MagickFalse)
11089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11090 " Writing PNG image data");
11092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11093 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11095 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11096 if (pixel_info == (MemoryInfo *) NULL)
11097 png_error(ping,"Allocation of memory for pixels failed");
11098 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11101 Initialize image scanlines.
11103 quantum_info=AcquireQuantumInfo(image_info,image);
11104 if (quantum_info == (QuantumInfo *) NULL)
11105 png_error(ping,"Memory allocation for quantum_info failed");
11106 quantum_info->format=UndefinedQuantumFormat;
11107 SetQuantumDepth(image,quantum_info,image_depth);
11108 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11109 num_passes=png_set_interlace_handling(ping);
11111 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11112 !mng_info->write_png48 && !mng_info->write_png64 &&
11113 !mng_info->write_png32) &&
11114 (mng_info->IsPalette ||
11115 (image_info->type == BilevelType)) &&
11116 image_matte == MagickFalse &&
11117 ping_have_non_bw == MagickFalse)
11119 /* Palette, Bilevel, or Opaque Monochrome */
11120 register const Quantum
11123 SetQuantumDepth(image,quantum_info,8);
11124 for (pass=0; pass < num_passes; pass++)
11127 Convert PseudoClass image to a PNG monochrome image.
11129 for (y=0; y < (ssize_t) image->rows; y++)
11131 if (logging != MagickFalse && y == 0)
11132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11133 " Writing row of pixels (0)");
11135 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11137 if (p == (const Quantum *) NULL)
11140 if (mng_info->IsPalette)
11142 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11143 quantum_info,GrayQuantum,ping_pixels,exception);
11144 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11145 mng_info->write_png_depth &&
11146 mng_info->write_png_depth != old_bit_depth)
11148 /* Undo pixel scaling */
11149 for (i=0; i < (ssize_t) image->columns; i++)
11150 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11151 >> (8-old_bit_depth));
11157 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11158 quantum_info,RedQuantum,ping_pixels,exception);
11161 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11162 for (i=0; i < (ssize_t) image->columns; i++)
11163 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11166 if (logging != MagickFalse && y == 0)
11167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11168 " Writing row of pixels (1)");
11170 png_write_row(ping,ping_pixels);
11172 status=SetImageProgress(image,SaveImageTag,
11173 (MagickOffsetType) (pass * image->rows + y),
11174 num_passes * image->rows);
11176 if (status == MagickFalse)
11182 else /* Not Palette, Bilevel, or Opaque Monochrome */
11184 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11185 !mng_info->write_png48 && !mng_info->write_png64 &&
11186 !mng_info->write_png32) && (image_matte != MagickFalse ||
11187 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11188 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11190 register const Quantum
11193 for (pass=0; pass < num_passes; pass++)
11196 for (y=0; y < (ssize_t) image->rows; y++)
11198 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11200 if (p == (const Quantum *) NULL)
11203 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11205 if (mng_info->IsPalette)
11206 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11207 quantum_info,GrayQuantum,ping_pixels,exception);
11210 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11211 quantum_info,RedQuantum,ping_pixels,exception);
11213 if (logging != MagickFalse && y == 0)
11214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11215 " Writing GRAY PNG pixels (2)");
11218 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11220 if (logging != MagickFalse && y == 0)
11221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11222 " Writing GRAY_ALPHA PNG pixels (2)");
11224 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11225 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11228 if (logging != MagickFalse && y == 0)
11229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11230 " Writing row of pixels (2)");
11232 png_write_row(ping,ping_pixels);
11234 status=SetImageProgress(image,SaveImageTag,
11235 (MagickOffsetType) (pass * image->rows + y),
11236 num_passes * image->rows);
11238 if (status == MagickFalse)
11246 register const Quantum
11249 for (pass=0; pass < num_passes; pass++)
11251 if ((image_depth > 8) ||
11252 mng_info->write_png24 ||
11253 mng_info->write_png32 ||
11254 mng_info->write_png48 ||
11255 mng_info->write_png64 ||
11256 (!mng_info->write_png8 && !mng_info->IsPalette))
11258 for (y=0; y < (ssize_t) image->rows; y++)
11260 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11262 if (p == (const Quantum *) NULL)
11265 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11267 if (image->storage_class == DirectClass)
11268 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11269 quantum_info,RedQuantum,ping_pixels,exception);
11272 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11273 quantum_info,GrayQuantum,ping_pixels,exception);
11276 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11278 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11279 quantum_info,GrayAlphaQuantum,ping_pixels,
11282 if (logging != MagickFalse && y == 0)
11283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11284 " Writing GRAY_ALPHA PNG pixels (3)");
11287 else if (image_matte != MagickFalse)
11288 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11289 quantum_info,RGBAQuantum,ping_pixels,exception);
11292 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11293 quantum_info,RGBQuantum,ping_pixels,exception);
11295 if (logging != MagickFalse && y == 0)
11296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11297 " Writing row of pixels (3)");
11299 png_write_row(ping,ping_pixels);
11301 status=SetImageProgress(image,SaveImageTag,
11302 (MagickOffsetType) (pass * image->rows + y),
11303 num_passes * image->rows);
11305 if (status == MagickFalse)
11311 /* not ((image_depth > 8) ||
11312 mng_info->write_png24 || mng_info->write_png32 ||
11313 mng_info->write_png48 || mng_info->write_png64 ||
11314 (!mng_info->write_png8 && !mng_info->IsPalette))
11317 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11318 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11320 if (logging != MagickFalse)
11321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11322 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11324 SetQuantumDepth(image,quantum_info,8);
11328 for (y=0; y < (ssize_t) image->rows; y++)
11330 if (logging != MagickFalse && y == 0)
11331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11332 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11335 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11337 if (p == (const Quantum *) NULL)
11340 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11342 SetQuantumDepth(image,quantum_info,image->depth);
11344 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11345 quantum_info,GrayQuantum,ping_pixels,exception);
11348 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11350 if (logging != MagickFalse && y == 0)
11351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11352 " Writing GRAY_ALPHA PNG pixels (4)");
11354 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11355 quantum_info,GrayAlphaQuantum,ping_pixels,
11361 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11362 quantum_info,IndexQuantum,ping_pixels,exception);
11364 if (logging != MagickFalse && y <= 2)
11366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11367 " Writing row of non-gray pixels (4)");
11369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11371 (int)ping_pixels[0],(int)ping_pixels[1]);
11374 png_write_row(ping,ping_pixels);
11376 status=SetImageProgress(image,SaveImageTag,
11377 (MagickOffsetType) (pass * image->rows + y),
11378 num_passes * image->rows);
11380 if (status == MagickFalse)
11388 if (quantum_info != (QuantumInfo *) NULL)
11389 quantum_info=DestroyQuantumInfo(quantum_info);
11391 if (logging != MagickFalse)
11393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394 " Wrote PNG image data");
11396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397 " Width: %.20g",(double) ping_width);
11399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11400 " Height: %.20g",(double) ping_height);
11402 if (mng_info->write_png_depth)
11404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11405 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11409 " PNG bit-depth written: %d",ping_bit_depth);
11411 if (mng_info->write_png_colortype)
11413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11414 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11418 " PNG color-type written: %d",ping_color_type);
11420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11421 " PNG Interlace method: %d",ping_interlace_method);
11424 Generate text chunks after IDAT.
11426 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11428 ResetImagePropertyIterator(image);
11429 property=GetNextImageProperty(image);
11430 while (property != (const char *) NULL)
11435 value=GetImageProperty(image,property,exception);
11437 /* Don't write any "png:" or "jpeg:" properties; those are just for
11438 * "identify" or for passing through to another JPEG
11440 if ((LocaleNCompare(property,"png:",4) != 0 &&
11441 LocaleNCompare(property,"jpeg:",5) != 0) &&
11444 /* Suppress density and units if we wrote a pHYs chunk */
11445 (ping_exclude_pHYs != MagickFalse ||
11446 LocaleCompare(property,"density") != 0 ||
11447 LocaleCompare(property,"units") != 0) &&
11449 /* Suppress the IM-generated Date:create and Date:modify */
11450 (ping_exclude_date == MagickFalse ||
11451 LocaleNCompare(property, "Date:",5) != 0))
11453 if (value != (const char *) NULL)
11456 #if PNG_LIBPNG_VER >= 10400
11457 text=(png_textp) png_malloc(ping,
11458 (png_alloc_size_t) sizeof(png_text));
11460 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11462 text[0].key=(char *) property;
11463 text[0].text=(char *) value;
11464 text[0].text_length=strlen(value);
11466 if (ping_exclude_tEXt != MagickFalse)
11467 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11469 else if (ping_exclude_zTXt != MagickFalse)
11470 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11474 text[0].compression=image_info->compression == NoCompression ||
11475 (image_info->compression == UndefinedCompression &&
11476 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11477 PNG_TEXT_COMPRESSION_zTXt ;
11480 if (logging != MagickFalse)
11482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11483 " Setting up text chunk");
11485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11486 " keyword: '%s'",text[0].key);
11489 png_set_text(ping,ping_info,text,1);
11490 png_free(ping,text);
11493 property=GetNextImageProperty(image);
11497 /* write eXIf profile */
11498 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11503 ResetImageProfileIterator(image);
11505 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
11507 if (LocaleCompare(name,"exif") == 0)
11512 profile=GetImageProfile(image,name);
11514 if (profile != (StringInfo *) NULL)
11526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11527 " Have eXIf profile");
11529 ping_profile=CloneStringInfo(profile);
11530 data=GetStringInfoDatum(ping_profile),
11531 length=(png_uint_32) GetStringInfoLength(ping_profile);
11533 PNGType(chunk,mng_eXIf);
11536 ping_profile=DestroyStringInfo(ping_profile);
11537 break; /* otherwise crashes */
11540 /* skip the "Exif\0\0" JFIF Exif Header ID */
11543 LogPNGChunk(logging,chunk,length);
11544 (void) WriteBlobMSBULong(image,length);
11545 (void) WriteBlob(image,4,chunk);
11546 (void) WriteBlob(image,length,data+6);
11547 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11548 data+6, (uInt) length));
11549 ping_profile=DestroyStringInfo(ping_profile);
11553 name=GetNextImageProfile(image);
11557 if (logging != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " Writing PNG end info");
11561 png_write_end(ping,ping_info);
11563 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11565 if (mng_info->page.x || mng_info->page.y ||
11566 (ping_width != mng_info->page.width) ||
11567 (ping_height != mng_info->page.height))
11573 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11575 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11576 PNGType(chunk,mng_FRAM);
11577 LogPNGChunk(logging,mng_FRAM,27L);
11579 chunk[5]=0; /* frame name separator (no name) */
11580 chunk[6]=1; /* flag for changing delay, for next frame only */
11581 chunk[7]=0; /* flag for changing frame timeout */
11582 chunk[8]=1; /* flag for changing frame clipping for next frame */
11583 chunk[9]=0; /* flag for changing frame sync_id */
11584 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11585 chunk[14]=0; /* clipping boundaries delta type */
11586 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11588 (png_uint_32) (mng_info->page.x + ping_width));
11589 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11591 (png_uint_32) (mng_info->page.y + ping_height));
11592 (void) WriteBlob(image,31,chunk);
11593 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11594 mng_info->old_framing_mode=4;
11595 mng_info->framing_mode=1;
11599 mng_info->framing_mode=3;
11601 if (mng_info->write_mng && !mng_info->need_fram &&
11602 ((int) image->dispose == 3))
11603 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11606 Free PNG resources.
11609 png_destroy_write_struct(&ping,&ping_info);
11611 pixel_info=RelinquishVirtualMemory(pixel_info);
11613 if (ping_have_blob != MagickFalse)
11614 (void) CloseBlob(image);
11616 image_info=DestroyImageInfo(image_info);
11617 image=DestroyImage(image);
11619 /* Store bit depth actually written */
11620 s[0]=(char) ping_bit_depth;
11623 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11625 if (logging != MagickFalse)
11626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11627 " exit WriteOnePNGImage()");
11629 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11630 UnlockSemaphoreInfo(ping_semaphore);
11633 /* } for navigation to beginning of SETJMP-protected block. Revert to
11634 * Throwing an Exception when an error occurs.
11637 return(MagickTrue);
11638 /* End write one PNG image */
11643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11647 % W r i t e P N G I m a g e %
11651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11653 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11654 % Multiple-image Network Graphics (MNG) image file.
11656 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11658 % The format of the WritePNGImage method is:
11660 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11661 % Image *image,ExceptionInfo *exception)
11663 % A description of each parameter follows:
11665 % o image_info: the image info.
11667 % o image: The image.
11669 % o exception: return any errors or warnings in this structure.
11671 % Returns MagickTrue on success, MagickFalse on failure.
11673 % Communicating with the PNG encoder:
11675 % While the datastream written is always in PNG format and normally would
11676 % be given the "png" file extension, this method also writes the following
11677 % pseudo-formats which are subsets of png:
11679 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11680 % a depth greater than 8, the depth is reduced. If transparency
11681 % is present, the tRNS chunk must only have values 0 and 255
11682 % (i.e., transparency is binary: fully opaque or fully
11683 % transparent). If other values are present they will be
11684 % 50%-thresholded to binary transparency. If more than 256
11685 % colors are present, they will be quantized to the 4-4-4-1,
11686 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11687 % of any resulting fully-transparent pixels is changed to
11688 % the image's background color.
11690 % If you want better quantization or dithering of the colors
11691 % or alpha than that, you need to do it before calling the
11692 % PNG encoder. The pixels contain 8-bit indices even if
11693 % they could be represented with 1, 2, or 4 bits. Grayscale
11694 % images will be written as indexed PNG files even though the
11695 % PNG grayscale type might be slightly more efficient. Please
11696 % note that writing to the PNG8 format may result in loss
11697 % of color and alpha data.
11699 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11700 % chunk can be present to convey binary transparency by naming
11701 % one of the colors as transparent. The only loss incurred
11702 % is reduction of sample depth to 8. If the image has more
11703 % than one transparent color, has semitransparent pixels, or
11704 % has an opaque pixel with the same RGB components as the
11705 % transparent color, an image is not written.
11707 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11708 % transparency is permitted, i.e., the alpha sample for
11709 % each pixel can have any value from 0 to 255. The alpha
11710 % channel is present even if the image is fully opaque.
11711 % The only loss in data is the reduction of the sample depth
11714 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11715 % chunk can be present to convey binary transparency by naming
11716 % one of the colors as transparent. If the image has more
11717 % than one transparent color, has semitransparent pixels, or
11718 % has an opaque pixel with the same RGB components as the
11719 % transparent color, an image is not written.
11721 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11722 % transparency is permitted, i.e., the alpha sample for
11723 % each pixel can have any value from 0 to 65535. The alpha
11724 % channel is present even if the image is fully opaque.
11726 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11727 % image, if the input was a PNG, is written. If these values
11728 % cannot be found, or if the pixels have been changed in a way
11729 % that makes this impossible, then "PNG00" falls back to the
11730 % regular "PNG" format.
11732 % o -define: For more precise control of the PNG output, you can use the
11733 % Image options "png:bit-depth" and "png:color-type". These
11734 % can be set from the commandline with "-define" and also
11735 % from the application programming interfaces. The options
11736 % are case-independent and are converted to lowercase before
11737 % being passed to this encoder.
11739 % png:color-type can be 0, 2, 3, 4, or 6.
11741 % When png:color-type is 0 (Grayscale), png:bit-depth can
11742 % be 1, 2, 4, 8, or 16.
11744 % When png:color-type is 2 (RGB), png:bit-depth can
11747 % When png:color-type is 3 (Indexed), png:bit-depth can
11748 % be 1, 2, 4, or 8. This refers to the number of bits
11749 % used to store the index. The color samples always have
11750 % bit-depth 8 in indexed PNG files.
11752 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11753 % png:bit-depth can be 8 or 16.
11755 % If the image cannot be written without loss with the
11756 % requested bit-depth and color-type, a PNG file will not
11757 % be written, a warning will be issued, and the encoder will
11758 % return MagickFalse.
11760 % Since image encoders should not be responsible for the "heavy lifting",
11761 % the user should make sure that ImageMagick has already reduced the
11762 % image depth and number of colors and limit transparency to binary
11763 % transparency prior to attempting to write the image with depth, color,
11764 % or transparency limitations.
11766 % Note that another definition, "png:bit-depth-written" exists, but it
11767 % is not intended for external use. It is only used internally by the
11768 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11770 % As of version 6.6.6 the following optimizations are always done:
11772 % o 32-bit depth is reduced to 16.
11773 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11774 % high byte and low byte are identical.
11775 % o Palette is sorted to remove unused entries and to put a
11776 % transparent color first, if BUILD_PNG_PALETTE is defined.
11777 % o Opaque matte channel is removed when writing an indexed PNG.
11778 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11779 % this can be done without loss and a larger bit depth N was not
11780 % requested via the "-define png:bit-depth=N" option.
11781 % o If matte channel is present but only one transparent color is
11782 % present, RGB+tRNS is written instead of RGBA
11783 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11784 % was requested when converting an opaque image).
11786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11788 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11789 Image *image,ExceptionInfo *exception)
11808 assert(image_info != (const ImageInfo *) NULL);
11809 assert(image_info->signature == MagickCoreSignature);
11810 assert(image != (Image *) NULL);
11811 assert(image->signature == MagickCoreSignature);
11812 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11813 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11815 Allocate a MngInfo structure.
11817 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11819 if (mng_info == (MngInfo *) NULL)
11820 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11823 Initialize members of the MngInfo structure.
11825 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11826 mng_info->image=image;
11827 mng_info->equal_backgrounds=MagickTrue;
11829 /* See if user has requested a specific PNG subformat */
11831 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11832 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11833 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11834 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11835 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11837 value=GetImageOption(image_info,"png:format");
11839 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842 " Format=%s",value);
11844 mng_info->write_png8 = MagickFalse;
11845 mng_info->write_png24 = MagickFalse;
11846 mng_info->write_png32 = MagickFalse;
11847 mng_info->write_png48 = MagickFalse;
11848 mng_info->write_png64 = MagickFalse;
11850 if (LocaleCompare(value,"png8") == 0)
11851 mng_info->write_png8 = MagickTrue;
11853 else if (LocaleCompare(value,"png24") == 0)
11854 mng_info->write_png24 = MagickTrue;
11856 else if (LocaleCompare(value,"png32") == 0)
11857 mng_info->write_png32 = MagickTrue;
11859 else if (LocaleCompare(value,"png48") == 0)
11860 mng_info->write_png48 = MagickTrue;
11862 else if (LocaleCompare(value,"png64") == 0)
11863 mng_info->write_png64 = MagickTrue;
11865 else if ((LocaleCompare(value,"png00") == 0) ||
11866 LocaleCompare(image_info->magick,"PNG00") == 0)
11868 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11869 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11871 if (value != (char *) NULL)
11873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11874 " png00 inherited bit depth=%s",value);
11876 if (LocaleCompare(value,"1") == 0)
11877 mng_info->write_png_depth = 1;
11879 else if (LocaleCompare(value,"2") == 0)
11880 mng_info->write_png_depth = 2;
11882 else if (LocaleCompare(value,"4") == 0)
11883 mng_info->write_png_depth = 4;
11885 else if (LocaleCompare(value,"8") == 0)
11886 mng_info->write_png_depth = 8;
11888 else if (LocaleCompare(value,"16") == 0)
11889 mng_info->write_png_depth = 16;
11892 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11894 if (value != (char *) NULL)
11896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11897 " png00 inherited color type=%s",value);
11899 if (LocaleCompare(value,"0") == 0)
11900 mng_info->write_png_colortype = 1;
11902 else if (LocaleCompare(value,"2") == 0)
11903 mng_info->write_png_colortype = 3;
11905 else if (LocaleCompare(value,"3") == 0)
11906 mng_info->write_png_colortype = 4;
11908 else if (LocaleCompare(value,"4") == 0)
11909 mng_info->write_png_colortype = 5;
11911 else if (LocaleCompare(value,"6") == 0)
11912 mng_info->write_png_colortype = 7;
11917 if (mng_info->write_png8)
11919 mng_info->write_png_colortype = /* 3 */ 4;
11920 mng_info->write_png_depth = 8;
11924 if (mng_info->write_png24)
11926 mng_info->write_png_colortype = /* 2 */ 3;
11927 mng_info->write_png_depth = 8;
11930 if (image->alpha_trait != UndefinedPixelTrait)
11931 (void) SetImageType(image,TrueColorAlphaType,exception);
11934 (void) SetImageType(image,TrueColorType,exception);
11936 (void) SyncImage(image,exception);
11939 if (mng_info->write_png32)
11941 mng_info->write_png_colortype = /* 6 */ 7;
11942 mng_info->write_png_depth = 8;
11944 image->alpha_trait = BlendPixelTrait;
11946 (void) SetImageType(image,TrueColorAlphaType,exception);
11947 (void) SyncImage(image,exception);
11950 if (mng_info->write_png48)
11952 mng_info->write_png_colortype = /* 2 */ 3;
11953 mng_info->write_png_depth = 16;
11956 if (image->alpha_trait != UndefinedPixelTrait)
11957 (void) SetImageType(image,TrueColorAlphaType,exception);
11960 (void) SetImageType(image,TrueColorType,exception);
11962 (void) SyncImage(image,exception);
11965 if (mng_info->write_png64)
11967 mng_info->write_png_colortype = /* 6 */ 7;
11968 mng_info->write_png_depth = 16;
11970 image->alpha_trait = BlendPixelTrait;
11972 (void) SetImageType(image,TrueColorAlphaType,exception);
11973 (void) SyncImage(image,exception);
11976 value=GetImageOption(image_info,"png:bit-depth");
11978 if (value != (char *) NULL)
11980 if (LocaleCompare(value,"1") == 0)
11981 mng_info->write_png_depth = 1;
11983 else if (LocaleCompare(value,"2") == 0)
11984 mng_info->write_png_depth = 2;
11986 else if (LocaleCompare(value,"4") == 0)
11987 mng_info->write_png_depth = 4;
11989 else if (LocaleCompare(value,"8") == 0)
11990 mng_info->write_png_depth = 8;
11992 else if (LocaleCompare(value,"16") == 0)
11993 mng_info->write_png_depth = 16;
11996 (void) ThrowMagickException(exception,
11997 GetMagickModule(),CoderWarning,
11998 "ignoring invalid defined png:bit-depth",
12001 if (logging != MagickFalse)
12002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12003 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12006 value=GetImageOption(image_info,"png:color-type");
12008 if (value != (char *) NULL)
12010 /* We must store colortype+1 because 0 is a valid colortype */
12011 if (LocaleCompare(value,"0") == 0)
12012 mng_info->write_png_colortype = 1;
12014 else if (LocaleCompare(value,"1") == 0)
12015 mng_info->write_png_colortype = 2;
12017 else if (LocaleCompare(value,"2") == 0)
12018 mng_info->write_png_colortype = 3;
12020 else if (LocaleCompare(value,"3") == 0)
12021 mng_info->write_png_colortype = 4;
12023 else if (LocaleCompare(value,"4") == 0)
12024 mng_info->write_png_colortype = 5;
12026 else if (LocaleCompare(value,"6") == 0)
12027 mng_info->write_png_colortype = 7;
12030 (void) ThrowMagickException(exception,
12031 GetMagickModule(),CoderWarning,
12032 "ignoring invalid defined png:color-type",
12035 if (logging != MagickFalse)
12036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12037 " png:color-type=%d was defined.\n",
12038 mng_info->write_png_colortype-1);
12041 /* Check for chunks to be excluded:
12043 * The default is to not exclude any known chunks except for any
12044 * listed in the "unused_chunks" array, above.
12046 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12047 * define (in the image properties or in the image artifacts)
12048 * or via a mng_info member. For convenience, in addition
12049 * to or instead of a comma-separated list of chunks, the
12050 * "exclude-chunk" string can be simply "all" or "none".
12052 * Note that the "-strip" option provides a convenient way of
12053 * doing the equivalent of
12055 * -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12056 * iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12058 * The exclude-chunk define takes priority over the mng_info.
12060 * A "png:include-chunk" define takes priority over both the
12061 * mng_info and the "png:exclude-chunk" define. Like the
12062 * "exclude-chunk" string, it can define "all" or "none" as
12063 * well as a comma-separated list. Chunks that are unknown to
12064 * ImageMagick are always excluded, regardless of their "copy-safe"
12065 * status according to the PNG specification, and even if they
12066 * appear in the "include-chunk" list. Such defines appearing among
12067 * the image options take priority over those found among the image
12070 * Finally, all chunks listed in the "unused_chunks" array are
12071 * automatically excluded, regardless of the other instructions
12074 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12075 * will not be written and the gAMA chunk will only be written if it
12076 * is not between .45 and .46, or approximately (1.0/2.2).
12078 * If you exclude tRNS and the image has transparency, the colortype
12079 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12081 * The -strip option causes StripImage() to set the png:include-chunk
12082 * artifact to "none,trns,gama".
12085 mng_info->ping_exclude_bKGD=MagickFalse;
12086 mng_info->ping_exclude_caNv=MagickFalse;
12087 mng_info->ping_exclude_cHRM=MagickFalse;
12088 mng_info->ping_exclude_date=MagickFalse;
12089 mng_info->ping_exclude_eXIf=MagickFalse;
12090 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12091 mng_info->ping_exclude_gAMA=MagickFalse;
12092 mng_info->ping_exclude_iCCP=MagickFalse;
12093 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12094 mng_info->ping_exclude_oFFs=MagickFalse;
12095 mng_info->ping_exclude_pHYs=MagickFalse;
12096 mng_info->ping_exclude_sRGB=MagickFalse;
12097 mng_info->ping_exclude_tEXt=MagickFalse;
12098 mng_info->ping_exclude_tIME=MagickFalse;
12099 mng_info->ping_exclude_tRNS=MagickFalse;
12100 mng_info->ping_exclude_vpAg=MagickFalse;
12101 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12102 mng_info->ping_exclude_zTXt=MagickFalse;
12104 mng_info->ping_preserve_colormap=MagickFalse;
12106 value=GetImageOption(image_info,"png:preserve-colormap");
12108 value=GetImageArtifact(image,"png:preserve-colormap");
12110 mng_info->ping_preserve_colormap=MagickTrue;
12112 mng_info->ping_preserve_iCCP=MagickFalse;
12114 value=GetImageOption(image_info,"png:preserve-iCCP");
12116 value=GetImageArtifact(image,"png:preserve-iCCP");
12118 mng_info->ping_preserve_iCCP=MagickTrue;
12120 /* These compression-level, compression-strategy, and compression-filter
12121 * defines take precedence over values from the -quality option.
12123 value=GetImageOption(image_info,"png:compression-level");
12125 value=GetImageArtifact(image,"png:compression-level");
12128 /* We have to add 1 to everything because 0 is a valid input,
12129 * and we want to use 0 (the default) to mean undefined.
12131 if (LocaleCompare(value,"0") == 0)
12132 mng_info->write_png_compression_level = 1;
12134 else if (LocaleCompare(value,"1") == 0)
12135 mng_info->write_png_compression_level = 2;
12137 else if (LocaleCompare(value,"2") == 0)
12138 mng_info->write_png_compression_level = 3;
12140 else if (LocaleCompare(value,"3") == 0)
12141 mng_info->write_png_compression_level = 4;
12143 else if (LocaleCompare(value,"4") == 0)
12144 mng_info->write_png_compression_level = 5;
12146 else if (LocaleCompare(value,"5") == 0)
12147 mng_info->write_png_compression_level = 6;
12149 else if (LocaleCompare(value,"6") == 0)
12150 mng_info->write_png_compression_level = 7;
12152 else if (LocaleCompare(value,"7") == 0)
12153 mng_info->write_png_compression_level = 8;
12155 else if (LocaleCompare(value,"8") == 0)
12156 mng_info->write_png_compression_level = 9;
12158 else if (LocaleCompare(value,"9") == 0)
12159 mng_info->write_png_compression_level = 10;
12162 (void) ThrowMagickException(exception,
12163 GetMagickModule(),CoderWarning,
12164 "ignoring invalid defined png:compression-level",
12168 value=GetImageOption(image_info,"png:compression-strategy");
12170 value=GetImageArtifact(image,"png:compression-strategy");
12173 if (LocaleCompare(value,"0") == 0)
12174 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12176 else if (LocaleCompare(value,"1") == 0)
12177 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12179 else if (LocaleCompare(value,"2") == 0)
12180 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12182 else if (LocaleCompare(value,"3") == 0)
12183 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12184 mng_info->write_png_compression_strategy = Z_RLE+1;
12186 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12189 else if (LocaleCompare(value,"4") == 0)
12190 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12191 mng_info->write_png_compression_strategy = Z_FIXED+1;
12193 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12197 (void) ThrowMagickException(exception,
12198 GetMagickModule(),CoderWarning,
12199 "ignoring invalid defined png:compression-strategy",
12203 value=GetImageOption(image_info,"png:compression-filter");
12205 value=GetImageArtifact(image,"png:compression-filter");
12208 /* To do: combinations of filters allowed by libpng
12209 * masks 0x08 through 0xf8
12211 * Implement this as a comma-separated list of 0,1,2,3,4,5
12212 * where 5 is a special case meaning PNG_ALL_FILTERS.
12215 if (LocaleCompare(value,"0") == 0)
12216 mng_info->write_png_compression_filter = 1;
12218 else if (LocaleCompare(value,"1") == 0)
12219 mng_info->write_png_compression_filter = 2;
12221 else if (LocaleCompare(value,"2") == 0)
12222 mng_info->write_png_compression_filter = 3;
12224 else if (LocaleCompare(value,"3") == 0)
12225 mng_info->write_png_compression_filter = 4;
12227 else if (LocaleCompare(value,"4") == 0)
12228 mng_info->write_png_compression_filter = 5;
12230 else if (LocaleCompare(value,"5") == 0)
12231 mng_info->write_png_compression_filter = 6;
12234 (void) ThrowMagickException(exception,
12235 GetMagickModule(),CoderWarning,
12236 "ignoring invalid defined png:compression-filter",
12240 for (source=0; source<8; source++)
12245 value=GetImageOption(image_info,"png:exclude-chunks");
12248 value=GetImageArtifact(image,"png:exclude-chunks");
12251 value=GetImageOption(image_info,"png:exclude-chunk");
12254 value=GetImageArtifact(image,"png:exclude-chunk");
12257 value=GetImageOption(image_info,"png:include-chunks");
12260 value=GetImageArtifact(image,"png:include-chunks");
12263 value=GetImageOption(image_info,"png:include-chunk");
12266 value=GetImageArtifact(image,"png:include-chunk");
12272 excluding = MagickTrue;
12274 excluding = MagickFalse;
12276 if (logging != MagickFalse)
12278 if (source == 0 || source == 2)
12279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12280 " png:exclude-chunk=%s found in image options.\n", value);
12281 else if (source == 1 || source == 3)
12282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12283 " png:exclude-chunk=%s found in image artifacts.\n", value);
12284 else if (source == 4 || source == 6)
12285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12286 " png:include-chunk=%s found in image options.\n", value);
12287 else /* if (source == 5 || source == 7) */
12288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12289 " png:include-chunk=%s found in image artifacts.\n", value);
12292 if (IsOptionMember("all",value) != MagickFalse)
12294 mng_info->ping_exclude_bKGD=excluding;
12295 mng_info->ping_exclude_caNv=excluding;
12296 mng_info->ping_exclude_cHRM=excluding;
12297 mng_info->ping_exclude_date=excluding;
12298 mng_info->ping_exclude_EXIF=excluding;
12299 mng_info->ping_exclude_eXIf=excluding;
12300 mng_info->ping_exclude_gAMA=excluding;
12301 mng_info->ping_exclude_iCCP=excluding;
12302 /* mng_info->ping_exclude_iTXt=excluding; */
12303 mng_info->ping_exclude_oFFs=excluding;
12304 mng_info->ping_exclude_pHYs=excluding;
12305 mng_info->ping_exclude_sRGB=excluding;
12306 mng_info->ping_exclude_tEXt=excluding;
12307 mng_info->ping_exclude_tIME=excluding;
12308 mng_info->ping_exclude_tRNS=excluding;
12309 mng_info->ping_exclude_vpAg=excluding;
12310 mng_info->ping_exclude_zCCP=excluding;
12311 mng_info->ping_exclude_zTXt=excluding;
12314 if (IsOptionMember("none",value) != MagickFalse)
12316 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12318 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12320 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12322 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12324 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12326 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12328 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12330 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12332 /* mng_info->ping_exclude_iTXt=!excluding; */
12333 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12335 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12337 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12339 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12341 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12343 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12345 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12347 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12349 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12353 if (IsOptionMember("bkgd",value) != MagickFalse)
12354 mng_info->ping_exclude_bKGD=excluding;
12356 if (IsOptionMember("caNv",value) != MagickFalse)
12357 mng_info->ping_exclude_caNv=excluding;
12359 if (IsOptionMember("chrm",value) != MagickFalse)
12360 mng_info->ping_exclude_cHRM=excluding;
12362 if (IsOptionMember("date",value) != MagickFalse)
12363 mng_info->ping_exclude_date=excluding;
12365 if (IsOptionMember("exif",value) != MagickFalse)
12367 mng_info->ping_exclude_EXIF=excluding;
12368 mng_info->ping_exclude_eXIf=excluding;
12371 if (IsOptionMember("gama",value) != MagickFalse)
12372 mng_info->ping_exclude_gAMA=excluding;
12374 if (IsOptionMember("iccp",value) != MagickFalse)
12375 mng_info->ping_exclude_iCCP=excluding;
12378 if (IsOptionMember("itxt",value) != MagickFalse)
12379 mng_info->ping_exclude_iTXt=excluding;
12382 if (IsOptionMember("offs",value) != MagickFalse)
12383 mng_info->ping_exclude_oFFs=excluding;
12385 if (IsOptionMember("phys",value) != MagickFalse)
12386 mng_info->ping_exclude_pHYs=excluding;
12388 if (IsOptionMember("srgb",value) != MagickFalse)
12389 mng_info->ping_exclude_sRGB=excluding;
12391 if (IsOptionMember("text",value) != MagickFalse)
12392 mng_info->ping_exclude_tEXt=excluding;
12394 if (IsOptionMember("time",value) != MagickFalse)
12395 mng_info->ping_exclude_tIME=excluding;
12397 if (IsOptionMember("trns",value) != MagickFalse)
12398 mng_info->ping_exclude_tRNS=excluding;
12400 if (IsOptionMember("vpag",value) != MagickFalse)
12401 mng_info->ping_exclude_vpAg=excluding;
12403 if (IsOptionMember("zccp",value) != MagickFalse)
12404 mng_info->ping_exclude_zCCP=excluding;
12406 if (IsOptionMember("ztxt",value) != MagickFalse)
12407 mng_info->ping_exclude_zTXt=excluding;
12410 if (logging != MagickFalse)
12412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12413 " Chunks to be excluded from the output png:");
12414 if (mng_info->ping_exclude_bKGD != MagickFalse)
12415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12417 if (mng_info->ping_exclude_caNv != MagickFalse)
12418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420 if (mng_info->ping_exclude_cHRM != MagickFalse)
12421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12423 if (mng_info->ping_exclude_date != MagickFalse)
12424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12426 if (mng_info->ping_exclude_EXIF != MagickFalse)
12427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12429 if (mng_info->ping_exclude_eXIf != MagickFalse)
12430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12432 if (mng_info->ping_exclude_gAMA != MagickFalse)
12433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12435 if (mng_info->ping_exclude_iCCP != MagickFalse)
12436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12439 if (mng_info->ping_exclude_iTXt != MagickFalse)
12440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12444 if (mng_info->ping_exclude_oFFs != MagickFalse)
12445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12447 if (mng_info->ping_exclude_pHYs != MagickFalse)
12448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12450 if (mng_info->ping_exclude_sRGB != MagickFalse)
12451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12453 if (mng_info->ping_exclude_tEXt != MagickFalse)
12454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12456 if (mng_info->ping_exclude_tIME != MagickFalse)
12457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12459 if (mng_info->ping_exclude_tRNS != MagickFalse)
12460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12462 if (mng_info->ping_exclude_vpAg != MagickFalse)
12463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12465 if (mng_info->ping_exclude_zCCP != MagickFalse)
12466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12468 if (mng_info->ping_exclude_zTXt != MagickFalse)
12469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12473 mng_info->need_blob = MagickTrue;
12475 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12477 mng_info=MngInfoFreeStruct(mng_info);
12479 if (logging != MagickFalse)
12480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12485 #if defined(JNG_SUPPORTED)
12487 /* Write one JNG image */
12488 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12489 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12510 jng_alpha_compression_method,
12511 jng_alpha_sample_depth,
12519 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12520 " Enter WriteOneJNGImage()");
12522 blob=(unsigned char *) NULL;
12523 jpeg_image=(Image *) NULL;
12524 jpeg_image_info=(ImageInfo *) NULL;
12528 transparent=image_info->type==GrayscaleAlphaType ||
12529 image_info->type==TrueColorAlphaType ||
12530 image->alpha_trait != UndefinedPixelTrait;
12532 jng_alpha_sample_depth = 0;
12534 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12536 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12538 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12539 image_info->quality;
12541 if (jng_alpha_quality >= 1000)
12542 jng_alpha_quality /= 1000;
12546 if (transparent != 0)
12550 /* Create JPEG blob, image, and image_info */
12551 if (logging != MagickFalse)
12552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12553 " Creating jpeg_image_info for alpha.");
12555 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12557 if (jpeg_image_info == (ImageInfo *) NULL)
12558 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12560 if (logging != MagickFalse)
12561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562 " Creating jpeg_image.");
12564 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12565 if (jpeg_image == (Image *) NULL)
12566 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12567 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12568 jpeg_image->alpha_trait=UndefinedPixelTrait;
12569 jpeg_image->quality=jng_alpha_quality;
12570 jpeg_image_info->type=GrayscaleType;
12571 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12572 (void) AcquireUniqueFilename(jpeg_image->filename);
12573 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12574 "%s",jpeg_image->filename);
12578 jng_alpha_compression_method=0;
12580 jng_alpha_sample_depth=0;
12583 /* To do: check bit depth of PNG alpha channel */
12585 /* Check if image is grayscale. */
12586 if (image_info->type != TrueColorAlphaType && image_info->type !=
12587 TrueColorType && SetImageGray(image,exception))
12590 if (logging != MagickFalse)
12592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12593 " JNG Quality = %d",(int) jng_quality);
12594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12595 " JNG Color Type = %d",jng_color_type);
12596 if (transparent != 0)
12598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12599 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12601 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12603 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12607 if (transparent != 0)
12609 if (jng_alpha_compression_method==0)
12614 /* Encode alpha as a grayscale PNG blob */
12615 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12617 if (status == MagickFalse)
12618 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12620 if (logging != MagickFalse)
12621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12622 " Creating PNG blob.");
12624 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12626 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12627 jpeg_image_info->interlace=NoInterlace;
12629 /* Exclude all ancillary chunks */
12630 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12632 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12633 &length,exception);
12635 /* Retrieve sample depth used */
12636 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12637 if (value != (char *) NULL)
12638 jng_alpha_sample_depth= (unsigned int) value[0];
12642 /* Encode alpha as a grayscale JPEG blob */
12644 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12646 if (status == MagickFalse)
12647 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12649 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12651 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12652 jpeg_image_info->interlace=NoInterlace;
12653 if (logging != MagickFalse)
12654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12655 " Creating blob.");
12656 blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12657 jpeg_image,&length,
12659 jng_alpha_sample_depth=8;
12661 if (logging != MagickFalse)
12662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12663 " Successfully read jpeg_image into a blob, length=%.20g.",
12667 /* Destroy JPEG image and image_info */
12668 jpeg_image=DestroyImage(jpeg_image);
12669 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12670 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12673 /* Write JHDR chunk */
12674 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12675 PNGType(chunk,mng_JHDR);
12676 LogPNGChunk(logging,mng_JHDR,16L);
12677 PNGLong(chunk+4,(png_uint_32) image->columns);
12678 PNGLong(chunk+8,(png_uint_32) image->rows);
12679 chunk[12]=jng_color_type;
12680 chunk[13]=8; /* sample depth */
12681 chunk[14]=8; /*jng_image_compression_method */
12682 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12683 chunk[16]=jng_alpha_sample_depth;
12684 chunk[17]=jng_alpha_compression_method;
12685 chunk[18]=0; /*jng_alpha_filter_method */
12686 chunk[19]=0; /*jng_alpha_interlace_method */
12687 (void) WriteBlob(image,20,chunk);
12688 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12689 if (logging != MagickFalse)
12691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692 " JNG width:%15lu",(unsigned long) image->columns);
12694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12695 " JNG height:%14lu",(unsigned long) image->rows);
12697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698 " JNG color type:%10d",jng_color_type);
12700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12701 " JNG sample depth:%8d",8);
12703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704 " JNG compression:%9d",8);
12706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707 " JNG interlace:%11d",0);
12709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12710 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12713 " JNG alpha compression:%3d",jng_alpha_compression_method);
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 " JNG alpha filter:%8d",0);
12718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719 " JNG alpha interlace:%5d",0);
12723 Write leading ancillary chunks
12726 if (transparent != 0)
12729 Write JNG bKGD chunk
12740 if (jng_color_type == 8 || jng_color_type == 12)
12744 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12745 PNGType(chunk,mng_bKGD);
12746 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12747 red=ScaleQuantumToChar(image->background_color.red);
12748 green=ScaleQuantumToChar(image->background_color.green);
12749 blue=ScaleQuantumToChar(image->background_color.blue);
12756 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12757 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12760 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12763 Write JNG sRGB chunk
12765 (void) WriteBlobMSBULong(image,1L);
12766 PNGType(chunk,mng_sRGB);
12767 LogPNGChunk(logging,mng_sRGB,1L);
12769 if (image->rendering_intent != UndefinedIntent)
12770 chunk[4]=(unsigned char)
12771 Magick_RenderingIntent_to_PNG_RenderingIntent(
12772 (image->rendering_intent));
12775 chunk[4]=(unsigned char)
12776 Magick_RenderingIntent_to_PNG_RenderingIntent(
12777 (PerceptualIntent));
12779 (void) WriteBlob(image,5,chunk);
12780 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12784 if (image->gamma != 0.0)
12787 Write JNG gAMA chunk
12789 (void) WriteBlobMSBULong(image,4L);
12790 PNGType(chunk,mng_gAMA);
12791 LogPNGChunk(logging,mng_gAMA,4L);
12792 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12793 (void) WriteBlob(image,8,chunk);
12794 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12797 if ((mng_info->equal_chrms == MagickFalse) &&
12798 (image->chromaticity.red_primary.x != 0.0))
12804 Write JNG cHRM chunk
12806 (void) WriteBlobMSBULong(image,32L);
12807 PNGType(chunk,mng_cHRM);
12808 LogPNGChunk(logging,mng_cHRM,32L);
12809 primary=image->chromaticity.white_point;
12810 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12811 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12812 primary=image->chromaticity.red_primary;
12813 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12814 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12815 primary=image->chromaticity.green_primary;
12816 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12817 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12818 primary=image->chromaticity.blue_primary;
12819 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12820 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12821 (void) WriteBlob(image,36,chunk);
12822 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12826 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12829 Write JNG pHYs chunk
12831 (void) WriteBlobMSBULong(image,9L);
12832 PNGType(chunk,mng_pHYs);
12833 LogPNGChunk(logging,mng_pHYs,9L);
12834 if (image->units == PixelsPerInchResolution)
12836 PNGLong(chunk+4,(png_uint_32)
12837 (image->resolution.x*100.0/2.54+0.5));
12839 PNGLong(chunk+8,(png_uint_32)
12840 (image->resolution.y*100.0/2.54+0.5));
12847 if (image->units == PixelsPerCentimeterResolution)
12849 PNGLong(chunk+4,(png_uint_32)
12850 (image->resolution.x*100.0+0.5));
12852 PNGLong(chunk+8,(png_uint_32)
12853 (image->resolution.y*100.0+0.5));
12860 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12861 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12865 (void) WriteBlob(image,13,chunk);
12866 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12869 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12872 Write JNG oFFs chunk
12874 (void) WriteBlobMSBULong(image,9L);
12875 PNGType(chunk,mng_oFFs);
12876 LogPNGChunk(logging,mng_oFFs,9L);
12877 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12878 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12880 (void) WriteBlob(image,13,chunk);
12881 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12883 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12885 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12886 PNGType(chunk,mng_vpAg);
12887 LogPNGChunk(logging,mng_vpAg,9L);
12888 PNGLong(chunk+4,(png_uint_32) image->page.width);
12889 PNGLong(chunk+8,(png_uint_32) image->page.height);
12890 chunk[12]=0; /* unit = pixels */
12891 (void) WriteBlob(image,13,chunk);
12892 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12895 if (transparent != 0)
12897 if (jng_alpha_compression_method==0)
12905 /* Write IDAT chunk header */
12906 if (logging != MagickFalse)
12907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12908 " Write IDAT chunks from blob, length=%.20g.",(double)
12911 /* Copy IDAT chunks */
12914 for (i=8; i<(ssize_t) length; i+=len+12)
12916 len=(((unsigned int) *(p ) & 0xff) << 24) +
12917 (((unsigned int) *(p + 1) & 0xff) << 16) +
12918 (((unsigned int) *(p + 2) & 0xff) << 8) +
12919 (((unsigned int) *(p + 3) & 0xff) ) ;
12922 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12924 /* Found an IDAT chunk. */
12925 (void) WriteBlobMSBULong(image,len);
12926 LogPNGChunk(logging,mng_IDAT,len);
12927 (void) WriteBlob(image,len+4,p);
12928 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12933 if (logging != MagickFalse)
12934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12935 " Skipping %c%c%c%c chunk, length=%.20g.",
12936 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12941 else if (length != 0)
12943 /* Write JDAA chunk header */
12944 if (logging != MagickFalse)
12945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12946 " Write JDAA chunk, length=%.20g.",(double) length);
12947 (void) WriteBlobMSBULong(image,(size_t) length);
12948 PNGType(chunk,mng_JDAA);
12949 LogPNGChunk(logging,mng_JDAA,length);
12950 /* Write JDAT chunk(s) data */
12951 (void) WriteBlob(image,4,chunk);
12952 (void) WriteBlob(image,length,blob);
12953 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12956 blob=(unsigned char *) RelinquishMagickMemory(blob);
12959 /* Encode image as a JPEG blob */
12960 if (logging != MagickFalse)
12961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12962 " Creating jpeg_image_info.");
12963 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12964 if (jpeg_image_info == (ImageInfo *) NULL)
12965 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12967 if (logging != MagickFalse)
12968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969 " Creating jpeg_image.");
12971 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12972 if (jpeg_image == (Image *) NULL)
12973 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12974 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12976 (void) AcquireUniqueFilename(jpeg_image->filename);
12977 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
12978 jpeg_image->filename);
12980 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12983 if (logging != MagickFalse)
12984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12985 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12986 (double) jpeg_image->rows);
12988 if (status == MagickFalse)
12989 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12991 if (jng_color_type == 8 || jng_color_type == 12)
12992 jpeg_image_info->type=GrayscaleType;
12994 jpeg_image_info->quality=jng_quality;
12995 jpeg_image->quality=jng_quality;
12996 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12997 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12999 if (logging != MagickFalse)
13000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13001 " Creating blob.");
13003 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13006 if (logging != MagickFalse)
13008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13009 " Successfully read jpeg_image into a blob, length=%.20g.",
13012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13013 " Write JDAT chunk, length=%.20g.",(double) length);
13016 /* Write JDAT chunk(s) */
13017 (void) WriteBlobMSBULong(image,(size_t) length);
13018 PNGType(chunk,mng_JDAT);
13019 LogPNGChunk(logging,mng_JDAT,length);
13020 (void) WriteBlob(image,4,chunk);
13021 (void) WriteBlob(image,length,blob);
13022 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13024 jpeg_image=DestroyImage(jpeg_image);
13025 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13026 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13027 blob=(unsigned char *) RelinquishMagickMemory(blob);
13029 /* Write IEND chunk */
13030 (void) WriteBlobMSBULong(image,0L);
13031 PNGType(chunk,mng_IEND);
13032 LogPNGChunk(logging,mng_IEND,0);
13033 (void) WriteBlob(image,4,chunk);
13034 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13036 if (logging != MagickFalse)
13037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13038 " exit WriteOneJNGImage()");
13044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13048 % W r i t e J N G I m a g e %
13052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13054 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13056 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13058 % The format of the WriteJNGImage method is:
13060 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13061 % Image *image,ExceptionInfo *exception)
13063 % A description of each parameter follows:
13065 % o image_info: the image info.
13067 % o image: The image.
13069 % o exception: return any errors or warnings in this structure.
13071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13073 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13074 Image *image, ExceptionInfo *exception)
13086 assert(image_info != (const ImageInfo *) NULL);
13087 assert(image_info->signature == MagickCoreSignature);
13088 assert(image != (Image *) NULL);
13089 assert(image->signature == MagickCoreSignature);
13090 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13091 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13092 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13093 if (status == MagickFalse)
13095 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13096 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13099 Allocate a MngInfo structure.
13101 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13102 if (mng_info == (MngInfo *) NULL)
13103 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13105 Initialize members of the MngInfo structure.
13107 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13108 mng_info->image=image;
13110 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13112 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13113 mng_info=MngInfoFreeStruct(mng_info);
13114 (void) CloseBlob(image);
13116 (void) CatchImageException(image);
13117 if (logging != MagickFalse)
13118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13123 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13124 Image *image, ExceptionInfo *exception)
13132 volatile MagickBooleanType
13144 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13145 defined(PNG_MNG_FEATURES_SUPPORTED)
13148 all_images_are_gray,
13158 volatile unsigned int
13169 #if (PNG_LIBPNG_VER < 10200)
13170 if (image_info->verbose)
13171 printf("Your PNG library (libpng-%s) is rather old.\n",
13172 PNG_LIBPNG_VER_STRING);
13178 assert(image_info != (const ImageInfo *) NULL);
13179 assert(image_info->signature == MagickCoreSignature);
13180 assert(image != (Image *) NULL);
13181 assert(image->signature == MagickCoreSignature);
13182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13183 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13184 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13185 if (status == MagickFalse)
13189 Allocate a MngInfo structure.
13191 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13192 if (mng_info == (MngInfo *) NULL)
13193 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13195 Initialize members of the MngInfo structure.
13197 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13198 mng_info->image=image;
13199 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13202 * See if user has requested a specific PNG subformat to be used
13203 * for all of the PNGs in the MNG being written, e.g.,
13205 * convert *.png png8:animation.mng
13207 * To do: check -define png:bit_depth and png:color_type as well,
13208 * or perhaps use mng:bit_depth and mng:color_type instead for
13212 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13213 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13214 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13216 write_jng=MagickFalse;
13217 if (image_info->compression == JPEGCompression)
13218 write_jng=MagickTrue;
13220 mng_info->adjoin=image_info->adjoin &&
13221 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13223 if (logging != MagickFalse)
13225 /* Log some info about the input */
13229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13230 " Checking input image(s)\n"
13231 " Image_info depth: %.20g, Type: %d",
13232 (double) image_info->depth, image_info->type);
13235 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13239 " Scene: %.20g\n, Image depth: %.20g",
13240 (double) scene++, (double) p->depth);
13242 if (p->alpha_trait != UndefinedPixelTrait)
13243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13250 if (p->storage_class == PseudoClass)
13251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13252 " Storage class: PseudoClass");
13255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13256 " Storage class: DirectClass");
13259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13260 " Number of colors: %.20g",(double) p->colors);
13263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13264 " Number of colors: unspecified");
13266 if (mng_info->adjoin == MagickFalse)
13271 use_global_plte=MagickFalse;
13272 all_images_are_gray=MagickFalse;
13273 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13274 need_local_plte=MagickTrue;
13276 need_defi=MagickFalse;
13277 need_matte=MagickFalse;
13278 mng_info->framing_mode=1;
13279 mng_info->old_framing_mode=1;
13282 if (image_info->page != (char *) NULL)
13285 Determine image bounding box.
13287 SetGeometry(image,&mng_info->page);
13288 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13289 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13304 mng_info->page=image->page;
13305 need_geom=MagickTrue;
13306 if (mng_info->page.width || mng_info->page.height)
13307 need_geom=MagickFalse;
13309 Check all the scenes.
13311 initial_delay=image->delay;
13312 need_iterations=MagickFalse;
13313 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13314 mng_info->equal_physs=MagickTrue,
13315 mng_info->equal_gammas=MagickTrue;
13316 mng_info->equal_srgbs=MagickTrue;
13317 mng_info->equal_backgrounds=MagickTrue;
13319 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13320 defined(PNG_MNG_FEATURES_SUPPORTED)
13321 all_images_are_gray=MagickTrue;
13322 mng_info->equal_palettes=MagickFalse;
13323 need_local_plte=MagickFalse;
13325 for (next_image=image; next_image != (Image *) NULL; )
13329 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13330 mng_info->page.width=next_image->columns+next_image->page.x;
13332 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13333 mng_info->page.height=next_image->rows+next_image->page.y;
13336 if (next_image->page.x || next_image->page.y)
13337 need_defi=MagickTrue;
13339 if (next_image->alpha_trait != UndefinedPixelTrait)
13340 need_matte=MagickTrue;
13342 if ((int) next_image->dispose >= BackgroundDispose)
13343 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13344 next_image->page.x || next_image->page.y ||
13345 ((next_image->columns < mng_info->page.width) &&
13346 (next_image->rows < mng_info->page.height)))
13347 mng_info->need_fram=MagickTrue;
13349 if (next_image->iterations)
13350 need_iterations=MagickTrue;
13352 final_delay=next_image->delay;
13354 if (final_delay != initial_delay || final_delay > 1UL*
13355 next_image->ticks_per_second)
13356 mng_info->need_fram=1;
13358 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13359 defined(PNG_MNG_FEATURES_SUPPORTED)
13361 check for global palette possibility.
13363 if (image->alpha_trait != UndefinedPixelTrait)
13364 need_local_plte=MagickTrue;
13366 if (need_local_plte == 0)
13368 if (SetImageGray(image,exception) == MagickFalse)
13369 all_images_are_gray=MagickFalse;
13370 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13371 if (use_global_plte == 0)
13372 use_global_plte=mng_info->equal_palettes;
13373 need_local_plte=!mng_info->equal_palettes;
13376 if (GetNextImageInList(next_image) != (Image *) NULL)
13378 if (next_image->background_color.red !=
13379 next_image->next->background_color.red ||
13380 next_image->background_color.green !=
13381 next_image->next->background_color.green ||
13382 next_image->background_color.blue !=
13383 next_image->next->background_color.blue)
13384 mng_info->equal_backgrounds=MagickFalse;
13386 if (next_image->gamma != next_image->next->gamma)
13387 mng_info->equal_gammas=MagickFalse;
13389 if (next_image->rendering_intent !=
13390 next_image->next->rendering_intent)
13391 mng_info->equal_srgbs=MagickFalse;
13393 if ((next_image->units != next_image->next->units) ||
13394 (next_image->resolution.x != next_image->next->resolution.x) ||
13395 (next_image->resolution.y != next_image->next->resolution.y))
13396 mng_info->equal_physs=MagickFalse;
13398 if (mng_info->equal_chrms)
13400 if (next_image->chromaticity.red_primary.x !=
13401 next_image->next->chromaticity.red_primary.x ||
13402 next_image->chromaticity.red_primary.y !=
13403 next_image->next->chromaticity.red_primary.y ||
13404 next_image->chromaticity.green_primary.x !=
13405 next_image->next->chromaticity.green_primary.x ||
13406 next_image->chromaticity.green_primary.y !=
13407 next_image->next->chromaticity.green_primary.y ||
13408 next_image->chromaticity.blue_primary.x !=
13409 next_image->next->chromaticity.blue_primary.x ||
13410 next_image->chromaticity.blue_primary.y !=
13411 next_image->next->chromaticity.blue_primary.y ||
13412 next_image->chromaticity.white_point.x !=
13413 next_image->next->chromaticity.white_point.x ||
13414 next_image->chromaticity.white_point.y !=
13415 next_image->next->chromaticity.white_point.y)
13416 mng_info->equal_chrms=MagickFalse;
13420 next_image=GetNextImageInList(next_image);
13422 if (image_count < 2)
13424 mng_info->equal_backgrounds=MagickFalse;
13425 mng_info->equal_chrms=MagickFalse;
13426 mng_info->equal_gammas=MagickFalse;
13427 mng_info->equal_srgbs=MagickFalse;
13428 mng_info->equal_physs=MagickFalse;
13429 use_global_plte=MagickFalse;
13430 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13431 need_local_plte=MagickTrue;
13433 need_iterations=MagickFalse;
13436 if (mng_info->need_fram == MagickFalse)
13439 Only certain framing rates 100/n are exactly representable without
13440 the FRAM chunk but we'll allow some slop in VLC files
13442 if (final_delay == 0)
13444 if (need_iterations != MagickFalse)
13447 It's probably a GIF with loop; don't run it *too* fast.
13449 if (mng_info->adjoin)
13452 (void) ThrowMagickException(exception,GetMagickModule(),
13454 "input has zero delay between all frames; assuming",
13459 mng_info->ticks_per_second=0;
13461 if (final_delay != 0)
13462 mng_info->ticks_per_second=(png_uint_32)
13463 (image->ticks_per_second/final_delay);
13464 if (final_delay > 50)
13465 mng_info->ticks_per_second=2;
13467 if (final_delay > 75)
13468 mng_info->ticks_per_second=1;
13470 if (final_delay > 125)
13471 mng_info->need_fram=MagickTrue;
13473 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13474 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13475 (final_delay != 25) && (final_delay != 50) &&
13476 (final_delay != (size_t) image->ticks_per_second))
13477 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13480 if (mng_info->need_fram != MagickFalse)
13481 mng_info->ticks_per_second=image->ticks_per_second;
13483 If pseudocolor, we should also check to see if all the
13484 palettes are identical and write a global PLTE if they are.
13488 Write the MNG version 1.0 signature and MHDR chunk.
13490 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13491 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13492 PNGType(chunk,mng_MHDR);
13493 LogPNGChunk(logging,mng_MHDR,28L);
13494 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13495 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13496 PNGLong(chunk+12,mng_info->ticks_per_second);
13497 PNGLong(chunk+16,0L); /* layer count=unknown */
13498 PNGLong(chunk+20,0L); /* frame count=unknown */
13499 PNGLong(chunk+24,0L); /* play time=unknown */
13504 if (need_defi || mng_info->need_fram || use_global_plte)
13505 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13508 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13513 if (need_defi || mng_info->need_fram || use_global_plte)
13514 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13517 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13525 if (need_defi || mng_info->need_fram || use_global_plte)
13526 PNGLong(chunk+28,11L); /* simplicity=LC */
13529 PNGLong(chunk+28,9L); /* simplicity=VLC */
13534 if (need_defi || mng_info->need_fram || use_global_plte)
13535 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13538 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13541 (void) WriteBlob(image,32,chunk);
13542 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13543 option=GetImageOption(image_info,"mng:need-cacheoff");
13544 if (option != (const char *) NULL)
13549 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13551 PNGType(chunk,mng_nEED);
13552 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13553 (void) WriteBlobMSBULong(image,(size_t) length);
13554 LogPNGChunk(logging,mng_nEED,(size_t) length);
13556 (void) WriteBlob(image,length,chunk);
13557 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13559 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13560 (GetNextImageInList(image) != (Image *) NULL) &&
13561 (image->iterations != 1))
13564 Write MNG TERM chunk
13566 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13567 PNGType(chunk,mng_TERM);
13568 LogPNGChunk(logging,mng_TERM,10L);
13569 chunk[4]=3; /* repeat animation */
13570 chunk[5]=0; /* show last frame when done */
13571 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13572 final_delay/MagickMax(image->ticks_per_second,1)));
13574 if (image->iterations == 0)
13575 PNGLong(chunk+10,PNG_UINT_31_MAX);
13578 PNGLong(chunk+10,(png_uint_32) image->iterations);
13580 if (logging != MagickFalse)
13582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13583 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13584 final_delay/MagickMax(image->ticks_per_second,1)));
13586 if (image->iterations == 0)
13587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13588 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13592 " Image iterations: %.20g",(double) image->iterations);
13594 (void) WriteBlob(image,14,chunk);
13595 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13598 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13600 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13601 mng_info->equal_srgbs)
13604 Write MNG sRGB chunk
13606 (void) WriteBlobMSBULong(image,1L);
13607 PNGType(chunk,mng_sRGB);
13608 LogPNGChunk(logging,mng_sRGB,1L);
13610 if (image->rendering_intent != UndefinedIntent)
13611 chunk[4]=(unsigned char)
13612 Magick_RenderingIntent_to_PNG_RenderingIntent(
13613 (image->rendering_intent));
13616 chunk[4]=(unsigned char)
13617 Magick_RenderingIntent_to_PNG_RenderingIntent(
13618 (PerceptualIntent));
13620 (void) WriteBlob(image,5,chunk);
13621 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13622 mng_info->have_write_global_srgb=MagickTrue;
13627 if (image->gamma && mng_info->equal_gammas)
13630 Write MNG gAMA chunk
13632 (void) WriteBlobMSBULong(image,4L);
13633 PNGType(chunk,mng_gAMA);
13634 LogPNGChunk(logging,mng_gAMA,4L);
13635 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13636 (void) WriteBlob(image,8,chunk);
13637 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13638 mng_info->have_write_global_gama=MagickTrue;
13640 if (mng_info->equal_chrms)
13646 Write MNG cHRM chunk
13648 (void) WriteBlobMSBULong(image,32L);
13649 PNGType(chunk,mng_cHRM);
13650 LogPNGChunk(logging,mng_cHRM,32L);
13651 primary=image->chromaticity.white_point;
13652 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13653 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13654 primary=image->chromaticity.red_primary;
13655 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13656 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13657 primary=image->chromaticity.green_primary;
13658 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13659 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13660 primary=image->chromaticity.blue_primary;
13661 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13662 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13663 (void) WriteBlob(image,36,chunk);
13664 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13665 mng_info->have_write_global_chrm=MagickTrue;
13668 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13671 Write MNG pHYs chunk
13673 (void) WriteBlobMSBULong(image,9L);
13674 PNGType(chunk,mng_pHYs);
13675 LogPNGChunk(logging,mng_pHYs,9L);
13677 if (image->units == PixelsPerInchResolution)
13679 PNGLong(chunk+4,(png_uint_32)
13680 (image->resolution.x*100.0/2.54+0.5));
13682 PNGLong(chunk+8,(png_uint_32)
13683 (image->resolution.y*100.0/2.54+0.5));
13690 if (image->units == PixelsPerCentimeterResolution)
13692 PNGLong(chunk+4,(png_uint_32)
13693 (image->resolution.x*100.0+0.5));
13695 PNGLong(chunk+8,(png_uint_32)
13696 (image->resolution.y*100.0+0.5));
13703 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13704 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13708 (void) WriteBlob(image,13,chunk);
13709 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13712 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13713 or does not cover the entire frame.
13715 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13716 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13717 (image->page.width+image->page.x < mng_info->page.width))
13718 || (image->page.height && (image->page.height+image->page.y
13719 < mng_info->page.height))))
13721 (void) WriteBlobMSBULong(image,6L);
13722 PNGType(chunk,mng_BACK);
13723 LogPNGChunk(logging,mng_BACK,6L);
13724 red=ScaleQuantumToShort(image->background_color.red);
13725 green=ScaleQuantumToShort(image->background_color.green);
13726 blue=ScaleQuantumToShort(image->background_color.blue);
13727 PNGShort(chunk+4,red);
13728 PNGShort(chunk+6,green);
13729 PNGShort(chunk+8,blue);
13730 (void) WriteBlob(image,10,chunk);
13731 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13732 if (mng_info->equal_backgrounds)
13734 (void) WriteBlobMSBULong(image,6L);
13735 PNGType(chunk,mng_bKGD);
13736 LogPNGChunk(logging,mng_bKGD,6L);
13737 (void) WriteBlob(image,10,chunk);
13738 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13742 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13743 if ((need_local_plte == MagickFalse) &&
13744 (image->storage_class == PseudoClass) &&
13745 (all_images_are_gray == MagickFalse))
13751 Write MNG PLTE chunk
13753 data_length=3*image->colors;
13754 (void) WriteBlobMSBULong(image,data_length);
13755 PNGType(chunk,mng_PLTE);
13756 LogPNGChunk(logging,mng_PLTE,data_length);
13758 for (i=0; i < (ssize_t) image->colors; i++)
13760 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13761 image->colormap[i].red) & 0xff);
13762 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13763 image->colormap[i].green) & 0xff);
13764 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13765 image->colormap[i].blue) & 0xff);
13768 (void) WriteBlob(image,data_length+4,chunk);
13769 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13770 mng_info->have_write_global_plte=MagickTrue;
13776 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13777 defined(PNG_MNG_FEATURES_SUPPORTED)
13778 mng_info->equal_palettes=MagickFalse;
13782 if (mng_info->adjoin)
13784 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13785 defined(PNG_MNG_FEATURES_SUPPORTED)
13787 If we aren't using a global palette for the entire MNG, check to
13788 see if we can use one for two or more consecutive images.
13790 if (need_local_plte && use_global_plte && !all_images_are_gray)
13792 if (mng_info->IsPalette)
13795 When equal_palettes is true, this image has the same palette
13796 as the previous PseudoClass image
13798 mng_info->have_write_global_plte=mng_info->equal_palettes;
13799 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13800 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13803 Write MNG PLTE chunk
13808 data_length=3*image->colors;
13809 (void) WriteBlobMSBULong(image,data_length);
13810 PNGType(chunk,mng_PLTE);
13811 LogPNGChunk(logging,mng_PLTE,data_length);
13813 for (i=0; i < (ssize_t) image->colors; i++)
13815 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13816 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13817 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13820 (void) WriteBlob(image,data_length+4,chunk);
13821 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13822 (uInt) (data_length+4)));
13823 mng_info->have_write_global_plte=MagickTrue;
13827 mng_info->have_write_global_plte=MagickFalse;
13838 previous_x=mng_info->page.x;
13839 previous_y=mng_info->page.y;
13846 mng_info->page=image->page;
13847 if ((mng_info->page.x != previous_x) ||
13848 (mng_info->page.y != previous_y))
13850 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13851 PNGType(chunk,mng_DEFI);
13852 LogPNGChunk(logging,mng_DEFI,12L);
13853 chunk[4]=0; /* object 0 MSB */
13854 chunk[5]=0; /* object 0 LSB */
13855 chunk[6]=0; /* visible */
13856 chunk[7]=0; /* abstract */
13857 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13858 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13859 (void) WriteBlob(image,16,chunk);
13860 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13865 mng_info->write_mng=write_mng;
13867 if ((int) image->dispose >= 3)
13868 mng_info->framing_mode=3;
13870 if (mng_info->need_fram && mng_info->adjoin &&
13871 ((image->delay != mng_info->delay) ||
13872 (mng_info->framing_mode != mng_info->old_framing_mode)))
13874 if (image->delay == mng_info->delay)
13877 Write a MNG FRAM chunk with the new framing mode.
13879 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13880 PNGType(chunk,mng_FRAM);
13881 LogPNGChunk(logging,mng_FRAM,1L);
13882 chunk[4]=(unsigned char) mng_info->framing_mode;
13883 (void) WriteBlob(image,5,chunk);
13884 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13889 Write a MNG FRAM chunk with the delay.
13891 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13892 PNGType(chunk,mng_FRAM);
13893 LogPNGChunk(logging,mng_FRAM,10L);
13894 chunk[4]=(unsigned char) mng_info->framing_mode;
13895 chunk[5]=0; /* frame name separator (no name) */
13896 chunk[6]=2; /* flag for changing default delay */
13897 chunk[7]=0; /* flag for changing frame timeout */
13898 chunk[8]=0; /* flag for changing frame clipping */
13899 chunk[9]=0; /* flag for changing frame sync_id */
13900 PNGLong(chunk+10,(png_uint_32)
13901 ((mng_info->ticks_per_second*
13902 image->delay)/MagickMax(image->ticks_per_second,1)));
13903 (void) WriteBlob(image,14,chunk);
13904 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13905 mng_info->delay=(png_uint_32) image->delay;
13907 mng_info->old_framing_mode=mng_info->framing_mode;
13910 #if defined(JNG_SUPPORTED)
13911 if (image_info->compression == JPEGCompression)
13916 if (logging != MagickFalse)
13917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13918 " Writing JNG object.");
13919 /* To do: specify the desired alpha compression method. */
13920 write_info=CloneImageInfo(image_info);
13921 write_info->compression=UndefinedCompression;
13922 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13923 write_info=DestroyImageInfo(write_info);
13928 if (logging != MagickFalse)
13929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13930 " Writing PNG object.");
13932 mng_info->need_blob = MagickFalse;
13933 mng_info->ping_preserve_colormap = MagickFalse;
13935 /* We don't want any ancillary chunks written */
13936 mng_info->ping_exclude_bKGD=MagickTrue;
13937 mng_info->ping_exclude_caNv=MagickTrue;
13938 mng_info->ping_exclude_cHRM=MagickTrue;
13939 mng_info->ping_exclude_date=MagickTrue;
13940 mng_info->ping_exclude_EXIF=MagickTrue;
13941 mng_info->ping_exclude_gAMA=MagickTrue;
13942 mng_info->ping_exclude_iCCP=MagickTrue;
13943 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13944 mng_info->ping_exclude_oFFs=MagickTrue;
13945 mng_info->ping_exclude_pHYs=MagickTrue;
13946 mng_info->ping_exclude_sRGB=MagickTrue;
13947 mng_info->ping_exclude_tEXt=MagickTrue;
13948 mng_info->ping_exclude_tRNS=MagickTrue;
13949 mng_info->ping_exclude_vpAg=MagickTrue;
13950 mng_info->ping_exclude_zCCP=MagickTrue;
13951 mng_info->ping_exclude_zTXt=MagickTrue;
13953 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13956 if (status == MagickFalse)
13958 mng_info=MngInfoFreeStruct(mng_info);
13959 (void) CloseBlob(image);
13960 return(MagickFalse);
13962 (void) CatchImageException(image);
13963 if (GetNextImageInList(image) == (Image *) NULL)
13965 image=SyncNextImageInList(image);
13966 status=SetImageProgress(image,SaveImagesTag,scene++,
13967 GetImageListLength(image));
13969 if (status == MagickFalse)
13972 } while (mng_info->adjoin);
13976 while (GetPreviousImageInList(image) != (Image *) NULL)
13977 image=GetPreviousImageInList(image);
13979 Write the MEND chunk.
13981 (void) WriteBlobMSBULong(image,0x00000000L);
13982 PNGType(chunk,mng_MEND);
13983 LogPNGChunk(logging,mng_MEND,0L);
13984 (void) WriteBlob(image,4,chunk);
13985 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13988 Relinquish resources.
13990 (void) CloseBlob(image);
13991 mng_info=MngInfoFreeStruct(mng_info);
13993 if (logging != MagickFalse)
13994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13996 return(MagickTrue);
13998 #else /* PNG_LIBPNG_VER > 10011 */
14000 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14004 printf("Your PNG library is too old: You have libpng-%s\n",
14005 PNG_LIBPNG_VER_STRING);
14007 ThrowBinaryException(CoderError,"PNG library is too old",
14008 image_info->filename);
14011 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14014 return(WritePNGImage(image_info,image));
14016 #endif /* PNG_LIBPNG_VER > 10011 */